diff --git a/platform/lang-api/src/com/intellij/patterns/compiler/PatternCompilerFactory.java b/platform/lang-api/src/com/intellij/patterns/compiler/PatternCompilerFactory.java index 7d818bfb3d70..bf92cfc8f679 100644 --- a/platform/lang-api/src/com/intellij/patterns/compiler/PatternCompilerFactory.java +++ b/platform/lang-api/src/com/intellij/patterns/compiler/PatternCompilerFactory.java @@ -16,6 +16,8 @@ package com.intellij.patterns.compiler; import com.intellij.openapi.components.ServiceManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Gregory.Shrago @@ -25,5 +27,19 @@ public abstract class PatternCompilerFactory { return ServiceManager.getService(PatternCompilerFactory.class); } - public abstract PatternCompiler getPatternCompiler(Class[] patternClasses); + /** + * Retrieves pattern classes registered via com.intellij.patterns.patternClass extension. + * @param alias or null + * @return pattern classes + */ + @NotNull + public abstract Class[] getPatternClasses(@Nullable final String alias); + + @NotNull + public abstract PatternCompiler getPatternCompiler(@NotNull Class[] patternClasses); + + @NotNull + public PatternCompiler getPatternCompiler(@Nullable final String alias) { + return getPatternCompiler(getPatternClasses(alias)); + } } diff --git a/platform/lang-impl/src/com/intellij/patterns/compiler/PatternCompilerFactoryImpl.java b/platform/lang-impl/src/com/intellij/patterns/compiler/PatternCompilerFactoryImpl.java index 75e5d46239d3..4ecb53edc00c 100644 --- a/platform/lang-impl/src/com/intellij/patterns/compiler/PatternCompilerFactoryImpl.java +++ b/platform/lang-impl/src/com/intellij/patterns/compiler/PatternCompilerFactoryImpl.java @@ -15,13 +15,51 @@ */ package com.intellij.patterns.compiler; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.FactoryMap; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + /** * @author Gregory.Shrago */ public class PatternCompilerFactoryImpl extends PatternCompilerFactory { + public static final ExtensionPointName PATTERN_CLASS_EP = + new ExtensionPointName("com.intellij.patterns.patternClass"); + + private final Map myClasses = new FactoryMap() { + @Override + protected Class[] create(String key) { + final ArrayList result = new ArrayList(1); + final List typeList = key == null? null : Arrays.asList(key.split(",|\\s")); + for (PatternClassBean bean : PATTERN_CLASS_EP.getExtensions()) { + if (typeList == null || typeList.contains(bean.getAlias())) result.add(bean.getPatternClass()); + } + return result.isEmpty()? ArrayUtil.EMPTY_CLASS_ARRAY : result.toArray(new Class[result.size()]); + } + }; + private final Map myCompilers = new FactoryMap() { + @Override + protected PatternCompiler create(Class[] key) { + return new PatternCompilerImpl(key); + } + }; + + @NotNull @Override - public PatternCompiler getPatternCompiler(Class[] patternClasses) { - return new PatternCompilerImpl(patternClasses); + public Class[] getPatternClasses(String alias) { + return myClasses.get(alias); + } + + @NotNull + @Override + public PatternCompiler getPatternCompiler(@NotNull Class[] patternClasses) { + return myCompilers.get(patternClasses); } } diff --git a/platform/lang-impl/src/com/intellij/psi/PsiReferenceProviderBean.java b/platform/lang-impl/src/com/intellij/psi/PsiReferenceProviderBean.java index 58092e97b38d..70a54020c424 100644 --- a/platform/lang-impl/src/com/intellij/psi/PsiReferenceProviderBean.java +++ b/platform/lang-impl/src/com/intellij/psi/PsiReferenceProviderBean.java @@ -18,31 +18,23 @@ package com.intellij.psi; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.extensions.CustomLoadingExtensionPointBean; +import com.intellij.openapi.extensions.AbstractExtensionPointBean; import com.intellij.patterns.ElementPattern; import com.intellij.patterns.StandardPatterns; -import com.intellij.patterns.compiler.PatternCompiler; import com.intellij.patterns.compiler.PatternCompilerFactory; -import com.intellij.util.xmlb.annotations.AbstractCollection; -import com.intellij.util.xmlb.annotations.Property; -import com.intellij.util.xmlb.annotations.Tag; +import com.intellij.util.xmlb.annotations.*; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; - -public class PsiReferenceProviderBean extends CustomLoadingExtensionPointBean { +public class PsiReferenceProviderBean extends AbstractExtensionPointBean { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.PsiReferenceProviderBean"); @Tag("className") public String className; @Tag("description") public String description; + @Property(surroundWithTag = false) - @AbstractCollection(surroundWithTag = false, elementTag = "patternClass", elementValueAttribute = "") - public List patternClasses = new ArrayList(); - @Property(surroundWithTag = false) - @AbstractCollection(surroundWithTag = false, elementTag = "pattern", elementValueAttribute = "") - public List patterns = new ArrayList(); + @AbstractCollection(surroundWithTag = false) + public Info[] patterns; public String getDescription() { return description; @@ -50,7 +42,7 @@ public class PsiReferenceProviderBean extends CustomLoadingExtensionPointBean { public PsiReferenceProvider instantiate() { try { - return (PsiReferenceProvider)instantiateExtension(className, ApplicationManager.getApplication().getPicoContainer()); + return (PsiReferenceProvider)instantiate(className, ApplicationManager.getApplication().getPicoContainer()); } catch (ClassNotFoundException e) { LOG.error(e); @@ -60,30 +52,28 @@ public class PsiReferenceProviderBean extends CustomLoadingExtensionPointBean { @Nullable public ElementPattern createElementPattern() { - final ArrayList classes = new ArrayList(); - for (String patternClass : patternClasses) { - try { - classes.add(Class.forName(patternClass, true, getLoaderForClass())); - } - catch (ClassNotFoundException e) { - LOG.error(e); + final PatternCompilerFactory factory = PatternCompilerFactory.getFactory(); + if (patterns.length > 1) { + final ElementPattern[] result = new ElementPattern[this.patterns.length]; + for (int i = 0, len = this.patterns.length; i < len; i++) { + result[i] = factory.getPatternCompiler(patterns[i].type).compileElementPattern(patterns[i].text); } + return StandardPatterns.or(result); } - final PatternCompiler compiler = - PatternCompilerFactory.getFactory().getPatternCompiler(classes.toArray(new Class[classes.size()])); - if (patterns.size() > 1) { - final ElementPattern[] patterns = new ElementPattern[this.patterns.size()]; - for (int i = 0, len = this.patterns.size(); i < len; i++) { - patterns[i] = compiler.compileElementPattern(this.patterns.get(i)); - } - return StandardPatterns.or(patterns); - } - else if (!patterns.isEmpty()) { - return compiler.compileElementPattern(patterns.get(0)); + else if (patterns.length == 1) { + return factory.getPatternCompiler(patterns[0].type).compileElementPattern(patterns[0].text); } else { LOG.error("At least one pattern should be specified"); return null; } } + + @Tag("pattern") + public static class Info { + @Attribute("type") + public String type; + @Text + public String text; + } } diff --git a/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java b/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java index 5b7f296a156e..d0a28328b6f2 100644 --- a/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java +++ b/platform/platform-impl/src/com/intellij/patterns/compiler/PatternCompilerImpl.java @@ -39,18 +39,10 @@ public class PatternCompilerImpl implements PatternCompiler { private static final Logger LOG = Logger.getInstance(PatternCompilerImpl.class.getName()); - private final Class[] myPatternClasses; private Set myStaticMethods; public PatternCompilerImpl(final Class[] patternClasses) { - myPatternClasses = patternClasses; - } - - private Set getStaticMethods() { - if (myStaticMethods == null) { - myStaticMethods = getStaticMethods(myPatternClasses); - } - return myStaticMethods; + myStaticMethods = getStaticMethods(patternClasses); } protected void preInvoke(Object target, String methodName, Object[] arguments) { @@ -71,13 +63,12 @@ public class PatternCompilerImpl implements PatternCompiler { @Override public ElementPattern compileElementPattern(final String text) { - final Set staticMethods = getStaticMethods(); return processElementPatternText(text, new Function() { public Object fun(final Frame frame) { try { final Object[] args = frame.params.toArray(); preInvoke(frame.target, frame.methodName, args); - return invokeMethod(frame.target, frame.methodName, args, staticMethods); + return invokeMethod(frame.target, frame.methodName, args, myStaticMethods); } catch (Throwable throwable) { throw new IllegalArgumentException(text, throwable); @@ -338,12 +329,11 @@ public class PatternCompilerImpl implements PatternCompiler { @Override public String dumpContextDeclarations() { - final Set methods = getStaticMethods(); final StringBuilder sb = new StringBuilder(); final THashMap> classes = new THashMap>(); final THashSet missingClasses = new THashSet(); classes.put(Object.class, missingClasses); - for (Method method : methods) { + for (Method method : myStaticMethods) { for (Class type = method.getReturnType(); type != null && ElementPattern.class.isAssignableFrom(type); type = type.getSuperclass()) { final Class enclosingClass = type.getEnclosingClass(); if (enclosingClass != null) { @@ -363,7 +353,7 @@ public class PatternCompilerImpl implements PatternCompiler { if (aClass == Object.class) continue; printClass(aClass, classes, sb); } - for (Method method : methods) { + for (Method method : myStaticMethods) { printMethodDeclaration(method, sb, classes); } for (Class aClass : missingClasses) { diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 1405d1c09505..a57031b4547c 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -259,6 +259,7 @@ + diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 62aa33dd9df7..6d2bb5c4bc8b 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -267,6 +267,8 @@ + + diff --git a/plugins/IntelliLang/xml-support/resources/xmlInjections.xml b/plugins/IntelliLang/xml-support/resources/xmlInjections.xml index 496709f21375..27fec1150745 100644 --- a/plugins/IntelliLang/xml-support/resources/xmlInjections.xml +++ b/plugins/IntelliLang/xml-support/resources/xmlInjections.xml @@ -1,5 +1,10 @@ + + IntelliJ IDEA pattern + + + JAXB attribute node diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 05f396c29432..8b085b2dd8cf 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -250,6 +250,8 @@ + + org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle diff --git a/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java b/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java index 22a30647323b..75e30889351b 100644 --- a/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java +++ b/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java @@ -15,26 +15,44 @@ */ package org.intellij.plugins.intelliLang.inject.groovy; +import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderEx; -import com.intellij.patterns.compiler.PatternCompilerImpl; +import com.intellij.patterns.compiler.PatternClassBean; +import com.intellij.patterns.compiler.PatternCompilerFactory; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.FileContextUtil; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.TextOccurenceProcessor; +import com.intellij.psi.search.UsageSearchContext; +import com.intellij.psi.util.CachedValue; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlText; +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import org.intellij.plugins.intelliLang.inject.InjectorUtils; import org.intellij.plugins.intelliLang.inject.config.BaseInjection; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * @author Gregory.Shrago */ public class PatternEditorContextMembersProvider extends NonCodeMembersContributor { - public static final Key> INJECTION_PARSED_CONTEXT = Key.create("INJECTION_PARSED_CONTEXT"); + public static final Key>> INJECTION_PARSED_CONTEXT = Key.create("INJECTION_PARSED_CONTEXT"); @Override public void processDynamicElements(@NotNull PsiType qualifierType, @@ -43,22 +61,95 @@ public class PatternEditorContextMembersProvider extends NonCodeMembersContribut ResolveState state) { final PsiFile file = place.getContainingFile().getOriginalFile(); final BaseInjection injection = file.getUserData(BaseInjection.INJECTION_KEY); - if (injection == null) return; - final PsiFile contextFile = - ((UserDataHolderEx)file).putUserDataIfAbsent(INJECTION_PARSED_CONTEXT, new AtomicNotNullLazyValue() { - @NotNull - @Override - protected PsiFile compute() { - return parseInjectionContext(injection, file.getProject()); - } - }).getValue(); - for (PsiElement cur = contextFile.getFirstChild(); cur != null; cur = cur.getNextSibling()) { - if (cur instanceof PsiNamedElement && !ResolveUtil.processElement(processor, (PsiNamedElement)cur)) return; + final CachedValueProvider> provider; + if (injection == null) { + provider = createDevProvider(file); + } + else { + provider = createPatternProvider(injection, file); + } + if (provider == null) return; + final List roots = + ((UserDataHolderEx)file).putUserDataIfAbsent(INJECTION_PARSED_CONTEXT, + CachedValuesManager.getManager(file.getProject()).createCachedValue(provider, false)).getValue(); + for (PsiElement root : roots) { + for (PsiElement cur = root.getFirstChild(); cur != null; cur = cur.getNextSibling()) { + if (cur instanceof PsiNamedElement && !ResolveUtil.processElement(processor, (PsiNamedElement)cur)) return; + } } } - private static PsiFile parseInjectionContext(@NotNull BaseInjection injection, Project project) { - final String text = new PatternCompilerImpl(InjectorUtils.findInjectionSupport(injection.getSupportId()).getPatternClasses()).dumpContextDeclarations(); + private static CachedValueProvider> createPatternProvider(final BaseInjection injection, final PsiFile file) { + return new CachedValueProvider>() { + @Override + public Result> compute() { + return new Result>(Collections.singletonList( + getRootByClasses(InjectorUtils.findInjectionSupport(injection.getSupportId()).getPatternClasses(), file.getProject())), + ArrayUtil.EMPTY_OBJECT_ARRAY); + } + }; + } + + private static PsiFile getRootByClasses(Class[] classes, Project project) { + final String text = PatternCompilerFactory.getFactory().getPatternCompiler(classes).dumpContextDeclarations(); return PsiFileFactory.getInstance(project).createFileFromText("context.groovy", GroovyFileType.GROOVY_FILE_TYPE, text); } + + @Nullable + private static CachedValueProvider> createDevProvider(final PsiFile file) { + final SmartPsiElementPointer pointer = file.getUserData(FileContextUtil.INJECTED_IN_ELEMENT); + if (pointer == null) return null; + final PsiElement element = pointer.getElement(); + if (!(element instanceof XmlText)) return null; + final XmlTag tag = ((XmlText)element).getParentTag(); + final XmlTag parentTag = tag == null ? null : tag.getParentTag(); + final String parentTagName = parentTag == null ? null : parentTag.getName(); + final String name = tag == null ? null : tag.getName(); + if ("place".equals(name) && "injection".equals(parentTagName) && parentTag != null) { + return new CachedValueProvider>() { + @Override + public Result> compute() { + return new Result>(getRootsByClassNames(file, parentTag.getAttributeValue("injector-id")), parentTag.getContainingFile()); + } + }; + } + else if ("pattern".equals(name) && parentTag != null) { + return new CachedValueProvider>() { + @Override + public Result> compute() { + return new Result>(getRootsByClassNames(file, tag.getAttributeValue("type")), parentTag.getContainingFile()); + } + }; + } + else return null; + } + + private static List getRootsByClassNames(PsiFile file, String type) { + final ArrayList roots = new ArrayList(1); + + final Project project = file.getProject(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass beanClass = psiFacade.findClass(PatternClassBean.class.getName(), GlobalSearchScope.allScope(project)); + if (beanClass != null) { + final GlobalSearchScope scope = + GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), StdFileTypes.XML); + beanClass.getManager().getSearchHelper().processElementsWithWord(new TextOccurenceProcessor() { + @Override + public boolean execute(PsiElement element, int offsetInElement) { + final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); + final String className = tag == null ? null : tag.getAttributeValue("className"); + if (className != null && tag.getLocalName().endsWith("patternClass")) { + ContainerUtil.addIfNotNull(psiFacade.findClass(className, GlobalSearchScope.allScope(project)), roots); + } + return true; + } + }, scope, "patterns.patternClass", UsageSearchContext.IN_FOREIGN_LANGUAGES, true); + } + final Class[] classes = PatternCompilerFactory.getFactory().getPatternClasses(type); + if (classes.length != 0) { + roots.add(getRootByClasses(classes, project)); + } + return roots; + } + } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 3566109c6851..c31ebac04b20 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -915,6 +915,8 @@ + + diff --git a/xml/dom-impl/src/META-INF/DomPlugin.xml b/xml/dom-impl/src/META-INF/DomPlugin.xml index 4a3d5e0fbe5b..c49db1906d63 100644 --- a/xml/dom-impl/src/META-INF/DomPlugin.xml +++ b/xml/dom-impl/src/META-INF/DomPlugin.xml @@ -63,5 +63,7 @@ + +