/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.plugins.groovy.refactoring.inline; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrVariableDeclarationOwner; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; /** * Utility class to solve name conflicts related to local variable names of method to be inlined * @author ilyas */ public class InlineMethodConflictSolver { private InlineMethodConflictSolver() {} @NotNull public static String suggestNewName(@NotNull String startName, @Nullable GrMethod method, @NotNull PsiElement call, String... otherNames) { String newName; int i = 1; PsiElement parent = call.getParent(); while (!(parent instanceof GrVariableDeclarationOwner) && parent != null) { parent = parent.getParent(); } if (parent == null || isValidName(startName, parent, call) && isValid(startName, otherNames)) { return startName; } do { newName = startName + i; i++; } while (!((method == null || isValidNameInMethod(newName, method)) && isValidName(newName, parent, call) && isValid(newName, otherNames))); return newName; } static boolean isValid(@Nullable String name, String ... otherNames) { for (String otherName : otherNames) { if (otherName.equals(name)) { return false; } } return true; } private static boolean isValidNameInMethod(@NotNull String name, @NotNull GrMethod method) { for (GrParameter parameter : method.getParameters()) { if (name.equals(parameter.getName())) return false; } final GrOpenBlock block = method.getBlock(); if (block != null) { return isValidNameDown(name, block, null); } return true; } public static boolean isValidName(@NotNull String name, @NotNull PsiElement scopeElement, PsiElement call) { if (isValidNameDown(name, scopeElement, call)) { if (!(scopeElement instanceof GroovyFileBase)) { return isValidNameUp(name, scopeElement, call); } else { return true; } } else { return false; } } private static boolean isValidNameDown(@NotNull String name, @NotNull PsiElement startElement, @Nullable PsiElement call) { PsiElement child = startElement.getFirstChild(); while (child != null) { // Do not check defined classes, methods, closures and blocks before if (child instanceof GrTypeDefinition || child instanceof GrMethod || call != null && GroovyRefactoringUtil.isAppropriateContainerForIntroduceVariable(child) && child.getTextRange().getEndOffset() < call.getTextRange().getStartOffset()) { child = child.getNextSibling(); continue; } if (child instanceof GrAssignmentExpression) { GrExpression lValue = ((GrAssignmentExpression) child).getLValue(); if (lValue instanceof GrReferenceExpression) { GrReferenceExpression expr = (GrReferenceExpression) lValue; if (expr.getQualifierExpression() == null && name.equals(expr.getReferenceName())) { return false; } } } if (child instanceof GrVariable && name.equals(((GrVariable) child).getName())) { return false; } else { boolean inner = isValidNameDown(name, child, call); if (!inner) return false; } child = child.getNextSibling(); } return true; } private static boolean isValidNameUp(@NotNull String name, @NotNull PsiElement startElement, @Nullable PsiElement call) { if (startElement instanceof PsiFile) { return true; } PsiElement prevSibling = startElement.getPrevSibling(); while (prevSibling != null) { if (!isValidNameDown(name, prevSibling, call)) { return false; } prevSibling = prevSibling.getPrevSibling(); } PsiElement parent = startElement.getParent(); return parent == null || parent instanceof PsiDirectory || isValidNameUp(name, parent, call); } }