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 org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.dynamic
.ui
;
18 import com
.intellij
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
19 import com
.intellij
.openapi
.command
.CommandProcessor
;
20 import com
.intellij
.openapi
.command
.undo
.*;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.editor
.Document
;
23 import com
.intellij
.openapi
.editor
.event
.DocumentEvent
;
24 import com
.intellij
.openapi
.editor
.event
.DocumentListener
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.ui
.DialogWrapper
;
27 import com
.intellij
.openapi
.ui
.Messages
;
28 import com
.intellij
.openapi
.util
.IconLoader
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.search
.GlobalSearchScope
;
31 import com
.intellij
.psi
.search
.ProjectScope
;
32 import com
.intellij
.ui
.EditorComboBoxEditor
;
33 import com
.intellij
.ui
.EditorTextField
;
34 import com
.intellij
.util
.IncorrectOperationException
;
35 import org
.jetbrains
.annotations
.Nullable
;
36 import org
.jetbrains
.plugins
.groovy
.GroovyBundle
;
37 import org
.jetbrains
.plugins
.groovy
.GroovyFileType
;
38 import org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.QuickfixUtil
;
39 import org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.dynamic
.DynamicManager
;
40 import org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.dynamic
.MyPair
;
41 import org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.dynamic
.elements
.DClassElement
;
42 import org
.jetbrains
.plugins
.groovy
.annotator
.intentions
.dynamic
.elements
.DItemElement
;
43 import org
.jetbrains
.plugins
.groovy
.codeInspection
.GroovyInspectionBundle
;
44 import org
.jetbrains
.plugins
.groovy
.debugger
.fragments
.GroovyCodeFragment
;
45 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyPsiElementFactory
;
46 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.types
.GrTypeElement
;
47 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.expectedTypes
.TypeConstraint
;
48 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.statements
.expressions
.TypesUtil
;
49 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.util
.PsiUtil
;
52 import javax
.swing
.border
.Border
;
53 import javax
.swing
.event
.EventListenerList
;
55 import java
.awt
.event
.*;
56 import java
.util
.EventListener
;
57 import java
.util
.List
;
60 * User: Dmitry.Krasilschikov
63 public abstract class DynamicDialog
extends DialogWrapper
{
64 private JComboBox myClassComboBox
;
65 private JPanel myPanel
;
66 private JComboBox myTypeComboBox
;
67 private JLabel myClassLabel
;
68 private JLabel myTypeLabel
;
69 private JPanel myTypeStatusPanel
;
70 private JLabel myTypeStatusLabel
;
71 private JTable myParametersTable
;
72 private JLabel myTableLabel
;
73 private JCheckBox myStaticCheckBox
;
74 private final DynamicManager myDynamicManager
;
75 private final Project myProject
;
76 private final EventListenerList myListenerList
= new EventListenerList();
77 private final PsiElement myContext
;
78 private final DynamicElementSettings mySettings
;
80 private static final Logger LOG
= Logger
.getInstance("org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ui.DynamicDialog");
82 public DynamicDialog(PsiElement context
, DynamicElementSettings settings
, TypeConstraint
[] typeConstraints
) {
83 super(context
.getProject(), true);
84 myProject
= context
.getProject();
86 if (!isTableVisible()) {
87 myParametersTable
.setVisible(false);
88 myTableLabel
.setVisible(false);
91 setTitle(GroovyInspectionBundle
.message("dynamic.element"));
92 myDynamicManager
= DynamicManager
.getInstance(myProject
);
96 mySettings
= settings
;
98 setUpTypeComboBox(typeConstraints
);
99 setUpContainingClassComboBox();
101 setUpStaticComboBox();
103 myTableLabel
.setLabelFor(myParametersTable
);
104 setUpTableNameLabel(GroovyBundle
.message("dynamic.properties.table.name"));
106 final Border border2
= BorderFactory
.createLineBorder(Color
.BLACK
);
107 myParametersTable
.setBorder(border2
);
108 myParametersTable
.setBackground(Color
.WHITE
);
110 myTypeLabel
.setLabelFor(myTypeComboBox
);
111 myClassLabel
.setLabelFor(myClassComboBox
);
114 private void setUpStaticComboBox() {
115 myStaticCheckBox
.setMnemonic(KeyEvent
.VK_S
);
116 myStaticCheckBox
.setSelected(mySettings
.isStatic());
119 public DynamicElementSettings
getSettings() {
123 protected void setUpTableNameLabel(String text
) {
124 myTableLabel
.setText(text
);
127 private void setUpStatusLabel() {
128 if (!isTypeCheckerPanelEnable()) {
129 myTypeStatusPanel
.setVisible(false);
132 myTypeStatusLabel
.setHorizontalTextPosition(SwingConstants
.RIGHT
);
134 final GrTypeElement typeElement
= getEnteredTypeName();
135 if (typeElement
== null) {
136 setStatusTextAndIcon(IconLoader
.getIcon("/compiler/warning.png"), GroovyInspectionBundle
.message("no.type.specified"));
140 final PsiType type
= typeElement
.getType();
141 if (type
instanceof PsiClassType
&& ((PsiClassType
)type
).resolve() == null) {
142 setStatusTextAndIcon(IconLoader
.getIcon("/compiler/warning.png"),
143 GroovyInspectionBundle
.message("unresolved.type.status", type
.getPresentableText()));
146 setStatusTextAndIcon(null, "");
149 private void setStatusTextAndIcon(final Icon icon
, final String text
) {
150 myTypeStatusLabel
.setIcon(icon
);
151 myTypeStatusLabel
.setText(text
);
156 public PsiClass
getTargetClass() {
157 return JavaPsiFacade
.getInstance(myProject
).findClass(mySettings
.getContainingClassName(), GlobalSearchScope
.allScope(myProject
));
160 private void setUpContainingClassComboBox() {
161 PsiClass targetClass
= getTargetClass();
162 if (targetClass
== null || targetClass
instanceof SyntheticElement
) {
164 final GrTypeElement typeElement
= GroovyPsiElementFactory
.getInstance(myProject
).createTypeElement("java.lang.Object");
165 if (typeElement
== null) return;
167 final PsiType type
= typeElement
.getType();
169 if (!(type
instanceof PsiClassType
)) LOG
.error("Type java.lang.Object doesn't resolve");
170 targetClass
= ((PsiClassType
) type
).resolve();
171 } catch (IncorrectOperationException e
) {
176 if (targetClass
== null) return;
178 for (PsiClass aClass
: PsiUtil
.iterateSupers(targetClass
, true)) {
179 myClassComboBox
.addItem(new ContainingClassItem(aClass
));
182 myPanel
.registerKeyboardAction(new ActionListener() {
183 public void actionPerformed(ActionEvent e
) {
184 myClassComboBox
.requestFocus();
186 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_C
, KeyEvent
.ALT_MASK
), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
190 private Document
createDocument(final String text
) {
191 GroovyCodeFragment fragment
= new GroovyCodeFragment(myProject
, text
);
192 fragment
.setContext(myContext
);
193 return PsiDocumentManager
.getInstance(myProject
).getDocument(fragment
);
196 private void setUpTypeComboBox(TypeConstraint
[] typeConstraints
) {
197 final EditorComboBoxEditor comboEditor
= new EditorComboBoxEditor(myProject
, GroovyFileType
.GROOVY_FILE_TYPE
);
199 final Document document
= createDocument("");
200 assert document
!= null;
202 comboEditor
.setItem(document
);
204 myTypeComboBox
.setEditor(comboEditor
);
205 myTypeComboBox
.setEditable(true);
206 myTypeComboBox
.grabFocus();
208 addDataChangeListener();
210 myTypeComboBox
.addItemListener(
212 public void itemStateChanged(ItemEvent e
) {
218 myPanel
.registerKeyboardAction(new ActionListener() {
219 public void actionPerformed(ActionEvent e
) {
220 myTypeComboBox
.requestFocus();
222 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, KeyEvent
.ALT_MASK
), JComponent
.WHEN_IN_FOCUSED_WINDOW
);
225 final EditorTextField editorTextField
= (EditorTextField
) myTypeComboBox
.getEditor().getEditorComponent();
227 editorTextField
.addDocumentListener(new DocumentListener() {
228 public void beforeDocumentChange(DocumentEvent event
) {
231 public void documentChanged(DocumentEvent event
) {
236 PsiType type
= typeConstraints
.length
== 1 ? typeConstraints
[0].getDefaultType() : TypesUtil
.getJavaLangObject(myContext
);
238 type
= TypesUtil
.getJavaLangObject(myContext
);
240 myTypeComboBox
.getEditor().setItem(createDocument(type
.getCanonicalText()));
243 protected void addDataChangeListener() {
244 myListenerList
.add(DataChangedListener
.class, new DataChangedListener());
247 class DataChangedListener
implements EventListener
{
253 protected void updateOkStatus() {
254 GrTypeElement typeElement
= getEnteredTypeName();
255 if (typeElement
== null) {
256 setOKActionEnabled(false);
258 setOKActionEnabled(true);
265 public GrTypeElement
getEnteredTypeName() {
266 final Document typeEditorDocument
= getTypeEditorDocument();
268 if (typeEditorDocument
== null) return null;
271 final String typeText
= typeEditorDocument
.getText();
273 return GroovyPsiElementFactory
.getInstance(myProject
).createTypeElement(typeText
);
274 } catch (IncorrectOperationException e
) {
280 public Document
getTypeEditorDocument() {
281 final Object item
= myTypeComboBox
.getEditor().getItem();
283 return item
instanceof Document ?
(Document
) item
: null;
288 public ContainingClassItem
getEnteredContainingClass() {
289 final Object item
= myClassComboBox
.getSelectedItem();
290 if (!(item
instanceof ContainingClassItem
)) return null;
292 return ((ContainingClassItem
) item
);
295 protected void fireDataChanged() {
296 Object
[] list
= myListenerList
.getListenerList();
297 for (Object aList
: list
) {
298 if (aList
instanceof DataChangedListener
) {
299 ((DataChangedListener
) aList
).dataChanged();
305 protected JComponent
createCenterPanel() {
309 protected void doOKAction() {
312 mySettings
.setContainingClassName(getEnteredContainingClass().getContainingClass().getQualifiedName());
313 mySettings
.setStatic(myStaticCheckBox
.isSelected());
314 GrTypeElement typeElement
= getEnteredTypeName();
316 if (typeElement
== null) {
317 mySettings
.setType("java.lang.Object");
319 PsiType type
= typeElement
.getType();
320 if (type
instanceof PsiPrimitiveType
) {
321 type
= TypesUtil
.boxPrimitiveType(type
, typeElement
.getManager(), ProjectScope
.getAllScope(myProject
));
324 final String typeQualifiedName
= type
.getCanonicalText();
326 if (typeQualifiedName
!= null) {
327 mySettings
.setType(typeQualifiedName
);
329 mySettings
.setType(type
.getPresentableText());
333 Document document
= PsiDocumentManager
.getInstance(myProject
).getDocument(myContext
.getContainingFile());
334 final DocumentReference
[] refs
= new DocumentReference
[]{DocumentReferenceManager
.getInstance().create(document
)};
336 CommandProcessor
.getInstance().executeCommand(myProject
, new Runnable() {
338 UndoManager
.getInstance(myProject
).undoableActionPerformed(new UndoableAction() {
339 public void undo() throws UnexpectedUndoException
{
341 final DItemElement itemElement
;
342 if (mySettings
.isMethod()) {
343 final List
<MyPair
> myPairList
= mySettings
.getPairs();
344 final String
[] argumentsTypes
= QuickfixUtil
.getArgumentsTypes(myPairList
);
345 itemElement
= myDynamicManager
.findConcreteDynamicMethod(mySettings
.getContainingClassName(), mySettings
.getName(), argumentsTypes
);
347 itemElement
= myDynamicManager
.findConcreteDynamicProperty(mySettings
.getContainingClassName(), mySettings
.getName());
350 if (itemElement
== null) {
351 Messages
.showWarningDialog(myProject
, GroovyInspectionBundle
.message("Cannot.perform.undo.operation"), GroovyInspectionBundle
.message("Undo.disable"));
354 final DClassElement classElement
= myDynamicManager
.getClassElementByItem(itemElement
);
356 if (classElement
== null) {
357 Messages
.showWarningDialog(myProject
, GroovyInspectionBundle
.message("Cannot.perform.undo.operation"), GroovyInspectionBundle
.message("Undo.disable"));
361 removeElement(itemElement
);
363 if (classElement
.getMethods().size() == 0 && classElement
.getProperties().size() == 0) {
364 myDynamicManager
.removeClassElement(classElement
);
368 public void redo() throws UnexpectedUndoException
{
369 addElement(mySettings
);
372 public DocumentReference
[] getAffectedDocuments() {
376 public boolean isGlobal() {
381 addElement(mySettings
);
383 }, "Add dynamic element", null);
386 private void removeElement(DItemElement itemElement
) {
387 myDynamicManager
.removeItemElement(itemElement
);
388 myDynamicManager
.fireChange();
391 public void addElement(final DynamicElementSettings settings
) {
392 if (settings
.isMethod()) {
393 myDynamicManager
.addMethod(settings
);
395 myDynamicManager
.addProperty(settings
);
398 myDynamicManager
.fireChange();
401 static class ContainingClassItem
{
402 private final PsiClass myContainingClass
;
404 ContainingClassItem(PsiClass containingClass
) {
405 myContainingClass
= containingClass
;
408 public String
toString() {
409 return myContainingClass
.getName();
412 public PsiClass
getContainingClass() {
413 return myContainingClass
;
417 public void doCancelAction() {
418 super.doCancelAction();
420 DaemonCodeAnalyzer
.getInstance(myProject
).restart();
423 public JComponent
getPreferredFocusedComponent() {
424 return myTypeComboBox
;
427 protected boolean isTableVisible() {
431 public JTable
getParametersTable() {
432 return myParametersTable
;
435 protected static boolean isTypeCheckerPanelEnable() {
439 public Project
getProject() {
443 protected void setUpTypeLabel(String typeLabelText
) {
444 myTypeLabel
.setText(typeLabelText
);