com.intellij.uiDesigner.propertyInspector.properties.BindingProperty.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.uiDesigner.propertyInspector.properties.BindingProperty.java

Source

/*
 * Copyright 2000-2009 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.propertyInspector.properties;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.CommonBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.uiDesigner.FormEditingUtil;
import com.intellij.uiDesigner.GuiFormFileType;
import com.intellij.uiDesigner.UIDesignerBundle;
import com.intellij.uiDesigner.compiler.AsmCodeGenerator;
import com.intellij.uiDesigner.designSurface.GuiEditor;
import com.intellij.uiDesigner.designSurface.InsertComponentProcessor;
import com.intellij.uiDesigner.inspections.FormInspectionUtil;
import com.intellij.uiDesigner.propertyInspector.DesignerToolWindowManager;
import com.intellij.uiDesigner.propertyInspector.Property;
import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
import com.intellij.uiDesigner.propertyInspector.editors.BindingEditor;
import com.intellij.uiDesigner.propertyInspector.renderers.LabelPropertyRenderer;
import com.intellij.uiDesigner.quickFixes.CreateFieldFix;
import com.intellij.uiDesigner.radComponents.RadComponent;
import com.intellij.uiDesigner.radComponents.RadRootContainer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;

/**
 * @author Anton Katilin
 * @author Vladimir Kondratyev
 */
public final class BindingProperty extends Property<RadComponent, String> {
    private static final Logger LOG = Logger
            .getInstance("#com.intellij.uiDesigner.propertyInspector.properties.BindingProperty");

    private final PropertyRenderer<String> myRenderer = new LabelPropertyRenderer<String>() {
        protected void customize(@NotNull final String value) {
            setText(value);
        }
    };
    private final BindingEditor myEditor;
    @NonNls
    private static final String PREFIX_HTML = "<html>";

    public BindingProperty(final Project project) {
        super(null, "field name");
        myEditor = new BindingEditor(project);
    }

    public PropertyEditor<String> getEditor() {
        return myEditor;
    }

    @NotNull
    public PropertyRenderer<String> getRenderer() {
        return myRenderer;
    }

    public String getValue(final RadComponent component) {
        return component.getBinding();
    }

    protected void setValueImpl(final RadComponent component, final String value) throws Exception {
        if (Comparing.strEqual(value, component.getBinding(), true)) {
            return;
        }

        if (value.length() > 0
                && !JavaPsiFacade.getInstance(component.getProject()).getNameHelper().isIdentifier(value)) {
            throw new Exception("Value '" + value + "' is not a valid identifier");
        }

        final RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
        final String oldBinding = getValue(component);

        // Check that binding remains unique

        if (value.length() > 0) {
            if (!FormEditingUtil.isBindingUnique(component, value, root)) {
                throw new Exception(UIDesignerBundle.message("error.binding.not.unique"));
            }

            component.setBinding(value);
            component.setDefaultBinding(false);
        } else {
            if (component.isCustomCreateRequired()) {
                throw new Exception(UIDesignerBundle.message("error.custom.create.binding.required"));
            }
            component.setBinding(null);
            component.setCustomCreate(false);
        }

        // Set new value or rename old one. It means that previous binding exists
        // and the new one doesn't exist we need to ask user to create new field
        // or rename old one.

        updateBoundFieldName(root, oldBinding, value, component.getComponentClassName());
    }

    public static void updateBoundFieldName(final RadRootContainer root, final String oldName, final String newName,
            final String fieldClassName) {
        final String classToBind = root.getClassToBind();
        if (classToBind == null)
            return;

        final Project project = root.getProject();
        if (newName.length() == 0) {
            checkRemoveUnusedField(root, oldName, FormEditingUtil.getNextSaveUndoGroupId(project));
            return;
        }

        final PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(classToBind,
                GlobalSearchScope.allScope(project));
        if (aClass == null) {
            return;
        }

        if (oldName == null) {
            if (aClass.findFieldByName(newName, true) == null) {
                CreateFieldFix.runImpl(project, root, aClass, fieldClassName, newName, false,
                        FormEditingUtil.getNextSaveUndoGroupId(project));
            }
            return;
        }

        final PsiField oldField = aClass.findFieldByName(oldName, true);
        if (oldField == null) {
            return;
        }

        if (aClass.findFieldByName(newName, true) != null) {
            checkRemoveUnusedField(root, oldName, FormEditingUtil.getNextSaveUndoGroupId(project));
            return;
        }

        // Show question to the user

        if (!isFieldUnreferenced(oldField)) {
            final int option = Messages.showYesNoDialog(project,
                    MessageFormat.format(UIDesignerBundle.message("message.rename.field"), oldName, newName),
                    UIDesignerBundle.message("title.rename"), Messages.getQuestionIcon());

            if (option != 0/*Yes*/) {
                return;
            }
        }

        // Commit document before refactoring starts
        GuiEditor editor = DesignerToolWindowManager.getInstance(project).getActiveFormEditor();
        if (editor != null) {
            editor.refreshAndSave(false);
        }
        PsiDocumentManager.getInstance(project).commitAllDocuments();

        if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) {
            return;
        }

        final RenameProcessor processor = new RenameProcessor(project, oldField, newName, true, true);
        processor.run();
    }

    @Override
    public boolean isModified(final RadComponent component) {
        return component.getBinding() != null;
    }

    @Override
    public void resetValue(final RadComponent component) throws Exception {
        setValueImpl(component, "");
    }

    @Override
    public boolean appliesToSelection(final List<RadComponent> selection) {
        return selection.size() == 1;
    }

    @Nullable
    public static PsiField findBoundField(@NotNull final RadRootContainer root, final String fieldName) {
        final Project project = root.getProject();
        final String classToBind = root.getClassToBind();
        if (classToBind != null) {
            final PsiManager manager = PsiManager.getInstance(project);
            PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(classToBind,
                    GlobalSearchScope.allScope(project));
            if (aClass != null) {
                final PsiField oldBindingField = aClass.findFieldByName(fieldName, false);
                if (oldBindingField != null) {
                    return oldBindingField;
                }
            }
        }
        return null;
    }

    public static void checkRemoveUnusedField(final RadRootContainer rootContainer, final String fieldName,
            final Object undoGroupId) {
        final PsiField oldBindingField = findBoundField(rootContainer, fieldName);
        if (oldBindingField == null) {
            return;
        }
        final Project project = oldBindingField.getProject();
        final PsiClass aClass = oldBindingField.getContainingClass();
        if (isFieldUnreferenced(oldBindingField)) {
            if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) {
                return;
            }
            ApplicationManager.getApplication().runWriteAction(new Runnable() {
                public void run() {
                    CommandProcessor.getInstance().executeCommand(project, new Runnable() {
                        public void run() {
                            try {
                                oldBindingField.delete();
                            } catch (IncorrectOperationException e) {
                                Messages.showErrorDialog(project, UIDesignerBundle
                                        .message("error.cannot.delete.unused.field", e.getMessage()),
                                        CommonBundle.getErrorTitle());
                            }
                        }
                    }, UIDesignerBundle.message("command.delete.unused.field"), undoGroupId);
                }
            });
        }
    }

    private static boolean isFieldUnreferenced(final PsiField field) {
        try {
            return ReferencesSearch.search(field).forEach(new Processor<PsiReference>() {
                public boolean process(final PsiReference t) {
                    PsiFile f = t.getElement().getContainingFile();
                    if (f != null && f.getFileType().equals(GuiFormFileType.INSTANCE)) {
                        return true;
                    }
                    PsiMethod method = PsiTreeUtil.getParentOfType(t.getElement(), PsiMethod.class);
                    if (method != null && method.getName().equals(AsmCodeGenerator.SETUP_METHOD_NAME)) {
                        return true;
                    }
                    return false;
                }
            });
        } catch (IndexNotReadyException e) {
            return false;
        }
    }

    public static void checkCreateBindingFromText(final RadComponent component, final String text) {
        if (!component.isDefaultBinding()) {
            return;
        }
        RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
        PsiField boundField = findBoundField(root, component.getBinding());
        if (boundField == null || !isFieldUnreferenced(boundField)) {
            return;
        }

        String binding = suggestBindingFromText(component, text);
        if (binding != null) {
            new BindingProperty(component.getProject()).setValueEx(component, binding);
            // keep the binding marked as default
            component.setDefaultBinding(true);
        }
    }

    @Nullable
    public static String suggestBindingFromText(final RadComponent component, String text) {
        if (StringUtil.startsWithIgnoreCase(text, PREFIX_HTML)) {
            text = Pattern.compile("<.+?>").matcher(text).replaceAll("");
        }
        ArrayList<String> words = new ArrayList<String>(StringUtil.getWordsIn(text));
        if (words.size() > 0) {
            StringBuilder nameBuilder = new StringBuilder(StringUtil.decapitalize(words.get(0)));
            for (int i = 1; i < words.size() && i < 4; i++) {
                nameBuilder.append(StringUtil.capitalize(words.get(i)));
            }
            final String shortClassName = StringUtil
                    .capitalize(InsertComponentProcessor.getShortClassName(component.getComponentClassName()));
            if (shortClassName.equalsIgnoreCase(nameBuilder.toString())) {
                // avoid "buttonButton" case
                return null;
            }
            nameBuilder.append(shortClassName);

            RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
            Project project = root.getProject();
            String binding = JavaCodeStyleManager.getInstance(project)
                    .propertyNameToVariableName(nameBuilder.toString(), VariableKind.FIELD);
            if (FormEditingUtil.findComponentWithBinding(root, binding, component) != null) {
                binding = InsertComponentProcessor.getUniqueBinding(root, nameBuilder.toString());
            }
            return binding;
        }
        return null;
    }

    public static String getDefaultBinding(final RadComponent c) {
        RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(c);
        String binding = null;
        String text = FormInspectionUtil.getText(c.getModule(), c);
        if (text != null) {
            binding = suggestBindingFromText(c, text);
        }
        if (binding == null) {
            binding = InsertComponentProcessor.suggestBinding(root, c.getComponentClassName());
        }
        return binding;
    }
}