/* * Copyright 2009-2016 the original author or authors. * * 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.codehaus.groovy.eclipse.core.search; import java.util.Comparator; import java.util.Map; import java.util.TreeMap; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.ImportNode; import org.codehaus.groovy.ast.InnerClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.jdt.groovy.internal.compiler.ast.JDTClassNode; import org.codehaus.jdt.groovy.internal.compiler.ast.JDTMethodNode; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.groovy.search.EqualityVisitor; import org.eclipse.jdt.groovy.search.ITypeRequestor; import org.eclipse.jdt.groovy.search.TypeLookupResult; /** * Finds all references to a particular Declaration in a file * * @author andrew * @created Dec 31, 2010 */ public class FindAllReferencesRequestor implements ITypeRequestor { private final AnnotatedNode declaration; private final Map<ASTNode, Integer> references; public FindAllReferencesRequestor(AnnotatedNode declaration) { this.declaration = declaration; this.references = new TreeMap<ASTNode, Integer>(new Comparator<ASTNode>() { public int compare(ASTNode o1, ASTNode o2) { return o1.getStart() - o2.getStart(); } }); } public VisitStatus acceptASTNode(ASTNode node, TypeLookupResult result, IJavaElement enclosingElement) { if (node.getLength() == 0) { return VisitStatus.CONTINUE; } if (node instanceof AnnotatedNode) { ASTNode maybeDeclaration = result.declaration; if (maybeDeclaration == null) { return VisitStatus.CONTINUE; } if (maybeDeclaration instanceof ClassNode) { // sometimes generated methods and properties have a ClassNode // as the declaration. // we want to ignore these if (!(node instanceof ClassExpression || node instanceof ClassNode || node instanceof ImportNode)) { return VisitStatus.CONTINUE; } // also ignore sctipt declarations if (node instanceof ClassNode) { ClassNode script = (ClassNode) node; if (script.isScript()) { // ugghh..I don't like this. If the length of the node // is different from the length of the name of the // script // we know that this is the declaration, not a reference if (script.getNameWithoutPackage().length() != script.getLength()) { return VisitStatus.CONTINUE; } } } maybeDeclaration = ((ClassNode) maybeDeclaration).redirect(); } if (maybeDeclaration instanceof PropertyNode && ((PropertyNode) maybeDeclaration).getField() != null) { maybeDeclaration = ((PropertyNode) maybeDeclaration).getField(); } if (node instanceof ImportNode && ((ImportNode) node).getType() != null) { ImportNode imp = ((ImportNode) node); node = imp.getType(); if (imp.isStatic()) { // check for static import reference boolean isStaticDecl = (declaration instanceof FieldNode && ((FieldNode) declaration).isStatic()) || (declaration instanceof FieldNode && ((FieldNode) declaration).isStatic()); if (isStaticDecl) { String declarationName = getDeclarationName(); ClassNode declaringClass = declaration.getDeclaringClass(); if (declarationName.equals(imp.getFieldName()) && declaringClass.equals(imp.getType())) { // add just the name here } return VisitStatus.CONTINUE; } } } if (isEquivalent(maybeDeclaration)) { int flag = EqualityVisitor.checkForAssignment(node, result.enclosingAssignment) ? F_WRITE_OCCURRENCE : F_READ_OCCURRENCE; references.put(node, flag); } } return VisitStatus.CONTINUE; } private String getDeclarationName() { if (declaration instanceof FieldNode) { return ((FieldNode) declaration).getName(); } else if (declaration instanceof MethodNode) { return ((MethodNode) declaration).getName(); } else if (declaration instanceof ClassNode) { return ((ClassNode) declaration).getName(); } else { // Variable return declaration.getText(); } } // from IOccurrenceFinder public static final int F_WRITE_OCCURRENCE = 1; public static final int F_READ_OCCURRENCE = 2; private boolean isEquivalent(ASTNode maybeDeclaration) { if (maybeDeclaration == declaration) { return true; } // here we need to test for dynamically added fields and methods // they will not be the same instance, so we need to check // for equivalence some other way if (maybeDeclaration instanceof FieldNode && declaration instanceof FieldNode) { FieldNode maybeField = (FieldNode) maybeDeclaration; FieldNode field = (FieldNode) declaration; return maybeField.getName().equals(field.getName()) && maybeField.getDeclaringClass().equals(field.getDeclaringClass()); } else if (declaration instanceof MethodNode) { if (maybeDeclaration instanceof JDTMethodNode) { // GRECLIPSE-1255 Catches the case where the node comes from // JDT, we do not check for parameter count since JDT is // ignorant of possible default parameters MethodNode maybeMethod = (MethodNode) maybeDeclaration; MethodNode method = (MethodNode) declaration; return maybeMethod.getName().equals(method.getName()) && maybeMethod.getDeclaringClass().equals(method.getDeclaringClass()); } else if (maybeDeclaration instanceof MethodNode) { MethodNode maybeMethod = (MethodNode) maybeDeclaration; MethodNode method = (MethodNode) declaration; return checkParamLength(maybeMethod, method) && maybeMethod.getName().equals(method.getName()) && maybeMethod.getDeclaringClass().equals(method.getDeclaringClass()) && checkParams(maybeMethod, method); } } // here check for inner class nodes if ((maybeDeclaration instanceof InnerClassNode && declaration instanceof JDTClassNode) || (declaration instanceof InnerClassNode && maybeDeclaration instanceof JDTClassNode)) { return ((ClassNode) maybeDeclaration).getName().equals(((ClassNode) declaration).getName()); } return false; } private boolean checkParams(MethodNode maybeMethod, MethodNode method) { Parameter[] maybeParameters = maybeMethod.getParameters(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { if (!maybeParameters[i].getName().equals(parameters[i].getName()) || !typeEquals(maybeParameters[i], parameters[i])) { return false; } } return true; } private boolean typeEquals(Parameter maybeParameter, Parameter parameter) { ClassNode maybeType = maybeParameter.getType(); ClassNode type = parameter.getType(); if (maybeType == null) { return type == null; } else if (type == null) { return false; } return maybeType.getName().equals(type.getName()); } private boolean checkParamLength(MethodNode maybeMethod, MethodNode method) { Parameter[] maybeParameters = maybeMethod.getParameters(); Parameter[] parameters = method.getParameters(); if (maybeParameters == null) { return parameters == null; } else if (parameters == null) { return false; } if (maybeParameters.length != parameters.length) { return false; } return true; } public Map<ASTNode, Integer> getReferences() { return references; } }