IDEA-76384 in pom.xml editor does not resolve ${project.parent.basedir}

IDEA-26054
This commit is contained in:
Sergey Evdokimov
2012-02-20 14:33:29 +04:00
parent aafd460e77
commit ee3c17fbe4
6 changed files with 193 additions and 178 deletions
@@ -152,7 +152,6 @@ public class MavenDomProjectProcessorUtils {
return property[0];
}
@Nullable
public static Set<XmlTag> collectProperties(@NotNull MavenDomProjectModel projectDom, @NotNull final Project project) {
final Set<XmlTag> properties = new HashSet<XmlTag>();
@@ -55,7 +55,7 @@ public class MavenFilteredPropertyPsiReference extends MavenPropertyPsiReference
for (String each : myMavenProject.getFilters()) {
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(each);
if (file == null) continue;
collectPropertiesFileVariants(MavenDomUtil.getPropertiesFile(myProject, file), "", result);
collectPropertiesFileVariants(MavenDomUtil.getPropertiesFile(myProject, file), null, result);
}
}
@@ -28,11 +28,8 @@ import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTagChild;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.CollectionFactory;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.DomElement;
import com.intellij.util.xml.DomUtil;
import com.intellij.xml.XmlElementDescriptor;
@@ -46,19 +43,18 @@ import org.jetbrains.idea.maven.dom.MavenSchemaProvider;
import org.jetbrains.idea.maven.dom.model.MavenDomProfile;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;
import org.jetbrains.idea.maven.dom.model.MavenDomSettingsModel;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.utils.MavenIcons;
import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem;
import javax.swing.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MavenPropertyPsiReference extends MavenPsiReference {
private static final Set<String> BASEDIR_PROPS =
CollectionFactory.newTroveSet("basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.baseUri");
private static final String TIMESTAMP_PROP = "maven.build.timestamp";
protected final MavenDomProjectModel myProjectDom;
@@ -86,79 +82,103 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
return result;
}
// precedence
// 1. user/system
// 2. settings.xml
// 3. current profile (if property is used inside one)
// 4. profiles.xml
// 5. profiles in pom.xml
// 6. pom.xml
// 7. parent profiles.xml
// 8. profiles in parent pom.xml
// 9. parent pom.xml
// 10. model
// See org.apache.maven.project.interpolation.AbstractStringBasedModelInterpolator.createValueSources()
@Nullable
protected PsiElement doResolve() {
if (myText.startsWith("env.")) {
return resolveEnvPropety();
boolean hasPrefix = false;
String unprefixed = myText;
if (myText.startsWith("pom.")) {
unprefixed = myText.substring("pom.".length());
hasPrefix = true;
}
else if (myText.startsWith("project.")) {
unprefixed = myText.substring("project.".length());
hasPrefix = true;
}
if (BASEDIR_PROPS.contains(myText)) {
return resolveBasedir();
MavenProject mavenProject = myMavenProject;
while (unprefixed.startsWith("parent.")) {
if (unprefixed.equals("parent.groupId") || unprefixed.equals("parent.artifactId") || unprefixed.equals("parent.version")
|| unprefixed.equals("parent.relativePath")) {
break;
}
MavenId parentId = mavenProject.getParentId();
if (parentId == null) return null;
mavenProject = myProjectsManager.findProject(parentId);
if (mavenProject == null) return null;
unprefixed = unprefixed.substring("parent.".length());
}
if (unprefixed.equals("basedir") || (hasPrefix && mavenProject == myMavenProject && unprefixed.equals("baseUri"))) {
return getBaseDir(mavenProject);
}
if (myText.equals(TIMESTAMP_PROP)) {
return myElement;
}
PsiElement result = resolveSystemPropety();
if (result != null) return result;
if (hasPrefix) {
MavenDomProjectModel domProjectModel = MavenDomUtil.getMavenDomProjectModel(myProject, mavenProject.getFile());
if (domProjectModel != null) {
PsiElement res = resolveModelProperty(domProjectModel, "project." + unprefixed, new HashSet<DomElement>());
if (res != null) {
return res;
}
}
}
// todo resolve properties from config.
MavenDomProfile profile = DomUtil.findDomElement(myElement, MavenDomProfile.class);
if (profile != null) {
result = MavenDomProjectProcessorUtils.searchPropertyInProfile(myText, profile);
PsiElement result = MavenDomProjectProcessorUtils.searchPropertyInProfile(myText, profile);
if (result != null) return result;
}
result = MavenDomProjectProcessorUtils.searchProperty(myText, myProjectDom, myProject);
PsiElement result = MavenDomProjectProcessorUtils.searchProperty(myText, myProjectDom, myProject);
if (result != null) return result;
IProperty property = MavenDomUtil.findProperty(myProject, MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE, myText);
if (property != null) return property.getPsiElement();
if (myText.startsWith("env.")) {
property = MavenDomUtil.findProperty(myProject, MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE,
myText.substring("env.".length()));
if (property != null) return property.getPsiElement();
}
String textWithEnv = "env." + myText;
property = MavenDomUtil.findProperty(myProject, MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE, textWithEnv);
if (property != null) return property.getPsiElement();
property = MavenDomUtil.findProperty(myProject, MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, textWithEnv);
if (property != null) return property.getPsiElement();
if (!hasPrefix) {
MavenDomProjectModel domProjectModel = MavenDomUtil.getMavenDomProjectModel(myProject, mavenProject.getFile());
if (domProjectModel != null) {
PsiElement res = resolveModelProperty(domProjectModel, "project." + unprefixed, new HashSet<DomElement>());
if (res != null) {
return res;
}
}
}
if (myText.startsWith("settings.")) {
return resolveSettingsModelProperty();
}
String modelProperty = myText;
if (!modelProperty.startsWith("project.")) {
modelProperty = modelProperty.startsWith("pom.")
? "project." + modelProperty.substring("pom.".length())
: "project." + modelProperty;
}
return resolveModelProperty(myProjectDom, modelProperty, new THashSet<DomElement>());
return null;
}
@Nullable
private PsiElement resolveSystemPropety() {
IProperty property = MavenDomUtil.findProperty(myProject,
MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE,
myText);
return property == null ? null : property.getPsiElement();
}
@Nullable
private PsiElement resolveEnvPropety() {
IProperty property = MavenDomUtil.findProperty(myProject,
MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE,
myText.substring("env.".length()));
return property == null ? null : property.getPsiElement();
}
@Nullable
private PsiElement resolveBasedir() {
return getBaseDir();
}
private PsiDirectory getBaseDir() {
return PsiManager.getInstance(myProject).findDirectory(myMavenProject.getDirectoryFile());
private PsiDirectory getBaseDir(@NotNull MavenProject mavenProject) {
return PsiManager.getInstance(myProject).findDirectory(mavenProject.getDirectoryFile());
}
@Nullable
@@ -178,8 +198,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
private PsiElement resolveModelProperty(@NotNull MavenDomProjectModel projectDom,
@NotNull final String path,
@NotNull final Set<DomElement> recursionGuard) {
if (recursionGuard.contains(projectDom)) return null;
recursionGuard.add(projectDom);
if (!recursionGuard.add(projectDom)) return null;
if (!schemaHasProperty(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, path)) return null;
@@ -187,7 +206,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
if (result != null) return result;
if (path.equals("project.groupId") || path.equals("project.version")) {
return MavenDomUtil.findTag(projectDom, path.replace("project.", "project.parent."));
return MavenDomUtil.findTag(projectDom, "project.parent." + path.substring("project.".length()));
}
result = new MavenDomProjectProcessorUtils.DomParentProjectFileProcessor<PsiElement>(myProjectsManager) {
@@ -224,72 +243,98 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
return ArrayUtil.toObjectArray(result);
}
protected void collectVariants(List<Object> result) {
collectStandardVariants(result);
collectProjectSchemaVariants(result);
collectSettingsXmlSchemaVariants(result);
protected void collectVariants(final List<Object> result) {
int prefixLength = 0;
if (myText.startsWith("pom.")) {
prefixLength = "pom.".length();
}
else if (myText.startsWith("project.")) {
prefixLength = "project.".length();
}
MavenProject mavenProject = myMavenProject;
while (myText.startsWith("parent.", prefixLength)) {
MavenId parentId = mavenProject.getParentId();
if (parentId == null) return;
mavenProject = myProjectsManager.findProject(parentId);
if (mavenProject == null) return;
prefixLength += "parent.".length();
}
final String prefix = prefixLength == 0 ? null : myText.substring(0, prefixLength);
PsiDirectory baseDir = getBaseDir(mavenProject);
addVariant(result, "basedir", baseDir, prefix, MavenIcons.MAVEN_ICON);
if (prefix == null) {
result.add(createLookupElement(baseDir, "project.baseUri", MavenIcons.MAVEN_ICON));
result.add(createLookupElement(baseDir, "pom.baseUri", MavenIcons.MAVEN_ICON));
result.add(createLookupElement(myElement, TIMESTAMP_PROP, MavenIcons.MAVEN_ICON));
}
processSchema(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, new SchemaProcessor<Object>() {
@Override
public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
if (property.startsWith("project.")) {
addVariant(result, property.substring("project.".length()), descriptor, prefix, MavenIcons.MAVEN_ICON);
}
return null;
}
});
processSchema(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, new SchemaProcessor<Object>(){
@Override
public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
result.add(createLookupElement(descriptor, property, MavenIcons.MAVEN_ICON));
return null;
}
});
collectPropertiesVariants(result);
collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE, null, result);
collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, "env.", result);
}
private void collectStandardVariants(List<Object> result) {
PsiDirectory basedir = getBaseDir();
for (String each : BASEDIR_PROPS) {
result.add(createLookupElement(basedir, each, MavenIcons.MAVEN_ICON));
private static void addVariant(List<Object> result, String name, @NotNull Object element, @Nullable String prefix, @NotNull Icon icon) {
String nameWithPrefix;
if (prefix == null) {
nameWithPrefix = name;
result.add(createLookupElement(element, "pom." + name, icon));
result.add(createLookupElement(element, "project." + name, icon));
}
else {
nameWithPrefix = prefix + name;
}
result.add(createLookupElement(myElement, TIMESTAMP_PROP, MavenIcons.MAVEN_ICON));
}
private void collectProjectSchemaVariants(final List<Object> result) {
processSchema(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, new CollectingSchemaProcessor(result) {
@Override
public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
super.process(property, descriptor);
String prefix = "project.";
if (property.length() > prefix.length()) {
String unqualified = property.substring(prefix.length());
super.process("pom." + unqualified, descriptor);
super.process(unqualified, descriptor);
}
return null;
}
});
}
private void collectSettingsXmlSchemaVariants(final List<Object> result) {
processSchema(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, new CollectingSchemaProcessor(result));
result.add(createLookupElement(element, nameWithPrefix, icon));
}
private void collectPropertiesVariants(final List<Object> result) {
Set<XmlTag> properties = MavenDomProjectProcessorUtils.collectProperties(myProjectDom, myProject);
result.addAll(ContainerUtil.map(properties, new Function<XmlTag, LookupElement>() {
public LookupElement fun(XmlTag xmlTag) {
return createLookupElement(xmlTag, xmlTag.getName());
}
}));
for (XmlTag xmlTag : MavenDomProjectProcessorUtils.collectProperties(myProjectDom, myProject)) {
result.add(createLookupElement(xmlTag, xmlTag.getName(), PlatformIcons.PROPERTY_ICON));
}
}
private void collectSystemEnvProperties(String propertiesFileName, String prefix, List<Object> result) {
private void collectSystemEnvProperties(String propertiesFileName, @Nullable String prefix, List<Object> result) {
PropertiesFile file = MavenDomUtil.getPropertiesFile(myProject, propertiesFileName);
collectPropertiesFileVariants(file, prefix, result);
}
protected void collectPropertiesFileVariants(@Nullable PropertiesFile file, String prefix, List<Object> result) {
protected static void collectPropertiesFileVariants(@Nullable PropertiesFile file, @Nullable String prefix, List<Object> result) {
if (file == null) return;
for (IProperty each : file.getProperties()) {
String name = each.getKey();
if (prefix != null) name = prefix + name;
result.add(createLookupElement(each, name));
if (name != null) {
if (prefix != null) name = prefix + name;
result.add(createLookupElement(each, name, PlatformIcons.PROPERTY_ICON));
}
}
}
private static LookupElement createLookupElement(Object element, String name) {
return createLookupElement(element, name, PlatformIcons.PROPERTY_ICON);
}
private static LookupElement createLookupElement(Object element, String name, Icon icon) {
private static LookupElement createLookupElement(@NotNull Object element, @NotNull String name, @Nullable Icon icon) {
return LookupElementBuilder.create(element, name)
.setIcon(icon)
.setPresentableText(name);
@@ -314,9 +359,8 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
Set<XmlElementDescriptor> recursionGuard) {
for (XmlElementDescriptor each : descriptors) {
if (isCollection(each)) continue;
if (!recursionGuard.add(each)) continue;
if (recursionGuard.contains(each)) continue;
recursionGuard.add(each);
try {
String name = each.getName();
if (prefix != null) name = prefix + "." + name;
@@ -335,7 +379,7 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
return null;
}
private static <T> boolean isCollection(XmlElementDescriptor each) {
private static boolean isCollection(XmlElementDescriptor each) {
XmlTag declaration = (XmlTag)each.getDeclaration();
if (declaration != null) {
XmlTag complexType = declaration.findFirstSubTag("xs:complexType");
@@ -356,17 +400,4 @@ public class MavenPropertyPsiReference extends MavenPsiReference {
T process(@NotNull String property, XmlElementDescriptor descriptor);
}
private static class CollectingSchemaProcessor implements SchemaProcessor {
private final List<Object> myResult;
public CollectingSchemaProcessor(List<Object> result) {
myResult = result;
}
@Nullable
public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
myResult.add(createLookupElement(descriptor, property, MavenIcons.MAVEN_ICON));
return null;
}
}
}
@@ -64,7 +64,7 @@ public class MavenProjectReader {
new THashSet<MavenId>());
}
private File getBaseDir(VirtualFile file) {
private static File getBaseDir(VirtualFile file) {
return new File(file.getParent().getPath());
}
@@ -109,7 +109,7 @@ public class MavenProjectReader {
return new RawModelReadResult(result, problems, alwaysOnProfiles);
}
MavenParent parent = new MavenParent(new MavenId(UNKNOWN, UNKNOWN, UNKNOWN), "../pom.xml");
MavenParent parent;
if (MavenJDOMUtil.hasChildByPath(xmlProject, "parent")) {
parent = new MavenParent(new MavenId(MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.groupId", UNKNOWN),
MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.artifactId", UNKNOWN),
@@ -117,6 +117,9 @@ public class MavenProjectReader {
MavenJDOMUtil.findChildValueByPath(xmlProject, "parent.relativePath", "../pom.xml"));
result.setParent(parent);
}
else {
parent = new MavenParent(new MavenId(UNKNOWN, UNKNOWN, UNKNOWN), "../pom.xml");
}
result.setMavenId(new MavenId(MavenJDOMUtil.findChildValueByPath(xmlProject, "groupId", parent.getMavenId().getGroupId()),
MavenJDOMUtil.findChildValueByPath(xmlProject, "artifactId", UNKNOWN),
@@ -336,7 +339,7 @@ public class MavenProjectReader {
return true;
}
private void collectProperties(Element xmlProperties, MavenModelBase mavenModelBase) {
private static void collectProperties(Element xmlProperties, MavenModelBase mavenModelBase) {
if (xmlProperties == null) return;
Properties props = mavenModelBase.getProperties();
@@ -469,13 +472,14 @@ public class MavenProjectReader {
}
}
public MavenProjectReaderResult generateSources(MavenEmbedderWrapper embedder,
MavenImportingSettings importingSettings,
VirtualFile file,
Collection<String> profiles,
MavenConsole console) throws MavenProcessCanceledException {
@Nullable
public static MavenProjectReaderResult generateSources(MavenEmbedderWrapper embedder,
MavenImportingSettings importingSettings,
VirtualFile file,
Collection<String> profiles,
MavenConsole console) throws MavenProcessCanceledException {
try {
List<String> goals = Arrays.asList(importingSettings.getUpdateFoldersOnImportPhase());
List<String> goals = Collections.singletonList(importingSettings.getUpdateFoldersOnImportPhase());
MavenServerExecutionResult result = embedder.execute(file, profiles, goals);
if (result.projectData == null) return null;
@@ -488,11 +488,11 @@ public class MavenFilteredPropertiesCompletionAndResolutionTest extends MavenDom
VirtualFile f = createProjectSubFile("res/foo1.properties",
"foo1=${basedir}\n" +
"foo2=|baseUri|\n" +
"foo2=|pom.baseUri|\n" +
"foo3=a(ve|rsion]");
assertNotNull(resolveReference(f, "basedir"));
assertNotNull(resolveReference(f, "baseUri"));
assertNotNull(resolveReference(f, "pom.baseUri"));
assertNotNull(getReference(f, "ve|rsion"));
}
@@ -526,10 +526,10 @@ public class MavenFilteredPropertiesCompletionAndResolutionTest extends MavenDom
VirtualFile f = createProjectSubFile("res/foo1.properties",
"foo1=${basedir}\n" +
"foo2=|baseUri|");
"foo2=|pom.baseUri|");
assert !(getReference(f, "basedir") instanceof MavenPropertyPsiReference);
assertNotNull(resolveReference(f, "baseUri"));
assertNotNull(resolveReference(f, "pom.baseUri"));
}
}
@@ -36,16 +36,6 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase {
"<version>1</version>");
}
public void testBasicResolution() throws Exception {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project</artifactId>" +
"<version>1</version>" +
"<name>${<caret>foo}</name>");
assertUnresolved(myProjectPom);
}
public void testResolutionToProject() throws Exception {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project</artifactId>" +
@@ -101,24 +91,6 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase {
"<name>${<caret>pom.basedir}</name>");
assertResolved(myProjectPom, baseDir);
createProjectPom("<groupId>test</groupId" +
"<artifactId>project</artifactId>" +
"<version>1</version>" +
"<name>${<caret>baseUri}</name>");
assertResolved(myProjectPom, baseDir);
}
public void testBuiltInTimestampProperty() throws Exception {
createProjectPom("<groupId>test</groupId" +
"<artifactId>project</artifactId>" +
"<version>1</version>" +
"<name>${<caret>maven.build.timestamp}</name>");
assertResolved(myProjectPom, findTag("project.name"));
}
public void testResolutionWithSeveralProperties() throws Exception {
@@ -186,16 +158,6 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase {
assertUnresolved(myProjectPom);
}
public void testResolutionToUnknownExtraProjectProperty() throws Exception {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project</artifactId>" +
"<version>1</version>" +
"<name>${<caret>project.version.bar}</name>");
assertUnresolved(myProjectPom);
}
public void testResolutionToAbsentProjectProperty() throws Exception {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project</artifactId>" +
@@ -745,14 +707,31 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase {
}
public void testHighlightUnresolvedProperties() throws Exception {
createProjectPom("<groupId>test</groupId>" +
"<artifactId>project</artifactId>" +
"<version>1</version>" +
"<name>${<error>xxx</error>}</name>" +
createProjectPom("<groupId>test</groupId>\n" +
"<artifactId>child</artifactId>\n" +
"<version>1</version>\n" +
"<name>${<error>xxx</error>}</name>\n" +
"<properties>" +
" <foo>${<error>zzz</error>}</foo>" +
"</properties>");
"<parent>\n" +
" <groupId>test</groupId>\n" +
" <artifactId>parent</artifactId>\n" +
" <version>1</version>\n" +
"</parent>\n" +
"<properties>\n" +
" <foo>\n" +
"${<error>zzz</error>}\n" +
"${<error>pom.maven.build.timestamp</error>}\n" +
"${<error>project.maven.build.timestamp</error>}\n" +
"${<error>parent.maven.build.timestamp</error>}\n" +
"${<error>baseUri</error>}\n" +
"${<error>unknownProperty</error>}\n" +
"${<error>project.version.bar</error>}\n" +
"${maven.build.timestamp}\n" +
"</foo>\n" +
"</properties>"
);
checkHighlighting();
}
@@ -841,8 +820,10 @@ public class MavenPropertyCompletionAndResolutionTest extends MavenDomTestCase {
"parentPomProfilesProp",
"parentProfilesXmlProp");
assertContain(variants, "artifactId", "project.artifactId", "pom.artifactId");
assertContain(variants, "basedir", "project.basedir", "pom.basedir", "baseUri", "project.baseUri", "pom.basedir");
assertContain(variants, "basedir", "project.basedir", "pom.basedir", "project.baseUri", "pom.basedir");
assert !variants.contains("baseUri");
assertContain(variants, "maven.build.timestamp");
assert !variants.contains("project.maven.build.timestamp");
assertContain(variants, "settingsXmlProp");
assertContain(variants, "settings.localRepository");
assertContain(variants, "user.home", "env." + getEnvVar());