blob: 8a2034a39465b89141b7ad804e9a55e5a0246549 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2005, 2007 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
*
* </copyright>
*
* $Id: ValidationVisitorImpl.java,v 1.12 2007/01/29 20:31:18 cdamus Exp $
*/
package org.eclipse.emf.ocl.expressions.impl;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ocl.expressions.AssociationClassCallExp;
import org.eclipse.emf.ocl.expressions.BooleanLiteralExp;
import org.eclipse.emf.ocl.expressions.CollectionItem;
import org.eclipse.emf.ocl.expressions.CollectionKind;
import org.eclipse.emf.ocl.expressions.CollectionLiteralExp;
import org.eclipse.emf.ocl.expressions.CollectionLiteralPart;
import org.eclipse.emf.ocl.expressions.CollectionRange;
import org.eclipse.emf.ocl.expressions.EnumLiteralExp;
import org.eclipse.emf.ocl.expressions.ExpressionsFactory;
import org.eclipse.emf.ocl.expressions.FeatureCallExp;
import org.eclipse.emf.ocl.expressions.IfExp;
import org.eclipse.emf.ocl.expressions.IntegerLiteralExp;
import org.eclipse.emf.ocl.expressions.InvalidLiteralExp;
import org.eclipse.emf.ocl.expressions.IterateExp;
import org.eclipse.emf.ocl.expressions.IteratorExp;
import org.eclipse.emf.ocl.expressions.LetExp;
import org.eclipse.emf.ocl.expressions.MessageExp;
import org.eclipse.emf.ocl.expressions.NullLiteralExp;
import org.eclipse.emf.ocl.expressions.OCLExpression;
import org.eclipse.emf.ocl.expressions.OperationCallExp;
import org.eclipse.emf.ocl.expressions.PropertyCallExp;
import org.eclipse.emf.ocl.expressions.RealLiteralExp;
import org.eclipse.emf.ocl.expressions.StateExp;
import org.eclipse.emf.ocl.expressions.StringLiteralExp;
import org.eclipse.emf.ocl.expressions.TupleLiteralExp;
import org.eclipse.emf.ocl.expressions.TupleLiteralPart;
import org.eclipse.emf.ocl.expressions.TypeExp;
import org.eclipse.emf.ocl.expressions.UnspecifiedValueExp;
import org.eclipse.emf.ocl.expressions.Variable;
import org.eclipse.emf.ocl.expressions.VariableExp;
import org.eclipse.emf.ocl.expressions.Visitor;
import org.eclipse.emf.ocl.expressions.util.AbstractVisitor;
import org.eclipse.emf.ocl.expressions.util.ExpressionsUtil;
import org.eclipse.emf.ocl.internal.OCLPlugin;
import org.eclipse.emf.ocl.parser.EcoreEnvironment;
import org.eclipse.emf.ocl.parser.Environment;
import org.eclipse.emf.ocl.types.BagType;
import org.eclipse.emf.ocl.types.CollectionType;
import org.eclipse.emf.ocl.types.OrderedSetType;
import org.eclipse.emf.ocl.types.PrimitiveBoolean;
import org.eclipse.emf.ocl.types.SequenceType;
import org.eclipse.emf.ocl.types.SetType;
import org.eclipse.emf.ocl.types.TupleType;
import org.eclipse.emf.ocl.types.TypeType;
import org.eclipse.emf.ocl.types.VoidType;
import org.eclipse.emf.ocl.types.impl.AnyTypeImpl;
import org.eclipse.emf.ocl.types.impl.TypeUtil;
import org.eclipse.emf.ocl.types.util.Types;
import org.eclipse.emf.ocl.uml.Constraint;
import org.eclipse.emf.ocl.uml.UMLPackage;
import org.eclipse.emf.ocl.utilities.ASTNode;
import org.eclipse.emf.ocl.utilities.PredefinedType;
import org.eclipse.ocl.internal.l10n.OCLMessages;
/**
* @author Edith Schonberg (edith)
*
* Checks the well-formedness rules for the expressions package
*/
public class ValidationVisitorImpl
implements Visitor {
private static Environment NULL_ENVIRONMENT = new NullEnvironment();
private Environment environment = null;
/**
* Obtains an instance of the validation visitor that assumes an Ecore
* environment, inferred from the context of the constraint being validated.
*
* @return a validation visitor instance
*/
public static Visitor getInstance() {
return new ValidationVisitorImpl(null);
}
/**
* Obtains an instance of the validation visitor that validates against the
* specified environment, which presumably was used in parsing the OCL in
* the first place.
*
* @param environment an OCL environment
*
* @return a validation visitor instance for the specified environment
*/
public static Visitor getInstance(Environment environment) {
return new ValidationVisitorImpl(environment);
}
/**
* Default constructor.
*
* @param environment the environment
*/
private ValidationVisitorImpl(Environment environment) {
super();
this.environment = environment;
}
/**
* Obtains the current environment, which may be inferred from the context
* of the constraint that we are validating.
*
* @param node an AST node from which possibly to infer the environment.
* If <code>null</code>, it will be assumed that we were initialized
* with an environment
*
* @return a suitable environment, or a default if no node was
* provide for inference and we were not initialized with an explicit
* environment
*/
protected Environment getEnvironment(ASTNode node) {
Environment result = environment;
if (result == null) {
if (node != null) {
result = createEcoreEnvironment(node);
}
if (result == null) {
result = NULL_ENVIRONMENT;
}
environment = result; // cache the result
}
return result;
}
/**
* Creates an Ecore environment from the context of an AST node.
*
* @param node an AST node in the OCL constraint being validated
*
* @return the appropriate environment
*/
private Environment createEcoreEnvironment(ASTNode node) {
Environment result = null;
Object context = null;
Constraint constraint = (Constraint) ExpressionsUtil.containerOfType(
UMLPackage.Literals.CONSTRAINT, node);
if ((constraint != null) && !constraint.getConstrainedElement().isEmpty()) {
context = constraint.getConstrainedElement().get(0);
if (context instanceof EClassifier) {
result = ExpressionsUtil.createClassifierContext(
(EClassifier) context);
} else if (context instanceof EOperation) {
result = ExpressionsUtil.createOperationContext(
(EOperation) context);
} else if (context instanceof EStructuralFeature) {
result = ExpressionsUtil.createPropertyContext(
(EStructuralFeature) context);
}
}
return result;
}
/**
* Callback for an OperationCallExp visit.
*
* Well-formedness rule: All of the arguments must conform to the parameters
* of the referred operation. There must be exactly as many arguments as the
* referred operation has parameters.
*
* @param oc
* the operation call expression
* @return Boolean -- true if validated
*/
public Object visitOperationCallExp(OperationCallExp oc) {
OCLExpression source = oc.getSource();
EOperation oper = oc.getReferredOperation();
int opcode = oc.getOperationCode();
EList args = oc.getArgument();
if (oper == null) {
String message = OCLMessages.bind(
OCLMessages.NullOperation_ERROR_,
oc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin
.throwing(getClass(), "visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
if (source == null) {
String message = OCLMessages.bind(
OCLMessages.NullSourceOperation_ERROR_,
oc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin
.throwing(getClass(), "visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
EClassifier sourceType = source.getType();
int numArgs = args.size();
String operName = oper.getName();
for (int i = 0; i < numArgs; i++) {
((OCLExpression) args.get(i)).accept(this);
}
visitFeatureCallExp(oc);
if (opcode == PredefinedType.OCL_IS_NEW) {
// oclIsNew() may only be used in postcondition constraints
if (!ExpressionsUtil.isInPostcondition(oc)) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.OCLIsNewInPostcondition_ERROR_);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
source.accept(this);
// Check argument conformance.
try {
EOperation oper1 = getEnvironment(oc).lookupOperation(sourceType,
operName, args);
if (oper1 != oper) {
String message = OCLMessages.bind(
OCLMessages.IllegalOperation_ERROR_,
oc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
if (!getEnvironment(oc).isQuery(oper)) {
String message = OCLMessages.bind(
OCLMessages.NonQueryOperation_ERROR_,
oper.getName());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
EClassifier resultType = null;
if (sourceType instanceof PredefinedType) {
PredefinedType pt = (PredefinedType) sourceType;
if (opcode != pt.getOperationCodeFor(operName)) {
String message = OCLMessages.bind(
OCLMessages.IllegalOpcode_ERROR_,
operName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
resultType = pt.getResultTypeFor(sourceType, opcode, args);
if (resultType == null) {
// maybe this operation was an "extra" contribution by a
// custom environment implementation
resultType = TypeUtil.getOCLType(oper);
} else {
// be sure to compare apples to apples (the call expression's
// type is an OCL type)
resultType = TypeUtil.getOCLType(resultType);
}
} else {
// source is an EClass, an enumeration, or a user data type
if (opcode != AnyTypeImpl.getOperationCode(operName)) {
String message = OCLMessages.bind(
OCLMessages.IllegalOpcode_ERROR_,
operName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
resultType = AnyTypeImpl
.getResultType(sourceType, opcode, args);
if (resultType == null) {
resultType = TypeUtil.getOCLType(oper);
}
}
if (TypeUtil.typeCompare(resultType, oc.getType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceOperation_ERROR_,
oc.getType().toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
} catch (Exception e) {
IllegalArgumentException error = new IllegalArgumentException(e
.getMessage());
OCLPlugin
.throwing(getClass(), "visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for an EnumLiteralExp visit. Well-formedness rule: The type of
* an enum Literal expression is the type of the referred literal.
*
* @param el
* the enumeration literal expresion
* @return Boolean -- true if validated
*/
public Object visitEnumLiteralExp(EnumLiteralExp el) {
EEnumLiteral l = el.getReferredEnumLiteral();
EClassifier type = el.getType();
if (!(type instanceof EEnum) || l.getEEnum() != type) {
String message = OCLMessages.bind(
OCLMessages.IllegalEnumLiteral_ERROR_,
el.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitEnumLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a VariableExp visit. Well-formedness rule: The type of a
* VariableExp is the type of the Variable to which it refers.
*
* @param v
* the variable expression
* @return Boolean -- true if validated
*/
public Object visitVariableExp(VariableExp v) {
// get the referred variable name
Variable vd = v.getReferredVariable();
if (vd == null || v.getType() == null || vd.getName() == null
|| vd.getType() == null) {
String message = OCLMessages.bind(
OCLMessages.IncompleteVariableExp_ERROR_,
v.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitVariableExp", error);//$NON-NLS-1$
throw error;
}
vd.accept(this);
if (TypeUtil.typeCompare(vd.getType(), v.getType()) != 0) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(OCLMessages.VariableTypeMismatch_ERROR_, vd.getName()));
OCLPlugin.throwing(getClass(), "visitVariableExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for an PropertyCallExp visit. Well-formedness rule: The
* type of the PropertyCallExp is the type of the referred
* EStructuralFeature.
*
* @param pc the property call expression
* @return Boolean -- true if validated
*/
public Object visitPropertyCallExp(PropertyCallExp pc) {
EStructuralFeature property = pc.getReferredProperty();
OCLExpression source = pc.getSource();
EClassifier type = pc.getType();
if (property == null) {
String message = OCLMessages.bind(
OCLMessages.NullProperty_ERROR_,
pc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitPropertyCallExp", error);//$NON-NLS-1$
throw error;
}
if (source == null) {
String message = OCLMessages.bind(
OCLMessages.NullNavigationSource_ERROR_,
pc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitPropertyCallExp", error);//$NON-NLS-1$
throw error;
}
if (type == null) {
String message = OCLMessages.bind(
OCLMessages.NullNavigationType_ERROR_,
pc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitPropertyCallExp", error);//$NON-NLS-1$
throw error;
}
EList qualifiers = pc.getQualifier();
if (!qualifiers.isEmpty()) {
// navigation qualifiers must conform to expected qualifier types
EList expectedQualifierTypes = getEnvironment(pc).getQualifiers(property);
if (expectedQualifierTypes.size() != qualifiers.size()) {
String message = OCLMessages.bind(
OCLMessages.MismatchedQualifiers_ERROR_,
pc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitPropertyCallExp", error);//$NON-NLS-1$
throw error;
} else {
Iterator eiter = expectedQualifierTypes.iterator();
Iterator qiter = qualifiers.iterator();
while (eiter.hasNext()) {
EClassifier expectedType = TypeUtil.getOCLType(
(ETypedElement) eiter.next());
OCLExpression qualifier = (OCLExpression) qiter.next();
EClassifier qualifierType = qualifier.getType();
if ((TypeUtil.getRelationship(qualifierType, expectedType)
& PredefinedType.SUBTYPE) == 0) {
String message = OCLMessages.bind(
OCLMessages.MismatchedQualifiers_ERROR_,
pc.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitPropertyCallExp", error);//$NON-NLS-1$
throw error;
}
}
}
}
visitFeatureCallExp(pc);
source.accept(this);
EClassifier refType = TypeUtil.getOCLType(property);
if (!pc.getQualifier().isEmpty() && (refType instanceof CollectionType)) {
// qualifying the navigation results in a non-collection
// type
refType = ((CollectionType) refType).getElementType();
}
return Boolean.valueOf(TypeUtil.typeCompare(refType, type) == 0);
}
/**
* Callback for an AssociationClassCallExp visit. Well-formedness rules:
* <ul>
* <li>the type of the AssociationClassCallExp is the type of the
* referenced EReference</li>
* <li>the referenced EReference is an AssociationClassEnd, and its
* associationClass reference is not null</li>
* </ul>
*
* @param ae
* the association end expression
* @return Boolean -- true if validated
*/
public Object visitAssociationClassCallExp(AssociationClassCallExp ae) {
EClass ref = ae.getReferredAssociationClass();
OCLExpression source = ae.getSource();
EClassifier type = ae.getType();
if (ref == null) {
String message = OCLMessages.bind(
OCLMessages.MissingAssociationClass_ERROR_,
ae.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitAssociationClassCallExp", error);//$NON-NLS-1$
throw error;
}
if (source == null) {
String message = OCLMessages.bind(
OCLMessages.NullNavigationSource_ERROR_,
ae.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitAssociationClassCallExp", error);//$NON-NLS-1$
throw error;
}
EClassifier sourceType = source.getType();
if (type == null) {
String message = OCLMessages.bind(
OCLMessages.NullNavigationType_ERROR_,
ae.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitAssociationClassCallExp", error);//$NON-NLS-1$
throw error;
}
if (type instanceof CollectionType) {
type = ((CollectionType) type).getElementType();
}
if (ae.getNavigationSource() != null) {
// navigation source must be an end of the association class
EStructuralFeature end = ae.getNavigationSource();
if (!(end instanceof EReference)
|| (ref != getEnvironment(ae).getAssociationClass((EReference) end))
|| (end != getEnvironment(ae).lookupProperty(sourceType, end.getName()))) {
String message = OCLMessages.bind(
OCLMessages.AssociationClassQualifierType_ERROR_,
ae.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitAssociationClassCallExp", error);//$NON-NLS-1$
throw error;
}
}
visitFeatureCallExp(ae);
source.accept(this);
EClassifier refType = TypeUtil.getOCLType(ref);
if (TypeUtil.typeCompare(refType, type) == 0)
return Boolean.TRUE;
return Boolean.FALSE;
}
/**
* Callback for a VariableDeclaration visit. Well-formedness rule: The type
* of the initExpression must conform to the type of the declared variable.
*
* @param vd --
* variable declaration
* @return Boolean -- true if validated
*/
public Object visitVariable(Variable vd) {
String varName = vd.getName();
if (varName == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.MissingNameInVariableDeclaration_ERROR_);
OCLPlugin.throwing(getClass(),
"visitVariableDeclaration", error);//$NON-NLS-1$
throw error;
}
EClassifier type = vd.getType();
OCLExpression init = vd.getInitExpression();
if (init != null) {
init.accept(this);
if (TypeUtil.typeCompare(init.getType(), type) > 0) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.TypeConformanceInit_ERROR_,
varName));
OCLPlugin.throwing(getClass(),
"visitVariableDeclaration", error);//$NON-NLS-1$
throw error;
}
}
return Boolean.TRUE;
}
/**
* Callback for an IfExp visit. Well-formedness Rule: The type of the
* condition must be Boolean. The type of the if expression is the common
* supertype of the then and else
*
* @param i -
* if expression
* @return Boolean -- true if validated
*/
public Object visitIfExp(IfExp i) {
OCLExpression cond = i.getCondition();
OCLExpression thenexp = i.getThenExpression();
OCLExpression elseexp = i.getElseExpression();
if (cond == null || thenexp == null | elseexp == null) {
String message = OCLMessages.bind(
OCLMessages.IncompleteIfExp_ERROR_,
i.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIfExp", error);//$NON-NLS-1$
throw error;
}
cond.accept(this);
thenexp.accept(this);
elseexp.accept(this);
if (cond.getType() != Types.OCL_BOOLEAN) {
String message = OCLMessages.bind(
OCLMessages.NonBooleanIfExp_ERROR_,
cond.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIfExp", error);//$NON-NLS-1$
throw error;
}
EClassifier thenelsetype = null;
try {
thenelsetype = TypeUtil.commonSuperType(thenexp.getType(),
elseexp.getType());
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage());
}
if (TypeUtil.typeCompare(i.getType(), thenelsetype) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIfExp_ERROR_,
i.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIfExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
public Object visitMessageExp(MessageExp m) {
if (m.getTarget() == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingMessageTarget_ERROR_,
m.toString()));
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
m.getTarget().accept(this);
if (m.getCalledOperation() == null && m.getSentSignal() == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.UnrecognizedMessageType_ERROR_);
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
if (m.getCalledOperation() != null && m.getSentSignal() != null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.AmbiguousMessageType_ERROR_);
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
EList parameters;
if (m.getCalledOperation() != null) {
EOperation operation = m.getCalledOperation().getOperation();
if (operation == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingOperationInCallAction_ERROR_,
m.toString()));
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
parameters = operation.getEParameters();
} else {
EClass signal = m.getSentSignal().getSignal();
if (signal == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingSignalInCallAction_ERROR_,
m.toString()));
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
parameters = TypeUtil.getProperties(signal);
}
EList arguments = m.getArgument();
if (arguments.size() != parameters.size()) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(OCLMessages.MessageArgumentCount_ERROR_,
getName(m.getType())));
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
// check type conformance against operation/signal parameters
Iterator paramsIter = parameters.iterator();
Iterator argsIter = arguments.iterator();
while (paramsIter.hasNext()) {
ETypedElement param = (ETypedElement) paramsIter.next();
OCLExpression arg = (OCLExpression) argsIter.next();
if (TypeUtil.typeCompare(arg.getType(), TypeUtil.getOCLType(param)) > 0) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(OCLMessages.MessageArgConformance_ERROR_,
param.getName(), arg.toString()));
OCLPlugin.throwing(getClass(), "visitMessageExp", error);//$NON-NLS-1$
throw error;
}
// validate the argument
arg.accept(this);
}
return Boolean.TRUE;
}
/**
* Callback for an UnspecifiedValueExp visit.
*
* @param uv --
* unspecified value expression
* @return Boolean -- true if validated
*/
public Object visitUnspecifiedValueExp(UnspecifiedValueExp uv) {
// unspecified values need not declare a type (it can be OclVoid).
// The only restriction is that they can only be used in message expressions
if (!(uv.eContainer() instanceof MessageExp)) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.IllegalUnspecifiedValueExp_ERROR_,
uv.toString()));
OCLPlugin.throwing(getClass(), "visitUnspecifiedValueExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a TypeExp visit.
*/
public Object visitTypeExp(TypeExp t) {
if (!(t.getType() instanceof TypeType)) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(OCLMessages.TypeConformanceTypeExp_ERROR_,
getName(t.getType())));
OCLPlugin.throwing(getClass(),
"visitTypeExp", error);//$NON-NLS-1$
throw error;
}
if (t.getReferredType() == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.TypeExpMissingType_ERROR_,
t.toString()));
OCLPlugin.throwing(getClass(),
"visitTypeExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for an IntegerLiteralExp visit. Well-formedness rule: The type
* of an integer Literal expression is the type Integer
*
* @param il -
* integer literal expression
* @return Boolean -- true if validated
*/
public Object visitIntegerLiteralExp(IntegerLiteralExp il) {
if (il.getType() != Types.OCL_INTEGER) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceIntegerLiteral_ERROR_);
OCLPlugin.throwing(getClass(),
"visitIntegerLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a RealLiteralExp visit. Well-formedness rule: The type of a
* real literal expression is the type Real.
*
* @param rl --
* real literal expression
* @return Boolean -- true if validated
*/
public Object visitRealLiteralExp(RealLiteralExp rl) {
if (rl.getType() != Types.OCL_REAL) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceRealLiteral_ERROR_);
OCLPlugin.throwing(getClass(), "visitRealLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a StringLiteralExp visit. Well-formedness rule: The type of
* a string literal expression is the type of the string.
*
* @param sl --
* string literal expression
* @return Boolean -- true if validated
*/
public Object visitStringLiteralExp(StringLiteralExp sl) {
if (sl.getType() != Types.OCL_STRING) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceStringLiteral_ERROR_);
OCLPlugin
.throwing(getClass(), "visitStringLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a BooleanLiteralExp visit. Well-formedness rule: The type of
* a Boolean Literal expression is the type of the boolean.
*
* @param bl -
* boolean literal expression
* @return Boolean - true if validated
*/
public Object visitBooleanLiteralExp(BooleanLiteralExp bl) {
if (bl.getType() != Types.OCL_BOOLEAN) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceBooleanLiteral_ERROR_);
OCLPlugin.throwing(getClass(),
"visitBooleanLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for LetExp visit. Well-formedness rule: The type of the Let
* expression is the type of the in expression.
*
* @param l --
* let expression
* @return Boolean -- true if validated
*/
public Object visitLetExp(LetExp l) {
Variable vd = l.getVariable();
OCLExpression in = l.getIn();
EClassifier type = l.getType();
if (vd == null || in == null || type == null) {
String message = OCLMessages.bind(
OCLMessages.IncompleteLetExp_ERROR_,
l.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitLetExp", error);//$NON-NLS-1$
throw error;
}
vd.accept(this);
in.accept(this);
if (TypeUtil.typeCompare(type, in.getType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceLetExp_ERROR_,
type, in.getType());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitLetExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
*
* Callback for an IterateExp visit. *Well-formedness rule: The type of the
* iterate is the type of the result variable. The type of the body
* expression must conform to the declared type of the result variable. *A
* result variable must have an init expression. *The type of a source
* expression must be a collection. *The loop variable has no init
* expression. *The type of the iterator variable must be the type of the
* elements of the *source collection.
*
* @param ie -
* iterate expression
* @return Boolean -- true if validated
*/
public Object visitIterateExp(IterateExp ie) {
// get the variable declaration for the result
Variable vd = ie.getResult();
EClassifier type = ie.getType();
OCLExpression body = ie.getBody();
OCLExpression source = ie.getSource();
EList iterators = ie.getIterator();
if (vd == null || type == null || source == null || body == null
|| iterators.isEmpty()) {
String message = OCLMessages.bind(
OCLMessages.IncompleteIterateExp_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
int numIters = iterators.size();
// Validate all of the iterate parts
source.accept(this);
vd.accept(this);
body.accept(this);
if (vd.getInitExpression() == null) {
String message = OCLMessages.bind(
OCLMessages.MissingInitIterateExp_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
if (TypeUtil.typeCompare(type, vd.getType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIterateExp_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
if (TypeUtil.typeCompare(body.getType(), vd.getType()) > 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIterateExpBody_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
EClassifier sourceType = source.getType();
if (!(sourceType instanceof CollectionType)) {
String message = OCLMessages.bind(
OCLMessages.IteratorSource_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
for (int i = 0; i < numIters; i++) {
Variable loopiter = (Variable) iterators
.get(i);
// Validate the iterator expressions
loopiter.accept(this);
if (loopiter.getInitExpression() != null) {
String message = OCLMessages.bind(
OCLMessages.IterateExpLoopVarInit_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
if (TypeUtil.typeCompare(loopiter.getType(),
((CollectionType) sourceType).getElementType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIterateExpLoopVar_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIterateExp", error);//$NON-NLS-1$
throw error;
}
}
return Boolean.TRUE;
}
/**
* Callback for an IteratorExp visit. Well-formedness rule: If the iterator
* is "forall", "isUnique", "any", "one", or "exists", the type of the
* iterator must be Boolean. The result type of the collect operation on a
* sequence type is a sequence; the result type of collect on any other type
* is a bag. The select and reject iterators have the same type as its
* source. They type of the body of the select, reject, forall, exists must
* be boolean. The type of a source expression must be a collection. The
* loop variable has no init expression. The type of the iterator variable
* must be the type of the elements of the source collection.
*
* @param ie --
* iterator expression
* @return Boolean -- true if validated
*/
public Object visitIteratorExp(IteratorExp ie) {
EClassifier type = ie.getType();
OCLExpression body = ie.getBody();
OCLExpression source = ie.getSource();
EList iterators = ie.getIterator();
String name = ie.getName();
if (type == null || name == null || source == null || body == null
|| iterators.isEmpty()) {
String message = OCLMessages.bind(
OCLMessages.IncompleteIteratorExp_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
int opcode = 0;
if (source.getType() instanceof PredefinedType) {
opcode = ((PredefinedType) source.getType()).getOperationCodeFor(name);
}
int numIters = iterators.size();
// Validate all of the iterate parts
source.accept(this);
body.accept(this);
switch (opcode) {
case PredefinedType.FOR_ALL:
case PredefinedType.EXISTS:
case PredefinedType.IS_UNIQUE:
if (type != Types.OCL_BOOLEAN) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIteratorResult_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
if (opcode == PredefinedType.COLLECT) {
if (source.getType() instanceof SequenceType
|| source.getType() instanceof OrderedSetType) {
if (!(type instanceof SequenceType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceCollectSequence_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
} else if (!(type instanceof BagType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceCollectBag_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
switch (opcode) {
case PredefinedType.SELECT:
case PredefinedType.REJECT:
if (TypeUtil.typeCompare(type, source.getType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceSelectReject_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
switch (opcode) {
case PredefinedType.SELECT:
case PredefinedType.REJECT:
case PredefinedType.FOR_ALL:
case PredefinedType.ANY:
case PredefinedType.EXISTS:
case PredefinedType.ONE:
if (body.getType() != Types.OCL_BOOLEAN) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIteratorBodyBoolean_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
EClassifier sourceType = source.getType();
if (!(sourceType instanceof CollectionType)) {
String message = OCLMessages.bind(
OCLMessages.IteratorSource_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
if (opcode == PredefinedType.CLOSURE) {
if (!(type instanceof SetType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceClosure_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
// recursive reference must be to a type conforming
// to the source, otherwise it isn't recursive
// checked above that the source is a collection type
EClassifier sourceElementType =
((CollectionType) source.getType()).getElementType();
EClassifier bodyType =
((CollectionType) type).getElementType();
if (TypeUtil.typeCompare(sourceElementType, bodyType) < 0) {
String message = OCLMessages.bind(
OCLMessages.ElementTypeConformanceClosure_ERROR_,
getName(bodyType),
getName(sourceElementType));
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
for (int i = 0; i < numIters; i++) {
Variable loopiter = (Variable) iterators
.get(i);
// Validate the iterator expressions
loopiter.accept(this);
if (loopiter.getInitExpression() != null) {
String message = OCLMessages.bind(
OCLMessages.IterateExpLoopVarInit_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
if (TypeUtil.typeCompare(loopiter.getType(),
((CollectionType) sourceType).getElementType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceIteratorExpLoopVar_ERROR_,
ie.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(), "visitIteratorExp", error);//$NON-NLS-1$
throw error;
}
}
return Boolean.TRUE;
}
/**
* Callback for a CollectionLiteralExp visit. Well-formedness rule: The type
* of a collection literal expression is determined by the collection kind
* selection, and the common supertype of all elements. The empty collection
* has a Classifier as element type.
*
* @param cl --
* collection literal expression
* @return Boolean -- true if validated
*/
public Object visitCollectionLiteralExp(CollectionLiteralExp cl) {
CollectionKind kind = cl.getKind();
EClassifier collectionType = cl.getType();
if (collectionType == null
|| !(collectionType instanceof CollectionType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceCollectionLiteralExp_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
if (kind == CollectionKind.SET_LITERAL) {
if (!(collectionType instanceof SetType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceSetLiteral_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
} else if (kind == CollectionKind.ORDERED_SET_LITERAL) {
if (!(collectionType instanceof OrderedSetType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceOrderedSetLiteral_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
} else if (kind == CollectionKind.BAG_LITERAL) {
if (!(collectionType instanceof BagType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceBagLiteral_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
} else if (kind != CollectionKind.SEQUENCE_LITERAL
|| !(collectionType instanceof SequenceType)) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceSequenceLiteral_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
List parts = cl.getPart();
if (parts.isEmpty()) {
if (((CollectionType) collectionType).getElementType() != Types.OCL_VOID) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceEmptyCollection_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
} else
return Boolean.TRUE;
}
EClassifier partsType = ((CollectionLiteralPart) parts.get(0))
.getType();
for (Iterator it = parts.iterator(); it.hasNext();) {
CollectionLiteralPart part = (CollectionLiteralPart) it.next();
if (part instanceof CollectionItem) {
((CollectionItem) part).getItem().accept(this);
} else {
((CollectionRange) part).getFirst().accept(this);
((CollectionRange) part).getLast().accept(this);
}
try {
partsType = TypeUtil.commonSuperType(partsType, part
.getType());
} catch (Exception e) {
throw new IllegalArgumentException(e.getMessage());
}
}
if (TypeUtil.typeCompare(partsType,
((CollectionType) collectionType).getElementType()) != 0) {
String message = OCLMessages.bind(
OCLMessages.TypeConformanceCollectionElementType_ERROR_,
cl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitCollectionLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Callback for a TupleLiteralExp visit.
*
* Well-formedness rule: The type of a tuple literal is a TupleType the
* specified parts All tuple literal expression parts must have unique
* names. The type of each attribute in a tuple literal part must match the
* type of the initialization expression.
*
* @param tl
* tuple literal expression
* @return Boolean
*/
public Object visitTupleLiteralExp(TupleLiteralExp tl) {
EClassifier type = tl.getType();
if (!(type instanceof TupleType)) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.TypeConformanceTupleLiteralExp_ERROR_,
tl.toString()));
OCLPlugin.throwing(getClass(), "visitTupleLiteralExp", error);//$NON-NLS-1$
throw error;
}
EClass eclazz = (EClass) type;
// The fields of the tuple are the properties of the EClass.
EList tp = tl.getPart();
if (tp.size() != eclazz.getEStructuralFeatures().size()) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.TypeConformanceTupleLiteralExpParts_ERROR_,
tl.toString()));
OCLPlugin.throwing(getClass(), "visitTupleLiteralExp", error);//$NON-NLS-1$
throw error;
}
Set names = new java.util.HashSet();
// Match each property with a tuple part
Iterator iter = tl.getPart().iterator();
while (iter.hasNext()) {
TupleLiteralPart part = (TupleLiteralPart) iter.next();
String name = part.getName();
EStructuralFeature property = eclazz.getEStructuralFeature(name);
if (property == null) {
String message = OCLMessages.bind(
OCLMessages.TupleLiteralExpressionPart_ERROR_,
name,
tl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitTupleLiteralExp", error);//$NON-NLS-1$
throw error;
}
// Validate each TupleLiteralPart in the tuple literal
// At the same time, check for unique names
if (!names.add(name)) {
String message = OCLMessages.bind(
OCLMessages.TupleDuplicateName_ERROR_,
name, tl.toString());
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitTupleLiteralExp", error);//$NON-NLS-1$
throw error;
}
part.accept(this);
}
return Boolean.TRUE;
}
public Object visitTupleLiteralPart(TupleLiteralPart tp) {
EStructuralFeature property = tp.getAttribute();
if (property == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingPropertyInTupleLiteralPart_ERROR_,
tp.getName(),
tp.eContainer().toString()));
OCLPlugin.throwing(getClass(),
"visitTupleLiteralPart", error);//$NON-NLS-1$
throw error;
}
EClassifier type = tp.getType();
if (type == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingTypeInTupleLiteralPart_ERROR_,
tp.getName(),
tp.eContainer().toString()));
OCLPlugin.throwing(getClass(),
"visitTupleLiteralPart", error);//$NON-NLS-1$
throw error;
}
// convert property type to OCL type because it may be an Ecore primitive
// such as EIntegerObject
if (TypeUtil.typeCompare(TypeUtil.getOCLType(property), type) != 0) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.TuplePartType_ERROR_,
tp.getName(),
tp.eContainer().toString()));
OCLPlugin.throwing(getClass(),
"visitTupleLiteralPart", error);//$NON-NLS-1$
throw error;
}
OCLExpression init = tp.getValue();
if (init != null) {
init.accept(this);
if (TypeUtil.typeCompare(init.getType(), type) > 0) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceTuplePartValue_ERROR_);
OCLPlugin.throwing(getClass(),
"visitTupleLiteralPart", error);//$NON-NLS-1$
throw error;
}
}
return Boolean.TRUE;
}
public Object visitStateExp(StateExp s) {
EObject state = s.getReferredState();
if (state == null) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.bind(
OCLMessages.MissingStateInStateExp_ERROR_,
s.toString()));
OCLPlugin.throwing(getClass(),
"visitStateExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* Applies well-formedness rules to constraints.
*
* @param constraint the constraint to validate
*/
public Object visitConstraint(Constraint constraint) {
String stereo = constraint.getStereotype();
EClassifier bodyType = constraint.getBody().getType();
EClassifier operationType = null;
EClassifier propertyType = null;
String operationName = null;
String propertyName = null;
String classifierName = null;
if (!constraint.getConstrainedElement().isEmpty()) {
Object constrained = constraint.getConstrainedElement().get(0);
if (constrained instanceof EOperation) {
EOperation operation = (EOperation) constrained;
operationName = operation.getName();
if (operation.getEContainingClass() != null) {
classifierName = operation.getEContainingClass().getName();
}
if (operation.getEType() != null) {
operationType = TypeUtil.getOCLType(operation);
}
} else if (constrained instanceof EStructuralFeature) {
EStructuralFeature property = (EStructuralFeature) constrained;
propertyName = property.getName();
if (property.getEContainingClass() != null) {
classifierName = property.getEContainingClass().getName();
}
if (property.getEType() != null) {
propertyType = TypeUtil.getOCLType(property);
}
} else if (constrained instanceof EClassifier) {
classifierName = ((EClassifier) constrained).getName();
}
}
if (operationType == null) {
operationType = Types.OCL_VOID;
}
if (propertyType == null) {
propertyType = Types.OCL_VOID;
}
if (Constraint.BODY.equals(stereo)
|| Constraint.POSTCONDITION.equals(stereo)
|| Constraint.PRECONDITION.equals(stereo)) {
// operation constraints must be boolean-valued
if (!(bodyType instanceof PrimitiveBoolean)) {
String message = OCLMessages.bind(
OCLMessages.OperationConstraintBoolean_ERROR_,
operationName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
} else if (Constraint.INVARIANT.equals(stereo)) {
if (!(bodyType instanceof PrimitiveBoolean)) {
// so must invariants, but they have a different kind of context
String message = OCLMessages.bind(
OCLMessages.InvariantConstraintBoolean_ERROR_,
classifierName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
} else if (Constraint.DEFINITION.equals(stereo)) {
// expression type must conform to feature type
ETypedElement feature = null;
if (constraint.getConstrainedElement().size() >= 2) {
Object constrained = constraint.getConstrainedElement().get(1);
if (constrained instanceof ETypedElement) {
feature = (ETypedElement) constrained;
}
}
if (feature == null) {
String message = OCLMessages.bind(
OCLMessages.DefinitionConstraintFeature_ERROR_,
classifierName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
EClassifier featureType = TypeUtil.getOCLType(feature);
if ((featureType == null)
|| TypeUtil.typeCompare(bodyType, featureType) > 0) {
String message = OCLMessages.bind(
OCLMessages.DefinitionConstraintConformance_ERROR_,
getName(bodyType),
getName(featureType));
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
} else if (Constraint.INITIAL.equals(stereo) || Constraint.DERIVATION.equals(stereo)) {
// expression type must conform to property type
if (TypeUtil.typeCompare(bodyType, propertyType) > 0) {
String message = OCLMessages.bind(
OCLMessages.InitOrDerConstraintConformance_ERROR_,
new Object[] {
getName(bodyType),
propertyName,
getName(propertyType)});
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
}
if (Constraint.BODY.equals(constraint.getStereotype())) {
if (operationType instanceof VoidType) {
String message = OCLMessages.bind(
OCLMessages.BodyConditionNotAllowed_ERROR_,
operationName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
// the expression must be of the form result = <expr> or
// <expr> = result, where <expr> is some expression whose type
// conforms to the operation type. However, this expression is
// allowed to be nested inside any number of lets for the user's
// convenience
OCLExpression exp = constraint.getBody();
while (exp instanceof LetExp) {
exp = ((LetExp) exp).getIn();
}
OperationCallExp body = null;
if (exp instanceof OperationCallExp) {
body = (OperationCallExp) exp;
}
if ((body == null)
|| (body.getOperationCode() != PredefinedType.EQUAL)
|| (body.getArgument().size() != 1)) {
String message = OCLMessages.bind(
OCLMessages.BodyConditionForm_ERROR_,
operationName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
OCLExpression bodyExpr;
if (isResultVariable(body.getSource(), operationType)) {
bodyExpr = (OCLExpression) body.getArgument().get(0);
} else if (isResultVariable(
(OCLExpression) body.getArgument().get(0),
operationType)) {
bodyExpr = body.getSource();
} else {
String message = OCLMessages.bind(
OCLMessages.BodyConditionForm_ERROR_,
operationName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
bodyType = bodyExpr.getType();
if ((TypeUtil.getRelationship(bodyType, operationType) & PredefinedType.SUBTYPE) == 0) {
String message = OCLMessages.bind(
OCLMessages.BodyConditionConformance_ERROR_,
new Object[] {
operationName,
getName(bodyType),
getName(operationType)});
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
// one last check: does the "body" part of the condition include
// the result variable? It must not
if (findResultVariable(bodyExpr, operationType)) {
String message = OCLMessages.bind(
OCLMessages.BodyConditionForm_ERROR_,
operationName);
IllegalArgumentException error = new IllegalArgumentException(
message);
OCLPlugin.throwing(getClass(),
"visitConstraint", error);//$NON-NLS-1$
throw error;
}
}
// check the body condition, itself, for well-formedness
return constraint.getBody().accept(this);
}
/**
* Null-safe alternative to {@link ENamedElement#getName()}.
*
* @param element a named element that may be <code>null</code>
* @return the element's name, or <code>null</code> if the element is <code>null</code>
*/
static String getName(ENamedElement element) {
return (element == null)? null : element.getName();
}
/**
* Determines whether the specified expression is a reference to the
* special <code>result</code> variable of an operation body constraint.
*
* @param expr an OCL expression
* @param expectedType the expected type of the result variable (i.e.,
* the operation type
*
* @return <code>true</code> if it is the result variable;
* <code>false</code>, otherwise
*/
private static boolean isResultVariable(OCLExpression expr, EClassifier expectedType) {
// the implicitly defined "result" variable always has the same type
// as the operation
boolean result = (expr instanceof VariableExp);
if (result) {
try {
result = TypeUtil.typeCompare(expr.getType(), expectedType) == 0;
} catch (Exception e) {
// get an exception on incompatible types. This is expected
result = false;
}
}
if (result) {
Variable var = ((VariableExp) expr).getReferredVariable();
result = (var != null) && "result".equals(var.getName()); //$NON-NLS-1$
}
return result;
}
/**
* Queries whether the special <code>result</code> variable can be found
* anywhere in the specified OCL expression.
*
* @param expr the expression to search
* @param expectedType the expected type of the result variable
*
* @return <code>true</code> if it includes some reference to the result
* variable; <code>false</code>, otherwise
*/
private static boolean findResultVariable(OCLExpression expr, final EClassifier expectedType) {
class ResultFinder extends AbstractVisitor {
boolean found = false;
public Object visitVariableExp(VariableExp v) {
if (isResultVariable(v, expectedType)) {
found = true;
}
// no need to call super because this is a leaf expression
return null;
}
}
ResultFinder finder = new ResultFinder();
expr.accept(finder);
return finder.found;
}
/**
* Applies well-formedness rules for model property calls in general.
* This includes checking that "@pre" notation is only used in a
* postcondition constraint.
*
* @param exp the model property call expression to validate
*/
private void visitFeatureCallExp(FeatureCallExp exp) {
if (exp.isMarkedPre()) {
// check for a postcondition constraint
if (!ExpressionsUtil.isInPostcondition(exp)) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.AtPreInPostcondition_ERROR_);
OCLPlugin.throwing(getClass(),
"visitModelPropertyCallExp", error);//$NON-NLS-1$
throw error;
}
}
}
public Object visitInvalidLiteralExp(InvalidLiteralExp il) {
if (il.getType() != Types.INVALID) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceInvalidLiteral_ERROR_);
OCLPlugin.throwing(getClass(),
"visitInvalidLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
public Object visitNullLiteralExp(NullLiteralExp il) {
if (il.getType() != Types.OCL_VOID) {
IllegalArgumentException error = new IllegalArgumentException(
OCLMessages.TypeConformanceNullLiteral_ERROR_);
OCLPlugin.throwing(getClass(),
"visitNullLiteralExp", error);//$NON-NLS-1$
throw error;
}
return Boolean.TRUE;
}
/**
* A default environment in the context of the OclAny type, which simply
* provides the default EcoreEnvironment algorithms for looking up features
* of classifiers, which usually just delegates to <code>TypeUtil</code>
* or <code>UMLTypeUtil</code>. This environment is only used when no
* other environment is specified by the client or inferable from the
* expression's constraint.
*
* @author Christian W. Damus (cdamus)
*/
private static class NullEnvironment extends EcoreEnvironment {
public NullEnvironment() {
super(EcorePackage.eINSTANCE);
Variable selfVar = ExpressionsFactory.eINSTANCE.createVariable();
selfVar.setName("self"); //$NON-NLS-1$
selfVar.setType(Types.OCL_ANY_TYPE);
setSelfVariable(selfVar);
}
}
} // ValidationVisitorImpl