update copyright
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / propertyInspector / properties / BindingProperty.java
blobe293da3dc6042354569e283ca58bf280a8d9b800
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.propertyInspector.properties;
18 import com.intellij.CommonBundle;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.command.CommandProcessor;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.fileTypes.StdFileTypes;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.ui.Messages;
25 import com.intellij.openapi.util.Comparing;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.psi.*;
28 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
29 import com.intellij.psi.codeStyle.VariableKind;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.search.searches.ReferencesSearch;
32 import com.intellij.psi.util.PsiTreeUtil;
33 import com.intellij.refactoring.rename.RenameProcessor;
34 import com.intellij.refactoring.util.CommonRefactoringUtil;
35 import com.intellij.uiDesigner.FormEditingUtil;
36 import com.intellij.uiDesigner.UIDesignerBundle;
37 import com.intellij.uiDesigner.compiler.AsmCodeGenerator;
38 import com.intellij.uiDesigner.designSurface.GuiEditor;
39 import com.intellij.uiDesigner.designSurface.InsertComponentProcessor;
40 import com.intellij.uiDesigner.inspections.FormInspectionUtil;
41 import com.intellij.uiDesigner.propertyInspector.Property;
42 import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
43 import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
44 import com.intellij.uiDesigner.propertyInspector.UIDesignerToolWindowManager;
45 import com.intellij.uiDesigner.propertyInspector.editors.BindingEditor;
46 import com.intellij.uiDesigner.propertyInspector.renderers.LabelPropertyRenderer;
47 import com.intellij.uiDesigner.quickFixes.CreateFieldFix;
48 import com.intellij.uiDesigner.radComponents.RadComponent;
49 import com.intellij.uiDesigner.radComponents.RadRootContainer;
50 import com.intellij.util.IncorrectOperationException;
51 import com.intellij.util.Processor;
52 import org.jetbrains.annotations.NonNls;
53 import org.jetbrains.annotations.NotNull;
54 import org.jetbrains.annotations.Nullable;
56 import java.text.MessageFormat;
57 import java.util.ArrayList;
58 import java.util.List;
59 import java.util.regex.Pattern;
61 /**
62 * @author Anton Katilin
63 * @author Vladimir Kondratyev
65 public final class BindingProperty extends Property<RadComponent, String> {
66 private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.propertyInspector.properties.BindingProperty");
68 private final PropertyRenderer<String> myRenderer = new LabelPropertyRenderer<String>() {
69 protected void customize(@NotNull final String value) {
70 setText(value);
73 private final BindingEditor myEditor;
74 @NonNls private static final String PREFIX_HTML = "<html>";
76 public BindingProperty(final Project project){
77 super(null, "field name");
78 myEditor = new BindingEditor(project);
81 public PropertyEditor<String> getEditor(){
82 return myEditor;
85 @NotNull
86 public PropertyRenderer<String> getRenderer(){
87 return myRenderer;
90 public String getValue(final RadComponent component){
91 return component.getBinding();
94 protected void setValueImpl(final RadComponent component, final String value) throws Exception {
95 if (Comparing.strEqual(value, component.getBinding(), true)) {
96 return;
99 if (value.length() > 0 && !JavaPsiFacade.getInstance(component.getProject()).getNameHelper().isIdentifier(value)) {
100 throw new Exception("Value '" + value + "' is not a valid identifier");
103 final RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
104 final String oldBinding = getValue(component);
106 // Check that binding remains unique
108 if (value.length() > 0) {
109 if (!FormEditingUtil.isBindingUnique(component, value, root)) {
110 throw new Exception(UIDesignerBundle.message("error.binding.not.unique"));
113 component.setBinding(value);
114 component.setDefaultBinding(false);
116 else {
117 if (component.isCustomCreateRequired()) {
118 throw new Exception(UIDesignerBundle.message("error.custom.create.binding.required"));
120 component.setBinding(null);
121 component.setCustomCreate(false);
124 // Set new value or rename old one. It means that previous binding exists
125 // and the new one doesn't exist we need to ask user to create new field
126 // or rename old one.
128 updateBoundFieldName(root, oldBinding, value, component.getComponentClassName());
131 public static void updateBoundFieldName(final RadRootContainer root, final String oldName, final String newName, final String fieldClassName) {
132 final String classToBind = root.getClassToBind();
133 if (classToBind == null) return;
135 final Project project = root.getProject();
136 if (newName.length() == 0) {
137 checkRemoveUnusedField(root, oldName, FormEditingUtil.getNextSaveUndoGroupId(project));
138 return;
141 final PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(classToBind, GlobalSearchScope.allScope(project));
142 if(aClass == null){
143 return;
146 if(oldName == null) {
147 if (aClass.findFieldByName(newName, true) == null) {
148 CreateFieldFix.runImpl(project, root, aClass, fieldClassName, newName, false,
149 FormEditingUtil.getNextSaveUndoGroupId(project));
151 return;
154 final PsiField oldField = aClass.findFieldByName(oldName, true);
155 if(oldField == null){
156 return;
159 if(aClass.findFieldByName(newName, true) != null) {
160 checkRemoveUnusedField(root, oldName, FormEditingUtil.getNextSaveUndoGroupId(project));
161 return;
164 // Show question to the user
166 if (!isFieldUnreferenced(oldField)) {
167 final int option = Messages.showYesNoDialog(project,
168 MessageFormat.format(UIDesignerBundle.message("message.rename.field"), oldName, newName),
169 UIDesignerBundle.message("title.rename"),
170 Messages.getQuestionIcon()
173 if(option != 0/*Yes*/){
174 return;
178 // Commit document before refactoring starts
179 GuiEditor editor = UIDesignerToolWindowManager.getInstance(project).getActiveFormEditor();
180 if (editor != null) {
181 editor.refreshAndSave(false);
183 PsiDocumentManager.getInstance(project).commitAllDocuments();
185 if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) {
186 return;
189 final RenameProcessor processor = new RenameProcessor(project, oldField, newName, true, true);
190 processor.run();
194 @Override
195 public boolean isModified(final RadComponent component) {
196 return component.getBinding() != null;
199 @Override
200 public void resetValue(final RadComponent component) throws Exception {
201 setValueImpl(component, "");
204 @Override
205 public boolean appliesToSelection(final List<RadComponent> selection) {
206 return selection.size() == 1;
209 @Nullable
210 public static PsiField findBoundField(@NotNull final RadRootContainer root, final String fieldName) {
211 final Project project = root.getProject();
212 final String classToBind = root.getClassToBind();
213 if (classToBind != null) {
214 final PsiManager manager = PsiManager.getInstance(project);
215 PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(classToBind, GlobalSearchScope.allScope(project));
216 if (aClass != null) {
217 final PsiField oldBindingField = aClass.findFieldByName(fieldName, false);
218 if (oldBindingField != null) {
219 return oldBindingField;
223 return null;
226 public static void checkRemoveUnusedField(final RadRootContainer rootContainer, final String fieldName, final Object undoGroupId) {
227 final PsiField oldBindingField = findBoundField(rootContainer, fieldName);
228 if (oldBindingField == null) {
229 return;
231 final Project project = oldBindingField.getProject();
232 final PsiClass aClass = oldBindingField.getContainingClass();
233 if (isFieldUnreferenced(oldBindingField)) {
234 if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) {
235 return;
237 ApplicationManager.getApplication().runWriteAction(
238 new Runnable() {
239 public void run() {
240 CommandProcessor.getInstance().executeCommand(
241 project,
242 new Runnable() {
243 public void run() {
244 try {
245 oldBindingField.delete();
247 catch (IncorrectOperationException e) {
248 Messages.showErrorDialog(project, UIDesignerBundle.message("error.cannot.delete.unused.field", e.getMessage()),
249 CommonBundle.getErrorTitle());
253 UIDesignerBundle.message("command.delete.unused.field"), undoGroupId
261 private static boolean isFieldUnreferenced(final PsiField field) {
262 return ReferencesSearch.search(field).forEach(new Processor<PsiReference>() {
263 public boolean process(final PsiReference t) {
264 PsiFile f = t.getElement().getContainingFile();
265 if (f != null && f.getFileType().equals(StdFileTypes.GUI_DESIGNER_FORM)) {
266 return true;
268 PsiMethod method = PsiTreeUtil.getParentOfType(t.getElement(), PsiMethod.class);
269 if (method != null && method.getName().equals(AsmCodeGenerator.SETUP_METHOD_NAME)) {
270 return true;
272 return false;
277 public static void checkCreateBindingFromText(final RadComponent component, final String text) {
278 if (!component.isDefaultBinding()) {
279 return;
281 RadRootContainer root = (RadRootContainer)FormEditingUtil.getRoot(component);
282 PsiField boundField = findBoundField(root, component.getBinding());
283 if (boundField == null || !isFieldUnreferenced(boundField)) {
284 return;
287 String binding = suggestBindingFromText(component, text);
288 if (binding != null) {
289 new BindingProperty(component.getProject()).setValueEx(component, binding);
290 // keep the binding marked as default
291 component.setDefaultBinding(true);
295 @Nullable
296 public static String suggestBindingFromText(final RadComponent component, String text) {
297 if (StringUtil.startsWithIgnoreCase(text, PREFIX_HTML)) {
298 text = Pattern.compile("<.+?>").matcher(text).replaceAll("");
300 ArrayList<String> words = new ArrayList<String>(StringUtil.getWordsIn(text));
301 if (words.size() > 0) {
302 StringBuilder nameBuilder = new StringBuilder(StringUtil.decapitalize(words.get(0)));
303 for(int i=1; i<words.size() && i < 4; i++) {
304 nameBuilder.append(StringUtil.capitalize(words.get(i)));
306 final String shortClassName = StringUtil.capitalize(InsertComponentProcessor.getShortClassName(component.getComponentClassName()));
307 if (shortClassName.equalsIgnoreCase(nameBuilder.toString())) {
308 // avoid "buttonButton" case
309 return null;
311 nameBuilder.append(shortClassName);
313 RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component);
314 Project project = root.getModule().getProject();
315 String binding = JavaCodeStyleManager.getInstance(project).propertyNameToVariableName(nameBuilder.toString(), VariableKind.FIELD);
316 if (FormEditingUtil.findComponentWithBinding(root, binding, component) != null) {
317 binding = InsertComponentProcessor.getUniqueBinding(root, nameBuilder.toString());
319 return binding;
321 return null;
324 public static String getDefaultBinding(final RadComponent c) {
325 RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(c);
326 String binding = null;
327 String text = FormInspectionUtil.getText(c.getModule(), c);
328 if (text != null) {
329 binding = suggestBindingFromText(c, text);
331 if (binding == null) {
332 binding = InsertComponentProcessor.suggestBinding(root, c.getComponentClassName());
334 return binding;