/******************************************************************************* * Copyright (c) 2009 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 org.eclipse.core.runtime.CoreException; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.ASTVisitor; import org.eclipse.dltk.ast.declarations.FieldDeclaration; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.expressions.CallExpression; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.compiler.env.lookup.Scope; import org.eclipse.dltk.compiler.util.HashtableOfIntValues; import org.eclipse.dltk.core.*; import org.eclipse.dltk.core.search.SearchMatch; import org.eclipse.dltk.core.search.SearchPattern; import org.eclipse.dltk.core.search.matching.MatchLocator; import org.eclipse.dltk.core.search.matching.PatternLocator; import org.eclipse.dltk.internal.core.search.matching.MatchingNodeSet; import org.eclipse.dltk.internal.core.search.matching.MethodPattern; import org.eclipse.dltk.internal.core.search.matching.OrPattern; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.core.compiler.ast.nodes.NamespaceDeclaration; import org.eclipse.php.core.compiler.ast.nodes.PHPCallExpression; import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils; public class PHPMatchLocator extends MatchLocator { /** * An ast visitor that visits local type declarations. */ public class LocalDeclarationVisitor extends ASTVisitor { IModelElement parent; MatchingNodeSet nodeSet; HashtableOfIntValues occurrencesCounts = new HashtableOfIntValues(); public LocalDeclarationVisitor(IModelElement parent, MatchingNodeSet nodeSet) { this.parent = parent; this.nodeSet = nodeSet; } public boolean visit(Expression expression) { if (expression instanceof CallExpression) { try { FieldDeclaration constantDecl = ASTUtils.getConstantDeclaration((CallExpression) expression); if (constantDecl != null) { Integer level = (Integer) nodeSet.matchingNodes.removeKey(constantDecl); reportMatching(null, constantDecl, parent, level != null ? level.intValue() : -1, nodeSet); return false; } } catch (CoreException e) { throw new WrappedCoreException(e); } } return true; } public boolean visit(TypeDeclaration typeDeclaration) { if (typeDeclaration instanceof NamespaceDeclaration && ((NamespaceDeclaration) typeDeclaration).isGlobal()) { return false; } try { char[] simpleName = typeDeclaration.getName().toCharArray(); int occurrenceCount = occurrencesCounts.get(simpleName); if (occurrenceCount == HashtableOfIntValues.NO_VALUE) { occurrenceCount = 1; } else { occurrenceCount = occurrenceCount + 1; } occurrencesCounts.put(simpleName, occurrenceCount); Integer level = (Integer) nodeSet.matchingNodes.removeKey(typeDeclaration); reportMatching(typeDeclaration, parent, level != null ? level.intValue() : -1, nodeSet, occurrenceCount); return false; // don't visit members as this was done during } catch (CoreException e) { throw new WrappedCoreException(e); } } public boolean visit(MethodDeclaration method) { try { Integer level = (Integer) nodeSet.matchingNodes.removeKey(method); reportMatching(null, method, parent, level != null ? level.intValue() : -1, nodeSet); return false; // don't visit members as this was done during } catch (CoreException e) { throw new WrappedCoreException(e); } } } protected void reportMatching(ModuleDeclaration module, MethodDeclaration method, IModelElement parent, int accuracy, MatchingNodeSet nodeSet) throws CoreException { if (parent == null) { parent = createSourceModuleHandle(); } IModelElement enclosingElement = createHandle(method, parent); if (enclosingElement == null) { enclosingElement = createMethodHandle(method.getName()); } if (accuracy > -1 && enclosingElement != null) { // skip if unable to // find method if (encloses(enclosingElement)) { SearchMatch match = null; if (DLTKCore.DEBUG) { System.out.println("TODO: AST Add constructor support."); //$NON-NLS-1$ } match = this.patternLocator.newDeclarationMatch(method, enclosingElement, accuracy, this); if (match != null) { report(match); } } } // handle nodes for the local type first LocalDeclarationVisitor localDeclarationVisitor = new LocalDeclarationVisitor(enclosingElement, nodeSet); try { method.getBody().traverse(localDeclarationVisitor); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } // references in this method ASTNode[] nodes = nodeSet.matchingNodes(method.sourceStart(), method.sourceEnd()); if (nodes != null) { if (parent == null) { parent = createSourceModuleHandle(); } if ((this.matchContainer & PatternLocator.METHOD_CONTAINER) != 0) { if (enclosingElement == null) enclosingElement = createHandle(method, parent); if (encloses(enclosingElement)) { for (int i = 0, l = nodes.length; i < l; i++) { ASTNode node = nodes[i]; Integer level = (Integer) nodeSet.matchingNodes.removeKey(node); if (DLTKCore.DEBUG) { System.out.println("TODO: Searching. Add scope support."); //$NON-NLS-1$ } this.patternLocator.matchReportReference(node, enclosingElement, (Scope) null, level.intValue(), this); } return; } } for (int i = 0, l = nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } } protected void reportMatching(TypeDeclaration type, MethodDeclaration method, IModelElement parent, int accuracy, boolean typeInHierarchy, MatchingNodeSet nodeSet) throws CoreException { IModelElement enclosingElement = createHandle(method, parent); if (accuracy > -1 && enclosingElement != null) { // skip if unable to // find method if (encloses(enclosingElement)) { SearchMatch match = null; if (DLTKCore.DEBUG) { System.out.println("TODO: AST Add constructor support."); //$NON-NLS-1$ } match = this.patternLocator.newDeclarationMatch(method, enclosingElement, accuracy, this); if (match != null) { report(match); } } } // handle nodes for the local type first LocalDeclarationVisitor localDeclarationVisitor = new LocalDeclarationVisitor(enclosingElement, nodeSet); try { method.getBody().traverse(localDeclarationVisitor); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } // references in this method if (typeInHierarchy) { ASTNode[] nodes = nodeSet.matchingNodes(method.sourceStart(), method.sourceEnd()); if (nodes != null) { if ((this.matchContainer & PatternLocator.CLASS_CONTAINER) != 0) { if (enclosingElement == null) enclosingElement = createHandle(method, parent); if (encloses(enclosingElement)) { for (int i = 0, l = nodes.length; i < l; i++) { ASTNode node = nodes[i]; Integer level = (Integer) nodeSet.matchingNodes.removeKey(node); if (DLTKCore.DEBUG) { System.out.println("TODO: Searching. Add scope support."); //$NON-NLS-1$ } this.patternLocator.matchReportReference(node, enclosingElement, (Scope) null, level.intValue(), this); } return; } } for (int i = 0, l = nodes.length; i < l; i++) nodeSet.matchingNodes.removeKey(nodes[i]); } } } protected void reportMatching(TypeDeclaration type, IModelElement parent, int accuracy, MatchingNodeSet nodeSet, int occurrenceCount) throws CoreException { if (parent != null && parent.getElementType() == IModelElement.METHOD) { // All PHP elements declared inside of function are automatically // belonging // to the global scope. When parent = null parent implementation // uses source module: parent = null; } super.reportMatching(type, parent, accuracy, nodeSet, occurrenceCount); } @Override public SearchMatch newMethodReferenceMatch(IModelElement enclosingElement, int accuracy, int offset, int length, boolean isConstructor, boolean isSynthetic, ASTNode reference) { return newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference, pattern); } public SearchMatch newMethodReferenceMatch(IModelElement enclosingElement, int accuracy, int offset, int length, boolean isConstructor, boolean isSynthetic, ASTNode reference, SearchPattern pattern) { if (pattern instanceof MethodPattern && (reference instanceof PHPCallExpression)) { PHPCallExpression pce = (PHPCallExpression) reference; ISourceModule module = (ISourceModule) enclosingElement.getAncestor(IModelElement.SOURCE_MODULE); if (module != null) { try { MethodPattern methodPattern = (MethodPattern) pattern; IModelElement[] elements = module.codeSelect(pce.getCallName().sourceStart(), 0); if (elements == null || elements.length == 0) { return super.newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference); } else { for (int i = 0; i < elements.length; i++) { if (pattern.focus != null) { if (pattern.focus.equals(elements[i])) { return super.newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference); } } else { if (methodPattern.declaringSimpleName != null && elements[i].getAncestor(IModelElement.TYPE) != null && PHPFlags.isClass( ((IType) elements[i].getAncestor(IModelElement.TYPE)).getFlags()) && new String(methodPattern.declaringSimpleName) .equalsIgnoreCase(((IType) elements[i].getParent()).getElementName())) { return super.newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference); } else if (methodPattern.declaringSimpleName == null && (elements[i] .getAncestor(IModelElement.TYPE) == null || elements[i].getAncestor(IModelElement.TYPE) != null && !PHPFlags.isClass( ((IType) elements[i].getAncestor(IModelElement.TYPE)).getFlags()))) { return super.newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference); } else if (methodPattern.declaringSimpleName == null && methodPattern.selector != null && new String(methodPattern.selector).equals(elements[i].getElementName())) { return super.newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference); } // if (new String(methodPattern.selector) // .equals(elements[i].getElementName())) { // return super.newMethodReferenceMatch( // enclosingElement, accuracy, offset, // length, isConstructor, isSynthetic, // reference); // } } } } } catch (ModelException e) { e.printStackTrace(); } } } else if (pattern instanceof OrPattern) { for (SearchPattern searchPattern : ((OrPattern) pattern).getPatterns()) { if (searchPattern instanceof MethodPattern) { return newMethodReferenceMatch(enclosingElement, accuracy, offset, length, isConstructor, isSynthetic, reference, searchPattern); } } } return null; } private boolean isMethodPattern() { if (pattern instanceof MethodPattern) { return true; } else if (pattern instanceof OrPattern) { SearchPattern[] patterns = ((OrPattern) pattern).getPatterns(); for (int i = 0; i < patterns.length; i++) { if (patterns[i] instanceof MethodPattern) { return true; } } } return false; } }