IDEA-51893: Quick Fix "​Create Field" when using Groovy named parameters
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / intentions / dynamic / ui / DynamicDialog.java
blob95aa07dbf932dc68606da8ff9c156daed385d717
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 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;
51 import javax.swing.*;
52 import javax.swing.border.Border;
53 import javax.swing.event.EventListenerList;
54 import java.awt.*;
55 import java.awt.event.*;
56 import java.util.EventListener;
57 import java.util.List;
59 /**
60 * User: Dmitry.Krasilschikov
61 * Date: 18.12.2007
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);
90 myContext = context;
91 setTitle(GroovyInspectionBundle.message("dynamic.element"));
92 myDynamicManager = DynamicManager.getInstance(myProject);
94 init();
96 mySettings = settings;
98 setUpTypeComboBox(typeConstraints);
99 setUpContainingClassComboBox();
100 setUpStatusLabel();
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() {
120 return mySettings;
123 protected void setUpTableNameLabel(String text) {
124 myTableLabel.setText(text);
127 private void setUpStatusLabel() {
128 if (!isTypeCheckerPanelEnable()) {
129 myTypeStatusPanel.setVisible(false);
130 return;
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"));
137 return;
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()));
144 return;
146 setStatusTextAndIcon(null, "");
149 private void setStatusTextAndIcon(final Icon icon, final String text) {
150 myTypeStatusLabel.setIcon(icon);
151 myTypeStatusLabel.setText(text);
152 pack();
155 @Nullable
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) {
163 try {
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) {
172 LOG.error(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);
189 @Nullable
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(
211 new ItemListener() {
212 public void itemStateChanged(ItemEvent e) {
213 fireDataChanged();
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) {
232 fireDataChanged();
236 PsiType type = typeConstraints.length == 1 ? typeConstraints[0].getDefaultType() : TypesUtil.getJavaLangObject(myContext);
237 if (type == null) {
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 {
248 void dataChanged() {
249 updateOkStatus();
253 protected void updateOkStatus() {
254 GrTypeElement typeElement = getEnteredTypeName();
255 if (typeElement == null) {
256 setOKActionEnabled(false);
257 } else {
258 setOKActionEnabled(true);
261 setUpStatusLabel();
264 @Nullable
265 public GrTypeElement getEnteredTypeName() {
266 final Document typeEditorDocument = getTypeEditorDocument();
268 if (typeEditorDocument == null) return null;
270 try {
271 final String typeText = typeEditorDocument.getText();
273 return GroovyPsiElementFactory.getInstance(myProject).createTypeElement(typeText);
274 } catch (IncorrectOperationException e) {
275 return null;
279 @Nullable
280 public Document getTypeEditorDocument() {
281 final Object item = myTypeComboBox.getEditor().getItem();
283 return item instanceof Document ? (Document) item : null;
287 @Nullable
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();
304 @Nullable
305 protected JComponent createCenterPanel() {
306 return myPanel;
309 protected void doOKAction() {
310 super.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");
318 } else {
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);
328 } else {
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() {
337 public void run() {
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);
346 } else {
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"));
352 return;
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"));
358 return;
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() {
373 return refs;
376 public boolean isGlobal() {
377 return true;
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);
394 } else {
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() {
428 return false;
431 public JTable getParametersTable() {
432 return myParametersTable;
435 protected static boolean isTypeCheckerPanelEnable() {
436 return true;
439 public Project getProject() {
440 return myProject;
443 protected void setUpTypeLabel(String typeLabelText) {
444 myTypeLabel.setText(typeLabelText);