Merge branch 'ypankratyev/duplicate_extension_in_register_quickfix'
[idea/community.git] / plugins / devkit / src / util / ExtensionPointLocator.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.idea.devkit.util;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.psi.CommonClassNames;
20 import com.intellij.psi.PsiClass;
21 import com.intellij.psi.PsiElement;
22 import com.intellij.psi.SmartPointerManager;
23 import com.intellij.psi.search.GlobalSearchScope;
24 import com.intellij.psi.search.PsiSearchHelper;
25 import com.intellij.psi.util.PsiTreeUtil;
26 import com.intellij.psi.xml.XmlTag;
27 import com.intellij.util.containers.SmartHashSet;
28 import com.intellij.util.xml.DomElement;
29 import com.intellij.util.xml.DomUtil;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.idea.devkit.dom.ExtensionPoint;
32
33 import java.util.HashSet;
34 import java.util.Set;
35
36 public class ExtensionPointLocator {
37   private final PsiClass myPsiClass;
38
39   public ExtensionPointLocator(PsiClass psiClass) {
40     myPsiClass = psiClass;
41   }
42
43
44   public Set<ExtensionPointCandidate> findDirectCandidates() {
45     Set<ExtensionPointCandidate> candidates = new SmartHashSet<>();
46     findExtensionPointCandidates(myPsiClass, candidates);
47     return candidates;
48   }
49
50   public Set<ExtensionPointCandidate> findSuperCandidates() {
51     Set<ExtensionPointCandidate> candidates = new SmartHashSet<>();
52     findExtensionPointCandidatesInHierarchy(myPsiClass, candidates, new HashSet<>());
53     return candidates;
54   }
55
56   private static void findExtensionPointCandidatesInHierarchy(PsiClass psiClass,
57                                                               Set<ExtensionPointCandidate> candidates,
58                                                               HashSet<PsiClass> processed) {
59     for (PsiClass superClass : psiClass.getSupers()) {
60       if (!processed.add(superClass) || CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) {
61         continue;
62       }
63       findExtensionPointCandidates(superClass, candidates);
64       findExtensionPointCandidatesInHierarchy(superClass, candidates, processed);
65     }
66   }
67
68   private static void findExtensionPointCandidates(PsiClass psiClass, Set<ExtensionPointCandidate> candidates) {
69     String name = psiClass.getQualifiedName();
70     if (name == null) return;
71
72     Project project = psiClass.getProject();
73     GlobalSearchScope scope = PluginRelatedLocatorsUtils.getCandidatesScope(project);
74     PsiSearchHelper.SERVICE.getInstance(project).processUsagesInNonJavaFiles(name, (file, startOffset, endOffset) -> {
75       PsiElement element = file.findElementAt(startOffset);
76       processExtensionPointCandidate(element, candidates);
77       return true;
78     }, scope);
79   }
80
81   private static void processExtensionPointCandidate(PsiElement element, Set<ExtensionPointCandidate> candidates) {
82     XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
83     if (tag == null) return;
84     if ("extensionPoint".equals(tag.getName())) {
85       String epName = getEPName(tag);
86       if (epName != null) {
87         candidates.add(new ExtensionPointCandidate(SmartPointerManager.getInstance(tag.getProject()).createSmartPsiElementPointer(tag), epName));
88       }
89     }
90     else if ("with".equals(tag.getName())) {
91       XmlTag extensionPointTag = tag.getParentTag();
92       if (extensionPointTag == null) return;
93       if (!"extensionPoint".equals(extensionPointTag.getName())) return;
94       String attrName = tag.getAttributeValue("attribute");
95       String tagName = tag.getAttributeValue("tag");
96       String epName = getEPName(extensionPointTag);
97       String beanClassName = extensionPointTag.getAttributeValue("beanClass");
98       if ((attrName == null && tagName == null) || epName == null) return;
99       candidates.add(new ExtensionPointCandidate(SmartPointerManager.getInstance(extensionPointTag.getProject())
100                                              .createSmartPsiElementPointer(extensionPointTag), epName, attrName, tagName, beanClassName));
101     }
102   }
103
104   @Nullable
105   private static String getEPName(XmlTag tag) {
106     final DomElement domElement = DomUtil.getDomElement(tag);
107     if (!(domElement instanceof ExtensionPoint)) return null;
108     return ((ExtensionPoint)domElement).getEffectiveQualifiedName();
109   }
110 }