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
;
51 import java
.util
.ArrayList
;
52 import java
.util
.List
;
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
;
64 myProjectDom
= MavenDomUtil
.getMavenDomProjectModel(myProject
, mavenProject
.getFile());
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]);
85 // 4. profiles in pom.xml
87 // 6. parent profiles.xml
88 // 7. profiles in parent pom.xml
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
>());
121 private PsiElement
resolveSystemPropety() {
122 return MavenDomUtil
.findProperty(myProject
,
123 MavenPropertiesVirtualFileSystem
.SYSTEM_PROPERTIES_FILE
,
128 private PsiElement
resolveEnvPropety() {
129 return MavenDomUtil
.findProperty(myProject
,
130 MavenPropertiesVirtualFileSystem
.ENV_PROPERTIES_FILE
,
131 myText
.substring("env.".length()));
135 private PsiElement
resolveBasedir() {
139 private PsiDirectory
getBaseDir() {
140 return PsiManager
.getInstance(myProject
).findDirectory(myMavenProject
.getDirectoryFile());
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
;
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
;
183 private boolean schemaHasProperty(String schema
, final String property
) {
184 return processSchema(schema
, new SchemaProcessor
<Boolean
>() {
186 public Boolean
process(@NotNull String eachProperty
, XmlElementDescriptor descriptor
) {
187 if (eachProperty
.equals(property
)) return true;
194 public PsiElement
handleElementRename(String newElementName
) throws IncorrectOperationException
{
195 return ElementManipulators
.getManipulator(myElement
).handleContentChange(myElement
, myRange
, newElementName
);
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
) {
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
);
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());
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
)
270 .setPresentableText(name
);
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
,
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
);
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
;
306 recursionGuard
.remove(each
);
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;
325 public boolean isSoft() {
329 private interface SchemaProcessor
<T
> {
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
) {
342 public Object
process(@NotNull String property
, XmlElementDescriptor descriptor
) {
343 myResult
.add(createLookupElement(descriptor
, property
, MavenIcons
.MAVEN_ICON
));
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();
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
);