/*
* 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.codebrowsing.fragments;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.eclipse.codebrowsing.selection.IsSameExpression;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
/**
* A method call fragment
* This is the part of the method call expression that occurs after the '.'
* and it includes the expression for the method selector as well as the
* arguments expression.
*
* The next {@link IASTFragment} is optional.
*
* @author andrew
* @created Jun 4, 2010
*/
public class MethodCallFragment implements IASTFragment {
protected MethodCallExpression callExpression;
private final Expression methodExpression;
private final Expression arguments;
private final IASTFragment next;
private final int actualEndPosition;
MethodCallFragment(Expression methodExpression, Expression arguments, IASTFragment next, int actualEndPosition) {
this.methodExpression = methodExpression;
this.arguments = arguments;
this.next = next;
this.actualEndPosition = actualEndPosition;
}
public MethodCallFragment(Expression methodExpression, Expression arguments, int actualEndPosition) {
this(methodExpression, arguments, null, actualEndPosition);
}
public ASTFragmentKind kind() {
return ASTFragmentKind.METHOD_CALL;
}
public boolean hasNext() {
return next != null;
}
public IASTFragment getNext() {
return next;
}
public int getStart() {
return getAssociatedExpression().getStart();
}
public int getEnd() {
return hasNext() ? getNext().getEnd() : actualEndPosition;
}
public int getLength() {
return getEnd() - getStart();
}
public int getTrimmedEnd(GroovyCompilationUnit unit) {
if (hasNext()) {
return getNext().getTrimmedEnd(unit);
} else {
char[] contents = unit.getContents();
int end = actualEndPosition;
while (end > getStart() && Character.isWhitespace(contents[end])) {
end -= 1;
}
return end;
}
}
public int getTrimmedLength(GroovyCompilationUnit unit) {
return getTrimmedEnd(unit) - getStart();
}
public MethodCallExpression getAssociatedNode() {
return callExpression;
}
public Expression getAssociatedExpression() {
return methodExpression;
}
public Expression getArguments() {
return arguments;
}
public int fragmentLength() {
return hasNext() ? 1 + getNext().fragmentLength() : 1;
}
public void accept(FragmentVisitor visitor) {
if (visitor.previsit(this) && visitor.visit(this) && hasNext()) {
getNext().accept(visitor);
}
}
public String print(int indentLvl) {
return ASTFragmentFactory.spaces(indentLvl) + "(M) " + getAssociatedExpression() + '.' + getArguments() + (hasNext() ? '\n' + getNext().print(indentLvl + 1) : "");
}
public IASTFragment findMatchingSubFragment(IASTFragment that) {
if (that.kind() == this.kind() && that.fragmentLength() <= this.fragmentLength() && similar((MethodCallFragment) that)) {
IASTFragment frag = ((MethodCallFragment) that).next;
if (frag == null) {
// other has no more components, we have matched the current component only
return new MethodCallFragment(this.getAssociatedExpression(), this.getArguments(), actualEndPosition);
} else {
frag = next.findMatchingSubFragment(frag);
if (frag.kind() != ASTFragmentKind.EMPTY) {
return new MethodCallFragment(this.getAssociatedExpression(), this.getArguments(), frag, actualEndPosition);
}
}
}
return new EmptyASTFragment();
}
public boolean matches(IASTFragment that) {
if (that instanceof MethodCallFragment && ((MethodCallFragment) that).hasNext() == this.hasNext()) {
return similar((MethodCallFragment) that);
}
return false;
}
protected boolean similar(MethodCallFragment that) {
return new IsSameExpression().isSame(this.getAssociatedExpression(), that.getAssociatedExpression()) && new IsSameExpression().isSame(this.getArguments(), that.getArguments());
}
}