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
;
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
) {
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(){
86 public PropertyRenderer
<String
> getRenderer(){
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)) {
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);
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
));
141 final PsiClass aClass
= JavaPsiFacade
.getInstance(project
).findClass(classToBind
, GlobalSearchScope
.allScope(project
));
146 if(oldName
== null) {
147 if (aClass
.findFieldByName(newName
, true) == null) {
148 CreateFieldFix
.runImpl(project
, root
, aClass
, fieldClassName
, newName
, false,
149 FormEditingUtil
.getNextSaveUndoGroupId(project
));
154 final PsiField oldField
= aClass
.findFieldByName(oldName
, true);
155 if(oldField
== null){
159 if(aClass
.findFieldByName(newName
, true) != null) {
160 checkRemoveUnusedField(root
, oldName
, FormEditingUtil
.getNextSaveUndoGroupId(project
));
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*/){
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
)) {
189 final RenameProcessor processor
= new RenameProcessor(project
, oldField
, newName
, true, true);
195 public boolean isModified(final RadComponent component
) {
196 return component
.getBinding() != null;
200 public void resetValue(final RadComponent component
) throws Exception
{
201 setValueImpl(component
, "");
205 public boolean appliesToSelection(final List
<RadComponent
> selection
) {
206 return selection
.size() == 1;
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
;
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) {
231 final Project project
= oldBindingField
.getProject();
232 final PsiClass aClass
= oldBindingField
.getContainingClass();
233 if (isFieldUnreferenced(oldBindingField
)) {
234 if (!CommonRefactoringUtil
.checkReadOnlyStatus(project
, aClass
)) {
237 ApplicationManager
.getApplication().runWriteAction(
240 CommandProcessor
.getInstance().executeCommand(
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
)) {
268 PsiMethod method
= PsiTreeUtil
.getParentOfType(t
.getElement(), PsiMethod
.class);
269 if (method
!= null && method
.getName().equals(AsmCodeGenerator
.SETUP_METHOD_NAME
)) {
277 public static void checkCreateBindingFromText(final RadComponent component
, final String text
) {
278 if (!component
.isDefaultBinding()) {
281 RadRootContainer root
= (RadRootContainer
)FormEditingUtil
.getRoot(component
);
282 PsiField boundField
= findBoundField(root
, component
.getBinding());
283 if (boundField
== null || !isFieldUnreferenced(boundField
)) {
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);
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
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());
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
);
329 binding
= suggestBindingFromText(c
, text
);
331 if (binding
== null) {
332 binding
= InsertComponentProcessor
.suggestBinding(root
, c
.getComponentClassName());