/* * Copyright 2003-2010 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.codebrowsing.selection; import java.util.ArrayList; import java.util.List; import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.PropertyNode; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.ArrayExpression; import org.codehaus.groovy.ast.expr.AttributeExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; import org.codehaus.groovy.ast.expr.BooleanExpression; import org.codehaus.groovy.ast.expr.CastExpression; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ClosureListExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.groovy.ast.expr.DeclarationExpression; import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; import org.codehaus.groovy.ast.expr.FieldExpression; import org.codehaus.groovy.ast.expr.GStringExpression; import org.codehaus.groovy.ast.expr.ListExpression; import org.codehaus.groovy.ast.expr.MapEntryExpression; import org.codehaus.groovy.ast.expr.MapExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.MethodPointerExpression; import org.codehaus.groovy.ast.expr.NotExpression; import org.codehaus.groovy.ast.expr.PostfixExpression; import org.codehaus.groovy.ast.expr.PrefixExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.expr.RangeExpression; import org.codehaus.groovy.ast.expr.SpreadExpression; import org.codehaus.groovy.ast.expr.SpreadMapExpression; import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; import org.codehaus.groovy.ast.expr.TernaryExpression; import org.codehaus.groovy.ast.expr.TupleExpression; import org.codehaus.groovy.ast.expr.UnaryMinusExpression; import org.codehaus.groovy.ast.expr.UnaryPlusExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.classgen.BytecodeExpression; import org.codehaus.groovy.eclipse.codebrowsing.fragments.ASTFragmentFactory; import org.codehaus.groovy.eclipse.codebrowsing.fragments.ASTFragmentKind; import org.codehaus.groovy.eclipse.codebrowsing.fragments.FragmentVisitor; import org.codehaus.groovy.eclipse.codebrowsing.fragments.IASTFragment; import org.codehaus.groovy.eclipse.codebrowsing.fragments.MethodCallFragment; /** * Finds all occurrences of the passed in expression in the module. * * @author andrew * @created May 12, 2010 */ public class FindAllOccurrencesVisitor extends ClassCodeVisitorSupport { class FragmentMatcherVisitor extends FragmentVisitor { private boolean matchWasFound = false; @Override public boolean previsit(IASTFragment fragment) { IASTFragment matched = fragment.findMatchingSubFragment(toFind); if (matched.kind() != ASTFragmentKind.EMPTY) { // prevent double matching, which may occur in binary fragments when searching for a simple expression fragment if (occurrences.size() == 0 || occurrences.get(occurrences.size()-1).getStart() != matched.getStart()) { occurrences.add(matched); matchWasFound = true; } } // only continue for binary fragments since there may be multiple // matches inside of them return fragment.kind() == ASTFragmentKind.BINARY; } boolean matchWasFound() { boolean b = matchWasFound; matchWasFound = false; return b; } } class AssociatedExpressionMatcher extends FragmentVisitor { boolean ignoreNext = false; @Override public boolean previsit(IASTFragment fragment) { if (! ignoreNext) { fragment.getAssociatedExpression().visit(FindAllOccurrencesVisitor.this); } else { ignoreNext = false; } return true; } @Override public boolean visit(MethodCallFragment fragment) { fragment.getArguments().visit(FindAllOccurrencesVisitor.this); return true; } } private IASTFragment toFind; private List<IASTFragment> occurrences; private ModuleNode module; private AnnotatedNode limitTo; private ASTFragmentFactory factory; private FragmentMatcherVisitor fragmentMatcher; private AssociatedExpressionMatcher associatedExpressionMatcher; public FindAllOccurrencesVisitor(ModuleNode module) { this(module, null); } public FindAllOccurrencesVisitor(ModuleNode module, AnnotatedNode limitTo) { this.limitTo = limitTo; this.module = module; this.factory = new ASTFragmentFactory(); this.fragmentMatcher = new FragmentMatcherVisitor(); this.associatedExpressionMatcher = new AssociatedExpressionMatcher(); } public List<IASTFragment> findOccurrences(IASTFragment fragment) { this.toFind = fragment; this.occurrences = new ArrayList<IASTFragment>(); if (limitTo == null) { List<ClassNode> classes = module.getClasses(); for (ClassNode classNode : classes) { visitClass(classNode); } } else { if (limitTo instanceof ClassNode) { visitClass((ClassNode) limitTo); } else if (limitTo instanceof MethodNode) { visitMethod((MethodNode) limitTo); } else if (limitTo instanceof FieldNode) { visitField((FieldNode) limitTo); } else { limitTo.visit(this); } } return this.occurrences; } @Override protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { // need to visit clinit so that we can access static initializers if (node.getEnd() == 0 && !node.getName().equals("<clinit>")) { return; } super.visitConstructorOrMethod(node, isConstructor); } @Override public void visitField(FieldNode node) { if (node.getEnd() == 0) { return; } // if (node.getInitialExpression() != null && isSame.isSame(toFind, // node.getInitialExpression())) { // occurrences.add(node.getInitialExpression()); // return; // } super.visitField(node); } @Override public void visitProperty(PropertyNode node) { // ignore. don't want to visit the fields twice. } @Override public void visitArgumentlistExpression(ArgumentListExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitArgumentlistExpression(expression); } @Override public void visitArrayExpression(ArrayExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitArrayExpression(expression); } @Override public void visitAttributeExpression(AttributeExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitAttributeExpression(expression); } @Override public void visitBinaryExpression(BinaryExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); // If looking for a simple expression, then we have already visited the children // don't visit twice // if (toFind.kind() != ASTFragmentKind.SIMPLE_EXPRESSION) { // don't visit children directly because that may result in // unanticipated double matches // Don't ignore the first fragment associatedExpressionMatcher.ignoreNext = false; fragment.accept(associatedExpressionMatcher); // } } @Override public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitBitwiseNegationExpression(expression); } @Override public void visitBooleanExpression(BooleanExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitBooleanExpression(expression); } @Override public void visitBytecodeExpression(BytecodeExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitBytecodeExpression(expression); } @Override public void visitCastExpression(CastExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitCastExpression(expression); } @Override public void visitClassExpression(ClassExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitClassExpression(expression); } @Override public void visitClosureExpression(ClosureExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitClosureExpression(expression); } @Override public void visitClosureListExpression(ClosureListExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitClosureListExpression(expression); } @Override public void visitConstantExpression(ConstantExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitConstantExpression(expression); } @Override public void visitConstructorCallExpression(ConstructorCallExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitConstructorCallExpression(expression); } @Override public void visitDeclarationExpression(DeclarationExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitDeclarationExpression(expression); } @Override public void visitVariableExpression(VariableExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitVariableExpression(expression); } @Override public void visitFieldExpression(FieldExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitFieldExpression(expression); } @Override public void visitGStringExpression(GStringExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitGStringExpression(expression); } @Override public void visitListExpression(ListExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitListExpression(expression); } @Override public void visitMapEntryExpression(MapEntryExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitMapEntryExpression(expression); } @Override public void visitMapExpression(MapExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitMapExpression(expression); } @Override public void visitMethodCallExpression(MethodCallExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); // don't visit children directly because that may result in // unanticipated double matches // ignore the first fragment since that was visited above associatedExpressionMatcher.ignoreNext = true; fragment.accept(associatedExpressionMatcher); } @Override public void visitMethodPointerExpression(MethodPointerExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); // don't visit children directly because that may result in // unanticipated double matches // ignore the first fragment since that was visited above associatedExpressionMatcher.ignoreNext = true; fragment.accept(associatedExpressionMatcher); } @Override public void visitNotExpression(NotExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitNotExpression(expression); } @Override public void visitPostfixExpression(PostfixExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitPostfixExpression(expression); } @Override public void visitPrefixExpression(PrefixExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitPrefixExpression(expression); } @Override public void visitPropertyExpression(PropertyExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); // don't visit children directly because that may result in // unanticipated double matches // ignore the first fragment since that was visited above associatedExpressionMatcher.ignoreNext = true; fragment.accept(associatedExpressionMatcher); } @Override public void visitRangeExpression(RangeExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitRangeExpression(expression); } @Override public void visitShortTernaryExpression(ElvisOperatorExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitShortTernaryExpression(expression); } @Override public void visitSpreadExpression(SpreadExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitSpreadExpression(expression); } @Override public void visitSpreadMapExpression(SpreadMapExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitSpreadMapExpression(expression); } @Override public void visitStaticMethodCallExpression(StaticMethodCallExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitStaticMethodCallExpression(expression); } @Override public void visitTernaryExpression(TernaryExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitTernaryExpression(expression); } @Override public void visitTupleExpression(TupleExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitTupleExpression(expression); } @Override public void visitUnaryMinusExpression(UnaryMinusExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitUnaryMinusExpression(expression); } @Override public void visitUnaryPlusExpression(UnaryPlusExpression expression) { IASTFragment fragment = factory.createFragment(expression); fragment.accept(fragmentMatcher); if (!fragmentMatcher.matchWasFound()) super.visitUnaryPlusExpression(expression); } }