move fileScope and filesScope back from GlobalSearchScopes to GlobalSearchScope
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / typeMigration / ui / TypeMigrationDialog.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.refactoring.typeMigration.ui;
17
18 import com.intellij.find.FindSettings;
19 import com.intellij.ide.util.scopeChooser.ScopeChooserCombo;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.event.DocumentAdapter;
23 import com.intellij.openapi.editor.event.DocumentEvent;
24 import com.intellij.openapi.fileTypes.StdFileTypes;
25 import com.intellij.openapi.help.HelpManager;
26 import com.intellij.openapi.options.ConfigurationException;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.ui.DialogWrapper;
29 import com.intellij.openapi.ui.LabeledComponent;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.openapi.util.Disposer;
32 import com.intellij.pom.java.LanguageLevel;
33 import com.intellij.psi.*;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.searches.ReferencesSearch;
36 import com.intellij.psi.util.PsiTreeUtil;
37 import com.intellij.psi.util.PsiUtil;
38 import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
39 import com.intellij.refactoring.typeMigration.TypeMigrationProcessor;
40 import com.intellij.refactoring.typeMigration.TypeMigrationRules;
41 import com.intellij.refactoring.ui.RefactoringDialog;
42 import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
43 import com.intellij.refactoring.util.CanonicalTypes;
44 import com.intellij.ui.EditorComboBox;
45 import com.intellij.util.VisibilityUtil;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48
49 import javax.swing.*;
50 import java.awt.*;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.util.ArrayList;
54
55 /**
56  * @author anna
57  * Date: 25-Mar-2008
58  */
59 public class TypeMigrationDialog extends RefactoringDialog {
60   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.typeMigration.ui.TypeMigrationDialog");
61
62   public static final String REFACTORING_NAME = "Type Migration";
63
64   private final EditorComboBox myToTypeEditor;
65   private final PsiElement myRoot;
66   private TypeMigrationRules myRules;
67   private final PsiTypeCodeFragment myTypeCodeFragment;
68   private final ScopeChooserCombo myScopeChooserCombo;
69
70   public TypeMigrationDialog(@NotNull Project project, PsiElement root, TypeMigrationRules rules) {
71     super(project, false);
72     myRoot = root;
73     myRules = rules;
74
75     final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
76     final PsiType migrationRootType = rules != null ? rules.getMigrationRootType() : null;
77     final PsiType rootType = getRootType();
78     final String text = migrationRootType != null ? migrationRootType.getPresentableText()
79                                                   : rootType != null ? rootType.getPresentableText() : "";
80     int flags = 0;
81     if (root instanceof PsiParameter) {
82       final PsiElement scope = ((PsiParameter)root).getDeclarationScope();
83       if (scope instanceof PsiMethod) {
84         flags |= PsiElementFactory.ALLOW_ELLIPSIS;
85       }
86       else if (scope instanceof PsiCatchSection && PsiUtil.getLanguageLevel(root).isAtLeast(LanguageLevel.JDK_1_7)) {
87         flags |= PsiElementFactory.ALLOW_DISJUNCTION;
88       }
89     }
90     myTypeCodeFragment = elementFactory.createTypeCodeFragment(text, root, true, flags);
91
92     final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
93     final Document document = documentManager.getDocument(myTypeCodeFragment);
94     assert document != null;
95     myToTypeEditor = new EditorComboBox(document, project, StdFileTypes.JAVA);
96     final String[] types = getValidTypes(project, root);
97     if (types != null) {
98       myToTypeEditor.setHistory(types);
99     } else {
100       myToTypeEditor.setHistory(new String[]{document.getText()});
101     }
102     document.addDocumentListener(new DocumentAdapter() {
103       @Override
104       public void documentChanged(final DocumentEvent e) {
105         documentManager.commitDocument(document);
106         validateButtons();
107       }
108     });
109
110     myScopeChooserCombo = new ScopeChooserCombo(project, false, true, FindSettings.getInstance().getDefaultScopeName());
111     Disposer.register(myDisposable, myScopeChooserCombo);
112     myScopeChooserCombo.getChildComponent().addActionListener(new ActionListener() {
113       public void actionPerformed(ActionEvent e) {
114         validateButtons();
115       }
116     });
117     init();
118     setTitle(REFACTORING_NAME);
119   }
120
121   public PsiElement getRoot() {
122     return myRoot;
123   }
124
125   @Nullable
126   public PsiType getMigrationType() {
127     try {
128       return myTypeCodeFragment.getType();
129     }
130     catch (PsiTypeCodeFragment.TypeSyntaxException e) {
131       LOG.info(e);
132       return null;
133     }
134     catch (PsiTypeCodeFragment.NoTypeException e) {
135       LOG.info(e);
136       return null;
137     }
138   }
139
140   @Nullable
141   private String[] getValidTypes(final Project project, final PsiElement root) {
142     final ArrayList<PsiExpression> expressions = new ArrayList<PsiExpression>();
143     if (root instanceof PsiField || root instanceof PsiMethod) {
144       final PsiModifierList modifierList = ((PsiModifierListOwner)root).getModifierList();
145       if (VisibilityUtil.compare(VisibilityUtil.getVisibilityModifier(modifierList), PsiModifier.PRIVATE) < 0) return null;
146     }
147
148     for (PsiReference reference : ReferencesSearch.search(root, GlobalSearchScope.fileScope(root.getContainingFile()))) {
149       final PsiElement element = reference.getElement();
150       final PsiExpression expr = PsiTreeUtil.getParentOfType(element, PsiExpression.class, false);
151       if (expr != null) {
152         expressions.add(expr);
153       }
154     }
155     try {
156       final PsiExpression[] occurrences = expressions.toArray(new PsiExpression[expressions.size()]);
157       final PsiType[] psiTypes = new TypeSelectorManagerImpl(project, myTypeCodeFragment.getType(), occurrences).getTypesForAll();
158       if (psiTypes.length > 0) {
159         final String[] history = new String[psiTypes.length];
160         for (int i = 0; i < psiTypes.length; i++) {
161           PsiType psiType = psiTypes[i];
162           history[i] = psiType.getCanonicalText();
163         }
164         return history;
165       }
166     }
167     catch (PsiTypeCodeFragment.TypeSyntaxException e) {
168       LOG.info(e);
169       return null;
170     }
171     catch (PsiTypeCodeFragment.NoTypeException e) {
172       LOG.info(e);
173       return null;
174     }
175     return null;
176   }
177
178   @Override
179   protected void canRun() throws ConfigurationException {
180     if (!checkType(getMigrationType())) throw new ConfigurationException("\'" + myTypeCodeFragment.getText() + "\' is invalid type");
181     if (myScopeChooserCombo.getSelectedScope() == null) throw new ConfigurationException("Scope is not chosen");
182   }
183
184   private static boolean checkType(final PsiType type) {
185     if (type == null) return false;
186     if (!type.isValid()) return false;
187     if (type instanceof PsiClassType){
188       final PsiClassType psiClassType = (PsiClassType)type;
189       if (psiClassType.resolve() == null) return false;
190       final PsiType[] types = psiClassType.getParameters();
191       for (PsiType paramType : types) {
192         if (paramType instanceof PsiPrimitiveType || (paramType instanceof PsiWildcardType && ((PsiWildcardType)paramType).getBound() instanceof PsiPrimitiveType)) return false;
193         if (!checkType(paramType)) return false;
194       }
195     }
196     if (type instanceof PsiArrayType) {
197       return checkType(type.getDeepComponentType());
198     }
199     return true;
200   }
201
202   protected void doAction() {
203     FindSettings.getInstance().setDefaultScopeName(myScopeChooserCombo.getSelectedScopeName());
204
205     final PsiType rootType = getRootType();
206     final CanonicalTypes.Type typeWrapper = CanonicalTypes.createTypeWrapper(getMigrationType());
207     assert typeWrapper != null : getMigrationType();
208     final PsiType migrationType = typeWrapper.getType(myRoot, myRoot.getManager());
209
210     if (Comparing.equal(rootType, migrationType)) {
211       close(DialogWrapper.OK_EXIT_CODE);
212       return;
213     }
214
215     if (myRules == null) {
216       myRules = new TypeMigrationRules(rootType);
217       myRules.setMigrationRootType(migrationType);
218       myRules.setBoundScope(myScopeChooserCombo.getSelectedScope());
219     }
220     invokeRefactoring(new TypeMigrationProcessor(myProject, myRoot, myRules));
221   }
222
223   @Nullable
224   private PsiType getRootType() {
225     return TypeMigrationLabeler.getElementType(myRoot);
226   }
227
228   protected JComponent createCenterPanel() {
229     final JPanel panel = new JPanel(new GridBagLayout());
230     final GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST,
231                                                          GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0, 0), 0, 0);
232     final PsiType type = getRootType();
233     panel.add(new JLabel("Migrate " + getElementPresentation(myRoot) + " \"" + (type != null ? type.getCanonicalText() : "<unknown>") + "\" to"), gc);
234     panel.add(myToTypeEditor, gc);
235
236     LabeledComponent<ScopeChooserCombo> scopeChooserComponent = new LabeledComponent<ScopeChooserCombo>();
237     scopeChooserComponent.setComponent(myScopeChooserCombo);
238     scopeChooserComponent.setText("Choose scope where change signature may occur");
239     panel.add(scopeChooserComponent, gc);
240     return panel;
241   }
242
243   @Override
244   public JComponent getPreferredFocusedComponent() {
245     return myToTypeEditor;
246   }
247
248   private static String getElementPresentation(PsiElement element) {
249     if (element instanceof PsiMethod) {
250       return "return type of method " + ((PsiMethod)element).getName();
251     }
252
253     if (element instanceof PsiField) {
254       return "type of field " + ((PsiField)element).getName();
255     }
256
257     if (element instanceof PsiLocalVariable) {
258       return "type of variable " + ((PsiLocalVariable)element).getName();
259     }
260
261     if (element instanceof PsiParameter) {
262       final PsiParameter param = (PsiParameter)element;
263       String result = "type of parameter " + param.getName();
264       if (param.getParent() instanceof PsiParameterList) {
265         final PsiMethod method = PsiTreeUtil.getParentOfType(param, PsiMethod.class);
266         assert method != null;
267         result  += " of method " + method.getName();
268       }
269       return result;
270     }
271
272     return element.toString();
273   }
274
275   protected void doHelpAction() {
276     HelpManager.getInstance().invokeHelp("reference.typeMigrationDialog");
277   }
278 }