All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.uiDesigner.binding.FormReferenceProvider Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition ui-designer library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.uiDesigner.binding;

import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiReferenceProcessor;
import com.intellij.psi.util.*;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.uiDesigner.UIFormXmlConstants;
import com.intellij.uiDesigner.compiler.Utils;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yole
 */
public class FormReferenceProvider extends PsiReferenceProvider {
  private static class CachedFormData {
    PsiReference[] myReferences;
    Map> myFieldNameToTypeMap;

    public CachedFormData(final PsiReference[] refs, final Map> map) {
      myReferences = refs;
      myFieldNameToTypeMap = map;
    }
  }

  private static final Key> CACHED_DATA = Key.create("Cached form reference");

  @NotNull
  public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull final ProcessingContext context) {
    if (element instanceof PsiPlainTextFile) {
      final PsiPlainTextFile plainTextFile = (PsiPlainTextFile) element;
      if (plainTextFile.getFileType().equals(StdFileTypes.GUI_DESIGNER_FORM)) {
        return getCachedData(plainTextFile).myReferences;
      }
    }
    return PsiReference.EMPTY_ARRAY;
  }

  @Nullable
  public static PsiFile getFormFile(PsiField field) {
    PsiReference ref = getFormReference(field);
    if (ref != null) {
      return ref.getElement().getContainingFile();
    }
    return null;
  }

  @Nullable
  public static PsiReference getFormReference(PsiField field) {
    final PsiClass containingClass = field.getContainingClass();
    if (containingClass != null && containingClass.getQualifiedName() != null) {
      final List forms = FormClassIndex.findFormsBoundToClass(containingClass.getProject(), containingClass);
      for (PsiFile formFile : forms) {
        final PsiReference[] refs = formFile.getReferences();
        for (final PsiReference ref : refs) {
          if (ref.isReferenceTo(field)) {
            return ref;
          }
        }
      }
    }
    return null;
  }

  public static @Nullable
  PsiType getGUIComponentType(final PsiPlainTextFile file, String fieldName) {
    final Map> fieldNameToTypeMap = getCachedData(file).myFieldNameToTypeMap;
    final Pair typeRangePair = fieldNameToTypeMap.get(fieldName);
    return typeRangePair != null? typeRangePair.getFirst() : null;
  }

  public static void setGUIComponentType(PsiPlainTextFile file, String fieldName, String typeText) {
    final Map> fieldNameToTypeMap = getCachedData(file).myFieldNameToTypeMap;
    final Pair typeRangePair = fieldNameToTypeMap.get(fieldName);
    if (typeRangePair != null) {
      final TextRange range = typeRangePair.getSecond();
      if (range != null) {
        PsiDocumentManager.getInstance(file.getProject()).getDocument(file).replaceString(range.getStartOffset(), range.getEndOffset(), typeText);
      }
    }
  }

  private static void processReferences(final PsiPlainTextFile file, final PsiReferenceProcessor processor) {
    final Project project = file.getProject();
    final XmlTag rootTag = ApplicationManager.getApplication().runReadAction(new Computable() {
      public XmlTag compute() {
        final XmlFile xmlFile = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText("a.xml", XmlFileType.INSTANCE, file.getViewProvider().getContents());
        return xmlFile.getRootTag();
      }
    });

    if (rootTag == null || !Utils.FORM_NAMESPACE.equals(rootTag.getNamespace())) {
      return;
    }

    @NonNls final String name = rootTag.getName();
    if (!"form".equals(name)){
      return;
    }

    PsiReference classReference = null;

    final XmlAttribute classToBind = rootTag.getAttribute("bind-to-class", null);
    if (classToBind != null) {
      // reference to class
      final XmlAttributeValue valueElement = classToBind.getValueElement();
      if (valueElement == null) {
        return;
      }
      final String className = valueElement.getValue().replace('$','.');
      final PsiReference[] referencesByString = new JavaClassReferenceProvider().getReferencesByString(className, file, valueElement.getTextRange().getStartOffset() + 1);
      if(referencesByString.length < 1){
        // There are no references there
        return;
      }
      for (PsiReference aReferencesByString : referencesByString) {
        processor.execute(aReferencesByString);
      }
      classReference = referencesByString[referencesByString.length - 1];
    }

    final PsiReference finalClassReference = classReference;
    ApplicationManager.getApplication().runReadAction(new Runnable() {
      public void run() {
        processReferences(rootTag, finalClassReference, file, processor);
      }
    });
  }

  private static TextRange getValueRange(final XmlAttribute classToBind) {
    final XmlAttributeValue valueElement = classToBind.getValueElement();
    final TextRange textRange = valueElement.getTextRange();
    return new TextRange(textRange.getStartOffset() + 1, textRange.getEndOffset() - 1); // skip " "
  }

  private static void processReferences(final XmlTag tag,
                                        final PsiReference classReference,
                                        final PsiPlainTextFile file,
                                        final PsiReferenceProcessor processor) {
    final XmlAttribute clsAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_CLASS, null);
    final String classNameStr = clsAttribute != null? clsAttribute.getValue().replace('$','.') : null;
    // field
    {
      final XmlAttribute bindingAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_BINDING, null);
      if (bindingAttribute != null && classReference != null) {
        final XmlAttribute customCreateAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_CUSTOM_CREATE, null);
        boolean customCreate = (customCreateAttribute != null && Boolean.parseBoolean(customCreateAttribute.getValue()));
        final TextRange nameRange = clsAttribute != null ? getValueRange(clsAttribute) : null;
        processor.execute(new FieldFormReference(file, classReference, getValueRange(bindingAttribute), classNameStr, nameRange, customCreate));
      }
      final XmlAttribute titleBundleAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_TITLE_RESOURCE_BUNDLE, null);
      final XmlAttribute titleKeyAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_TITLE_KEY, null);
      if (titleBundleAttribute != null && titleKeyAttribute != null) {
        processResourceBundleFileReferences(file, processor, titleBundleAttribute);
        processor.execute(new ResourceBundleKeyReference(file, titleBundleAttribute.getValue(), getValueRange(titleKeyAttribute)));
      }

      final XmlAttribute bundleAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_RESOURCE_BUNDLE, null);
      final XmlAttribute keyAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_KEY, null);
      if (bundleAttribute != null && keyAttribute != null) {
        processResourceBundleFileReferences(file, processor, bundleAttribute);
        processor.execute(new ResourceBundleKeyReference(file, bundleAttribute.getValue(), getValueRange(keyAttribute)));
      }

      processNestedFormReference(tag, processor, file);
      processButtonGroupReference(tag, processor, file, classReference);
    }

    // component class
    {
      if (clsAttribute != null) {
        final JavaClassReferenceProvider provider = new JavaClassReferenceProvider();
        final PsiReference[] referencesByString = provider.getReferencesByString(classNameStr, file, clsAttribute.getValueElement().getTextRange().getStartOffset() + 1);
        if(referencesByString.length < 1){
          // There are no references there
          return;
        }
        for (PsiReference aReferencesByString : referencesByString) {
          processor.execute(aReferencesByString);
        }
      }
    }

    // property references
    XmlTag parentTag = tag.getParentTag();
    if (parentTag != null && parentTag.getName().equals(UIFormXmlConstants.ELEMENT_PROPERTIES)) {
      XmlTag componentTag = parentTag.getParentTag();
      if (componentTag != null) {
        String className = componentTag.getAttributeValue(UIFormXmlConstants.ATTRIBUTE_CLASS, Utils.FORM_NAMESPACE);
        if (className != null) {
          processPropertyReference(tag, processor, file, className.replace('$', '.'));
        }
      }
    }

    final XmlTag[] subtags = tag.getSubTags();
    for (XmlTag subtag : subtags) {
      processReferences(subtag, classReference, file, processor);
    }
  }

  private static void processResourceBundleFileReferences(final PsiPlainTextFile file,
                                                          final PsiReferenceProcessor processor,
                                                          final XmlAttribute titleBundleAttribute) {
    processPackageReferences(file, processor, titleBundleAttribute);
    processor.execute(new ResourceBundleFileReference(file, getValueRange(titleBundleAttribute)));
  }

  private static void processPackageReferences(final PsiPlainTextFile file,
                                               final PsiReferenceProcessor processor,
                                               final XmlAttribute attribute) {
    final TextRange valueRange = getValueRange(attribute);
    final String value = attribute.getValue();
    int pos=-1;
    while(true) {
      pos = value.indexOf('/', pos+1);
      if (pos < 0) {
        break;
      }
      processor.execute(new FormPackageReference(file, new TextRange(valueRange.getStartOffset(), valueRange.getStartOffset() + pos)));
    }
  }

  private static void processNestedFormReference(final XmlTag tag, final PsiReferenceProcessor processor, final PsiPlainTextFile file) {
    final XmlAttribute formFileAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_FORM_FILE, null);
    if (formFileAttribute != null) {
      processPackageReferences(file, processor, formFileAttribute);
      processor.execute(new ResourceFileReference(file, getValueRange(formFileAttribute)));
    }
  }

  private static void processButtonGroupReference(final XmlTag tag, final PsiReferenceProcessor processor, final PsiPlainTextFile file,
                                                  final PsiReference classReference) {
    final XmlAttribute boundAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_BOUND, null);
    final XmlAttribute nameAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_NAME, null);
    if (boundAttribute != null && Boolean.parseBoolean(boundAttribute.getValue()) && nameAttribute != null) {
      processor.execute(new FieldFormReference(file, classReference, getValueRange(nameAttribute), null, null, false));
    }
  }

  private static void processPropertyReference(final XmlTag tag, final PsiReferenceProcessor processor, final PsiPlainTextFile file,
                                               final String className) {
    final XmlAttribute valueAttribute = tag.getAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, null);
    if (valueAttribute != null) {
      PsiReference reference = ApplicationManager.getApplication().runReadAction(new Computable() {
        @Nullable
        public PsiReference compute() {
          final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(file.getProject());
          final Module module = ModuleUtil.findModuleForPsiElement(file);
          if (module == null) return null;
          final GlobalSearchScope scope = module.getModuleWithDependenciesAndLibrariesScope(false);
          PsiClass psiClass = psiFacade.findClass(className, scope);
          if (psiClass != null) {
            PsiMethod getter = PropertyUtil.findPropertyGetter(psiClass, tag.getName(), false, true);
            if (getter != null) {
              final PsiType returnType = getter.getReturnType();
              if (returnType instanceof PsiClassType) {
                PsiClassType propClassType = (PsiClassType)returnType;
                PsiClass propClass = propClassType.resolve();
                if (propClass != null) {
                  if (propClass.isEnum()) {
                    return new FormEnumConstantReference(file, getValueRange(valueAttribute), propClassType);
                  }
                  PsiClass iconClass = psiFacade.findClass("javax.swing.Icon", scope);
                  if (iconClass != null && InheritanceUtil.isInheritorOrSelf(propClass, iconClass, true)) {
                    return new ResourceFileReference(file, getValueRange(valueAttribute));
                  }
                }
              }
            }
          }
          return null;
      }});
      if (reference != null) {
        if (reference instanceof ResourceFileReference) {
          processPackageReferences(file, processor, valueAttribute);
        }
        processor.execute(reference);
      }
    }
  }

  @Nullable
  public static String getBundleName(final PropertiesFile propertiesFile) {
    final PsiDirectory directory = propertiesFile.getParent();
    if (directory == null) {
      return null;
    }
    final String packageName;
    final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory);
    if (aPackage == null) {
      packageName = "";
    }
    else {
      packageName = aPackage.getQualifiedName();
    }

    //noinspection NonConstantStringShouldBeStringBuffer
    String bundleName = propertiesFile.getResourceBundle().getBaseName();

    if (packageName.length() > 0) {
      bundleName = packageName + '.' + bundleName;
    }
    bundleName = bundleName.replace('.', '/');
    return bundleName;
  }

  private static CachedFormData getCachedData(final PsiPlainTextFile element) {
    CachedValue data = element.getUserData(CACHED_DATA);

    if(data == null) {
      data = CachedValuesManager.getManager(element.getProject()).createCachedValue(new CachedValueProvider() {
        final Map> map = new HashMap>();
        public Result compute() {
          final PsiReferenceProcessor.CollectElements processor = new PsiReferenceProcessor.CollectElements() {
            public boolean execute(PsiReference ref) {
              if (ref instanceof FieldFormReference) {
                final FieldFormReference fieldRef = ((FieldFormReference)ref);
                final String componentClassName = fieldRef.getComponentClassName();
                if (componentClassName != null) {
                  final PsiClassType type = JavaPsiFacade.getInstance(element.getProject()).getElementFactory()
                    .createTypeByFQClassName(componentClassName, element.getResolveScope());
                  map.put(fieldRef.getRangeText(), new Pair(type, fieldRef.getComponentClassNameTextRange()));
                }
              }
              return super.execute(ref);
            }
          };
          processReferences(element, processor);
          final PsiReference[] refs = processor.toArray(PsiReference.EMPTY_ARRAY);
          return new Result(new CachedFormData(refs, map), element);
        }
      }, false);
      element.putUserData(CACHED_DATA, data);
    }
    return data.getValue();
  }

  public void projectOpened() {
  }

  public void projectClosed() {
  }

  @NotNull @NonNls
  public String getComponentName() {
    return "FormReferenceProvider";
  }

  public void initComponent() {
  }

  public void disposeComponent() {
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy