Merge branch 'ypankratyev/duplicate_extension_in_register_quickfix'
[idea/community.git] / plugins / devkit / src / util / ExtensionLocator.java
1 /*
2  * Copyright 2000-2017 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.openapi.util.text.StringUtil;
20 import com.intellij.psi.PsiClass;
21 import com.intellij.psi.PsiElement;
22 import com.intellij.psi.PsiFile;
23 import com.intellij.psi.SmartPointerManager;
24 import com.intellij.psi.search.GlobalSearchScope;
25 import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
26 import com.intellij.psi.search.PsiSearchHelper;
27 import com.intellij.psi.util.PsiTreeUtil;
28 import com.intellij.psi.xml.XmlTag;
29 import com.intellij.psi.xml.XmlToken;
30 import com.intellij.util.CommonProcessors;
31 import com.intellij.util.Processor;
32 import com.intellij.util.SmartList;
33 import com.intellij.util.xml.DomElement;
34 import com.intellij.util.xml.DomUtil;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.idea.devkit.dom.Extension;
37
38 import java.util.Collections;
39 import java.util.List;
40
41 public class ExtensionLocator {
42   private final PsiClass myPsiClass;
43
44   public ExtensionLocator(PsiClass aClass) {
45     myPsiClass = aClass;
46   }
47
48   @NotNull
49   public List<ExtensionCandidate> findCandidates() {
50     String name = myPsiClass.getQualifiedName();
51     if (name == null) {
52       return Collections.emptyList();
53     }
54
55     List<ExtensionCandidate> result = new SmartList<>();
56     processExtensionDeclarations(myPsiClass, new ReferenceProcessor(name, tag -> {
57       result.add(new ExtensionCandidate(SmartPointerManager.getInstance(tag.getProject()).createSmartPsiElementPointer(tag)));
58       return true; // continue processing
59     }));
60     return result;
61   }
62
63   public static boolean isRegisteredExtension(@NotNull PsiClass psiClass) {
64     String name = psiClass.getQualifiedName();
65     if (name == null) {
66       return false;
67     }
68
69     CommonProcessors.FindFirstProcessor<XmlTag> processor = new CommonProcessors.FindFirstProcessor<>();
70     processExtensionDeclarations(psiClass, new ReferenceProcessor(name, processor));
71     return processor.isFound();
72   }
73
74   private static void processExtensionDeclarations(PsiClass psiClass, PsiNonJavaFileReferenceProcessor referenceProcessor) {
75     String name = psiClass.getQualifiedName();
76     if (name == null) return;
77
78     Project project = psiClass.getProject();
79     GlobalSearchScope scope = PluginRelatedLocatorsUtils.getCandidatesScope(project);
80
81     PsiSearchHelper.SERVICE.getInstance(project).processUsagesInNonJavaFiles(name, referenceProcessor, scope);
82   }
83
84   private static class ReferenceProcessor implements PsiNonJavaFileReferenceProcessor {
85     private final String myExtensionClassName;
86     private final Processor<XmlTag> myExtensionTagHandler;
87
88     private ReferenceProcessor(String name, Processor<XmlTag> extensionTagHandler) {
89       myExtensionClassName = name;
90       myExtensionTagHandler = extensionTagHandler;
91     }
92
93     @Override
94     public boolean process(PsiFile file, int startOffset, int endOffset) {
95       PsiElement element = file.findElementAt(startOffset);
96       String tokenText = element instanceof XmlToken ? element.getText() : null;
97       if (!StringUtil.equals(myExtensionClassName, tokenText)) return true;
98
99       XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class);
100       if (tag == null) return true;
101
102       DomElement dom = DomUtil.getDomElement(tag);
103       if (dom instanceof Extension && ((Extension)dom).getExtensionPoint() != null) {
104         return myExtensionTagHandler.process(tag);
105       }
106       return true;
107     }
108   }
109 }