/* * 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.fragments; import org.codehaus.groovy.ast.ASTNode; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.eclipse.codebrowsing.selection.IsSameExpression; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.runtime.Assert; /** * An {@link IASTFragment} that is a part of a binary expression * @author andrew * @created Jun 4, 2010 */ public class PropertyExpressionFragment implements IASTFragment { private final ASTFragmentKind kind; private final Expression expression; private final IASTFragment next; PropertyExpressionFragment(ASTFragmentKind token, Expression expression, IASTFragment next) { Assert.isNotNull(next); this.kind = token; this.expression = expression; this.next = next; } public Expression getAssociatedExpression() { return expression; } public ASTNode getAssociatedNode() { return expression; } public int getEnd() { return getNext().getEnd(); } public int getStart() { return expression.getStart(); } public int getLength() { return getEnd() - getStart(); } public int getTrimmedEnd(GroovyCompilationUnit unit) { return getNext().getTrimmedEnd(unit); } public int getTrimmedLength(GroovyCompilationUnit unit) { return getTrimmedEnd(unit) - getStart(); } public IASTFragment getNext() { return next; } public boolean matches(IASTFragment other) { if (!(other instanceof PropertyExpressionFragment)) { return false; } PropertyExpressionFragment otherBinary = (PropertyExpressionFragment) other; return otherBinary.kind() == this.kind && new IsSameExpression().isSame(expression, otherBinary.getAssociatedExpression()) && this.next.matches(otherBinary.getNext()); } @Override public String toString() { return print(0); } public String print(int indentLvl) { return ASTFragmentFactory.spaces(indentLvl) + "(P) " + expression.toString() + "\n" + next.print(indentLvl + 1); } public int fragmentLength() { return 1 + next.fragmentLength(); } public void accept(FragmentVisitor visitor) { if (visitor.previsit(this) && visitor.visit(this)) { next.accept(visitor); } } public ASTFragmentKind kind() { return kind; } /** * must match from the beginning of each fragment */ public IASTFragment findMatchingSubFragment(IASTFragment other) { if (this.fragmentLength() < other.fragmentLength()) { return new EmptyASTFragment(); } if (other.kind() == ASTFragmentKind.SIMPLE_EXPRESSION && new IsSameExpression().isSame(this.getAssociatedExpression(), other.getAssociatedExpression())) { return new SimpleExpressionASTFragment(expression); } else if (other.kind() == this.kind()) { PropertyExpressionFragment toMatchBinary = (PropertyExpressionFragment) other; if (new IsSameExpression().isSame(this.getAssociatedExpression(), toMatchBinary.getAssociatedExpression())) { IASTFragment result = this.getNext().findMatchingSubFragment(toMatchBinary.getNext()); if (result.kind() == ASTFragmentKind.EMPTY) { // the target expressions match, but not the remainder // so there is no match return new EmptyASTFragment(); } else { // remainder (or part of it) matches. return new PropertyExpressionFragment(this.kind(), this.getAssociatedExpression(), result); } } } return new EmptyASTFragment(); } /** * A fragment matches another if: * 1. either one is a simple expression and the simple expression matches OR * 2. Fragments are of the same kind AND * --a. associated nodes are the same (and arguments if both are method call * fragments) * --b. if next node is the same, then include the next node, but doesn't * have to be. * * @param other * @return */ // IASTFragment internalFindMatchingSubFragment(IASTFragment thiz, // IASTFragment toMatch) { // // complicated if statement: // // check that: // // 1. kinds are not the same, // // 2. at least one is a simple expression // // 3. neither are method calls (because would need to check args as // // well) // if (toMatch.kind() != thiz.kind() // && (toMatch.kind() == ASTFragmentKind.SIMPLE_EXPRESSION || thiz.kind() == // ASTFragmentKind.SIMPLE_EXPRESSION) // && toMatch.kind() != ASTFragmentKind.METHOD_CALL && thiz.kind() != // ASTFragmentKind.METHOD_CALL) { // if (new IsSameExpression().isSame(thiz.getAssociatedNode(), // toMatch.getAssociatedNode())) { // return new SimpleExpressionASTFragment(thiz.getAssociatedNode()); // } // } else if (thiz.kind() == ASTFragmentKind.PROPERTY || thiz.kind() == // ASTFragmentKind.METHOD_POINTER) { // PropertyExpressionFragment thizBinary = (PropertyExpressionFragment) // thiz; // PropertyExpressionFragment toMatchBinary = (PropertyExpressionFragment) // toMatch; // if (new IsSameExpression().isSame(thiz.getAssociatedNode(), // toMatch.getAssociatedNode())) { // IASTFragment result = // internalFindMatchingSubFragment(thizBinary.getNext(), // toMatchBinary.getNext()); // if (result.kind() == ASTFragmentKind.EMPTY) { // return new SimpleExpressionASTFragment(thiz.getAssociatedNode()); // } else { // return new PropertyExpressionFragment(thiz.kind(), // thiz.getAssociatedNode(), result); // } // } // } else if (thiz.kind() == ASTFragmentKind.METHOD_CALL) { // MethodCallFragment thizCall = (MethodCallFragment) thiz; // MethodCallFragment toMatchCall = (MethodCallFragment) toMatch; // if (new IsSameExpression().isSame(thizCall.getAssociatedNode(), // toMatchCall.getAssociatedNode()) // && new IsSameExpression().isSame(thizCall.getArguments(), // toMatchCall.getArguments())) { // // now check to see if we need to continue // if (thizCall.hasNext() && toMatchCall.hasNext()) { // IASTFragment result = internalFindMatchingSubFragment(thizCall.getNext(), // toMatchCall.getNext()); // if (result.kind() == ASTFragmentKind.EMPTY) { // return new MethodCallFragment(thiz.getAssociatedNode(), // thizCall.getArguments()); // } else { // return new MethodCallFragment(thiz.getAssociatedNode(), // thizCall.getArguments(), result); // } // } // } // } // return new EmptyASTFragment(); // } }