deprecation removed
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / binding / FormReferenceProvider.java
blob511042ddb875b1d1ab7a9493e0f4f9b7e80b5bc9
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 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;
43 import java.util.Map;
45 /**
46 * @author yole
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) {
54 myReferences = refs;
55 myFieldNameToTypeMap = map;
59 private static final Key<CachedValue<CachedFormData>> CACHED_DATA = Key.create("Cached form reference");
61 @NotNull
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;
72 @Nullable
73 public static PsiFile getFormFile(PsiField field) {
74 PsiReference ref = getFormReference(field);
75 if (ref != null) {
76 return ref.getElement().getContainingFile();
78 return null;
81 @Nullable
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)) {
90 return ref;
95 return null;
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();
110 if (range != null) {
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())) {
131 return;
134 @NonNls final String name = rootTag.getName();
135 if (!"form".equals(name)){
136 return;
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
149 return;
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;
172 // field
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);
199 // component class
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
206 return;
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();
244 int pos=-1;
245 while(true) {
246 pos = value.indexOf('/', pos+1);
247 if (pos < 0) {
248 break;
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>() {
276 @Nullable
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));
302 return null;
303 }});
304 if (reference != null) {
305 if (reference instanceof ResourceFileReference) {
306 processPackageReferences(file, processor, valueAttribute);
308 processor.execute(reference);
313 @Nullable
314 public static String getBundleName(final PropertiesFile propertiesFile) {
315 final PsiDirectory directory = propertiesFile.getContainingDirectory();
316 if (directory == null) {
317 return null;
319 final String packageName;
320 final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory);
321 if (aPackage == null) {
322 packageName = "";
324 else {
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('.', '/');
335 return bundleName;
338 private static CachedFormData getCachedData(final PsiPlainTextFile element) {
339 CachedValue<CachedFormData> data = element.getUserData(CACHED_DATA);
341 if(data == null) {
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);
363 }, false);
364 element.putUserData(CACHED_DATA, data);
366 return data.getValue();
369 public void projectOpened() {
372 public void projectClosed() {
375 @NotNull @NonNls
376 public String getComponentName() {
377 return "FormReferenceProvider";
380 public void initComponent() {
383 public void disposeComponent() {