3d5695e2d8760a20df951ca589fe8df7adc18e62
[fedora-idea.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / dom / references / MavenPropertyPsiReference.java
blob3d5695e2d8760a20df951ca589fe8df7adc18e62
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.Icons;
32 import com.intellij.util.IncorrectOperationException;
33 import com.intellij.util.xml.DomElement;
34 import com.intellij.util.xml.DomUtil;
35 import com.intellij.xml.XmlElementDescriptor;
36 import com.intellij.xml.XmlNSDescriptor;
37 import gnu.trove.THashSet;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40 import org.jetbrains.idea.maven.dom.MavenDomUtil;
41 import org.jetbrains.idea.maven.dom.MavenSchemaProvider;
42 import org.jetbrains.idea.maven.dom.model.*;
43 import org.jetbrains.idea.maven.project.*;
44 import org.jetbrains.idea.maven.utils.MavenIcons;
45 import org.jetbrains.idea.maven.utils.MavenUtil;
46 import org.jetbrains.idea.maven.vfs.MavenPropertiesVirtualFileSystem;
48 import javax.swing.*;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.List;
52 import java.util.Set;
54 public class MavenPropertyPsiReference extends MavenPsiReference {
55 protected final MavenDomProjectModel myProjectDom;
56 protected final MavenProject myMavenProject;
57 private final boolean mySoft;
59 public MavenPropertyPsiReference(MavenProject mavenProject, PsiElement element, String text, TextRange range, boolean isSoft) {
60 super(element, text, range);
61 myMavenProject = mavenProject;
62 mySoft = isSoft;
63 myProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, mavenProject.getFile());
66 @Nullable
67 public PsiElement resolve() {
68 PsiElement result = doResolve();
69 if (result == null) return result;
71 if (result instanceof XmlTag) {
72 XmlTagChild[] children = ((XmlTag)result).getValue().getChildren();
73 if (children.length != 1 || !(children[0] instanceof Navigatable)) return result;
74 return new MavenPsiElementWrapper(result, (Navigatable)children[0]);
77 return result;
80 // precedence
81 // 1. user/system
82 // 2. settings.xml
83 // 3. profiles.xml
84 // 4. profiles in pom.xml
85 // 5. pom.xml
86 // 6. parent profiles.xml
87 // 7. profiles in parent pom.xml
88 // 8. parent pom.xml
89 // 9. model
90 @Nullable
91 protected PsiElement doResolve() {
92 if (myText.startsWith("env.")) {
93 return resolveEnvPropety();
96 if (myText.equals("basedir") || myText.equals("project.basedir") || myText.equals("pom.basedir")) {
97 return resolveBasedir();
100 PsiElement result = resolveSystemPropety();
101 if (result != null) return result;
103 result = processProperties(myProjectDom, new PropertyProcessor<PsiElement>() {
104 @Nullable
105 public PsiElement process(@NotNull XmlTag property) {
106 if (property.getName().equals(myText)) return property;
107 return null;
110 if (result != null) return result;
112 if (myText.startsWith("settings.")) {
113 return resolveSettingsModelProperty();
116 String modelProperty = myText;
117 if (!modelProperty.startsWith("project.")) {
118 modelProperty = modelProperty.startsWith("pom.")
119 ? "project." + modelProperty.substring("pom.".length())
120 : "project." + modelProperty;
122 return resolveModelProperty(myProjectDom, modelProperty, new THashSet<DomElement>());
125 @Nullable
126 private PsiElement resolveSystemPropety() {
127 return MavenDomUtil.findProperty(myProject,
128 MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE,
129 myText);
132 @Nullable
133 private PsiElement resolveEnvPropety() {
134 return MavenDomUtil.findProperty(myProject,
135 MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE,
136 myText.substring("env.".length()));
139 @Nullable
140 private PsiElement resolveBasedir() {
141 return getBaseDir();
144 private PsiDirectory getBaseDir() {
145 return PsiManager.getInstance(myProject).findDirectory(myMavenProject.getDirectoryFile());
148 @Nullable
149 private <T> T processProperties(@NotNull MavenDomProjectModel projectDom, final PropertyProcessor<T> processor) {
150 T result;
151 MavenProject mavenProjectOrNull = MavenDomUtil.findProject(projectDom);
153 result = processSettingsXmlProperties(mavenProjectOrNull, processor);
154 if (result != null) return result;
156 result = processProjectProperties(projectDom, mavenProjectOrNull, processor);
157 if (result != null) return result;
159 return new MyMavenParentProjectFileProcessor<T>() {
160 protected T doProcessParent(VirtualFile parentFile) {
161 MavenDomProjectModel parentProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, parentFile);
162 if (parentProjectDom == null) return null;
163 MavenProject parentMavenProject = MavenDomUtil.findProject(parentProjectDom);
164 return processProjectProperties(parentProjectDom, parentMavenProject, processor);
166 }.process(projectDom);
169 @Nullable
170 private <T> T processSettingsXmlProperties(@Nullable MavenProject mavenProject, PropertyProcessor<T> processor) {
171 MavenGeneralSettings settings = myProjectsManager.getGeneralSettings();
172 T result;
174 for (VirtualFile each : settings.getEffectiveSettingsFiles()) {
175 MavenDomSettingsModel settingsDom = MavenDomUtil.getMavenDomModel(myProject, each, MavenDomSettingsModel.class);
176 if (settingsDom == null) continue;
178 result = processProfilesProperties(settingsDom.getProfiles(), mavenProject, processor);
179 if (result != null) return result;
181 return null;
184 @Nullable
185 private <T> T processProjectProperties(MavenDomProjectModel projectDom,
186 MavenProject mavenProjectOrNull,
187 PropertyProcessor<T> processor) {
188 T result;
190 result = processProfilesXmlProperties(MavenDomUtil.getVirtualFile(projectDom), mavenProjectOrNull, processor);
191 if (result != null) return result;
193 result = processProfilesProperties(projectDom.getProfiles(), mavenProjectOrNull, processor);
194 if (result != null) return result;
196 return processProperties(projectDom.getProperties(), processor);
199 @Nullable
200 private <T> T processProfilesXmlProperties(VirtualFile projectFile, MavenProject mavenProjectOrNull, PropertyProcessor<T> processor) {
201 VirtualFile profilesFile = MavenUtil.findProfilesXmlFile(projectFile);
202 if (profilesFile == null) return null;
204 MavenDomProfiles profiles = MavenDomUtil.getMavenDomProfilesModel(myProject, profilesFile);
205 if (profiles == null) return null;
207 return processProfilesProperties(profiles, mavenProjectOrNull, processor);
210 @Nullable
211 private <T> T processProfilesProperties(MavenDomProfiles profilesDom, MavenProject mavenProjectOrNull, PropertyProcessor<T> processor) {
212 Collection<String> activePropfiles = mavenProjectOrNull == null ? null : mavenProjectOrNull.getActiveProfilesIds();
213 for (MavenDomProfile each : profilesDom.getProfiles()) {
214 XmlTag idTag = each.getId().getXmlTag();
215 if (idTag == null) continue;
216 if (activePropfiles != null && !activePropfiles.contains(idTag.getValue().getText())) continue;
218 T result = processProperties(each.getProperties(), processor);
219 if (result != null) return result;
221 return null;
224 @Nullable
225 private <T> T processProperties(MavenDomProperties propertiesDom, PropertyProcessor<T> processor) {
226 XmlTag propertiesTag = propertiesDom.getXmlTag();
227 if (propertiesTag != null) {
228 for (XmlTag each : propertiesTag.getSubTags()) {
229 T result = processor.process(each);
230 if (result != null) return result;
233 return null;
236 @Nullable
237 private PsiElement resolveSettingsModelProperty() {
238 if (!schemaHasProperty(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, myText)) return null;
240 for (VirtualFile each : myProjectsManager.getGeneralSettings().getEffectiveSettingsFiles()) {
241 MavenDomSettingsModel settingsDom = MavenDomUtil.getMavenDomModel(myProject, each, MavenDomSettingsModel.class);
242 if (settingsDom == null) continue;
243 PsiElement result = MavenDomUtil.findTag(settingsDom, myText);
244 if (result != null) return result;
246 return myElement;
249 @Nullable
250 private PsiElement resolveModelProperty(@NotNull MavenDomProjectModel projectDom,
251 @NotNull final String path,
252 @NotNull final Set<DomElement> recursionGuard) {
253 if (recursionGuard.contains(projectDom)) return null;
254 recursionGuard.add(projectDom);
256 if (!schemaHasProperty(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, path)) return null;
258 PsiElement result = MavenDomUtil.findTag(projectDom, path);
259 if (result != null) return result;
261 if (path.equals("project.groupId") || path.equals("project.version")) {
262 return MavenDomUtil.findTag(projectDom, path.replace("project.", "project.parent."));
265 result = new MyMavenParentProjectFileProcessor<PsiElement>() {
266 protected PsiElement doProcessParent(VirtualFile parentFile) {
267 MavenDomProjectModel parentProjectDom = MavenDomUtil.getMavenDomProjectModel(myProject, parentFile);
268 return resolveModelProperty(parentProjectDom, path, recursionGuard);
270 }.process(projectDom);
271 if (result != null) return result;
273 return myElement;
276 private boolean schemaHasProperty(String schema, final String property) {
277 return processSchema(schema, new SchemaProcessor<Boolean>() {
278 @Nullable
279 public Boolean process(@NotNull String eachProperty, XmlElementDescriptor descriptor) {
280 if (eachProperty.equals(property)) return true;
281 return null;
283 }) != null;
286 @Override
287 public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
288 return ElementManipulators.getManipulator(myElement).handleContentChange(myElement, myRange, newElementName);
291 @NotNull
292 public Object[] getVariants() {
293 List<Object> result = new ArrayList<Object>();
294 collectVariants(result);
295 return result.toArray(new Object[result.size()]);
298 protected void collectVariants(List<Object> result) {
299 collectBasedirVariants(result);
300 collectProjectSchemaVariants(result);
301 collectSettingsXmlSchemaVariants(result);
302 collectPropertiesVariants(result);
303 collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.SYSTEM_PROPERTIES_FILE, null, result);
304 collectSystemEnvProperties(MavenPropertiesVirtualFileSystem.ENV_PROPERTIES_FILE, "env.", result);
307 private void collectBasedirVariants(List<Object> result) {
308 PsiDirectory basedir = getBaseDir();
309 result.add(createLookupElement(basedir, "basedir", MavenIcons.MAVEN_ICON));
310 result.add(createLookupElement(basedir, "pom.basedir", MavenIcons.MAVEN_ICON));
311 result.add(createLookupElement(basedir, "project.basedir", MavenIcons.MAVEN_ICON));
314 private void collectProjectSchemaVariants(final List<Object> result) {
315 processSchema(MavenSchemaProvider.MAVEN_PROJECT_SCHEMA_URL, new CollectingSchemaProcessor(result) {
316 @Override
317 public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
318 super.process(property, descriptor);
319 String prefix = "project.";
320 if (property.length() > prefix.length()) {
321 String unqualified = property.substring(prefix.length());
322 super.process("pom." + unqualified, descriptor);
323 super.process(unqualified, descriptor);
325 return null;
330 private void collectSettingsXmlSchemaVariants(final List<Object> result) {
331 processSchema(MavenSchemaProvider.MAVEN_SETTINGS_SCHEMA_URL, new CollectingSchemaProcessor(result));
334 private void collectPropertiesVariants(final List<Object> result) {
335 processProperties(myProjectDom, new PropertyProcessor<Object>() {
336 public Object process(@NotNull XmlTag property) {
337 result.add(createLookupElement(property, property.getName()));
338 return null;
343 private void collectSystemEnvProperties(String propertiesFileName, String prefix, List<Object> result) {
344 PropertiesFile file = MavenDomUtil.getPropertiesFile(myProject, propertiesFileName);
345 collectPropertiesFileVariants(file, prefix, result);
348 protected void collectPropertiesFileVariants(PropertiesFile file, String prefix, List<Object> result) {
349 for (Property each : file.getProperties()) {
350 String name = each.getKey();
351 if (prefix != null) name = prefix + name;
352 result.add(createLookupElement(each, name));
356 private static LookupElement createLookupElement(Object element, String name) {
357 return createLookupElement(element, name, Icons.PROPERTY_ICON);
360 private static LookupElement createLookupElement(Object element, String name, Icon icon) {
361 return LookupElementBuilder.create(element, name)
362 .setIcon(icon)
363 .setPresentableText(name);
366 @Nullable
367 private <T> T processSchema(String schema, SchemaProcessor<T> processor) {
368 VirtualFile file = MavenSchemaProvider.getSchemaFile(schema);
369 PsiFile psiFile = PsiManager.getInstance(myProject).findFile(file);
370 if (!(psiFile instanceof XmlFile)) return null;
372 XmlFile xmlFile = (XmlFile)psiFile;
373 XmlDocument document = xmlFile.getDocument();
374 XmlNSDescriptor desc = (XmlNSDescriptor)document.getMetaData();
375 XmlElementDescriptor[] descriptors = desc.getRootElementsDescriptors(document);
376 return doProcessSchema(descriptors, null, processor, new THashSet<XmlElementDescriptor>());
379 private <T> T doProcessSchema(XmlElementDescriptor[] descriptors,
380 String prefix,
381 SchemaProcessor<T> processor,
382 Set<XmlElementDescriptor> recursionGuard) {
383 for (XmlElementDescriptor each : descriptors) {
384 if (isCollection(each)) continue;
386 if (recursionGuard.contains(each)) continue;
387 recursionGuard.add(each);
388 try {
389 String name = each.getName();
390 if (prefix != null) name = prefix + "." + name;
392 T result = processor.process(name, each);
393 if (result != null) return result;
395 result = doProcessSchema(each.getElementsDescriptors(null), name, processor, recursionGuard);
396 if (result != null) return result;
398 finally {
399 recursionGuard.remove(each);
403 return null;
406 private <T> boolean isCollection(XmlElementDescriptor each) {
407 XmlTag declaration = (XmlTag)each.getDeclaration();
408 if (declaration != null) {
409 XmlTag complexType = declaration.findFirstSubTag("xs:complexType");
410 if (complexType != null) {
411 if (complexType.findFirstSubTag("xs:sequence") != null) return true;
414 return false;
417 @Override
418 public boolean isSoft() {
419 return mySoft;
422 private interface PropertyProcessor<T> {
423 @Nullable
424 T process(@NotNull XmlTag property);
427 private interface SchemaProcessor<T> {
428 @Nullable
429 T process(@NotNull String property, XmlElementDescriptor descriptor);
432 private class CollectingSchemaProcessor implements SchemaProcessor {
433 private final List<Object> myResult;
435 public CollectingSchemaProcessor(List<Object> result) {
436 myResult = result;
439 @Nullable
440 public Object process(@NotNull String property, XmlElementDescriptor descriptor) {
441 myResult.add(createLookupElement(descriptor, property, MavenIcons.MAVEN_ICON));
442 return null;
446 private abstract class MyMavenParentProjectFileProcessor<T> extends MavenParentProjectFileProcessor<T> {
447 protected VirtualFile findManagedFile(@NotNull MavenId id) {
448 MavenProject project = myProjectsManager.findProject(id);
449 return project == null ? null : project.getFile();
452 @Nullable
453 public T process(@NotNull MavenDomProjectModel projectDom) {
454 MavenDomParent parent = projectDom.getMavenParent();
455 MavenParentDesc parentDesc = null;
456 if (DomUtil.hasXml(parent)) {
457 String parentGroupId = parent.getGroupId().getStringValue();
458 String parentArtifactId = parent.getArtifactId().getStringValue();
459 String parentVersion = parent.getVersion().getStringValue();
460 String parentRelativePath = parent.getRelativePath().getStringValue();
461 if (StringUtil.isEmptyOrSpaces(parentRelativePath)) parentRelativePath = "../pom.xml";
462 MavenId parentId = new MavenId(parentGroupId, parentArtifactId, parentVersion);
463 parentDesc = new MavenParentDesc(parentId, parentRelativePath);
466 return process(myProjectsManager.getGeneralSettings(), MavenDomUtil.getVirtualFile(projectDom), parentDesc);