/******************************************************************************* * Copyright (c) 2009, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.php.internal.core.search; import java.util.List; import org.eclipse.dltk.core.*; import org.eclipse.php.core.PHPVersion; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.core.project.ProjectOptions; /** * Class members occurrences finder. * * @author shalom */ public class ClassMembersOccurrencesFinder extends AbstractOccurrencesFinder { public static final String ID = "ClassMembersOccurrencesFinder"; //$NON-NLS-1$ private String classMemberName; // The member's name private String typeDeclarationName; // Class or Interface name // TODO - use // Binding private boolean isMethod; private ITypeBinding dispatcherType; // might be null private ASTNode erroneousNode; private boolean isIncludesuper; private List<IType> traitList; public void setIncludesuper(boolean isIncludesuper) { this.isIncludesuper = isIncludesuper; } /** * @param root * the AST root * @param node * the selected node (must be an {@link Identifier} instance) * @return returns a message if there is a problem */ public String initialize(Program root, ASTNode node) { fASTRoot = root; fProblems = getProblems(root); typeDeclarationName = null; isMethod = false; PHPVersion phpVersion = PHPVersion.PHP5_4; if (root.getSourceModule().getScriptProject() != null && root.getSourceModule().getScriptProject().getProject() != null) { phpVersion = ProjectOptions.getPHPVersion(root.getSourceModule().getScriptProject().getProject()); } if (node.getType() == ASTNode.IDENTIFIER) { Identifier identifier = (Identifier) node; classMemberName = identifier.getName(); ASTNode parent = identifier.getParent(); int type = parent.getType(); isMethod = type == ASTNode.FUNCTION_DECLARATION || parent.getLocationInParent() == FunctionName.NAME_PROPERTY || parent.getLocationInParent() == FunctionInvocation.FUNCTION_PROPERTY || type == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE || type == ASTNode.TRAIT_ALIAS; dispatcherType = resolveDispatcherType(identifier); if (dispatcherType != null && phpVersion.isGreaterThan(PHPVersion.PHP5_3)) { String memberName = getRealName(identifier); // if (dispatcherType.isTrait()) { // traitList = new LinkedList<IType>(); // traitList.add((IType) dispatcherType.getPHPElement()); // } else { traitList = dispatcherType.getTraitList(isMethod, memberName, false); // } } while (typeDeclarationName == null && parent != fASTRoot) { if (type == ASTNode.CLASS_DECLARATION || type == ASTNode.INTERFACE_DECLARATION) { typeDeclarationName = ((TypeDeclaration) parent).getName().getName(); break; } parent = parent.getParent(); type = parent.getType(); } if (hasProblems(node.getStart(), node.getEnd())) { erroneousNode = node; } return null; } fDescription = "OccurrencesFinder_occurrence_description"; //$NON-NLS-1$ return fDescription; } /* * Tries to resolve the type of the dispatcher. */ private ITypeBinding resolveDispatcherType(Identifier identifier) { ITypeBinding typeBinding = null; ASTNode parent = identifier.getParent(); if (parent.getType() == ASTNode.VARIABLE) { Variable var = (Variable) parent; ASTNode varParent = var.getParent(); while (varParent.getType() == ASTNode.ARRAY_ACCESS) { varParent = varParent.getParent(); } if (varParent.getType() == ASTNode.FIELD_ACCESS && ((FieldAccess) varParent).getDispatcher() != null) { typeBinding = ((FieldAccess) varParent).getDispatcher().resolveTypeBinding(); } else if (varParent.getType() == ASTNode.STATIC_FIELD_ACCESS) { typeBinding = ((StaticFieldAccess) varParent).getClassName().resolveTypeBinding(); } else if (varParent.getType() == ASTNode.FUNCTION_NAME) { FunctionName fn = (FunctionName) varParent; if (fn.getParent().getType() == ASTNode.FUNCTION_INVOCATION) { FunctionInvocation fi = (FunctionInvocation) fn.getParent(); if (fi.getParent().getType() == ASTNode.METHOD_INVOCATION && ((MethodInvocation) fi.getParent()).getDispatcher() != null) { typeBinding = ((MethodInvocation) fi.getParent()).getDispatcher().resolveTypeBinding(); } } } else if (varParent.getType() == ASTNode.SINGLE_FIELD_DECLARATION) { return resolveDeclaringClassType(var.getParent()); } } else if (parent.getType() == ASTNode.FUNCTION_NAME) { FunctionName fn = (FunctionName) parent; if (fn.getParent().getType() == ASTNode.FUNCTION_INVOCATION) { FunctionInvocation fi = (FunctionInvocation) fn.getParent(); if (fi.getParent().getType() == ASTNode.STATIC_METHOD_INVOCATION) { typeBinding = ((StaticMethodInvocation) fi.getParent()).getClassName().resolveTypeBinding(); } } } else if (parent.getType() == ASTNode.STATIC_CONSTANT_ACCESS) { StaticConstantAccess sca = (StaticConstantAccess) parent; typeBinding = sca.getClassName().resolveTypeBinding(); } else if (parent.getType() == ASTNode.STATIC_FIELD_ACCESS) { StaticFieldAccess sfa = (StaticFieldAccess) parent; typeBinding = sfa.getClassName().resolveTypeBinding(); } else if (parent.getType() == ASTNode.METHOD_DECLARATION) { MethodDeclaration md = (MethodDeclaration) parent; return resolveDeclaringClassType(md); } else if (parent.getType() == ASTNode.FUNCTION_DECLARATION) { FunctionDeclaration fd = (FunctionDeclaration) parent; return resolveDeclaringClassType(fd); } else if (parent.getType() == ASTNode.CONSTANT_DECLARATION) { ConstantDeclaration ccd = (ConstantDeclaration) parent; return resolveDeclaringClassType(ccd); } else 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; } } return null; } if (typeBinding != null && (typeBinding.isClass() || typeBinding.isTrait()) && typeBinding.getPHPElement() != null) { return typeBinding; } return null; } 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$ if (modelElement instanceof IMethod) { if (dispatcherType == null && typeBinding.getPHPElement() != null) { isMethod = true; } } else { if (dispatcherType == null && typeBinding.getPHPElement() != null) { isMethod = false; } } return typeBinding; } } } catch (ModelException e) { } } return null; } private boolean isDispatcherTypeEquals(Identifier identifier, boolean includeSuper) { ITypeBinding type = resolveDispatcherType(identifier); if (type != null && traitList != null && !traitList.isEmpty()) { // if (type.isTrait()) { // for (IType trait : traitList) { // if (type.getPHPElement().equals(trait)) { // return true; // } // } // } else { String memberName = getRealName(identifier); List<IType> traitList1 = type.getTraitList(isMethod, memberName, false); for (IType trait1 : traitList1) { for (IType trait : traitList) { if (trait1.equals(trait)) { return true; } } } // } } return type != null && (type.equals(dispatcherType) || (includeSuper && (type.isSubTypeCompatible(dispatcherType) || dispatcherType.isSubTypeCompatible(type)))); } private String getRealName(Identifier identifier) { String memberName = this.classMemberName; 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; } /* * Resolve the class declaration type for the given node. This method * traverse upward to find a defining ClassDeclaration and then resolves its * IType. */ protected ITypeBinding resolveDeclaringClassType(ASTNode node) { IType[] types = new IType[2]; ASTNode parent = node.getParent(); TypeDeclaration typeDeclaration = null; NamespaceDeclaration namespaceDeclaration = null; while (typeDeclaration == null && parent != null) { if (parent.getType() == ASTNode.CLASS_DECLARATION || parent.getType() == ASTNode.INTERFACE_DECLARATION) { typeDeclaration = (TypeDeclaration) parent; } parent = parent.getParent(); } while (namespaceDeclaration == null && parent != null) { if (parent.getType() == ASTNode.NAMESPACE) { namespaceDeclaration = (NamespaceDeclaration) parent; } parent = parent.getParent(); } if (typeDeclaration != null) { if (namespaceDeclaration != null && namespaceDeclaration.getName() != null) { final ISourceModule source = namespaceDeclaration.getProgramRoot().getSourceModule(); types[0] = source != null ? source.getType(namespaceDeclaration.getName().getName()) : null; } ITypeBinding typeBinding = typeDeclaration.resolveTypeBinding(); if (typeBinding != null) { return typeBinding; } } return null; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.ui.search.AbstractOccurrencesFinder# * findOccurrences () */ protected void findOccurrences() { if (isMethod) { fDescription = Messages.format(BASE_DESCRIPTION, classMemberName + BRACKETS); } else { fDescription = Messages.format(BASE_DESCRIPTION, classMemberName); } if (erroneousNode != null) { // Add just this node in order to handle re-factoring properly fResult.add(new OccurrenceLocation(erroneousNode.getStart(), erroneousNode.getLength(), getOccurrenceType(erroneousNode), fDescription)); } else { fASTRoot.accept(this); } } /** * context + Mark var on: ... public $a; ... */ public boolean visit(ClassDeclaration classDeclaration) { checkTypeDeclaration(classDeclaration); return false; } public boolean visit(TraitDeclaration traitDeclaration) { checkTypeDeclaration(traitDeclaration); return false; } /** * context */ public boolean visit(InterfaceDeclaration interfaceDeclaration) { checkTypeDeclaration(interfaceDeclaration); return false; } /** * Mark foo() on: $a->foo(); */ public boolean visit(MethodInvocation methodInvocation) { if (isMethod) { checkDispatch(methodInvocation.getMethod().getFunctionName().getName()); } return super.visit(methodInvocation); } /** * Mark var on: $a->var; */ public boolean visit(FieldAccess fieldAccess) { if (!isMethod) { checkDispatch(fieldAccess.getField().getName()); } return super.visit(fieldAccess); } /** * Mark CON on: MyClass::CON; */ public boolean visit(StaticConstantAccess classConstantAccess) { Identifier constant = classConstantAccess.getConstant(); if (!isMethod && classMemberName.equals(constant.getName())) { if (dispatcherType != null) { if (isDispatcherTypeEquals(constant, isIncludesuper)) { addOccurrence(new OccurrenceLocation(constant.getStart(), constant.getLength(), getOccurrenceType(constant), fDescription)); } } else { addOccurrence(new OccurrenceLocation(constant.getStart(), constant.getLength(), getOccurrenceType(constant), fDescription)); } } return true; } /** * Mark foo() on: MyClass::foo(); */ public boolean visit(StaticMethodInvocation methodInvocation) { if (isMethod) { checkDispatch(methodInvocation.getMethod().getFunctionName().getName()); } return super.visit(methodInvocation); } /** * Mark var on: MyClass::var; */ public boolean visit(FullyQualifiedTraitMethodReference fieldAccess) { // if (isMethod) { checkDispatch(fieldAccess.getFunctionName()); // } return super.visit(fieldAccess); } public boolean visit(StaticFieldAccess fieldAccess) { if (!isMethod) { checkDispatch(fieldAccess.getField().getName()); } return super.visit(fieldAccess); } public boolean visit(TraitAlias node) { if (dispatcherType != null) { Expression expression = node.getTraitMethod(); if (expression.getType() == ASTNode.FULLY_QUALIFIED_TRAIT_METHOD_REFERENCE) { visit((FullyQualifiedTraitMethodReference) expression); } else { checkDispatch(expression); } if (node.getFunctionName() != null) { checkDispatch(node.getFunctionName()); } return false; } return super.visit(node); } /** * @param dispatch * @throws RuntimeException */ private void checkDispatch(ASTNode node) { while (node.getType() == ASTNode.ARRAY_ACCESS) { node = ((ArrayAccess) node).getName(); } if (node.getType() == ASTNode.IDENTIFIER) { Identifier id = (Identifier) node; if (id.getName().equals(classMemberName)) { if (dispatcherType != null) { if (isDispatcherTypeEquals(id, isIncludesuper)) { if (id.getParent() instanceof Variable) { addOccurrence(new OccurrenceLocation(id.getParent().getStart(), id.getParent().getLength(), getOccurrenceType(node), fDescription)); } else { addOccurrence(new OccurrenceLocation(node.getStart(), node.getLength(), getOccurrenceType(node), fDescription)); } } } else { if (id.getParent() instanceof Variable) { addOccurrence(new OccurrenceLocation(id.getParent().getStart(), id.getParent().getLength(), getOccurrenceType(node), fDescription)); } else { addOccurrence(new OccurrenceLocation(node.getStart(), node.getLength(), getOccurrenceType(node), fDescription)); } } } } if (node instanceof Variable /* && node.getParent().getType() != ASTNode.FUNCTION_NAME */) { Variable id = (Variable) node; checkDispatch(id.getName()); } } private void checkTypeDeclaration(TypeDeclaration typeDeclaration) { assert typeDeclaration != null; Block body = typeDeclaration.getBody(); // definitions of the class property List<Statement> statements = body.statements(); for (Statement statement : statements) { if (statement.getType() == ASTNode.METHOD_DECLARATION) { final MethodDeclaration classMethodDeclaration = (MethodDeclaration) statement; if (isMethod) { final Identifier functionName = classMethodDeclaration.getFunction().getFunctionName(); if (classMemberName.equals(functionName.getName())) { if (dispatcherType != null) { if (isDispatcherTypeEquals(functionName, isIncludesuper)) { addOccurrence(new OccurrenceLocation(functionName.getStart(), functionName.getLength(), getOccurrenceType(functionName), fDescription)); } } else { addOccurrence(new OccurrenceLocation(functionName.getStart(), functionName.getLength(), getOccurrenceType(functionName), fDescription)); } } } } else if (statement.getType() == ASTNode.FIELD_DECLARATION) { if (!isMethod) { FieldsDeclaration classVariableDeclaration = (FieldsDeclaration) statement; final Variable[] variableNames = classVariableDeclaration.getVariableNames(); for (int j = 0; j < variableNames.length; j++) { // safe cast to identifier assert variableNames[j].getName().getType() == ASTNode.IDENTIFIER; final Identifier variable = (Identifier) variableNames[j].getName(); if (classMemberName.equals(variable.getName())) { if (dispatcherType != null) { if (isDispatcherTypeEquals(variable, isIncludesuper)) { addOccurrence(new OccurrenceLocation(variable.getStart() - 1, variable.getLength() + 1, F_WRITE_OCCURRENCE, fDescription)); } } else { addOccurrence(new OccurrenceLocation(variable.getStart() - 1, variable.getLength() + 1, F_WRITE_OCCURRENCE, fDescription)); } } } } } else if (statement.getType() == ASTNode.CONSTANT_DECLARATION) { ConstantDeclaration classVariableDeclaration = (ConstantDeclaration) statement; List<Identifier> variableNames = classVariableDeclaration.names(); for (Identifier name : variableNames) { if (classMemberName.equals(name.getName())) { if (dispatcherType != null) { if (isDispatcherTypeEquals(name, isIncludesuper)) { addOccurrence(new OccurrenceLocation(name.getStart(), name.getLength(), getOccurrenceType(name), fDescription)); } } else { addOccurrence(new OccurrenceLocation(name.getStart(), name.getLength(), getOccurrenceType(name), fDescription)); } } } } } // } body.accept(this); } /* * (non-Javadoc) * * @seeorg.eclipse.php.internal.ui.search.AbstractOccurrencesFinder# * getOccurrenceReadWriteType * (org.eclipse.php.internal.core.ast.nodes.ASTNode) */ protected int getOccurrenceType(ASTNode node) { // Default return is F_READ_OCCURRENCE, although the implementation of // the Scalar visit might also use F_WRITE_OCCURRENCE if (node.getParent().getType() == ASTNode.CONSTANT_DECLARATION || isInAssignment(node)) { return IOccurrencesFinder.F_WRITE_OCCURRENCE; } return IOccurrencesFinder.F_READ_OCCURRENCE; } /** * Check if the given node is a variable in a field access that exists in an * assignment expression. * * @param node * @return */ protected boolean isInAssignment(ASTNode node) { if (node.getParent().getType() == ASTNode.VARIABLE) { Variable var = (Variable) node.getParent(); if (var.getParent().getType() == ASTNode.FIELD_ACCESS) { FieldAccess fAccess = (FieldAccess) var.getParent(); if (fAccess.getLocationInParent() == Assignment.LEFT_HAND_SIDE_PROPERTY) { return true; } } } return false; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.ui.search.IOccurrencesFinder#getElementName() */ public String getElementName() { return classMemberName; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.ui.search.IOccurrencesFinder#getID() */ public String getID() { return ID; } }