/******************************************************************************* * Copyright (c) 2006, 2015, 2016 Zend Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.refactoring.core.rename.logic; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.core.IMethod; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.IType; import org.eclipse.dltk.core.ModelException; import org.eclipse.php.core.PHPVersion; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.core.project.ProjectOptions; import org.eclipse.php.refactoring.core.PhpRefactoringCoreMessages; import org.eclipse.php.refactoring.core.RefactoringPlugin; import org.eclipse.php.refactoring.core.utils.RefactoringUtility; /** * This visitor locates the identifiers we need to change given a class property * * @author Roy, 2007 */ public class RenameClassMember extends AbstractRename { private int nodeType; private static final String RENAME_CLASS_MEMBER = PhpRefactoringCoreMessages.getString("RenameClassPropertyName.0"); //$NON-NLS-1$ // can be null private ITypeBinding type; private List<IType> traitList; private List<IType> traitListIncludingSuperClass; private Boolean isTraitMethod; public RenameClassMember(IFile file, String oldName, String newName, boolean searchTextual, int type) { super(file, oldName, newName, searchTextual); this.nodeType = type; } public RenameClassMember(IFile file, String oldName, String newName, boolean searchTextual, ITypeBinding iTypeBinding, int type, ASTNode identifier) { this(file, oldName, newName, searchTextual, type); this.type = iTypeBinding; PHPVersion phpVersion = PHPVersion.PHP5_4; if (file.getProject() != null) { phpVersion = ProjectOptions.getPHPVersion(file.getProject()); } if (iTypeBinding != null && phpVersion.isGreaterThan(PHPVersion.PHP5_3)) { String memberName = oldName; if (identifier instanceof Identifier && (identifier.getParent().getType() == ASTNode.TRAIT_ALIAS || identifier.getParent().getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE)) { memberName = getRealName(identifier); } isTraitMethod = isTraitMethod(iTypeBinding, memberName); traitList = iTypeBinding.getTraitList(isTraitMethod, memberName, false); traitListIncludingSuperClass = iTypeBinding.getTraitList(isTraitMethod, memberName, true); } // if (isTraitMethod == null) { // isTraitMethod = isChangeMethod(); // } } private boolean isTraitMethod(ITypeBinding typeBinding, String memberName) { if (typeBinding != null && typeBinding.isTrait() && typeBinding.getPHPElement() != null) { try { IModelElement[] members = ((IType) typeBinding.getPHPElement()).getChildren(); for (IModelElement modelElement : members) { if (modelElement.getElementName().equals(memberName)) { if (modelElement instanceof IMethod) { return true; } else { return false; } } } } catch (ModelException e) { } } return isChangeMethod(); } private String getRealName(ASTNode node) { String memberName = this.oldName; if (node instanceof Identifier) { Identifier identifier = (Identifier) node; if (identifier.getParent().getType() == ASTNode.TRAIT_ALIAS) { TraitAlias traitAlias = (TraitAlias) identifier.getParent(); if (identifier == traitAlias.getFunctionName()) { Expression expression = traitAlias.getTraitMethod(); if (expression.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { FullyQualifiedTraitMethodReference fqtm = (FullyQualifiedTraitMethodReference) expression; memberName = fqtm.getFunctionName().getName(); } else { memberName = ((Identifier) expression).getName(); } } } } return memberName; } /** * context + Rename foo on: ... public foo() { }; ... */ public boolean visit(MethodDeclaration methodDeclaration) { if (isChangeMethod()) { try { Identifier identifier = methodDeclaration.getFunction().getFunctionName(); if (!identifier.getName().equals(oldName) || methodDeclaration.resolveMethodBinding() == null) { return super.visit(methodDeclaration); } ITypeBinding declClass = methodDeclaration.resolveMethodBinding().getDeclaringClass(); if (declClass != null) { if (declClass.equals(type) || traitEqual(declClass, identifier)) { addChange(identifier.getStart()); } else if (type != null && (declClass.isSubTypeCompatible(type) || type.isSubTypeCompatible(declClass))) { if (methodDeclaration.getModifier() != Modifiers.AccPrivate) { addChange(identifier.getStart()); } } else if (traitInSuperEqual(declClass, identifier)) { addChange(identifier.getStart()); } } } catch (Exception e) { RefactoringPlugin.logException(e); } } return super.visit(methodDeclaration); } private boolean traitEqual(ITypeBinding declClass, ASTNode node) { if (declClass != null && traitList != null && !traitList.isEmpty()) { if (node instanceof Variable) { node = getIdentifer((Variable) node); } if (node instanceof Identifier) { Identifier identifier = (Identifier) node; String memberName = getRealName(identifier); List<IType> traitList1 = declClass.getTraitList(isChangeMethod(), memberName, false); for (IType trait1 : traitList1) { for (IType trait : traitList) { if (trait1.equals(trait)) { return true; } } } } } return false; } private boolean traitInSuperEqual(ITypeBinding declClass, ASTNode node) { if (declClass != null && traitListIncludingSuperClass != null && !traitListIncludingSuperClass.isEmpty()) { if (node instanceof Variable) { node = getIdentifer((Variable) node); } if (node instanceof Identifier) { Identifier identifier = (Identifier) node; String memberName = getRealName(identifier); List<IType> traitList1 = declClass.getTraitList(isChangeMethod(), memberName, true); for (IType trait1 : traitList1) { for (IType trait : traitListIncludingSuperClass) { if (trait1.equals(trait)) { return true; } } } } } return false; } private boolean isChangeMethod() { return nodeType == ASTNode.METHOD_DECLARATION || nodeType == ASTNode.FUNCTION_DECLARATION || nodeType == ASTNode.FUNCTION_NAME || (isTraitMethod != null && isTraitMethod); } @Override public boolean visit(ConstantDeclaration classConstantDeclaration) { if (isChangeConstant()) { final List<Identifier> variableNames = classConstantDeclaration.names(); for (int j = 0; j < variableNames.size(); j++) { // safe cast to identifier assert variableNames.get(j) instanceof Identifier; final Identifier variable = (Identifier) variableNames.get(j); handleIdentifier(variable); } } return super.visit(classConstantDeclaration); } private boolean isChangeConstant() { return nodeType == ASTNode.STATIC_CONSTANT_ACCESS || nodeType == ASTNode.CONSTANT_DECLARATION || (isTraitMethod != null && !isTraitMethod); } /** * context + Rename var on: ... public $a; ... */ @Override public boolean visit(FieldsDeclaration fieldsDeclaration) { if (isChangeField()) { // Work around for getting type binding of class field. // Get the TypeDeclaration of the field at first and then get the // type binding. TypeDeclaration typeDecl = RefactoringUtility.getType(fieldsDeclaration); ITypeBinding declClass = null; if (typeDecl != null) { declClass = typeDecl.resolveTypeBinding(); } if (declClass != null) { final Variable[] variableNames = fieldsDeclaration.getVariableNames(); for (int j = 0; j < variableNames.length; j++) { // safe cast to identifier if (declClass.equals(type) || traitEqual(declClass, variableNames[j].getName())) { assert variableNames[j].getName() instanceof Identifier; final Identifier variable = (Identifier) variableNames[j].getName(); handleIdentifier(variable); } else if (type != null && (declClass.isSubTypeCompatible(type) || type.isSubTypeCompatible(declClass))) { if (fieldsDeclaration.getModifier() != Modifiers.AccPrivate) { final Identifier variable = (Identifier) variableNames[j].getName(); handleIdentifier(variable); } } else if (traitInSuperEqual(declClass, (Identifier) variableNames[j].getName())) { final Identifier variable = (Identifier) variableNames[j].getName(); handleIdentifier(variable); } } } } return super.visit(fieldsDeclaration); } private boolean isChangeField() { return nodeType == ASTNode.FIELD_DECLARATION || nodeType == ASTNode.VARIABLE || (isTraitMethod != null && !isTraitMethod); } /** * Rename foo() on: $a->foo(); */ public boolean visit(MethodInvocation methodInvocation) { if (isChangeMethod() && methodInvocation.getDispatcher() != null) { ITypeBinding declClass = methodInvocation.getDispatcher().resolveTypeBinding(); if (declClass != null) { if (declClass.equals(type) || traitEqual(declClass, methodInvocation.getMethod().getFunctionName().getName())) { handleDispatch(methodInvocation); } else if (type != null && (declClass.isSubTypeCompatible(type) || type.isSubTypeCompatible(declClass))) { IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); if (methodBinding != null && methodBinding.getModifiers() != Modifiers.AccPrivate) { handleDispatch(methodInvocation); } } else if (traitInSuperEqual(declClass, methodInvocation.getMethod().getFunctionName().getName())) { handleDispatch(methodInvocation); } } } return super.visit(methodInvocation); } private void handleDispatch(Dispatch dispatch) { VariableBase variable = (VariableBase) dispatch.getMember(); Identifier identifier = null; if (variable instanceof Variable) { identifier = getIdentifer((Variable) variable); } if (variable instanceof FunctionInvocation) { Variable functionName = (Variable) ((FunctionInvocation) variable).getFunctionName().getName(); identifier = getIdentifer(functionName); } if (identifier != null && identifier.getName().equals(oldName)) { // ITypeBinding typeBinding = dispatch.getDispatcher() // .resolveTypeBinding(); // if (typeBinding != null && type != null) { // if (typeBinding.equals(type) // || type.isSubTypeCompatible(typeBinding) // || typeBinding.isSubTypeCompatible(type)) { addChange(identifier.getStart()); // } // } } } private Identifier getIdentifer(Variable node) { Expression temp = node.getName(); while (temp != null && !(temp instanceof Identifier)) { if (temp instanceof Variable) { temp = ((Variable) temp).getName(); } else { return null; } } return (Identifier) temp; } private void handleIdentifier(Identifier identifier) { if (identifier.getName().equals(oldName)) { addChange(identifier.getStart()); } } /** * Rename var on: $a->var; */ public boolean visit(FieldAccess fieldAccess) { if (isChangeField()) { ITypeBinding declClass = fieldAccess.getDispatcher().resolveTypeBinding(); if (declClass != null) { if (declClass.equals(type) || traitEqual(declClass, fieldAccess.getMember())) { handleDispatch(fieldAccess); } else if (type != null && (declClass.isSubTypeCompatible(type) || type.isSubTypeCompatible(declClass))) { IVariableBinding binding = fieldAccess.resolveFieldBinding(); if (binding != null && binding.getModifiers() != Modifiers.AccPrivate) { handleDispatch(fieldAccess); } } else if (traitInSuperEqual(declClass, fieldAccess.getMember())) { handleDispatch(fieldAccess); } } } return super.visit(fieldAccess); } public boolean visit(StaticConstantAccess classConstantAccess) { handlStaticDispatch(classConstantAccess); return super.visit(classConstantAccess); } /** * Rename foo() on: MyClass::foo(); */ public boolean visit(StaticMethodInvocation methodInvocation) { if (isChangeMethod()) { handlStaticDispatch(methodInvocation); } return super.visit(methodInvocation); } private void handlStaticDispatch(StaticDispatch dispatch) { ASTNode member = dispatch.getMember(); Identifier identifier = null; VariableBase variable = null; if (member instanceof Variable) { variable = (VariableBase) dispatch.getMember(); } if (member instanceof FunctionInvocation) { variable = (VariableBase) member; } if (variable instanceof Variable) { identifier = getIdentifer((Variable) variable); } if (variable instanceof FunctionInvocation) { Expression functionName = ((FunctionInvocation) variable).getFunctionName().getName(); if (functionName instanceof Variable) { identifier = getIdentifer((Variable) functionName); } if (functionName instanceof Identifier) { identifier = (Identifier) functionName; } } if (member instanceof Identifier) { identifier = (Identifier) member; } if (identifier != null && identifier.getName().equals(oldName)) { ITypeBinding typeBinding = dispatch.getClassName().resolveTypeBinding(); if (typeBinding != null) { if (typeBinding.equals(type) || (type != null && (type.isSubTypeCompatible(typeBinding) || typeBinding.isSubTypeCompatible(type))) || traitEqual(typeBinding, identifier) || traitInSuperEqual(typeBinding, identifier)) { addChange(identifier.getStart()); } } } } /** * Rename var on: MyClass::var; */ public boolean visit(StaticFieldAccess fieldAccess) { if (isChangeField()) { handlStaticDispatch(fieldAccess); } return super.visit(fieldAccess); } public boolean visit(TraitAlias node) { if (type != null) { Expression expression = node.getTraitMethod(); if (expression.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { checkIdentifier(((FullyQualifiedTraitMethodReference) expression).getFunctionName(), (node.getModifier() & Modifiers.AccPrivate) != 0); } else if (expression.getType() == ASTNode.IDENTIFIER) { checkIdentifier((Identifier) expression, (node.getModifier() & Modifiers.AccPrivate) != 0); } if (node.getFunctionName() != null) { checkIdentifier(node.getFunctionName(), (node.getModifier() & Modifiers.AccPrivate) != 0); } } return false; } private void checkIdentifier(Identifier identifier, boolean isPrivate) { if (identifier.getName().equals(oldName)) { ITypeBinding declClass = resolveDispatcherType(identifier); if (declClass != null) { if (declClass.equals(type) || traitEqual(declClass, identifier)) { addChange(identifier.getStart()); } else if (type != null && (declClass.isSubTypeCompatible(type) || type.isSubTypeCompatible(declClass))) { if (!isPrivate) { addChange(identifier.getStart()); } } else if (traitInSuperEqual(declClass, identifier)) { addChange(identifier.getStart()); } } } } private ITypeBinding resolveDispatcherType(Identifier identifier) { ITypeBinding typeBinding = null; ASTNode parent = identifier.getParent(); if (parent.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { FullyQualifiedTraitMethodReference reference = (FullyQualifiedTraitMethodReference) parent; typeBinding = reference.getClassName().resolveTypeBinding(); ITypeBinding temp = getTypeBinding(typeBinding, reference.getFunctionName().getName()); if (temp != null) { return temp; } } else if (parent.getType() == ASTNode.TRAIT_ALIAS) { TraitAlias traitAlias = (TraitAlias) parent; List<NamespaceName> nameList = ((TraitUseStatement) traitAlias.getParent().getParent()).getTraitList(); String memberName = null; if (identifier == traitAlias.getFunctionName()) { Expression expression = traitAlias.getTraitMethod(); if (expression.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { FullyQualifiedTraitMethodReference fqtm = (FullyQualifiedTraitMethodReference) expression; memberName = fqtm.getFunctionName().getName(); } else { memberName = ((Identifier) expression).getName(); } } else { memberName = identifier.getName(); } for (NamespaceName namespaceName : nameList) { typeBinding = namespaceName.resolveTypeBinding(); ITypeBinding temp = getTypeBinding(typeBinding, memberName); if (temp != null) { return temp; } // if (typeBinding != null && typeBinding.isTrait() // && typeBinding.getPHPElement() != null) { // try { // IModelElement[] members = ((IType) typeBinding // .getPHPElement()).getChildren(); // for (IModelElement modelElement : members) { // if (modelElement.getElementName() // .equals(memberName)) { // return typeBinding; // } // } // } catch (ModelException e) { // } // // } } return null; } return typeBinding; } private ITypeBinding getTypeBinding(ITypeBinding typeBinding, String memberName) { if (typeBinding != null && typeBinding.isTrait() && typeBinding.getPHPElement() != null) { try { IModelElement[] members = ((IType) typeBinding.getPHPElement()).getChildren(); for (IModelElement modelElement : members) { if (modelElement.getElementName().equals(memberName) || modelElement.getElementName().equals("$" + memberName)) { //$NON-NLS-1$ return typeBinding; } } } catch (ModelException e) { } } return null; } public String getRenameDescription() { return RenameClassMember.RENAME_CLASS_MEMBER; } public void close() { groups.clear(); } }