/*******************************************************************************
* Copyright (c) 2005, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
* E.D. Willink - Fix NPE in visiting operation call with null source
*******************************************************************************/
package org.eclipse.ocl.utilities;
import java.util.Collections;
import java.util.List;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionLiteralExp;
import org.eclipse.ocl.expressions.CollectionLiteralPart;
import org.eclipse.ocl.expressions.CollectionRange;
import org.eclipse.ocl.expressions.EnumLiteralExp;
import org.eclipse.ocl.expressions.IfExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.InvalidLiteralExp;
import org.eclipse.ocl.expressions.IterateExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.LetExp;
import org.eclipse.ocl.expressions.MessageExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StateExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralPart;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.expressions.UnspecifiedValueExp;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.expressions.VariableExp;
/**
* <p>
* An abstract implementation of the {@link Visitor} API, in which subclasses
* need only selectively override <code>handleXxx(...)</code> methods for
* internal AST nodes and <code>visitXxx(...)</code> methods for leaf nodes.
* </p><p>
* The {@link #result} value is convenient for accumulating the result of the
* visitation. In the subclass, simply assign/modify the result value as
* necessary in the overridden visitation methods, and this framework will
* ensure that it is returned as the overall value of the
* {@link Visitable#accept(Visitor)} call.
* </p>
*
* @author Christian W. Damus (cdamus)
*/
public abstract class AbstractVisitor<T, C, O, P, EL, PM, S, COA, SSA, CT>
implements Visitor<T, C, O, P, EL, PM, S, COA, SSA, CT> {
/**
* Accumulator for the result of the AST visitation.
*/
protected T result;
/**
* Initializes me.
*/
protected AbstractVisitor() {
super();
}
/**
* Initializes me with an initial value for my result.
*
* @param initialValue my initial result value
*/
protected AbstractVisitor(T initialValue) {
this.result = initialValue;
}
/**
* A null-safe visitation of the specified visitable.
*
* @param v a visitable, or <code>null</code>
* @return <code>null</code> if the visitable is <code>null</code>;
* otherwise, the result of visiting it
*
* @since 1.2
*/
protected T safeVisit(Visitable v) {
return (v == null)? null : v.accept(this);
}
/**
* Visits the operation-call source and then its arguments.
* Returns the result of {@link #handleOperationCallExp(OperationCallExp, Object, List)}.
*/
public T visitOperationCallExp(OperationCallExp<C, O> callExp) {
OCLExpression<C> source = callExp.getSource();
T sourceResult = safeVisit(source);
List<T> argumentResults;
List<OCLExpression<C>> arguments = callExp.getArgument();
if (arguments.isEmpty()) {
argumentResults = Collections.emptyList();
} else {
argumentResults = new java.util.ArrayList<T>(arguments.size());
for (OCLExpression<C> qual : arguments) {
argumentResults.add(safeVisit(qual));
}
}
return handleOperationCallExp(callExp, sourceResult, argumentResults);
}
/**
* Visits the specified operation call with the results of visiting
* its source and arguments (if any).
*
* @param callExp the operation call expression
* @param sourceResult the result of visiting the expression's source
* @param argumentResults the results of visiting the expression's
* arguments, or an empty list if there are no arguments
*
* @return the accumulated {@link #result}, by default
*
* @see #visitOperationCallExp(OperationCallExp)
*/
protected T handleOperationCallExp(OperationCallExp<C, O> callExp,
T sourceResult, List<T> argumentResults) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitVariableExp(VariableExp<C, PM> v) {
return result;
}
/**
* Visits the property-call source and then its qualifiers (if any).
* Returns the result of {@link #handlePropertyCallExp(PropertyCallExp, Object, List)}.
*/
public T visitPropertyCallExp(PropertyCallExp<C, P> callExp) {
// source is null when the property call expression is an
// association class navigation qualifier
T sourceResult = safeVisit(callExp.getSource());
List<T> qualifierResults;
List<OCLExpression<C>> qualifiers = callExp.getQualifier();
if (qualifiers.isEmpty()) {
qualifierResults = Collections.emptyList();
} else {
qualifierResults = new java.util.ArrayList<T>(qualifiers.size());
for (OCLExpression<C> qual : qualifiers) {
qualifierResults.add(safeVisit(qual));
}
}
return handlePropertyCallExp(callExp, sourceResult, qualifierResults);
}
/**
* Visits the specified property call with the results of visiting
* its source and qualifiers (if any). Note that in the case of a property
* call expression as a qualifier of an association class call, the
* property call does not have a source and, therefore, the
* <code>sourceResult</tt> will be <code>null</code> in that case.
*
* @param callExp the property call expression, if there is a source
* @param sourceResult the result of visiting the expression's source
* @param qualifierResults the results of visiting the expression's
* qualifiers, or an empty list if there are no qualifiers
*
* @return the accumulated {@link #result}, by default
*
* @see #visitPropertyCallExp(PropertyCallExp)
*/
protected T handlePropertyCallExp(PropertyCallExp<C, P> callExp,
T sourceResult, List<T> qualifierResults) {
return result;
}
/**
* Visits the association-class-call source and then its qualifiers (if any).
* Returns the result of {@link #handleAssociationClassCallExp(AssociationClassCallExp, Object, List)}.
*/
public T visitAssociationClassCallExp(AssociationClassCallExp<C, P> callExp) {
T sourceResult = safeVisit(callExp.getSource());
List<T> qualifierResults;
List<OCLExpression<C>> qualifiers = callExp.getQualifier();
if (qualifiers.isEmpty()) {
qualifierResults = Collections.emptyList();
} else {
qualifierResults = new java.util.ArrayList<T>(qualifiers.size());
for (OCLExpression<C> qual : qualifiers) {
qualifierResults.add(safeVisit(qual));
}
}
return handleAssociationClassCallExp(callExp, sourceResult, qualifierResults);
}
/**
* Visits the specified association-class call with the results of visiting
* its source and qualifiers (if any).
*
* @param callExp the association-class call expression
* @param sourceResult the result of visiting the expression's source
* @param qualifierResults the results of visiting the expression's
* qualifiers, or an empty list if there are no qualifiers
*
* @return the accumulated {@link #result}, by default
*
* @see #visitAssociationClassCallExp(AssociationClassCallExp)
*/
protected T handleAssociationClassCallExp(AssociationClassCallExp<C, P> callExp,
T sourceResult, List<T> qualifierResults) {
return result;
}
/**
* Visits the variable's initialization expression (if any).
* Returns the result of {@link #handleVariable(Variable, Object)}.
*/
public T visitVariable(Variable<C, PM> variable) {
T initResult = safeVisit(variable.getInitExpression());
return handleVariable(variable, initResult);
}
/**
* Visits the specified variable with the results of visiting
* its initializer (if any).
*
* @param variable the variable
* @param initResult the result of visiting the expression's initializer,
* or <code>null</code> if it has none
*
* @return the accumulated {@link #result}, by default
*
* @see #visitVariable(Variable)
*/
protected T handleVariable(Variable<C, PM> variable,
T initResult) {
return result;
}
/**
* Visits the if expression's condition, then, and else expressions.
* Returns the result of {@link #handleIfExp(IfExp, Object, Object, Object)}.
*/
public T visitIfExp(IfExp<C> ifExp) {
return handleIfExp(ifExp,
safeVisit(ifExp.getCondition()),
safeVisit(ifExp.getThenExpression()),
safeVisit(ifExp.getElseExpression()));
}
/**
* Visits the specified if expression with the results of visiting
* its condition, then, and else expressions.
*
* @param ifExp the if expression
* @param conditionResult the result of visiting the expression's condition
* @param thenResult the result of visiting the expression's then
* @param elseResult the result of visiting the expression's else
*
* @return the accumulated {@link #result}, by default
*
* @see #visitIfExp(IfExp)
*/
protected T handleIfExp(IfExp<C> ifExp, T conditionResult, T thenResult,
T elseResult) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitTypeExp(TypeExp<C> t) {
return result;
}
/**
* Visits the message expression's target and then its arguments.
* Returns the result of {@link #handleMessageExp(MessageExp, Object, List)}.
*/
public T visitMessageExp(MessageExp<C, COA, SSA> messageExp) {
T targetResult = safeVisit(messageExp.getTarget());
List<T> argumentResults;
List<OCLExpression<C>> arguments = messageExp.getArgument();
if (arguments.isEmpty()) {
argumentResults = Collections.emptyList();
} else {
argumentResults = new java.util.ArrayList<T>(arguments.size());
for (OCLExpression<C> qual : arguments) {
argumentResults.add(safeVisit(qual));
}
}
return handleMessageExp(messageExp, targetResult, argumentResults);
}
/**
* Visits the specified message expression with the results of visiting
* its target and arguments (if any).
*
* @param messageExp the message expression
* @param targetResult the result of visiting the expression's target
* @param argumentResults the results of visiting the expression's
* arguments, or an empty list if there are no arguments
*
* @return the accumulated {@link #result}, by default
*
* @see #visitMessageExp(MessageExp)
*/
protected T handleMessageExp(MessageExp<C, COA, SSA> messageExp,
T targetResult, List<T> argumentResults) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitUnspecifiedValueExp(UnspecifiedValueExp<C> unspecExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitStateExp(StateExp<C, S> stateExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitIntegerLiteralExp(IntegerLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitRealLiteralExp(RealLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitStringLiteralExp(StringLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitBooleanLiteralExp(BooleanLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitNullLiteralExp(NullLiteralExp<C> literalExp) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitInvalidLiteralExp(InvalidLiteralExp<C> literalExp) {
return result;
}
/**
* Visits the tuple literal's parts.
* Returns the result of {@link #handleTupleLiteralExp(TupleLiteralExp, List)}.
*/
public T visitTupleLiteralExp(TupleLiteralExp<C, P> literalExp) {
List<T> partResults;
List<TupleLiteralPart<C, P>> parts = literalExp.getPart();
if (parts.isEmpty()) {
partResults = Collections.emptyList();
} else {
partResults = new java.util.ArrayList<T>(parts.size());
for (TupleLiteralPart<C, P> part : parts) {
partResults.add(safeVisit(part));
}
}
return handleTupleLiteralExp(literalExp, partResults);
}
/**
* Visits the specified tuple literal expression with the results of visiting
* its parts (if any).
*
* @param literalExp the tuple literal expression
* @param partResults the results of visiting the expression's
* parts, or an empty list if there are no parts
*
* @return the accumulated {@link #result}, by default
*
* @see #visitTupleLiteralExp(TupleLiteralExp)
*/
protected T handleTupleLiteralExp(TupleLiteralExp<C, P> literalExp,
List<T> partResults) {
return result;
}
/**
* Visits the tuple literal part's value, if any.
* Returns the result of {@link #handleTupleLiteralPart(TupleLiteralPart, Object)}.
*/
public T visitTupleLiteralPart(TupleLiteralPart<C, P> part) {
T valueResult = safeVisit(part.getValue());
return handleTupleLiteralPart(part, valueResult);
}
/**
* Visits the specified tuple literal part with the results of visiting
* its value (if any).
*
* @param part the tuple literal part
* @param valueResult the result of visiting the expression's value, or
* <code>null</code> if it has no value
*
* @return the accumulated {@link #result}, by default
*
* @see #visitTupleLiteralPart(TupleLiteralPart)
*/
protected T handleTupleLiteralPart(TupleLiteralPart<C, P> part,
T valueResult) {
return result;
}
/**
* Visits the let's variable declaration then its 'in' expression.
* Returns the result of {@link #handleLetExp(LetExp, Object, Object)}.
*/
public T visitLetExp(LetExp<C, PM> letExp) {
return handleLetExp(letExp,
safeVisit(letExp.getVariable()),
safeVisit(letExp.getIn()));
}
/**
* Visits the specified let expression with the results of visiting
* its variable and in expression.
*
* @param letExp the let expression
* @param variableResult the result of visiting the expression's variable
* @param inResult the result of visiting the expression's in expression
*
* @return the accumulated {@link #result}, by default
*
* @see #visitLetExp(LetExp)
*/
protected T handleLetExp(LetExp<C, PM> letExp, T variableResult, T inResult) {
return result;
}
/**
* Simply returns {@link #result}.
*/
public T visitEnumLiteralExp(EnumLiteralExp<C, EL> literalExp) {
return result;
}
/**
* Visits the collection literal's parts.
*
* Returns the result of {@link #handleCollectionLiteralExp(CollectionLiteralExp, List)}.
*/
public T visitCollectionLiteralExp(CollectionLiteralExp<C> literalExp) {
List<T> partResults;
List<CollectionLiteralPart<C>> parts = literalExp.getPart();
if (parts.isEmpty()) {
partResults = Collections.emptyList();
} else {
partResults = new java.util.ArrayList<T>(parts.size());
for (CollectionLiteralPart<C> part : parts) {
partResults.add(safeVisit(part));
}
}
return handleCollectionLiteralExp(literalExp, partResults);
}
/**
* Visits the specified collection literal expression with the results of visiting
* its parts (if any).
*
* @param literalExp the collection literal expression
* @param partResults the results of visiting the expression's
* parts, or an empty list if there are no parts
*
* @return the accumulated {@link #result}, by default
*
* @see #visitCollectionLiteralExp(CollectionLiteralExp)
*/
protected T handleCollectionLiteralExp(CollectionLiteralExp<C> literalExp,
List<T> partResults) {
return result;
}
/**
* Visits the item's item expression.
*
* Returns the result of {@link #handleCollectionItem(CollectionItem, Object)}
*/
public T visitCollectionItem(CollectionItem<C> item) {
T itemResult = safeVisit(item.getItem());
return handleCollectionItem(item, itemResult);
}
/**
* Visits the specified collection item with the result of visiting
* its item expression.
*
* @param item the collection item
* @param itemResult the result of visiting the item's item expression
*
* @return the accumulated {@link #result}, by default
*
* @see #visitCollectionItem(CollectionItem)
*/
protected T handleCollectionItem(CollectionItem<C> item, T itemResult) {
return result;
}
/**
* Visits the range's first and last expressions.
*
* Returns the result of {@link #handleCollectionRange(CollectionRange, Object, Object)}.
*/
public T visitCollectionRange(CollectionRange<C> range) {
return handleCollectionRange(range,
safeVisit(range.getFirst()),
safeVisit(range.getLast()));
}
/**
* Visits the specified collection range with the results of visiting
* its first and last expressions.
*
* @param range the collection range
* @param firstResult the result of visiting the range's first expression
* @param lastResult the result of visiting the range's last expression
*
* @return the accumulated {@link #result}, by default
*
* @see #visitCollectionRange(CollectionRange)
*/
protected T handleCollectionRange(CollectionRange<C> range, T firstResult,
T lastResult) {
return result;
}
/**
* Visits the iterator's source, then its variables, followed by its body
* expression.
* Returns the result of {@link #handleIteratorExp(IteratorExp, Object, List, Object)}.
*/
public T visitIteratorExp(IteratorExp<C, PM> callExp) {
T sourceResult = safeVisit(callExp.getSource());
List<T> variableResults;
List<Variable<C, PM>> variables = callExp.getIterator();
if (variables.isEmpty()) {
variableResults = Collections.emptyList();
} else {
variableResults = new java.util.ArrayList<T>(variables.size());
for (Variable<C, PM> iterVar : variables) {
variableResults.add(safeVisit(iterVar));
}
}
T bodyResult = safeVisit(callExp.getBody());
return handleIteratorExp(callExp, sourceResult, variableResults, bodyResult);
}
/**
* Visits the specified iterator expression with the results of visiting
* its source, its iterator variables, and its body expression.
*
* @param callExp the iterator expression
* @param sourceResult the result of visiting the expression's source
* @param variableResults the results of visiting the expression's
* iterator variables
* @param bodyResult the result of visiting the expression's body
*
* @return the accumulated {@link #result}, by default
*
* @see #visitIteratorExp(IteratorExp)
*/
protected T handleIteratorExp(IteratorExp<C, PM> callExp,
T sourceResult, List<T> variableResults, T bodyResult) {
return result;
}
/**
* Visits the iterate's source, then its iterator variables,
* result variable, and body expression.
* Returns the result of {@link #handleIterateExp(IterateExp, Object, List, Object, Object)}.
*/
public T visitIterateExp(IterateExp<C, PM> callExp) {
T sourceResult = safeVisit(callExp.getSource());
List<T> variableResults;
List<Variable<C, PM>> variables = callExp.getIterator();
if (variables.isEmpty()) {
variableResults = Collections.emptyList();
} else {
variableResults = new java.util.ArrayList<T>(variables.size());
for (Variable<C, PM> iterVar : variables) {
variableResults.add(safeVisit(iterVar));
}
}
T resultResult = safeVisit(callExp.getResult());
T bodyResult = safeVisit(callExp.getBody());
return handleIterateExp(callExp, sourceResult, variableResults,
resultResult, bodyResult);
}
/**
* Visits the specified iterate expression with the results of visiting
* its source, its iterator variables, its result variable, and its body
* expression.
*
* @param callExp the iterate expression
* @param sourceResult the result of visiting the expression's source
* @param variableResults the results of visiting the expression's
* iterator variables
* @param resultResult the result of visiting the expressions' result variable
* @param bodyResult the result of visiting the expression's body
*
* @return the accumulated {@link #result}, by default
*
* @see #visitIterateExp(IterateExp)
*/
protected T handleIterateExp(IterateExp<C, PM> callExp,
T sourceResult, List<T> variableResults, T resultResult, T bodyResult) {
return result;
}
/**
* Visits the expressions context variable, its parameter variables (if any),
* its result variable (if any), and finally its body expression.
*
* Returns the result of
* {@link #handleExpressionInOCL(ExpressionInOCL, Object, Object, List, Object)}.
*/
public T visitExpressionInOCL(ExpressionInOCL<C, PM> expression) {
T contextResult = safeVisit(expression.getContextVariable());
Variable<C, PM> resultVar = expression.getResultVariable();
T resultResult = safeVisit(resultVar);
List<T> parameterResults;
List<Variable<C, PM>> parameters = expression.getParameterVariable();
if (parameters.isEmpty()) {
parameterResults = Collections.emptyList();
} else {
parameterResults = new java.util.ArrayList<T>(parameters.size());
for (Variable<C, PM> iterVar : parameters) {
parameterResults.add(safeVisit(iterVar));
}
}
T bodyResult = safeVisit(expression.getBodyExpression());
return handleExpressionInOCL(expression, contextResult, resultResult,
parameterResults, bodyResult);
}
/**
* Visits the specified expression-in-OCL with the results of visiting
* its context variable, its result variable (if any), its parameter
* variables (if any), and its body expression.
*
* @param expression the expression-in-OCL
* @param contextResult the result of visiting the expression's context variable
* @param resultResult the result of visiting the expressions' result variable,
* or <code>null</code> if there is no result variable
* @param parameterResults the results of visiting the expression's
* parameter variables, or an empty list if there are none
* @param bodyResult the result of visiting the expression's body
*
* @return the accumulated {@link #result}, by default
*
* @see #visitExpressionInOCL(ExpressionInOCL)
*/
protected T handleExpressionInOCL(ExpressionInOCL<C, PM> expression,
T contextResult, T resultResult, List<T> parameterResults, T bodyResult) {
return result;
}
/**
* Visits the constraint's specification, if any (and if the
* {@link #getSpecification(Object)} method is overridden).
*
* Returns the result of {@link #handleConstraint(Object, Object)}.
*
* @see #getSpecification(Object)
*/
public T visitConstraint(CT constraint) {
T specificationResult = safeVisit(getSpecification(constraint));
return handleConstraint(constraint, specificationResult);
}
/**
* Visits the specified constraint with the results of visiting
* its specification.
*
* @param constraint the constraint
* @param specificationResult the result of visiting the constraint's
* specification, or <code>null</code> if either it has none or the
* {@link #getSpecification(Object)} method is not overridden
*
* @return the accumulated {@link #result}, by default
*
* #see {@link #getSpecification(Object)}
* @see #visitConstraint(Object)
*/
protected T handleConstraint(CT constraint, T specificationResult) {
return result;
}
/**
* Overridden by subclasses interested in visiting constraints, to get the
* constraint's specification.
*
* @param constraint a constraint
* @return its specification
*
* @see #visitConstraint(Object)
*/
protected ExpressionInOCL<C, PM> getSpecification(CT constraint) {
return null;
}
} //VisitorImpl