3d2a137bbd8386831e22922e22fc0b5e62d3ae0f
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / intentions / QuickfixUtil.java
1 /*
2  * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.annotator.intentions;
17
18 import com.intellij.openapi.editor.Editor;
19 import com.intellij.openapi.fileEditor.FileEditorManager;
20 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
24 import com.intellij.openapi.vfs.VirtualFile;
25 import com.intellij.psi.*;
26 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
27 import com.intellij.psi.codeStyle.SuggestedNameInfo;
28 import com.intellij.psi.codeStyle.VariableKind;
29 import com.intellij.psi.util.PsiTreeUtil;
30 import com.intellij.psi.util.PsiTypesUtil;
31 import com.intellij.util.ArrayUtil;
32 import gnu.trove.THashSet;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.MyPair;
36 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ui.DynamicElementSettings;
37 import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
38 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
44 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
45 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
46 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
47
48 import java.util.*;
49
50 /**
51  * User: Dmitry.Krasilschikov
52  * Date: 20.12.2007
53  */
54 public class QuickfixUtil {
55   @Nullable
56   public static PsiClass findTargetClass(GrReferenceExpression refExpr) {
57     final PsiClass psiClass;
58     if (refExpr.isQualified()) {
59       GrExpression qualifier = refExpr.getQualifierExpression();
60       PsiType type = qualifier.getType();
61       if (!(type instanceof PsiClassType)) return null;
62
63       psiClass = ((PsiClassType)type).resolve();
64     } else {
65       GroovyPsiElement context = PsiTreeUtil.getParentOfType(refExpr, GrTypeDefinition.class, GroovyFileBase.class);
66       if (context instanceof GrTypeDefinition) {
67         return (PsiClass)context;
68       } else if (context instanceof GroovyFileBase) return ((GroovyFileBase)context).getScriptClass();
69       return null;
70     }
71     return psiClass;
72   }
73
74   public static boolean isStaticCall(GrReferenceExpression refExpr) {
75
76     //todo: look more carefully
77     GrExpression qualifierExpression = refExpr.getQualifierExpression();
78
79     if (!(qualifierExpression instanceof GrReferenceExpression)) return false;
80
81     GrReferenceExpression referenceExpression = (GrReferenceExpression)qualifierExpression;
82     GroovyPsiElement resolvedElement = ResolveUtil.resolveProperty(referenceExpression, referenceExpression.getName());
83
84     if (resolvedElement == null) return false;
85     if (resolvedElement instanceof PsiClass) return true;
86
87     return false;
88   }
89
90
91   public static boolean ensureFileWritable(Project project, PsiFile file) {
92     final VirtualFile virtualFile = file.getVirtualFile();
93     final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(project);
94     final ReadonlyStatusHandler.OperationStatus operationStatus = readonlyStatusHandler.ensureFilesWritable(virtualFile);
95     return !operationStatus.hasReadonlyFiles();
96   }
97
98   public static Editor positionCursor(@NotNull Project project, @NotNull PsiFile targetFile, @NotNull PsiElement element) {
99     TextRange range = element.getTextRange();
100     int textOffset = range.getStartOffset();
101
102     VirtualFile vFile = targetFile.getVirtualFile();
103     assert vFile != null;
104     OpenFileDescriptor descriptor = new OpenFileDescriptor(project, vFile, textOffset);
105     return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
106   }
107
108   public static String[] getMethodArgumentsNames(Project project, PsiType[] types) {
109     Set<String> uniqNames = new LinkedHashSet<String>();
110     Set<String> nonUniqNames = new THashSet<String>();
111     for (PsiType type : types) {
112       final SuggestedNameInfo nameInfo =
113         JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.PARAMETER, null, null, type);
114
115       final String name = nameInfo.names[0];
116       if (uniqNames.contains(name)) {
117         int i = 2;
118         while (uniqNames.contains(name + i)) i++;
119         uniqNames.add(name + i);
120         nonUniqNames.add(name);
121       } else {
122         uniqNames.add(name);
123       }
124     }
125
126     final String[] result = new String[uniqNames.size()];
127     int i = 0;
128     for (String name : uniqNames) {
129       result[i] = nonUniqNames.contains(name) ? name + 1 : name;
130       i++;
131     }
132     return result;
133   }
134
135   public static List<MyPair> swapArgumentsAndTypes(String[] names, PsiType[] types) {
136     List<MyPair> result = new ArrayList<MyPair>();
137
138     if (names.length != types.length) return Collections.emptyList();
139
140     for (int i = 0; i < names.length; i++) {
141       String name = names[i];
142       final PsiType type = types[i];
143
144       result.add(new MyPair(name, type.getCanonicalText()));
145     }
146
147     return result;
148   }
149
150   public static boolean isCall(GrReferenceExpression referenceExpression) {
151     return referenceExpression.getParent() instanceof GrCall;
152   }
153
154   public static String[] getArgumentsTypes(List<MyPair> listOfPairs) {
155     final List<String> result = new ArrayList<String>();
156
157     if (listOfPairs == null) return ArrayUtil.EMPTY_STRING_ARRAY;
158     for (MyPair listOfPair : listOfPairs) {
159       String type = PsiTypesUtil.unboxIfPossible(listOfPair.second);
160       result.add(type);
161     }
162
163     return ArrayUtil.toStringArray(result);
164   }
165
166   public static String[] getArgumentsNames(List<MyPair> listOfPairs) {
167     final ArrayList<String> result = new ArrayList<String>();
168     for (MyPair listOfPair : listOfPairs) {
169       String name = listOfPair.first;
170       result.add(name);
171     }
172
173     return ArrayUtil.toStringArray(result);
174   }
175
176   public static String shortenType(String typeText) {
177     if (typeText == null) return "";
178     final int i = typeText.lastIndexOf(".");
179     if (i != -1) {
180       return typeText.substring(i + 1);
181     }
182     return typeText;
183   }
184
185   public static DynamicElementSettings createSettings(GrReferenceExpression referenceExpression) {
186     DynamicElementSettings settings = new DynamicElementSettings();
187     final PsiClass containingClass = findTargetClass(referenceExpression);
188
189     assert containingClass != null;
190     String className = containingClass.getQualifiedName();
191     className = className == null ? containingClass.getContainingFile().getName() : className;
192
193     if (isStaticCall(referenceExpression)) {
194       settings.setStatic(true);
195     }
196
197     settings.setContainingClassName(className);
198     settings.setName(referenceExpression.getName());
199
200     if (isCall(referenceExpression)) {
201       List<PsiType> unboxedTypes = new ArrayList<PsiType>();
202       for (PsiType type : PsiUtil.getArgumentTypes(referenceExpression, false)) {
203         unboxedTypes.add(TypesUtil.unboxPrimitiveTypeWrapperAndEraseGenerics(type));
204       }
205       final PsiType[] types = unboxedTypes.toArray(new PsiType[unboxedTypes.size()]);
206       final String[] names = getMethodArgumentsNames(referenceExpression.getProject(), types);
207       final List<MyPair> pairs = swapArgumentsAndTypes(names, types);
208
209       settings.setMethod(true);
210       settings.setPairs(pairs);
211     } else {
212       settings.setMethod(false);
213     }
214     return settings;
215   }
216
217   public static DynamicElementSettings createSettings(GrArgumentLabel label, PsiClass targetClass) {
218     DynamicElementSettings settings = new DynamicElementSettings();
219
220     assert targetClass != null;
221     String className = targetClass.getQualifiedName();
222     className = className == null ? targetClass.getContainingFile().getName() : className;
223
224     settings.setContainingClassName(className);
225     settings.setName(label.getName());
226
227     return settings;
228   }
229 }