From 2273ef938da843f4de9f4a5d03232476eca9d655 Mon Sep 17 00:00:00 2001 From: Sergey Evdokimov Date: Fri, 7 Dec 2012 20:29:42 +0400 Subject: [PATCH] Add reference to parameter 'additionalClasspathElements' inside configuration of plugin 'maven-surefire-plugin' --- .../MavenPathReferenceConverter.java | 14 +- .../api/MavenParamReferenceProvider.java | 31 +++ .../plugins/api/MavenPluginDescriptor.java | 61 +++++ .../MavenPluginParamReferenceContributor.java | 255 ++++++++++++++++++ .../MavenCommonParamReferenceProviders.java | 54 ++++ .../src/main/resources/META-INF/plugin.xml | 11 + .../surefire/MavenSurefirePluginTest.groovy | 54 ++++ 7 files changed, 476 insertions(+), 4 deletions(-) create mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenParamReferenceProvider.java create mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginDescriptor.java create mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginParamReferenceContributor.java create mode 100644 plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/common/MavenCommonParamReferenceProviders.java create mode 100644 plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/surefire/MavenSurefirePluginTest.groovy diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java index 86a28d2adf56..56672d738b10 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/references/MavenPathReferenceConverter.java @@ -49,9 +49,9 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { myCondition = condition; } - @NotNull - @Override - public PsiReference[] createReferences(final GenericDomValue genericDomValue, PsiElement element, ConvertContext context) { + public static PsiReference[] createReferences(final DomElement genericDomValue, + PsiElement element, + @NotNull final Condition fileFilter) { ElementManipulator manipulator = ElementManipulators.getManipulator(element); TextRange range = manipulator.getRangeInElement(element); String text = range.substring(element.getText()); @@ -62,7 +62,7 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { @Override protected Condition getReferenceCompletionFilter() { - return myCondition; + return fileFilter; } @Override @@ -120,6 +120,12 @@ public class MavenPathReferenceConverter extends PathReferenceConverter { return set.getAllReferences(); } + @NotNull + @Override + public PsiReference[] createReferences(final GenericDomValue genericDomValue, PsiElement element, ConvertContext context) { + return createReferences(genericDomValue, element, myCondition); + } + @NotNull @Override public PsiReference[] createReferences(@NotNull PsiElement psiElement, boolean soft) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenParamReferenceProvider.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenParamReferenceProvider.java new file mode 100644 index 000000000000..e1585268e7f5 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenParamReferenceProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.plugins.api; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.maven.dom.model.MavenDomConfiguration; + +/** + * @author Sergey Evdokimov + */ +public interface MavenParamReferenceProvider { + + PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull MavenDomConfiguration domCfg, @NotNull ProcessingContext context); + +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginDescriptor.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginDescriptor.java new file mode 100644 index 000000000000..9176bd9f40a1 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginDescriptor.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.plugins.api; + +import com.intellij.openapi.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xml.Required; +import com.intellij.util.xmlb.annotations.AbstractCollection; +import com.intellij.util.xmlb.annotations.Attribute; +import com.intellij.util.xmlb.annotations.Property; +import com.intellij.util.xmlb.annotations.Tag; + +/** + * @author Sergey Evdokimov + */ +public class MavenPluginDescriptor extends AbstractExtensionPointBean { + + public static final ExtensionPointName EP_NAME = new ExtensionPointName("org.jetbrains.idea.maven.pluginDescriptor"); + + @Attribute("mavenId") + @Required + public String mavenId; + + @Property(surroundWithTag = false) + @AbstractCollection(surroundWithTag = false) + public Param[] params; + + /** + * @author Sergey Evdokimov + */ + @Tag("param") + public static class Param { + + @Attribute("name") + @Required + public String name; + + @Attribute("goal") + public String goal; + + /** + * Class name of reference provider. The reference provider must implement MavenParamReferenceProvider or PsiReferenceProvider. + */ + @Attribute("refProvider") + public String refProvider; + + } +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginParamReferenceContributor.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginParamReferenceContributor.java new file mode 100644 index 000000000000..9739c9a993dc --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/MavenPluginParamReferenceContributor.java @@ -0,0 +1,255 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.plugins.api; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Pair; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.patterns.XmlPatterns; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.LeafPsiElement; +import com.intellij.psi.xml.XmlTag; +import com.intellij.psi.xml.XmlText; +import com.intellij.psi.xml.XmlTokenType; +import com.intellij.util.ProcessingContext; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.maven.dom.model.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author Sergey Evdokimov + */ +public class MavenPluginParamReferenceContributor extends PsiReferenceContributor { + + private static final Logger LOG = Logger.getInstance(MavenPluginParamReferenceContributor.class); + + @Override + public void registerReferenceProviders(PsiReferenceRegistrar registrar) { + registrar.registerReferenceProvider( + PlatformPatterns.psiElement(XmlTokenType.XML_DATA_CHARACTERS).withParent( + XmlPatterns.xmlText().inFile(XmlPatterns.xmlFile().withName("pom.xml")) + ), + new MavenPluginParamRefProvider()); + } + + private static class MavenPluginParamRefProvider extends PsiReferenceProvider { + + /** + * This map contains descriptions of all plugins. + */ + private volatile Map myMap; + + public Map getMap() { + Map res = myMap; + + if (res == null) { + res = new HashMap(); + + for (MavenPluginDescriptor pluginDescriptor : MavenPluginDescriptor.EP_NAME.getExtensions()) { + Pair pluginId = parsePluginId(pluginDescriptor.mavenId); + + for (MavenPluginDescriptor.Param param : pluginDescriptor.params) { + String[] paramPath = param.name.split("/"); + + Map pluginsMap = res; + + for (int i = paramPath.length - 1; i >= 0; i--) { + pluginsMap = getOrCreate(pluginsMap, paramPath[i]); + } + + ParamInfo paramInfo = new ParamInfo(pluginDescriptor.getPluginDescriptor().getPluginClassLoader(), param.refProvider); + + Map goalsMap = getOrCreate(pluginsMap, pluginId); + + ParamInfo oldValue = goalsMap.put(param.goal, paramInfo); + if (oldValue != null) { + LOG.error("Duplicated maven plugin parameter descriptor: " + + pluginId.first + ':' + pluginId.second + " -> " + + (param.goal != null ? "[" + param.goal + ']' : "") + param.name); + } + } + } + + myMap = res; + } + + return res; + } + + private static Pair parsePluginId(String mavenId) { + int idx = mavenId.indexOf(':'); + if (idx <= 0 || idx == mavenId.length() - 1 || mavenId.lastIndexOf(':') != idx) { + throw new RuntimeException("Failed to parse mavenId: " + mavenId + " (mavenId should has format 'groupId:artifactId')"); + } + + return new Pair(mavenId.substring(0, idx), mavenId.substring(idx + 1)); + } + + @NotNull + private static V getOrCreate(Map map, K key) { + Map res = (Map)map.get(key); + if (res == null) { + res = new HashMap(); + map.put(key, res); + } + + return (V)res; + } + + @NotNull + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { + XmlText xmlText = (XmlText)element.getParent(); + + PsiElement prevSibling = xmlText.getPrevSibling(); + if (!(prevSibling instanceof LeafPsiElement) || ((LeafPsiElement)prevSibling).getElementType() != XmlTokenType.XML_TAG_END) return PsiReference.EMPTY_ARRAY; + + PsiElement nextSibling = xmlText.getNextSibling(); + if (!(nextSibling instanceof LeafPsiElement) || ((LeafPsiElement)nextSibling).getElementType() != XmlTokenType.XML_END_TAG_START) return PsiReference.EMPTY_ARRAY; + + XmlTag paramTag = xmlText.getParentTag(); + if (paramTag == null) return PsiReference.EMPTY_ARRAY; + + XmlTag configurationTag = paramTag; + DomElement domElement; + + Map m = getMap().get(paramTag.getName()); + + while (true) { + if (m == null) return PsiReference.EMPTY_ARRAY; + + configurationTag = configurationTag.getParentTag(); + if (configurationTag == null) return PsiReference.EMPTY_ARRAY; + + String tagName = configurationTag.getName(); + if ("configuration".equals(tagName)) { + domElement = DomManager.getDomManager(configurationTag.getProject()).getDomElement(configurationTag); + if (domElement instanceof MavenDomConfiguration) { + break; + } + + if (domElement != null) return PsiReference.EMPTY_ARRAY; + } + + m = (Map)m.get(tagName); + } + + Map, Map> pluginsMap = m; + + MavenDomConfiguration domCfg = (MavenDomConfiguration)domElement; + + MavenDomPlugin domPlugin = domCfg.getParentOfType(MavenDomPlugin.class, true); + if (domPlugin == null) return PsiReference.EMPTY_ARRAY; + + String pluginGroupId = domPlugin.getGroupId().getStringValue(); + String pluginArtifactId = domPlugin.getArtifactId().getStringValue(); + + Map goalsMap; + + if (pluginGroupId == null) { + goalsMap = pluginsMap.get(Pair.create("org.apache.maven.plugins", pluginArtifactId)); + if (goalsMap == null) { + goalsMap = pluginsMap.get(Pair.create("org.codehaus.mojo", pluginArtifactId)); + } + } + else { + goalsMap = pluginsMap.get(Pair.create(pluginGroupId, pluginArtifactId)); + } + + if (goalsMap == null) return PsiReference.EMPTY_ARRAY; + + DomElement parent = domCfg.getParent(); + if (parent instanceof MavenDomPluginExecution) { + MavenDomGoals goals = ((MavenDomPluginExecution)parent).getGoals(); + for (MavenDomGoal goal : goals.getGoals()) { + ParamInfo info = goalsMap.get(goal.getStringValue()); + if (info != null) { + MavenParamReferenceProvider providerInstance = info.getProviderInstance(); + if (providerInstance != null) { + return providerInstance.getReferencesByElement(element, domCfg, context); + } + } + } + } + + ParamInfo defaultInfo = goalsMap.get(null); + if (defaultInfo != null) { + MavenParamReferenceProvider providerInstance = defaultInfo.getProviderInstance(); + if (providerInstance != null) { + return providerInstance.getReferencesByElement(element, domCfg, context); + } + } + + return PsiReference.EMPTY_ARRAY; + } + } + + private static class ParamInfo { + private final ClassLoader myClassLoader; + + private final String myProviderClass; + + private volatile MavenParamReferenceProvider myProviderInstance; + + public ParamInfo(ClassLoader classLoader, String providerClass) { + myClassLoader = classLoader; + myProviderClass = providerClass; + } + + public MavenParamReferenceProvider getProviderInstance() { + if (myProviderClass == null) { + return null; + } + + MavenParamReferenceProvider res = myProviderInstance; + + if (res == null) { + Object instance; + + try { + instance = myClassLoader.loadClass(myProviderClass).newInstance(); + } + catch (Exception e) { + throw new RuntimeException("Failed to create reference provider instance", e); + } + + if (instance instanceof MavenParamReferenceProvider) { + res = (MavenParamReferenceProvider)instance; + } + else { + final PsiReferenceProvider psiReferenceProvider = (PsiReferenceProvider)instance; + + res = new MavenParamReferenceProvider() { + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, + @NotNull MavenDomConfiguration domCfg, + @NotNull ProcessingContext context) { + return psiReferenceProvider.getReferencesByElement(element, context); + } + }; + } + + myProviderInstance = res; + } + + return res; + } + } +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/common/MavenCommonParamReferenceProviders.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/common/MavenCommonParamReferenceProviders.java new file mode 100644 index 000000000000..4467c6d50165 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/plugins/api/common/MavenCommonParamReferenceProviders.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.plugins.api.common; + +import com.intellij.openapi.util.Condition; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.maven.dom.model.MavenDomConfiguration; +import org.jetbrains.idea.maven.dom.references.MavenPathReferenceConverter; +import org.jetbrains.idea.maven.plugins.api.MavenParamReferenceProvider; + +/** + * @author Sergey Evdokimov + */ +public class MavenCommonParamReferenceProviders { + + private MavenCommonParamReferenceProviders() { + } + + public static class FilePath implements MavenParamReferenceProvider { + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, + @NotNull MavenDomConfiguration domCfg, + @NotNull ProcessingContext context) { + return MavenPathReferenceConverter.createReferences(domCfg, element, Condition.TRUE); + } + } + + public static class DirPath implements MavenParamReferenceProvider { + @Override + public PsiReference[] getReferencesByElement(@NotNull PsiElement element, + @NotNull MavenDomConfiguration domCfg, + @NotNull ProcessingContext context) { + return MavenPathReferenceConverter.createReferences(domCfg, element, FileReferenceSet.DIRECTORY_FILTER); + } + } + +} diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 746886a64fbf..a7541c8ba48b 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -9,6 +9,8 @@ + + com.intellij.properties @@ -98,6 +100,9 @@ implementationClass="org.jetbrains.idea.maven.dom.model.completion.MavenSmartCompletionContributor"/> + + + @@ -135,6 +140,12 @@ + + + + + + org.jetbrains.idea.maven.utils.MavenEnvironmentRegistrar diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/surefire/MavenSurefirePluginTest.groovy b/plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/surefire/MavenSurefirePluginTest.groovy new file mode 100644 index 000000000000..2d76b7028440 --- /dev/null +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/plugins/surefire/MavenSurefirePluginTest.groovy @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.maven.plugins.surefire + +import org.jetbrains.idea.maven.MavenImportingTestCase +import org.jetbrains.idea.maven.dom.MavenDomTestCase + +/** + * @author Sergey Evdokimov + */ +class MavenSurefirePluginTest extends MavenDomTestCase { + + void testCompletion() { + importProject(""" + simpleMaven + simpleMaven + jar + 1.0 + + + + + maven-surefire-plugin + + + \${basedir}/src/ + + + + + +""") + + createProjectSubFile("src/main/A.txt", "") + createProjectSubFile("src/test/A.txt", "") + createProjectSubFile("src/A.txt", "") + + assertCompletionVariants(myProjectPom, "main", "test") + } + +}