/*
* 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.groovy.syntax.Token;
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 BinaryExpressionFragment implements IASTFragment {
private final Token token;
private final Expression expression;
private final IASTFragment next;
private int actualStartPosition;
BinaryExpressionFragment(Token token, Expression expression, IASTFragment next) {
Assert.isNotNull(next);
this.token = token;
this.expression = expression;
this.next = next;
this.actualStartPosition = expression.getStart();
}
/**
* Ensure that fragments that are the LHS of a declaration expression have t
* the correct start position
*
* @param actualStartPosition the actual start of the fragment (include the
* 'def' keyword or type)
*/
void setActualStartPosition(int actualStartPosition) {
this.actualStartPosition = actualStartPosition;
}
public Expression getAssociatedExpression() {
return expression;
}
public ASTNode getAssociatedNode() {
return expression;
}
public int getEnd() {
return getNext().getEnd();
}
public int getStart() {
return actualStartPosition;
}
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 Token getToken() {
return token;
}
public IASTFragment getNext() {
return next;
}
/**
* @return true iff this fragment completely matches other
*/
public boolean matches(IASTFragment other) {
if (!(other instanceof BinaryExpressionFragment)) {
return false;
}
BinaryExpressionFragment otherBinary = (BinaryExpressionFragment) other;
return otherBinary.getToken().getText().equals(this.token.getText())
&& 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) + "(B) " + 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 ASTFragmentKind.BINARY;
}
/**
* Finds a subfragment starting at this fragment that matches other.
* Returns an empty fragment if no match.
*
* There may be further matches inside this one, but this method only loos
* for fragments
* that start at the beginning
*/
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() == ASTFragmentKind.BINARY) {
BinaryExpressionFragment otherBinary = (BinaryExpressionFragment) other;
if (new IsSameExpression().isSame(this.getAssociatedExpression(), other.getAssociatedExpression())
&& this.getToken().getText().equals(otherBinary.getToken().getText())) {
// current component of the fragment matches. Now check to see
// if there are any more pieces that match
IASTFragment result = this.next.findMatchingSubFragment(otherBinary.next);
if (result.kind() == ASTFragmentKind.EMPTY) {
// no more pieces match, so just return a simple expression
return new EmptyASTFragment();
} else {
// more matches, include that in the sub-fragment
BinaryExpressionFragment newResult = new BinaryExpressionFragment(token, expression, result);
newResult.setActualStartPosition(actualStartPosition);
return newResult;
}
}
}
// current component does not match.
return new EmptyASTFragment();
}
}