maven dependency management gutter support (override icons)
[fedora-idea.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / dom / references / MavenPropertyPsiReference.java
blob71234c65b98f661b364863d79c183797d6b38f82
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package org.jetbrains.idea.maven.dom.references;
18 import com.intellij.codeInsight.lookup.LookupElement;
19 import com.intellij.codeInsight.lookup.LookupElementBuilder;
20 import com.intellij.lang.properties.psi.PropertiesFile;
21 import com.intellij.lang.properties.psi.Property;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.pom.Navigatable;
26 import com.intellij.psi.*;
27 import com.intellij.psi.xml.XmlDocument;
28 import com.intellij.psi.xml.XmlFile;
29 import com.intellij.psi.xml.XmlTag;
30 import com.intellij.psi.xml.XmlTagChild;
31 import com.intellij.util.Function;
32 import com.intellij.util.Icons;
33 import com.intellij.util.IncorrectOperationException;
34 import com.intellij.util.containers.ContainerUtil;
35 import com.intellij.util.xml.DomElement;
36 import com.intellij.util.xml.DomUtil;
37 import com.intellij.xml.XmlElementDescriptor;
38 import com.intellij.xml.XmlNSDescriptor;
39 import gnu.trove.THashSet;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42 import org.jetbrains.idea.maven.dom.MavenDomProjectProcessorUtils;
43 import org.jetbrains.idea.maven.dom.MavenDomUtil;
44 import org.jetbrains.idea.maven.dom.MavenSchemaProvider;
45 import org.jetbrains.idea.maven.dom.model.*;
46 import org.jetbrains.idea.maven.project.*;
47 import org.jetbrains.idea.maven.utils.MavenIcons;
48 import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem;
50 import javax.swing.*;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Set;
55 public class MavenPropertyPsiReference extends MavenPsiReference {
56 protected final MavenDomProjectModel myProjectDom;
57 protected final MavenProject myMavenProject;
58 private final boolean mySoft;
60 public MavenPropertyPsiReference(MavenProject mavenProject, PsiElement element, String text, TextRange range, boolean isSoft) {
61 super(element, text, range);
62 myMavenProject = mavenProject;
63 mySoft = isSoft;
64 myProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, mavenProject.getFile());
67 @Nullable
68 public PsiElement resolve() {
69 PsiElement result = doResolve();
70 if (result == null) return result;
72 if (result instanceof XmlTag) {
73 XmlTagChild[] children = ((XmlTag)result).getValue().getChildren();
74 if (children.length != 1 || !(children[0] instanceof Navigatable)) return result;
75 return new MavenPsiElementWrapper(result, (Navigatable)children[0]);
78 return result;
81 // precedence
82 // 1. user/system
83 // 2. settings.xml
84 // 3. profiles.xml
85 // 4. profiles in pom.xml
86 // 5. pom.xml
87 // 6. parent profiles.xml
88 // 7. profiles in parent pom.xml
89 // 8. parent pom.xml
90 // 9. model
91 @Nullable
92 protected PsiElement doResolve() {
93 if (myText.startsWith("env.")) {
94 return resolveEnvPropety();
97 if (myText.equals("basedir") || myText.equals("project.basedir") || myText.equals("pom.basedir")) {
98 return resolveBasedir();
101 PsiElement result = resolveSystemPropety();
102 if (result != null) return result;
104 result = MavenDomProjectProcessorUtils.searchProperty(myText, myProjectDom, myProject);
105 if (result != null) return result;
107 if (myText.startsWith("settings.")) {
108 return resolveSettingsModelProperty();
111 String modelProperty = myText;
112 if (!modelProperty.startsWith("project.")) {
113 modelProperty = modelProperty.startsWith("pom.")
114 ? "project." + modelProperty.substring("pom.".length())
115 : "project." + modelProperty;
117 return resolveModelProperty(myProjectDom, modelProperty, new THashSet<DomElement>());
120 @Nullable
121 private PsiElement resolveSystemPropety() {
122 return MavenDomUtil.findProperty(myProject,
123 MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE,
124 myText);
127 @Nullable
128 private PsiElement resolveEnvPropety() {
129 return MavenDomUtil.findProperty(myProject,
130 MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE,
131 myText.substring("env.".length()));
134 @Nullable
135 private PsiElement resolveBasedir() {
136 return getBaseDir();
139 private PsiDirectory getBaseDir() {
140 return PsiManager.getInstance(myProject).findDirectory(myMavenProject.getDirectoryFile());
143 @Nullable
144 private PsiElement resolveSettingsModelProperty() {
145 if (!schemaHasProperty(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, myText)) return null;
147 for (VirtualFile each : myProjectsManager.getGeneralSettings().getEffectiveSettingsFiles()) {
148 MavenDomSettingsModel settingsDom = MavenDomUtil.getMavenDomModel(myProject, each, MavenDomSettingsModel.class);
149 if (settingsDom == null) continue;
150 PsiElement result = MavenDomUtil.findTag(settingsDom, myText);
151 if (result != null) return result;
153 return myElement;
156 @Nullable
157 private PsiElement resolveModelProperty(@NotNull MavenDomProjectModel projectDom,
158 @NotNull final String path,
159 @NotNull final Set<DomElement> recursionGuard) {
160 if (recursionGuard.contains(projectDom)) return null;
161 recursionGuard.add(projectDom);
163 if (!schemaHasProperty(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, path)) return null;
165 PsiElement result = MavenDomUtil.findTag(projectDom, path);
166 if (result != null) return result;
168 if (path.equals("project.groupId") || path.equals("project.version")) {
169 return MavenDomUtil.findTag(projectDom, path.replace("project.", "project.parent."));
172 result = new MyMavenParentProjectFileProcessor<PsiElement>() {
173 protected PsiElement doProcessParent(VirtualFile parentFile) {
174 MavenDomProjectModel parentProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, parentFile);
175 return resolveModelProperty(parentProjectDom, path, recursionGuard);
177 }.process(projectDom);
178 if (result != null) return result;
180 return myElement;
183 private boolean schemaHasProperty(String schema, final String property) {
184 return processSchema(schema, new SchemaProcessor<Boolean>() {
185 @Nullable
186 public Boolean process(@NotNull String eachProperty, XmlElementDescriptor descriptor) {
187 if (eachProperty.equals(property)) return true;
188 return null;
190 }) != null;
193 @Override
194 public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
195 return ElementManipulators.getManipulator(myElement).handleContentChange(myElement, myRange, newElementName);
198 @NotNull
199 public Object[] getVariants() {
200 List<Object> result = new ArrayList<Object>();
201 collectVariants(result);
202 return result.toArray(new Object[result.size()]);
205 protected void collectVariants(List<Object> result) {
206 collectBasedirVariants(result);
207 collectProjectSchemaVariants(result);
208 collectSettingsXmlSchemaVariants(result);
209 collectPropertiesVariants(result);
210 collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE, null, result);
211 collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, "env.", result);
214 private void collectBasedirVariants(List<Object> result) {
215 PsiDirectory basedir = getBaseDir();
216 result.add(createLookupElement(basedir, "basedir", MavenIcons.MAVEN_ICON));
217 result.add(createLookupElement(basedir, "pom.basedir", MavenIcons.MAVEN_ICON));
218 result.add(createLookupElement(basedir, "project.basedir", MavenIcons.MAVEN_ICON));
221 private void collectProjectSchemaVariants(final List<Object> result) {
222 processSchema(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, new CollectingSchemaProcessor(result) {
223 @Override
224 public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
225 super.process(property, descriptor);
226 String prefix = "project.";
227 if (property.length() > prefix.length()) {
228 String unqualified = property.substring(prefix.length());
229 super.process("pom." + unqualified, descriptor);
230 super.process(unqualified, descriptor);
232 return null;
237 private void collectSettingsXmlSchemaVariants(final List<Object> result) {
238 processSchema(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, new CollectingSchemaProcessor(result));
241 private void collectPropertiesVariants(final List<Object> result) {
242 Set<XmlTag> properties = MavenDomProjectProcessorUtils.collectProperties(myProjectDom, myProject);
243 result.addAll(ContainerUtil.map(properties, new Function<XmlTag, LookupElement>() {
244 public LookupElement fun(XmlTag xmlTag) {
245 return createLookupElement(xmlTag, xmlTag.getName());
247 })) ;
250 private void collectSystemEnvProperties(String propertiesFileName, String prefix, List<Object> result) {
251 PropertiesFile file = MavenDomUtil.getPropertiesFile(myProject, propertiesFileName);
252 collectPropertiesFileVariants(file, prefix, result);
255 protected void collectPropertiesFileVariants(PropertiesFile file, String prefix, List<Object> result) {
256 for (Property each : file.getProperties()) {
257 String name = each.getKey();
258 if (prefix != null) name = prefix + name;
259 result.add(createLookupElement(each, name));
263 private static LookupElement createLookupElement(Object element, String name) {
264 return createLookupElement(element, name, Icons.PROPERTY_ICON);
267 private static LookupElement createLookupElement(Object element, String name, Icon icon) {
268 return LookupElementBuilder.create(element, name)
269 .setIcon(icon)
270 .setPresentableText(name);
273 @Nullable
274 private <T> T processSchema(String schema, SchemaProcessor<T> processor) {
275 VirtualFile file = MavenSchemaProvider.getSchemaFile(schema);
276 PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
277 if (!(psiFile instanceof XmlFile)) return null;
279 XmlFile xmlFile = (XmlFile)psiFile;
280 XmlDocument document = xmlFile.getDocument();
281 XmlNSDescriptor desc = (XmlNSDescriptor)document.getMetaData();
282 XmlElementDescriptor[] descriptors = desc.getRootElementsDescriptors(document);
283 return doProcessSchema(descriptors, null, processor, new THashSet<XmlElementDescriptor>());
286 private static <T> T doProcessSchema(XmlElementDescriptor[] descriptors,
287 String prefix,
288 SchemaProcessor<T> processor,
289 Set<XmlElementDescriptor> recursionGuard) {
290 for (XmlElementDescriptor each : descriptors) {
291 if (isCollection(each)) continue;
293 if (recursionGuard.contains(each)) continue;
294 recursionGuard.add(each);
295 try {
296 String name = each.getName();
297 if (prefix != null) name = prefix + "." + name;
299 T result = processor.process(name, each);
300 if (result != null) return result;
302 result = doProcessSchema(each.getElementsDescriptors(null), name, processor, recursionGuard);
303 if (result != null) return result;
305 finally {
306 recursionGuard.remove(each);
310 return null;
313 private static <T> boolean isCollection(XmlElementDescriptor each) {
314 XmlTag declaration = (XmlTag)each.getDeclaration();
315 if (declaration != null) {
316 XmlTag complexType = declaration.findFirstSubTag("xs:complexType");
317 if (complexType != null) {
318 if (complexType.findFirstSubTag("xs:sequence") != null) return true;
321 return false;
324 @Override
325 public boolean isSoft() {
326 return mySoft;
329 private interface SchemaProcessor<T> {
330 @Nullable
331 T process(@NotNull String property, XmlElementDescriptor descriptor);
334 private static class CollectingSchemaProcessor implements SchemaProcessor {
335 private final List<Object> myResult;
337 public CollectingSchemaProcessor(List<Object> result) {
338 myResult = result;
341 @Nullable
342 public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
343 myResult.add(createLookupElement(descriptor, property, MavenIcons.MAVEN_ICON));
344 return null;
348 private abstract class MyMavenParentProjectFileProcessor<T> extends MavenParentProjectFileProcessor<T> {
349 protected VirtualFile findManagedFile(@NotNull MavenId id) {
350 MavenProject project = myProjectsManager.findProject(id);
351 return project == null ? null : project.getFile();
354 @Nullable
355 public T process(@NotNull MavenDomProjectModel projectDom) {
356 MavenDomParent parent = projectDom.getMavenParent();
357 MavenParentDesc parentDesc = null;
358 if (DomUtil.hasXml(parent)) {
359 String parentGroupId = parent.getGroupId().getStringValue();
360 String parentArtifactId = parent.getArtifactId().getStringValue();
361 String parentVersion = parent.getVersion().getStringValue();
362 String parentRelativePath = parent.getRelativePath().getStringValue();
363 if (StringUtil.isEmptyOrSpaces(parentRelativePath)) parentRelativePath = "../pom.xml";
364 MavenId parentId = new MavenId(parentGroupId, parentArtifactId, parentVersion);
365 parentDesc = new MavenParentDesc(parentId, parentRelativePath);
368 return process(myProjectsManager.getGeneralSettings(), MavenDomUtil.getVirtualFile(projectDom), parentDesc);