e79950900934024106fac51b83c73d5324ae9bc4
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / refactoring / GroovyRefactoringUtil.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
17 package org.jetbrains.plugins.groovy.refactoring;
18
19 import com.intellij.codeInsight.PsiEquivalenceUtil;
20 import com.intellij.codeInsight.highlighting.HighlightManager;
21 import com.intellij.lang.Language;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.editor.Editor;
24 import com.intellij.openapi.editor.colors.EditorColors;
25 import com.intellij.openapi.editor.colors.EditorColorsManager;
26 import com.intellij.openapi.editor.markup.RangeHighlighter;
27 import com.intellij.openapi.editor.markup.TextAttributes;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Ref;
30 import com.intellij.openapi.util.TextRange;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.psi.*;
33 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
34 import com.intellij.psi.impl.source.tree.LeafPsiElement;
35 import com.intellij.psi.tree.IElementType;
36 import com.intellij.psi.util.MethodSignature;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.psi.util.PsiUtilBase;
39 import com.intellij.util.ArrayUtil;
40 import com.intellij.util.IncorrectOperationException;
41 import com.intellij.util.ReflectionCache;
42 import com.intellij.util.containers.ContainerUtil;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
45 import org.jetbrains.plugins.groovy.GroovyFileType;
46 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
47 import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
48 import org.jetbrains.plugins.groovy.lang.psi.*;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
52 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrBreakStatement;
54 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrContinueStatement;
55 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
56 import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection;
57 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
58 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
59 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
60 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
61 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
62 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
63 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant;
64 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
65 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
66 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList;
67 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrDeclarationHolder;
68 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
69 import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner;
70
71 import java.util.*;
72
73 import static com.intellij.refactoring.util.RefactoringUtil.*;
74 import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.isNewLine;
75 import static org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil.skipParentheses;
76
77 /**
78  * @author ilyas
79  */
80 public abstract class GroovyRefactoringUtil {
81   private static final Logger LOG = Logger.getInstance(GroovyRefactoringUtil.class);
82
83   public static final Collection<String> KEYWORDS = ContainerUtil.map(
84       TokenSets.KEYWORDS.getTypes(), StringUtil.createToStringFunction(IElementType.class));
85
86   private static final String[] finalModifiers = new String[]{PsiModifier.FINAL};
87
88   @Nullable
89   public static PsiElement getEnclosingContainer(PsiElement place) {
90     PsiElement parent = place.getParent();
91     while (true) {
92       if (parent == null) {
93         return null;
94       }
95       if (parent instanceof GrDeclarationHolder && !(parent instanceof GrClosableBlock && parent.getParent() instanceof GrStringInjection)) {
96         return parent;
97       }
98       if (parent instanceof GrLoopStatement) {
99         return parent;
100       }
101
102       parent = parent.getParent();
103     }
104   }
105
106   @Nullable
107   public static <T extends PsiElement> T findElementInRange(final PsiFile file,
108                                                             int startOffset,
109                                                             int endOffset,
110                                                             final Class<T> klass) {
111     PsiElement element1 = file.getViewProvider().findElementAt(startOffset, file.getLanguage());
112     PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, file.getLanguage());
113     if (element1 == null) return null;
114
115     if (TokenSets.WHITE_SPACES_SET.contains(element1.getNode().getElementType())) {
116       startOffset = element1.getTextRange().getEndOffset();
117       element1 = file.getViewProvider().findElementAt(startOffset, file.getLanguage());
118     }
119     if (TokenSets.WHITE_SPACES_SET.contains(element2.getNode().getElementType())) {
120       endOffset = element2.getTextRange().getStartOffset();
121       element2 = file.getViewProvider().findElementAt(endOffset - 1, file.getLanguage());
122     }
123     if (element2 == null || element1 == null) return null;
124     final PsiElement commonParent = PsiTreeUtil.findCommonParent(element1, element2);
125     assert commonParent != null;
126     final T element = ReflectionCache.isAssignable(klass, commonParent.getClass()) ? (T) commonParent : PsiTreeUtil.getParentOfType(commonParent, klass);
127     if (element == null || element.getTextRange().getStartOffset() != startOffset) {
128       return null;
129     }
130     return element;
131   }
132
133   public static PsiElement[] getExpressionOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope) {
134     return getExpressionOccurrences(expr, scope, false);
135   }
136
137   public static PsiElement[] getExpressionOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope, boolean gotoInner) {
138     ArrayList<PsiElement> occurrences = new ArrayList<PsiElement>();
139     Comparator<PsiElement> comparator = new Comparator<PsiElement>() {
140       public int compare(PsiElement element1, PsiElement element2) {
141         if (element1.equals(element2)) return 0;
142
143         if (element1 instanceof GrParameter &&
144             element2 instanceof GrParameter) {
145           final String name1 = ((GrParameter) element1).getName();
146           final String name2 = ((GrParameter) element2).getName();
147           if (name1 != null && name2 != null) {
148             return name1.compareTo(name2);
149           }
150         }
151         return 1;
152       }
153     };
154
155     if (scope instanceof GrLoopStatement) {
156       PsiElement son = expr;
157       while (son.getParent() != null && !(son.getParent() instanceof GrLoopStatement)) {
158         son = son.getParent();
159       }
160       assert scope.equals(son.getParent());
161       collectOccurrences(expr, son, occurrences, comparator, false);
162     } else {
163       collectOccurrences(expr, scope, occurrences, comparator, scope instanceof GrTypeDefinition || scope instanceof GroovyFileBase);
164     }
165     return PsiUtilBase.toPsiElementArray(occurrences);
166   }
167
168
169   private static void collectOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope, @NotNull ArrayList<PsiElement> acc, Comparator<PsiElement> comparator, boolean goIntoInner) {
170     if (scope.equals(expr)) {
171       acc.add(expr);
172       return;
173     }
174     for (PsiElement child : scope.getChildren()) {
175       if (goIntoInner || !(child instanceof GrTypeDefinition) && !(child instanceof GrMethod && scope instanceof GroovyFileBase)) {
176         if (PsiEquivalenceUtil.areElementsEquivalent(child, expr, comparator, false)) {
177           acc.add(child);
178         } else {
179           collectOccurrences(expr, child, acc, comparator, goIntoInner);
180         }
181       }
182     }
183   }
184
185
186   public static boolean isAppropriateContainerForIntroduceVariable(PsiElement tempContainer) {
187     return tempContainer instanceof GrOpenBlock ||
188         tempContainer instanceof GrClosableBlock ||
189         tempContainer instanceof GroovyFileBase ||
190         tempContainer instanceof GrCaseSection ||
191         tempContainer instanceof GrLoopStatement ||
192         tempContainer instanceof GrIfStatement;
193   }
194
195   public static void sortOccurrences(PsiElement[] occurences) {
196     Arrays.sort(occurences, new Comparator<PsiElement>() {
197       public int compare(PsiElement elem1, PsiElement elem2) {
198         final int offset1 = elem1.getTextRange().getStartOffset();
199         final int offset2 = elem2.getTextRange().getStartOffset();
200         return offset1 - offset2;
201       }
202     });
203   }
204
205   public static boolean isLocalVariable(GrVariable variable) {
206     return !(variable instanceof GrField || variable instanceof GrParameter);
207   }
208
209
210   public static void highlightOccurrences(Project project, Editor editor, PsiElement[] elements) {
211     if (editor == null) return;
212     ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
213     HighlightManager highlightManager = HighlightManager.getInstance(project);
214     EditorColorsManager colorsManager = EditorColorsManager.getInstance();
215     TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
216     if (elements.length > 0) {
217       highlightManager.addOccurrenceHighlights(editor, elements, attributes, false, highlighters);
218     }
219   }
220
221   public static void highlightOccurrencesByRanges(Project project, Editor editor, TextRange[] ranges) {
222     if (editor == null) return;
223     ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
224     HighlightManager highlightManager = HighlightManager.getInstance(project);
225     EditorColorsManager colorsManager = EditorColorsManager.getInstance();
226     TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
227     for (TextRange range : ranges) {
228       highlightManager.addRangeHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, false, highlighters);
229     }
230   }
231
232   public static void trimSpacesAndComments(Editor editor, PsiFile file, boolean trimComments) {
233     int start = editor.getSelectionModel().getSelectionStart();
234     int end = editor.getSelectionModel().getSelectionEnd();
235     while (file.findElementAt(start) instanceof PsiWhiteSpace ||
236         (file.findElementAt(start) instanceof PsiComment && trimComments) ||
237         (file.findElementAt(start) != null &&
238             GroovyTokenTypes.mNLS.equals(file.findElementAt(start).getNode().getElementType()))) {
239       start++;
240     }
241     while (file.findElementAt(end - 1) instanceof PsiWhiteSpace ||
242         (file.findElementAt(end - 1) instanceof PsiComment && trimComments) ||
243         (file.findElementAt(end - 1) != null &&
244             (GroovyTokenTypes.mNLS.equals(file.findElementAt(end - 1).getNode().getElementType()) ||
245                 GroovyTokenTypes.mSEMI.equals(file.findElementAt(end - 1).getNode().getElementType())))) {
246       end--;
247     }
248
249     editor.getSelectionModel().setSelection(start, end);
250   }
251
252   @NotNull public static PsiElement[] findStatementsInRange(PsiFile file, int startOffset, int endOffset, boolean strict) {
253     if (!(file instanceof GroovyFileBase)) return PsiElement.EMPTY_ARRAY;
254     Language language = GroovyFileType.GROOVY_LANGUAGE;
255     PsiElement element1 = file.getViewProvider().findElementAt(startOffset, language);
256     PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, language);
257
258     if (element1 instanceof PsiWhiteSpace || isNewLine(element1)) {
259       startOffset = element1.getTextRange().getEndOffset();
260       element1 = file.findElementAt(startOffset);
261     }
262     if (element2 instanceof PsiWhiteSpace || isNewLine(element2)) {
263       endOffset = element2.getTextRange().getStartOffset();
264       element2 = file.findElementAt(endOffset - 1);
265     }
266     if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
267
268     PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
269     if (parent == null) return PsiElement.EMPTY_ARRAY;
270     while (true) {
271       if (parent instanceof GrCodeBlock) break;
272       if (parent instanceof GroovyFileBase) break;
273       if (parent instanceof GrCaseSection) break;
274       if (parent instanceof GrStatement) {
275         parent = parent.getParent();
276         break;
277       }
278       if (parent == null) return PsiElement.EMPTY_ARRAY;
279       final PsiElement prev = parent;
280       parent = parent.getParent();
281       if (parent instanceof GrCodeBlock && prev instanceof LeafPsiElement) { //braces
282         parent = parent.getParent();
283       }
284     }
285
286     if (!parent.equals(element1)) {
287       while (!parent.equals(element1.getParent())) {
288         element1 = element1.getParent();
289       }
290     }
291     if (startOffset != element1.getTextRange().getStartOffset() && strict) return PsiElement.EMPTY_ARRAY;
292
293     if (!parent.equals(element2)) {
294       while (!parent.equals(element2.getParent())) {
295         element2 = element2.getParent();
296       }
297     }
298     if (endOffset != element2.getTextRange().getEndOffset() && strict) return PsiElement.EMPTY_ARRAY;
299
300     if (parent instanceof GrCodeBlock && parent.getParent() instanceof GrBlockStatement &&
301         element1 == ((GrCodeBlock) parent).getLBrace() && element2 == ((GrCodeBlock) parent).getRBrace()) {
302       return new PsiElement[]{parent.getParent()};
303     }
304
305     // calcualte children
306     PsiElement[] children = PsiElement.EMPTY_ARRAY;
307     PsiElement psiChild = parent.getFirstChild();
308     if (psiChild != null) {
309       List<PsiElement> result = new ArrayList<PsiElement>();
310       while (psiChild != null) {
311         result.add(psiChild);
312         psiChild = psiChild.getNextSibling();
313       }
314       children = PsiUtilBase.toPsiElementArray(result);
315     }
316
317
318     ArrayList<PsiElement> possibleStatements = new ArrayList<PsiElement>();
319     boolean flag = false;
320     for (PsiElement child : children) {
321       if (child == element1) {
322         flag = true;
323       }
324       if (flag) {
325         possibleStatements.add(child);
326       }
327       if (child == element2) {
328         break;
329       }
330     }
331
332     for (PsiElement element : possibleStatements) {
333       if (!(element instanceof GrStatement ||
334           element instanceof PsiWhiteSpace ||
335           element instanceof PsiComment ||
336           TokenSets.SEPARATORS.contains(element.getNode().getElementType()))) {
337         return PsiElement.EMPTY_ARRAY;
338       }
339     }
340
341     return PsiUtilBase.toPsiElementArray(possibleStatements);
342   }
343
344   public static boolean isSuperOrThisCall(GrStatement statement, boolean testForSuper, boolean testForThis) {
345     if (!(statement instanceof GrConstructorInvocation)) return false;
346     GrConstructorInvocation expr = (GrConstructorInvocation) statement;
347     return (testForSuper && expr.isSuperCall()) || (testForThis && expr.isThisCall());
348   }
349
350   public static boolean hasWrongBreakStatements(PsiElement element) {
351     ArrayList<GrBreakStatement> vector = new ArrayList<GrBreakStatement>();
352     addBreakStatements(element, vector);
353     return !vector.isEmpty();
354   }
355
356   private static void addBreakStatements(PsiElement element, ArrayList<GrBreakStatement> vector) {
357     if (element instanceof GrBreakStatement) {
358       vector.add(((GrBreakStatement) element));
359     } else if (!(element instanceof GrLoopStatement ||
360         element instanceof GrSwitchStatement ||
361         element instanceof GrClosableBlock)) {
362       for (PsiElement psiElement : element.getChildren()) {
363         addBreakStatements(psiElement, vector);
364       }
365     }
366   }
367
368   public static boolean haswrongContinueStatements(PsiElement element) {
369     ArrayList<GrContinueStatement> vector = new ArrayList<GrContinueStatement>();
370     addContinueStatements(element, vector);
371     return !vector.isEmpty();
372   }
373
374   private static void addContinueStatements(PsiElement element, ArrayList<GrContinueStatement> vector) {
375     if (element instanceof GrContinueStatement) {
376       vector.add(((GrContinueStatement) element));
377     } else if (!(element instanceof GrLoopStatement || element instanceof GrClosableBlock)) {
378       for (PsiElement psiElement : element.getChildren()) {
379         addContinueStatements(psiElement, vector);
380       }
381     }
382   }
383
384
385   public static String getMethodSignature(PsiMethod method) {
386     MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
387     String s = signature.getName() + "(";
388     int i = 0;
389     PsiType[] types = signature.getParameterTypes();
390     for (PsiType type : types) {
391       s += type.getPresentableText();
392       if (i < types.length - 1) {
393         s += ", ";
394       }
395       i++;
396     }
397     s += ")";
398     return s;
399
400   }
401
402   @Nullable
403   public static GrCall getCallExpressionByMethodReference(@Nullable PsiElement ref) {
404     if (ref == null) return null;
405     if (ref instanceof GrEnumConstant) return (GrEnumConstant)ref;
406     if (ref instanceof GrConstructorInvocation) return (GrCall)ref;
407     PsiElement parent = ref.getParent();
408     if (parent instanceof GrCall) {
409       return (GrCall)parent;
410     }
411     else {
412       return null;
413     }
414   }
415
416   public static boolean isMethodUsage(PsiElement ref) {
417     return (ref instanceof GrEnumConstant) || (ref.getParent() instanceof GrCall) || (ref instanceof GrConstructorInvocation);
418   }
419
420   public static String createTempVar(GrExpression expr, final GroovyPsiElement context, boolean declareFinal) {
421     expr = addBlockIntoParent(expr);
422     final GrVariableDeclarationOwner block = PsiTreeUtil.getParentOfType(expr, GrVariableDeclarationOwner.class);
423     LOG.assertTrue(block != null);
424     final PsiElement anchorStatement = PsiTreeUtil.findPrevParent(block, expr);
425     LOG.assertTrue(anchorStatement instanceof GrStatement);
426
427     Project project = expr.getProject();
428     String[] suggestedNames =GroovyNameSuggestionUtil.suggestVariableNames(expr, new NameValidator() {
429       public String validateName(String name, boolean increaseNumber) {
430         return name;
431       }
432
433       public Project getProject() {
434         return context.getProject();
435       }
436     });
437 /*
438       JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;*/
439     final String prefix = suggestedNames[0];
440     final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true);
441
442     GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expr.getProject());
443     String[] modifiers;
444     if (declareFinal) {
445       modifiers = finalModifiers;
446     }
447     else {
448       modifiers = ArrayUtil.EMPTY_STRING_ARRAY;
449     }
450     GrVariableDeclaration decl =
451       factory.createVariableDeclaration(modifiers, (GrExpression)skipParentheses(expr, false), expr.getType(), id);
452 /*    if (declareFinal) {
453       com.intellij.psi.util.PsiUtil.setModifierProperty((decl.getMembers()[0]), PsiModifier.FINAL, true);
454     }*/
455     ((GrStatementOwner)anchorStatement.getParent()).addStatementBefore(decl, (GrStatement)anchorStatement);
456
457     return id;
458   }
459
460   public static GrExpression convertJavaExpr2GroovyExpr(PsiElement expr) {
461     final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expr.getProject());
462
463     final List<PsiLocalVariable> localVariables = new ArrayList<PsiLocalVariable>();
464     final List<PsiField> fields = new ArrayList<PsiField>();
465     final List<PsiParameter> parameters = new ArrayList<PsiParameter>();
466
467     expr.accept(new JavaRecursiveElementVisitor() {
468       @Override
469       public void visitReferenceExpression(PsiReferenceExpression expression) {
470         final PsiExpression qualifierExpression = expression.getQualifierExpression();
471         if (qualifierExpression != null && !(qualifierExpression instanceof PsiThisExpression)) return;
472
473         PsiElement el = expression.resolve();
474         if (el instanceof PsiField) {
475           fields.add((PsiField)el);
476         }
477         else if (el instanceof PsiParameter) {
478           parameters.add((PsiParameter)el);
479         }
480         else if (el instanceof PsiLocalVariable) {
481           localVariables.add((PsiLocalVariable)el);
482         }
483         super.visitReferenceExpression(expression);
484       }
485     });
486
487     PsiJavaFile file = (PsiJavaFile)expr.getContainingFile();
488
489     StringBuilder cf = new StringBuilder();
490
491     final PsiPackageStatement packageStatement = file.getPackageStatement();
492     if (packageStatement != null) cf.append(packageStatement.getText());
493
494     final PsiImportList importList = file.getImportList();
495     if (importList != null) cf.append(importList.getText());
496
497     cf.append("class A{");
498     for (PsiField field : fields) {
499       cf.append(field.getText());
500     }
501
502     cf.append("void foo(");
503     for (int i = 0, parametersSize = parameters.size() - 1; i < parametersSize; i++) {
504       PsiParameter parameter = parameters.get(i);
505       cf.append(parameter.getText()).append(',');
506     }
507     if (parameters.size() > 0) {
508       cf.append(parameters.get(parameters.size() - 1).getText());
509     }
510     cf.append("){");
511     for (PsiLocalVariable localVariable : localVariables) {
512       cf.append(localVariable.getText());
513     }
514     cf.append("Object _________________ooooooo_______________=");
515     cf.append(expr.getText());
516     cf.append(";}}");
517     final GroovyFile grFile = factory.createGroovyFile(cf.toString(), false, expr);
518
519     final GrMethod method = (GrMethod)grFile.getClasses()[0].getMethods()[0];
520     final GrVariableDeclaration variableDeclaration = (GrVariableDeclaration)method.getBlock().getStatements()[0];
521     return variableDeclaration.getVariables()[0].getInitializerGroovy();
522   }
523
524   public static int verifySafeCopyExpression(GrExpression expression) {
525     return verifySafeCopyExpressionSubElement(expression);
526   }
527
528   private static int verifySafeCopyExpressionSubElement(PsiElement element) {
529     int result = EXPR_COPY_SAFE;
530     if (element == null) return result;
531
532     if (element instanceof GrThisReferenceExpression || element instanceof GrSuperReferenceExpression || element instanceof GrNamedElement) {
533       return EXPR_COPY_SAFE;
534     }
535
536     if (element instanceof GrMethodCallExpression) {
537       result = EXPR_COPY_UNSAFE;
538     }
539
540     if (element instanceof GrNewExpression) {
541       return EXPR_COPY_PROHIBITED;
542     }
543
544     if (element instanceof GrAssignmentExpression) {
545       return EXPR_COPY_PROHIBITED;
546     }
547
548     if (element instanceof GrClosableBlock) {
549       return EXPR_COPY_PROHIBITED;
550     }
551
552     if (isPlusPlusOrMinusMinus(element)) {
553       return EXPR_COPY_PROHIBITED;
554     }
555
556     PsiElement[] children = element.getChildren();
557
558     for (PsiElement child : children) {
559       int childResult = verifySafeCopyExpressionSubElement(child);
560       result = Math.max(result, childResult);
561     }
562     return result;
563   }
564
565   public static boolean isPlusPlusOrMinusMinus(PsiElement element) {
566     if (element instanceof GrUnaryExpression) {
567       IElementType operandSign = ((GrUnaryExpression)element).getOperationTokenType();
568       return operandSign == GroovyTokenTypes.mDEC || operandSign == GroovyTokenTypes.mINC;
569     }
570     return false;
571   }
572
573   public static boolean isCorrectReferenceName(String newName, Project project) {
574     if (newName.startsWith("'''") || newName.startsWith("\"\"\"")) {
575       if (newName.length() < 6 || !newName.endsWith("'''")) {
576         return false;
577       }
578     }
579     else if (newName.startsWith("'") || newName.startsWith("\"")) {
580       if (newName.length() < 2 || !newName.endsWith("'")) {
581         return false;
582       }
583     }
584     if (KEYWORDS.contains(newName)) {
585       return false;
586     }
587     try {
588       GroovyPsiElementFactory.getInstance(project).createReferenceNameFromText(newName);
589     }
590     catch (IncorrectOperationException e) {
591       return false;
592     }
593     return true;
594   }
595
596   public static GrExpression generateArgFromMultiArg(PsiSubstitutor substitutor,
597                                                      List<? extends PsiElement> arguments,
598                                                      @Nullable PsiType type,
599                                                      final Project project) {
600     StringBuilder argText = new StringBuilder();
601     argText.append("[");
602     for (PsiElement argument : arguments) {
603       argText.append(argument.getText()).append(", ");
604       argument.delete();
605     }
606     if (arguments.size() > 0) {
607       argText.delete(argText.length() - 2, argText.length());
608     }
609     argText.append("]");
610     if (type instanceof PsiArrayType) {
611       type = substitutor.substitute(type);
612       String typeText = type.getCanonicalText();
613       if (type instanceof PsiEllipsisType) {
614         typeText = typeText.replace("...", "[]");
615       }
616       argText.append(" as ").append(typeText);
617     }
618     return GroovyPsiElementFactory.getInstance(project).createExpressionFromText(argText.toString());
619   }
620   
621   public static boolean hasSideEffect(@NotNull GroovyPsiElement statement) {
622     final Ref<Boolean> hasSideEffect = new Ref<Boolean>(false);
623     statement.accept(new GroovyRecursiveElementVisitor() {
624       @Override
625       public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) {
626         hasSideEffect.set(true);
627       }
628
629       @Override
630       public void visitCallExpression(GrCallExpression callExpression) {
631         hasSideEffect.set(true);
632       }
633
634       @Override
635       public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
636         hasSideEffect.set(true);
637       }
638
639
640
641       @Override
642       public void visitElement(GroovyPsiElement element) {
643         if (hasSideEffect.get()) return;
644         super.visitElement(element);
645       }
646     });
647
648     return hasSideEffect.get();
649   }
650
651   /**
652    *  adds block statement in parent of expr if needed. For Example:
653    *    while (true) a=foo()
654    *  will be replaced with
655    *    while(true) {a=foo()}
656    * @param expr
657    * @return corresponding expr inside block if it has been created or expr itself.
658    * @throws com.intellij.util.IncorrectOperationException
659    */
660
661   public static GrExpression addBlockIntoParent(GrExpression expr) throws IncorrectOperationException {
662
663     PsiElement parent = expr.getParent();
664     PsiElement child = expr;
665     while (!(parent instanceof GrLoopStatement) &&
666            !(parent instanceof GrIfStatement) &&
667            !(parent instanceof GrVariableDeclarationOwner) &&
668            parent != null) {
669       parent = parent.getParent();
670       child = child.getParent();
671     }
672     if (parent instanceof GrWhileStatement && child == ((GrWhileStatement)parent).getCondition() ||
673         parent instanceof GrIfStatement && child == ((GrIfStatement)parent).getCondition()) {
674       parent = parent.getParent();
675     }
676     assert parent != null;
677     if (parent instanceof GrVariableDeclarationOwner) {
678       return expr;
679     }
680
681     GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expr.getProject());
682     PsiElement tempStmt = expr;
683     while (parent != tempStmt.getParent()) {
684       tempStmt = tempStmt.getParent();
685     }
686     GrStatement toAdd = (GrStatement)tempStmt.copy();
687     GrBlockStatement blockStatement = factory.createBlockStatement();
688     if (parent instanceof GrLoopStatement) {
689       ((GrLoopStatement)parent).replaceBody(blockStatement);
690     }
691     else {
692       GrIfStatement ifStatement = (GrIfStatement)parent;
693       if (tempStmt == ifStatement.getThenBranch()) {
694         ifStatement.replaceThenBranch(blockStatement);
695       }
696       else if (tempStmt == ifStatement.getElseBranch()) {
697         ifStatement.replaceElseBranch(blockStatement);
698       }
699     }
700     GrStatement statement = blockStatement.getBlock().addStatementBefore(toAdd, null);
701     if (statement instanceof GrReturnStatement) {
702       expr = ((GrReturnStatement)statement).getReturnValue();
703     }
704     else {
705       expr = (GrExpression)statement;
706     }
707     return expr;
708   }
709
710   public static boolean isDiamondNewOperator(GrExpression expression) {
711     PsiElement element = skipParentheses(expression, false);
712     if (!(element instanceof GrNewExpression)) return false;
713     if (((GrNewExpression)element).getArrayCount() > 0) return false;
714
715     GrCodeReferenceElement referenceElement = ((GrNewExpression)element).getReferenceElement();
716     if (referenceElement == null) return false;
717
718     GrTypeArgumentList typeArgumentList = referenceElement.getTypeArgumentList();
719     return typeArgumentList != null && typeArgumentList.isDiamond();
720   }
721
722   public static boolean checkPsiElementsAreEqual(PsiElement l, PsiElement r) {
723     if (!l.getText().equals(r.getText())) return false;
724     if (l.getNode().getElementType() != r.getNode().getElementType()) return false;
725
726     final PsiElement[] lChildren = l.getChildren();
727     final PsiElement[] rChildren = r.getChildren();
728
729     if (lChildren.length != rChildren.length) return false;
730
731     for (int i = 0; i < rChildren.length; i++) {
732       if (!checkPsiElementsAreEqual(lChildren[i], rChildren[i])) return false;
733     }
734     return true;
735   }
736 }