EA-32658 - initialize field in method while introduce field
[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.PsiUtilCore;
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     ArrayList<PsiElement> occurrences = new ArrayList<PsiElement>();
135     Comparator<PsiElement> comparator = new Comparator<PsiElement>() {
136       public int compare(PsiElement element1, PsiElement element2) {
137         if (element1.equals(element2)) return 0;
138
139         if (element1 instanceof GrParameter &&
140             element2 instanceof GrParameter) {
141           final String name1 = ((GrParameter) element1).getName();
142           final String name2 = ((GrParameter) element2).getName();
143           return name1.compareTo(name2);
144         }
145         return 1;
146       }
147     };
148
149     if (scope instanceof GrLoopStatement) {
150       PsiElement son = expr;
151       while (son.getParent() != null && !(son.getParent() instanceof GrLoopStatement)) {
152         son = son.getParent();
153       }
154       assert scope.equals(son.getParent());
155       collectOccurrences(expr, son, occurrences, comparator, false);
156     } else {
157       collectOccurrences(expr, scope, occurrences, comparator, scope instanceof GrTypeDefinition || scope instanceof GroovyFileBase);
158     }
159     return PsiUtilCore.toPsiElementArray(occurrences);
160   }
161
162
163   private static void collectOccurrences(@NotNull PsiElement expr, @NotNull PsiElement scope, @NotNull ArrayList<PsiElement> acc, Comparator<PsiElement> comparator, boolean goIntoInner) {
164     if (scope.equals(expr)) {
165       acc.add(expr);
166       return;
167     }
168     for (PsiElement child : scope.getChildren()) {
169       if (goIntoInner || !(child instanceof GrTypeDefinition) && !(child instanceof GrMethod && scope instanceof GroovyFileBase)) {
170         if (PsiEquivalenceUtil.areElementsEquivalent(child, expr, comparator, false)) {
171           acc.add(child);
172         } else {
173           collectOccurrences(expr, child, acc, comparator, goIntoInner);
174         }
175       }
176     }
177   }
178
179
180   public static boolean isAppropriateContainerForIntroduceVariable(PsiElement tempContainer) {
181     return tempContainer instanceof GrOpenBlock ||
182         tempContainer instanceof GrClosableBlock ||
183         tempContainer instanceof GroovyFileBase ||
184         tempContainer instanceof GrCaseSection ||
185         tempContainer instanceof GrLoopStatement ||
186         tempContainer instanceof GrIfStatement;
187   }
188
189   public static void sortOccurrences(PsiElement[] occurrences) {
190     Arrays.sort(occurrences, new Comparator<PsiElement>() {
191       public int compare(PsiElement elem1, PsiElement elem2) {
192         final int offset1 = elem1.getTextRange().getStartOffset();
193         final int offset2 = elem2.getTextRange().getStartOffset();
194         return offset1 - offset2;
195       }
196     });
197   }
198
199   public static boolean isLocalVariable(GrVariable variable) {
200     return !(variable instanceof GrField || variable instanceof GrParameter);
201   }
202
203
204   public static void highlightOccurrences(Project project, Editor editor, PsiElement[] elements) {
205     if (editor == null) return;
206     ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
207     HighlightManager highlightManager = HighlightManager.getInstance(project);
208     EditorColorsManager colorsManager = EditorColorsManager.getInstance();
209     TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
210     if (elements.length > 0) {
211       highlightManager.addOccurrenceHighlights(editor, elements, attributes, false, highlighters);
212     }
213   }
214
215   public static void highlightOccurrencesByRanges(Project project, Editor editor, TextRange[] ranges) {
216     if (editor == null) return;
217     ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
218     HighlightManager highlightManager = HighlightManager.getInstance(project);
219     EditorColorsManager colorsManager = EditorColorsManager.getInstance();
220     TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
221     for (TextRange range : ranges) {
222       highlightManager.addRangeHighlight(editor, range.getStartOffset(), range.getEndOffset(), attributes, false, highlighters);
223     }
224   }
225
226   public static void trimSpacesAndComments(Editor editor, PsiFile file, boolean trimComments) {
227     int start = editor.getSelectionModel().getSelectionStart();
228     int end = editor.getSelectionModel().getSelectionEnd();
229     while (file.findElementAt(start) instanceof PsiWhiteSpace ||
230         (file.findElementAt(start) instanceof PsiComment && trimComments) ||
231         (file.findElementAt(start) != null &&
232             GroovyTokenTypes.mNLS.equals(file.findElementAt(start).getNode().getElementType()))) {
233       start++;
234     }
235     while (file.findElementAt(end - 1) instanceof PsiWhiteSpace ||
236         (file.findElementAt(end - 1) instanceof PsiComment && trimComments) ||
237         (file.findElementAt(end - 1) != null &&
238             (GroovyTokenTypes.mNLS.equals(file.findElementAt(end - 1).getNode().getElementType()) ||
239                 GroovyTokenTypes.mSEMI.equals(file.findElementAt(end - 1).getNode().getElementType())))) {
240       end--;
241     }
242
243     editor.getSelectionModel().setSelection(start, end);
244   }
245
246   @NotNull public static PsiElement[] findStatementsInRange(PsiFile file, int startOffset, int endOffset, boolean strict) {
247     if (!(file instanceof GroovyFileBase)) return PsiElement.EMPTY_ARRAY;
248     Language language = GroovyFileType.GROOVY_LANGUAGE;
249     PsiElement element1 = file.getViewProvider().findElementAt(startOffset, language);
250     PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, language);
251
252     if (element1 instanceof PsiWhiteSpace || isNewLine(element1)) {
253       startOffset = element1.getTextRange().getEndOffset();
254       element1 = file.findElementAt(startOffset);
255     }
256     if (element2 instanceof PsiWhiteSpace || isNewLine(element2)) {
257       endOffset = element2.getTextRange().getStartOffset();
258       element2 = file.findElementAt(endOffset - 1);
259     }
260     if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
261
262     PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
263     if (parent == null) return PsiElement.EMPTY_ARRAY;
264     while (true) {
265       if (parent instanceof GrCodeBlock) break;
266       if (parent instanceof GroovyFileBase) break;
267       if (parent instanceof GrCaseSection) break;
268       if (parent instanceof GrStatement) {
269         parent = parent.getParent();
270         break;
271       }
272       if (parent == null) return PsiElement.EMPTY_ARRAY;
273       final PsiElement prev = parent;
274       parent = parent.getParent();
275       if (parent instanceof GrCodeBlock && prev instanceof LeafPsiElement) { //braces
276         parent = parent.getParent();
277       }
278     }
279
280     if (!parent.equals(element1)) {
281       while (!parent.equals(element1.getParent())) {
282         element1 = element1.getParent();
283       }
284     }
285     if (startOffset != element1.getTextRange().getStartOffset() && strict) return PsiElement.EMPTY_ARRAY;
286
287     if (!parent.equals(element2)) {
288       while (!parent.equals(element2.getParent())) {
289         element2 = element2.getParent();
290       }
291     }
292     if (endOffset != element2.getTextRange().getEndOffset() && strict) return PsiElement.EMPTY_ARRAY;
293
294     if (parent instanceof GrCodeBlock && parent.getParent() instanceof GrBlockStatement &&
295         element1 == ((GrCodeBlock) parent).getLBrace() && element2 == ((GrCodeBlock) parent).getRBrace()) {
296       return new PsiElement[]{parent.getParent()};
297     }
298
299     // calculate children
300     PsiElement[] children = PsiElement.EMPTY_ARRAY;
301     PsiElement psiChild = parent.getFirstChild();
302     if (psiChild != null) {
303       List<PsiElement> result = new ArrayList<PsiElement>();
304       while (psiChild != null) {
305         result.add(psiChild);
306         psiChild = psiChild.getNextSibling();
307       }
308       children = PsiUtilCore.toPsiElementArray(result);
309     }
310
311
312     ArrayList<PsiElement> possibleStatements = new ArrayList<PsiElement>();
313     boolean flag = false;
314     for (PsiElement child : children) {
315       if (child == element1) {
316         flag = true;
317       }
318       if (flag) {
319         possibleStatements.add(child);
320       }
321       if (child == element2) {
322         break;
323       }
324     }
325
326     for (PsiElement element : possibleStatements) {
327       if (!(element instanceof GrStatement ||
328           element instanceof PsiWhiteSpace ||
329           element instanceof PsiComment ||
330           TokenSets.SEPARATORS.contains(element.getNode().getElementType()))) {
331         return PsiElement.EMPTY_ARRAY;
332       }
333     }
334
335     return PsiUtilCore.toPsiElementArray(possibleStatements);
336   }
337
338   public static boolean isSuperOrThisCall(GrStatement statement, boolean testForSuper, boolean testForThis) {
339     if (!(statement instanceof GrConstructorInvocation)) return false;
340     GrConstructorInvocation expr = (GrConstructorInvocation) statement;
341     return (testForSuper && expr.isSuperCall()) || (testForThis && expr.isThisCall());
342   }
343
344   public static boolean hasWrongBreakStatements(PsiElement element) {
345     ArrayList<GrBreakStatement> vector = new ArrayList<GrBreakStatement>();
346     addBreakStatements(element, vector);
347     return !vector.isEmpty();
348   }
349
350   private static void addBreakStatements(PsiElement element, ArrayList<GrBreakStatement> vector) {
351     if (element instanceof GrBreakStatement) {
352       vector.add(((GrBreakStatement) element));
353     } else if (!(element instanceof GrLoopStatement ||
354         element instanceof GrSwitchStatement ||
355         element instanceof GrClosableBlock)) {
356       for (PsiElement psiElement : element.getChildren()) {
357         addBreakStatements(psiElement, vector);
358       }
359     }
360   }
361
362   public static boolean hasWrongContinueStatements(PsiElement element) {
363     ArrayList<GrContinueStatement> vector = new ArrayList<GrContinueStatement>();
364     addContinueStatements(element, vector);
365     return !vector.isEmpty();
366   }
367
368   private static void addContinueStatements(PsiElement element, ArrayList<GrContinueStatement> vector) {
369     if (element instanceof GrContinueStatement) {
370       vector.add(((GrContinueStatement) element));
371     } else if (!(element instanceof GrLoopStatement || element instanceof GrClosableBlock)) {
372       for (PsiElement psiElement : element.getChildren()) {
373         addContinueStatements(psiElement, vector);
374       }
375     }
376   }
377
378
379   public static String getMethodSignature(PsiMethod method) {
380     MethodSignature signature = method.getSignature(PsiSubstitutor.EMPTY);
381     String s = signature.getName() + "(";
382     int i = 0;
383     PsiType[] types = signature.getParameterTypes();
384     for (PsiType type : types) {
385       s += type.getPresentableText();
386       if (i < types.length - 1) {
387         s += ", ";
388       }
389       i++;
390     }
391     s += ")";
392     return s;
393
394   }
395
396   @Nullable
397   public static GrCall getCallExpressionByMethodReference(@Nullable PsiElement ref) {
398     if (ref == null) return null;
399     if (ref instanceof GrEnumConstant) return (GrEnumConstant)ref;
400     if (ref instanceof GrConstructorInvocation) return (GrCall)ref;
401     PsiElement parent = ref.getParent();
402     if (parent instanceof GrCall) {
403       return (GrCall)parent;
404     }
405     else {
406       return null;
407     }
408   }
409
410   public static boolean isMethodUsage(PsiElement ref) {
411     return (ref instanceof GrEnumConstant) || (ref.getParent() instanceof GrCall) || (ref instanceof GrConstructorInvocation);
412   }
413
414   public static String createTempVar(GrExpression expr, final GroovyPsiElement context, boolean declareFinal) {
415     expr = addBlockIntoParent(expr);
416     final GrVariableDeclarationOwner block = PsiTreeUtil.getParentOfType(expr, GrVariableDeclarationOwner.class);
417     LOG.assertTrue(block != null);
418     final PsiElement anchorStatement = PsiTreeUtil.findPrevParent(block, expr);
419     LOG.assertTrue(anchorStatement instanceof GrStatement);
420
421     Project project = expr.getProject();
422     String[] suggestedNames =GroovyNameSuggestionUtil.suggestVariableNames(expr, new NameValidator() {
423       public String validateName(String name, boolean increaseNumber) {
424         return name;
425       }
426
427       public Project getProject() {
428         return context.getProject();
429       }
430     });
431 /*
432       JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;*/
433     final String prefix = suggestedNames[0];
434     final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true);
435
436     GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(expr.getProject());
437     String[] modifiers;
438     if (declareFinal) {
439       modifiers = finalModifiers;
440     }
441     else {
442       modifiers = ArrayUtil.EMPTY_STRING_ARRAY;
443     }
444     GrVariableDeclaration decl =
445       factory.createVariableDeclaration(modifiers, (GrExpression)skipParentheses(expr, false), expr.getType(), id);
446 /*    if (declareFinal) {
447       com.intellij.psi.util.PsiUtil.setModifierProperty((decl.getMembers()[0]), PsiModifier.FINAL, true);
448     }*/
449     ((GrStatementOwner)anchorStatement.getParent()).addStatementBefore(decl, (GrStatement)anchorStatement);
450
451     return id;
452   }
453
454   public static int verifySafeCopyExpression(GrExpression expression) {
455     return verifySafeCopyExpressionSubElement(expression);
456   }
457
458   private static int verifySafeCopyExpressionSubElement(PsiElement element) {
459     int result = EXPR_COPY_SAFE;
460     if (element == null) return result;
461
462     if (element instanceof GrThisReferenceExpression || element instanceof GrSuperReferenceExpression || element instanceof GrNamedElement) {
463       return EXPR_COPY_SAFE;
464     }
465
466     if (element instanceof GrMethodCallExpression) {
467       result = EXPR_COPY_UNSAFE;
468     }
469
470     if (element instanceof GrNewExpression) {
471       return EXPR_COPY_PROHIBITED;
472     }
473
474     if (element instanceof GrAssignmentExpression) {
475       return EXPR_COPY_PROHIBITED;
476     }
477
478     if (element instanceof GrClosableBlock) {
479       return EXPR_COPY_PROHIBITED;
480     }
481
482     if (isPlusPlusOrMinusMinus(element)) {
483       return EXPR_COPY_PROHIBITED;
484     }
485
486     PsiElement[] children = element.getChildren();
487
488     for (PsiElement child : children) {
489       int childResult = verifySafeCopyExpressionSubElement(child);
490       result = Math.max(result, childResult);
491     }
492     return result;
493   }
494
495   public static boolean isPlusPlusOrMinusMinus(PsiElement element) {
496     if (element instanceof GrUnaryExpression) {
497       IElementType operandSign = ((GrUnaryExpression)element).getOperationTokenType();
498       return operandSign == GroovyTokenTypes.mDEC || operandSign == GroovyTokenTypes.mINC;
499     }
500     return false;
501   }
502
503   public static boolean isCorrectReferenceName(String newName, Project project) {
504     if (newName.startsWith("'''") || newName.startsWith("\"\"\"")) {
505       if (newName.length() < 6 || !newName.endsWith("'''")) {
506         return false;
507       }
508     }
509     else if (newName.startsWith("'") || newName.startsWith("\"")) {
510       if (newName.length() < 2 || !newName.endsWith("'")) {
511         return false;
512       }
513     }
514     if (KEYWORDS.contains(newName)) {
515       return false;
516     }
517     try {
518       GroovyPsiElementFactory.getInstance(project).createReferenceNameFromText(newName);
519     }
520     catch (IncorrectOperationException e) {
521       return false;
522     }
523     return true;
524   }
525
526   public static GrExpression generateArgFromMultiArg(PsiSubstitutor substitutor,
527                                                      List<? extends PsiElement> arguments,
528                                                      @Nullable PsiType type,
529                                                      final Project project) {
530     StringBuilder argText = new StringBuilder();
531     argText.append("[");
532     for (PsiElement argument : arguments) {
533       argText.append(argument.getText()).append(", ");
534       argument.delete();
535     }
536     if (arguments.size() > 0) {
537       argText.delete(argText.length() - 2, argText.length());
538     }
539     argText.append("]");
540     if (type instanceof PsiArrayType) {
541       type = substitutor.substitute(type);
542       String typeText = type.getCanonicalText();
543       if (type instanceof PsiEllipsisType) {
544         typeText = typeText.replace("...", "[]");
545       }
546       argText.append(" as ").append(typeText);
547     }
548     return GroovyPsiElementFactory.getInstance(project).createExpressionFromText(argText.toString());
549   }
550   
551   public static boolean hasSideEffect(@NotNull GroovyPsiElement statement) {
552     final Ref<Boolean> hasSideEffect = new Ref<Boolean>(false);
553     statement.accept(new GroovyRecursiveElementVisitor() {
554       @Override
555       public void visitMethodCallExpression(GrMethodCallExpression methodCallExpression) {
556         hasSideEffect.set(true);
557       }
558
559       @Override
560       public void visitCallExpression(GrCallExpression callExpression) {
561         hasSideEffect.set(true);
562       }
563
564       @Override
565       public void visitApplicationStatement(GrApplicationStatement applicationStatement) {
566         hasSideEffect.set(true);
567       }
568
569
570
571       @Override
572       public void visitElement(GroovyPsiElement element) {
573         if (hasSideEffect.get()) return;
574         super.visitElement(element);
575       }
576     });
577
578     return hasSideEffect.get();
579   }
580
581   /**
582    *  adds block statement in parent of statement if needed. For Example:
583    *    while (true) a=foo()
584    *  will be replaced with
585    *    while(true) {a=foo()}
586    * @param statement
587    * @return corresponding statement inside block if it has been created or statement itself.
588    * @throws com.intellij.util.IncorrectOperationException
589    */
590
591   @NotNull
592   public static <Type extends PsiElement> Type addBlockIntoParent(@NotNull Type statement) throws IncorrectOperationException {
593
594     PsiElement parent = statement.getParent();
595     PsiElement child = statement;
596     while (!(parent instanceof GrLoopStatement) &&
597            !(parent instanceof GrIfStatement) &&
598            !(parent instanceof GrVariableDeclarationOwner) &&
599            parent != null) {
600       parent = parent.getParent();
601       child = child.getParent();
602     }
603     if (parent instanceof GrWhileStatement && child == ((GrWhileStatement)parent).getCondition() ||
604         parent instanceof GrIfStatement && child == ((GrIfStatement)parent).getCondition()) {
605       parent = parent.getParent();
606     }
607     assert parent != null;
608     if (parent instanceof GrVariableDeclarationOwner) {
609       return statement;
610     }
611
612     GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(statement.getProject());
613     PsiElement tempStmt = statement;
614     while (parent != tempStmt.getParent()) {
615       tempStmt = tempStmt.getParent();
616     }
617     GrStatement toAdd = (GrStatement)tempStmt.copy();
618     GrBlockStatement blockStatement = factory.createBlockStatement();
619     if (parent instanceof GrLoopStatement) {
620       ((GrLoopStatement)parent).replaceBody(blockStatement);
621     }
622     else {
623       GrIfStatement ifStatement = (GrIfStatement)parent;
624       if (tempStmt == ifStatement.getThenBranch()) {
625         ifStatement.replaceThenBranch(blockStatement);
626       }
627       else if (tempStmt == ifStatement.getElseBranch()) {
628         ifStatement.replaceElseBranch(blockStatement);
629       }
630     }
631     GrStatement result = blockStatement.getBlock().addStatementBefore(toAdd, null);
632     if (result instanceof GrReturnStatement) {
633       //noinspection ConstantConditions,unchecked
634       statement = (Type)((GrReturnStatement)result).getReturnValue();
635     }
636     else {
637       //noinspection unchecked
638       statement = (Type)result;
639     }
640
641     return statement;
642   }
643
644   public static boolean isDiamondNewOperator(GrExpression expression) {
645     PsiElement element = skipParentheses(expression, false);
646     if (!(element instanceof GrNewExpression)) return false;
647     if (((GrNewExpression)element).getArrayCount() > 0) return false;
648
649     GrCodeReferenceElement referenceElement = ((GrNewExpression)element).getReferenceElement();
650     if (referenceElement == null) return false;
651
652     GrTypeArgumentList typeArgumentList = referenceElement.getTypeArgumentList();
653     return typeArgumentList != null && typeArgumentList.isDiamond();
654   }
655
656   public static boolean checkPsiElementsAreEqual(PsiElement l, PsiElement r) {
657     if (!l.getText().equals(r.getText())) return false;
658     if (l.getNode().getElementType() != r.getNode().getElementType()) return false;
659
660     final PsiElement[] lChildren = l.getChildren();
661     final PsiElement[] rChildren = r.getChildren();
662
663     if (lChildren.length != rChildren.length) return false;
664
665     for (int i = 0; i < rChildren.length; i++) {
666       if (!checkPsiElementsAreEqual(lChildren[i], rChildren[i])) return false;
667     }
668     return true;
669   }
670 }