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 com
.intellij
.uiDesigner
.binding
;
18 import com
.intellij
.lang
.properties
.psi
.PropertiesFile
;
19 import com
.intellij
.openapi
.application
.ApplicationManager
;
20 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
21 import com
.intellij
.openapi
.module
.Module
;
22 import com
.intellij
.openapi
.module
.ModuleUtil
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.util
.Computable
;
25 import com
.intellij
.openapi
.util
.Key
;
26 import com
.intellij
.openapi
.util
.Pair
;
27 import com
.intellij
.openapi
.util
.TextRange
;
28 import com
.intellij
.psi
.*;
29 import com
.intellij
.psi
.impl
.source
.resolve
.reference
.impl
.providers
.JavaClassReferenceProvider
;
30 import com
.intellij
.psi
.search
.GlobalSearchScope
;
31 import com
.intellij
.psi
.search
.PsiReferenceProcessor
;
32 import com
.intellij
.psi
.util
.*;
33 import com
.intellij
.psi
.xml
.*;
34 import com
.intellij
.uiDesigner
.UIFormXmlConstants
;
35 import com
.intellij
.uiDesigner
.compiler
.Utils
;
36 import com
.intellij
.util
.ProcessingContext
;
37 import org
.jetbrains
.annotations
.NonNls
;
38 import org
.jetbrains
.annotations
.NotNull
;
39 import org
.jetbrains
.annotations
.Nullable
;
41 import java
.util
.HashMap
;
42 import java
.util
.List
;
48 public class FormReferenceProvider
extends PsiReferenceProvider
{
49 private static class CachedFormData
{
50 PsiReference
[] myReferences
;
51 Map
<String
, Pair
<PsiType
, TextRange
>> myFieldNameToTypeMap
;
53 public CachedFormData(final PsiReference
[] refs
, final Map
<String
, Pair
<PsiType
, TextRange
>> map
) {
55 myFieldNameToTypeMap
= map
;
59 private static final Key
<CachedValue
<CachedFormData
>> CACHED_DATA
= Key
.create("Cached form reference");
62 public PsiReference
[] getReferencesByElement(@NotNull final PsiElement element
, @NotNull final ProcessingContext context
) {
63 if (element
instanceof PsiPlainTextFile
) {
64 final PsiPlainTextFile plainTextFile
= (PsiPlainTextFile
) element
;
65 if (plainTextFile
.getFileType().equals(StdFileTypes
.GUI_DESIGNER_FORM
)) {
66 return getCachedData(plainTextFile
).myReferences
;
69 return PsiReference
.EMPTY_ARRAY
;
73 public static PsiFile
getFormFile(PsiField field
) {
74 PsiReference ref
= getFormReference(field
);
76 return ref
.getElement().getContainingFile();
82 public static PsiReference
getFormReference(PsiField field
) {
83 final PsiClass containingClass
= field
.getContainingClass();
84 if (containingClass
!= null && containingClass
.getQualifiedName() != null) {
85 final List
<PsiFile
> forms
= FormClassIndex
.findFormsBoundToClass(containingClass
);
86 for (PsiFile formFile
: forms
) {
87 final PsiReference
[] refs
= formFile
.getReferences();
88 for (final PsiReference ref
: refs
) {
89 if (ref
.isReferenceTo(field
)) {
98 public static @Nullable
99 PsiType
getGUIComponentType(final PsiPlainTextFile file
, String fieldName
) {
100 final Map
<String
, Pair
<PsiType
, TextRange
>> fieldNameToTypeMap
= getCachedData(file
).myFieldNameToTypeMap
;
101 final Pair
<PsiType
, TextRange
> typeRangePair
= fieldNameToTypeMap
.get(fieldName
);
102 return typeRangePair
!= null? typeRangePair
.getFirst() : null;
105 public static void setGUIComponentType(PsiPlainTextFile file
, String fieldName
, PsiType componentTypeToSet
) {
106 final Map
<String
, Pair
<PsiType
, TextRange
>> fieldNameToTypeMap
= getCachedData(file
).myFieldNameToTypeMap
;
107 final Pair
<PsiType
, TextRange
> typeRangePair
= fieldNameToTypeMap
.get(fieldName
);
108 if (typeRangePair
!= null) {
109 final TextRange range
= typeRangePair
.getSecond();
111 PsiDocumentManager
.getInstance(file
.getProject()).getDocument(file
).replaceString(range
.getStartOffset(), range
.getEndOffset(), componentTypeToSet
.getCanonicalText());
116 private static void processReferences(final PsiPlainTextFile file
, final PsiReferenceProcessor processor
) {
117 final Project project
= file
.getProject();
118 final PsiFile _f
= PsiFileFactory
.getInstance(project
).createFileFromText("a.xml", file
.getText());
120 final XmlFile xmlFile
= (XmlFile
)_f
;
121 final XmlTag rootTag
= ApplicationManager
.getApplication().runReadAction(new Computable
<XmlTag
>() {
122 public XmlTag
compute() {
123 final XmlDocument document
= xmlFile
.getDocument();
125 return document
.getRootTag();
130 if (rootTag
== null || !Utils
.FORM_NAMESPACE
.equals(rootTag
.getNamespace())) {
134 @NonNls final String name
= rootTag
.getName();
135 if (!"form".equals(name
)){
139 PsiReference classReference
= null;
141 final XmlAttribute classToBind
= rootTag
.getAttribute("bind-to-class", null);
142 if (classToBind
!= null) {
143 // reference to class
144 final String className
= classToBind
.getValue().replace('$','.');
145 final XmlAttributeValue valueElement
= classToBind
.getValueElement();
146 final PsiReference
[] referencesByString
= new JavaClassReferenceProvider(project
).getReferencesByString(className
, file
, valueElement
.getTextRange().getStartOffset() + 1);
147 if(referencesByString
.length
< 1){
148 // There are no references there
151 for (PsiReference aReferencesByString
: referencesByString
) {
152 processor
.execute(aReferencesByString
);
154 classReference
= referencesByString
[referencesByString
.length
- 1];
157 processReferences(rootTag
, classReference
, file
, processor
);
160 private static TextRange
getValueRange(final XmlAttribute classToBind
) {
161 final XmlAttributeValue valueElement
= classToBind
.getValueElement();
162 final TextRange textRange
= valueElement
.getTextRange();
163 return new TextRange(textRange
.getStartOffset() + 1, textRange
.getEndOffset() - 1); // skip " "
166 private static void processReferences(final XmlTag tag
,
167 final PsiReference classReference
,
168 final PsiPlainTextFile file
,
169 final PsiReferenceProcessor processor
) {
170 final XmlAttribute clsAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_CLASS
, null);
171 final String classNameStr
= clsAttribute
!= null? clsAttribute
.getValue().replace('$','.') : null;
174 final XmlAttribute bindingAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_BINDING
, null);
175 if (bindingAttribute
!= null && classReference
!= null) {
176 final XmlAttribute customCreateAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_CUSTOM_CREATE
, null);
177 boolean customCreate
= (customCreateAttribute
!= null && Boolean
.parseBoolean(customCreateAttribute
.getValue()));
178 final TextRange nameRange
= clsAttribute
!= null ?
getValueRange(clsAttribute
) : null;
179 processor
.execute(new FieldFormReference(file
, classReference
, getValueRange(bindingAttribute
), classNameStr
, nameRange
, customCreate
));
181 final XmlAttribute titleBundleAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_TITLE_RESOURCE_BUNDLE
, null);
182 final XmlAttribute titleKeyAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_TITLE_KEY
, null);
183 if (titleBundleAttribute
!= null && titleKeyAttribute
!= null) {
184 processResourceBundleFileReferences(file
, processor
, titleBundleAttribute
);
185 processor
.execute(new ResourceBundleKeyReference(file
, titleBundleAttribute
.getValue(), getValueRange(titleKeyAttribute
)));
188 final XmlAttribute bundleAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_RESOURCE_BUNDLE
, null);
189 final XmlAttribute keyAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_KEY
, null);
190 if (bundleAttribute
!= null && keyAttribute
!= null) {
191 processResourceBundleFileReferences(file
, processor
, bundleAttribute
);
192 processor
.execute(new ResourceBundleKeyReference(file
, bundleAttribute
.getValue(), getValueRange(keyAttribute
)));
195 processNestedFormReference(tag
, processor
, file
);
196 processButtonGroupReference(tag
, processor
, file
, classReference
);
201 if (clsAttribute
!= null) {
202 final JavaClassReferenceProvider provider
= new JavaClassReferenceProvider(tag
.getProject());
203 final PsiReference
[] referencesByString
= provider
.getReferencesByString(classNameStr
, file
, clsAttribute
.getValueElement().getTextRange().getStartOffset() + 1);
204 if(referencesByString
.length
< 1){
205 // There are no references there
208 for (PsiReference aReferencesByString
: referencesByString
) {
209 processor
.execute(aReferencesByString
);
214 // property references
215 XmlTag parentTag
= tag
.getParentTag();
216 if (parentTag
!= null && parentTag
.getName().equals(UIFormXmlConstants
.ELEMENT_PROPERTIES
)) {
217 XmlTag componentTag
= parentTag
.getParentTag();
218 if (componentTag
!= null) {
219 String className
= componentTag
.getAttributeValue(UIFormXmlConstants
.ATTRIBUTE_CLASS
, Utils
.FORM_NAMESPACE
);
220 if (className
!= null) {
221 processPropertyReference(tag
, processor
, file
, className
.replace('$', '.'));
226 final XmlTag
[] subtags
= tag
.getSubTags();
227 for (XmlTag subtag
: subtags
) {
228 processReferences(subtag
, classReference
, file
, processor
);
232 private static void processResourceBundleFileReferences(final PsiPlainTextFile file
,
233 final PsiReferenceProcessor processor
,
234 final XmlAttribute titleBundleAttribute
) {
235 processPackageReferences(file
, processor
, titleBundleAttribute
);
236 processor
.execute(new ResourceBundleFileReference(file
, getValueRange(titleBundleAttribute
)));
239 private static void processPackageReferences(final PsiPlainTextFile file
,
240 final PsiReferenceProcessor processor
,
241 final XmlAttribute attribute
) {
242 final TextRange valueRange
= getValueRange(attribute
);
243 final String value
= attribute
.getValue();
246 pos
= value
.indexOf('/', pos
+1);
250 processor
.execute(new FormPackageReference(file
, new TextRange(valueRange
.getStartOffset(), valueRange
.getStartOffset() + pos
)));
254 private static void processNestedFormReference(final XmlTag tag
, final PsiReferenceProcessor processor
, final PsiPlainTextFile file
) {
255 final XmlAttribute formFileAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_FORM_FILE
, null);
256 if (formFileAttribute
!= null) {
257 processPackageReferences(file
, processor
, formFileAttribute
);
258 processor
.execute(new ResourceFileReference(file
, getValueRange(formFileAttribute
)));
262 private static void processButtonGroupReference(final XmlTag tag
, final PsiReferenceProcessor processor
, final PsiPlainTextFile file
,
263 final PsiReference classReference
) {
264 final XmlAttribute boundAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_BOUND
, null);
265 final XmlAttribute nameAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_NAME
, null);
266 if (boundAttribute
!= null && Boolean
.parseBoolean(boundAttribute
.getValue()) && nameAttribute
!= null) {
267 processor
.execute(new FieldFormReference(file
, classReference
, getValueRange(nameAttribute
), null, null, false));
271 private static void processPropertyReference(final XmlTag tag
, final PsiReferenceProcessor processor
, final PsiPlainTextFile file
,
272 final String className
) {
273 final XmlAttribute valueAttribute
= tag
.getAttribute(UIFormXmlConstants
.ATTRIBUTE_VALUE
, null);
274 if (valueAttribute
!= null) {
275 PsiReference reference
= ApplicationManager
.getApplication().runReadAction(new Computable
<PsiReference
>() {
277 public PsiReference
compute() {
278 final JavaPsiFacade psiFacade
= JavaPsiFacade
.getInstance(file
.getProject());
279 final Module module
= ModuleUtil
.findModuleForPsiElement(file
);
280 if (module
== null) return null;
281 final GlobalSearchScope scope
= module
.getModuleWithDependenciesAndLibrariesScope(false);
282 PsiClass psiClass
= psiFacade
.findClass(className
, scope
);
283 if (psiClass
!= null) {
284 PsiMethod getter
= PropertyUtil
.findPropertyGetter(psiClass
, tag
.getName(), false, true);
285 if (getter
!= null) {
286 final PsiType returnType
= getter
.getReturnType();
287 if (returnType
instanceof PsiClassType
) {
288 PsiClassType propClassType
= (PsiClassType
)returnType
;
289 PsiClass propClass
= propClassType
.resolve();
290 if (propClass
!= null) {
291 if (propClass
.isEnum()) {
292 return new FormEnumConstantReference(file
, getValueRange(valueAttribute
), propClassType
);
294 PsiClass iconClass
= psiFacade
.findClass("javax.swing.Icon", scope
);
295 if (iconClass
!= null && InheritanceUtil
.isInheritorOrSelf(propClass
, iconClass
, true)) {
296 return new ResourceFileReference(file
, getValueRange(valueAttribute
));
304 if (reference
!= null) {
305 if (reference
instanceof ResourceFileReference
) {
306 processPackageReferences(file
, processor
, valueAttribute
);
308 processor
.execute(reference
);
314 public static String
getBundleName(final PropertiesFile propertiesFile
) {
315 final PsiDirectory directory
= propertiesFile
.getContainingDirectory();
316 if (directory
== null) {
319 final String packageName
;
320 final PsiPackage aPackage
= JavaDirectoryService
.getInstance().getPackage(directory
);
321 if (aPackage
== null) {
325 packageName
= aPackage
.getQualifiedName();
328 //noinspection NonConstantStringShouldBeStringBuffer
329 String bundleName
= propertiesFile
.getResourceBundle().getBaseName();
331 if (packageName
.length() > 0) {
332 bundleName
= packageName
+ '.' + bundleName
;
334 bundleName
= bundleName
.replace('.', '/');
338 private static CachedFormData
getCachedData(final PsiPlainTextFile element
) {
339 CachedValue
<CachedFormData
> data
= element
.getUserData(CACHED_DATA
);
342 data
= CachedValuesManager
.getManager(element
.getProject()).createCachedValue(new CachedValueProvider
<CachedFormData
>() {
343 final Map
<String
, Pair
<PsiType
, TextRange
>> map
= new HashMap
<String
, Pair
<PsiType
, TextRange
>>();
344 public Result
<CachedFormData
> compute() {
345 final PsiReferenceProcessor
.CollectElements processor
= new PsiReferenceProcessor
.CollectElements() {
346 public boolean execute(PsiReference ref
) {
347 if (ref
instanceof FieldFormReference
) {
348 final FieldFormReference fieldRef
= ((FieldFormReference
)ref
);
349 final String componentClassName
= fieldRef
.getComponentClassName();
350 if (componentClassName
!= null) {
351 final PsiClassType type
= JavaPsiFacade
.getInstance(element
.getProject()).getElementFactory()
352 .createTypeByFQClassName(componentClassName
, element
.getResolveScope());
353 map
.put(fieldRef
.getRangeText(), new Pair
<PsiType
, TextRange
>(type
, fieldRef
.getComponentClassNameTextRange()));
356 return super.execute(ref
);
359 processReferences(element
, processor
);
360 final PsiReference
[] refs
= processor
.toArray(PsiReference
.EMPTY_ARRAY
);
361 return new Result
<CachedFormData
>(new CachedFormData(refs
, map
), element
);
364 element
.putUserData(CACHED_DATA
, data
);
366 return data
.getValue();
369 public void projectOpened() {
372 public void projectClosed() {
376 public String
getComponentName() {
377 return "FormReferenceProvider";
380 public void initComponent() {
383 public void disposeComponent() {