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
;
49 import java
.util
.ArrayList
;
50 import java
.util
.Collection
;
51 import java
.util
.List
;
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
;
63 myProjectDom
= MavenDomUtil
.getMavenDomProjectModel(myProject
, mavenProject
.getFile());
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]);
84 // 4. profiles in pom.xml
86 // 6. parent profiles.xml
87 // 7. profiles in parent pom.xml
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
>() {
105 public PsiElement
process(@NotNull XmlTag property
) {
106 if (property
.getName().equals(myText
)) return property
;
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
>());
126 private PsiElement
resolveSystemPropety() {
127 return MavenDomUtil
.findProperty(myProject
,
128 MavenPropertiesVirtualFileSystem
.SYSTEM_PROPERTIES_FILE
,
133 private PsiElement
resolveEnvPropety() {
134 return MavenDomUtil
.findProperty(myProject
,
135 MavenPropertiesVirtualFileSystem
.ENV_PROPERTIES_FILE
,
136 myText
.substring("env.".length()));
140 private PsiElement
resolveBasedir() {
144 private PsiDirectory
getBaseDir() {
145 return PsiManager
.getInstance(myProject
).findDirectory(myMavenProject
.getDirectoryFile());
149 private <T
> T
processProperties(@NotNull MavenDomProjectModel projectDom
, final PropertyProcessor
<T
> processor
) {
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
);
170 private <T
> T
processSettingsXmlProperties(@Nullable MavenProject mavenProject
, PropertyProcessor
<T
> processor
) {
171 MavenGeneralSettings settings
= myProjectsManager
.getGeneralSettings();
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
;
185 private <T
> T
processProjectProperties(MavenDomProjectModel projectDom
,
186 MavenProject mavenProjectOrNull
,
187 PropertyProcessor
<T
> processor
) {
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
);
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
);
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
;
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
;
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
;
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
;
276 private boolean schemaHasProperty(String schema
, final String property
) {
277 return processSchema(schema
, new SchemaProcessor
<Boolean
>() {
279 public Boolean
process(@NotNull String eachProperty
, XmlElementDescriptor descriptor
) {
280 if (eachProperty
.equals(property
)) return true;
287 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
288 return ElementManipulators
.getManipulator(myElement
).handleContentChange(myElement
, myRange
, newElementName
);
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
) {
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
);
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()));
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
)
363 .setPresentableText(name
);
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
,
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
);
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
;
399 recursionGuard
.remove(each
);
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;
418 public boolean isSoft() {
422 private interface PropertyProcessor
<T
> {
424 T
process(@NotNull XmlTag property
);
427 private interface SchemaProcessor
<T
> {
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
) {
440 public Object
process(@NotNull String property
, XmlElementDescriptor descriptor
) {
441 myResult
.add(createLookupElement(descriptor
, property
, MavenIcons
.MAVEN_ICON
));
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();
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
);