Add reference to parameter 'additionalClasspathElements' inside configuration of plugin 'maven-surefire-plugin'

This commit is contained in:
Sergey Evdokimov
2012-12-07 20:30:18 +04:00
parent 1dcd62b374
commit 2273ef938d
7 changed files with 476 additions and 4 deletions
@@ -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<PsiFileSystemItem> fileFilter) {
ElementManipulator<PsiElement> 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<PsiFileSystemItem> 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) {
@@ -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);
}
@@ -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<MavenPluginDescriptor> EP_NAME = new ExtensionPointName<MavenPluginDescriptor>("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;
}
}
@@ -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<String, Map> myMap;
public Map<String, Map> getMap() {
Map<String, Map> res = myMap;
if (res == null) {
res = new HashMap<String, Map>();
for (MavenPluginDescriptor pluginDescriptor : MavenPluginDescriptor.EP_NAME.getExtensions()) {
Pair<String, String> 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<String, ParamInfo> 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<String, String> 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<String, String>(mavenId.substring(0, idx), mavenId.substring(idx + 1));
}
@NotNull
private static <K, V extends Map> 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<Pair<String, String>, Map<String, ParamInfo>> 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<String, ParamInfo> 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;
}
}
}
@@ -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);
}
}
}
@@ -9,6 +9,8 @@
<extensionPoint name="importer" interface="org.jetbrains.idea.maven.importing.MavenImporter"/>
<extensionPoint name="additional.importing.settings" interface="org.jetbrains.idea.maven.project.AdditionalMavenImportingSettings"/>
<extensionPoint name="archetypesProvider" interface="org.jetbrains.idea.maven.indices.MavenArchetypesProvider"/>
<extensionPoint name="pluginDescriptor" beanClass="org.jetbrains.idea.maven.plugins.api.MavenPluginDescriptor" />
</extensionPoints>
<depends>com.intellij.properties</depends>
@@ -98,6 +100,9 @@
implementationClass="org.jetbrains.idea.maven.dom.model.completion.MavenSmartCompletionContributor"/>
<psi.referenceContributor implementation="org.jetbrains.idea.maven.dom.references.MavenPropertyPsiReferenceContributor"/>
<psi.referenceContributor language="XML" implementation="org.jetbrains.idea.maven.plugins.api.MavenPluginParamReferenceContributor" />
<usageTargetProvider implementation="org.jetbrains.idea.maven.dom.references.MavenUsageTargetProvider"/>
<renameHandler implementation="org.jetbrains.idea.maven.dom.refactorings.MavenPropertyRenameHandler" order="first"/>
@@ -135,6 +140,12 @@
</extensions>
<extensions defaultExtensionNs="org.jetbrains.idea.maven">
<pluginDescriptor mavenId="org.apache.maven.plugins:maven-surefire-plugin">
<param name="additionalClasspathElements/additionalClasspathElement" refProvider="org.jetbrains.idea.maven.plugins.api.common.MavenCommonParamReferenceProviders$DirPath"/>
</pluginDescriptor>
</extensions>
<application-components>
<component>
<implementation-class>org.jetbrains.idea.maven.utils.MavenEnvironmentRegistrar</implementation-class>
@@ -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("""
<groupId>simpleMaven</groupId>
<artifactId>simpleMaven</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>\${basedir}/src/<caret></additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
</plugins>
</build>
""")
createProjectSubFile("src/main/A.txt", "")
createProjectSubFile("src/test/A.txt", "")
createProjectSubFile("src/A.txt", "")
assertCompletionVariants(myProjectPom, "main", "test")
}
}