Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
*
*
* Copyright (c) 2005, 2010, 2011 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
* Zeligsoft - Bug 253252
* Radek Dvorak - Bugs 261128, 265066
* E.D.Willink - Bug 297541
* Axel Uhl (SAP AG) - Bug 342644
*
*
*
* $Id: EvaluationVisitorImpl.java,v 1.8 2011/05/01 10:56:50 auhl Exp $
*/
package org.eclipse.ocl;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionKind;
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;
import org.eclipse.ocl.internal.OCLPlugin;
import org.eclipse.ocl.internal.OCLStatusCodes;
import org.eclipse.ocl.internal.evaluation.IterationTemplate;
import org.eclipse.ocl.internal.evaluation.IterationTemplateAny;
import org.eclipse.ocl.internal.evaluation.IterationTemplateClosure;
import org.eclipse.ocl.internal.evaluation.IterationTemplateCollect;
import org.eclipse.ocl.internal.evaluation.IterationTemplateCollectNested;
import org.eclipse.ocl.internal.evaluation.IterationTemplateExists;
import org.eclipse.ocl.internal.evaluation.IterationTemplateForAll;
import org.eclipse.ocl.internal.evaluation.IterationTemplateIsUnique;
import org.eclipse.ocl.internal.evaluation.IterationTemplateOne;
import org.eclipse.ocl.internal.evaluation.IterationTemplateReject;
import org.eclipse.ocl.internal.evaluation.IterationTemplateSelect;
import org.eclipse.ocl.internal.evaluation.IterationTemplateSortedBy;
import org.eclipse.ocl.internal.l10n.OCLMessages;
import org.eclipse.ocl.parser.AbstractOCLAnalyzer;
import org.eclipse.ocl.types.AnyType;
import org.eclipse.ocl.types.BagType;
import org.eclipse.ocl.types.CollectionType;
import org.eclipse.ocl.types.InvalidType;
import org.eclipse.ocl.types.OrderedSetType;
import org.eclipse.ocl.types.PrimitiveType;
import org.eclipse.ocl.types.SequenceType;
import org.eclipse.ocl.types.SetType;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.ocl.util.CollectionUtil;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.util.ObjectUtil;
import org.eclipse.ocl.util.UnicodeSupport;
import org.eclipse.ocl.utilities.PredefinedType;
/**
* An evaluation visitor implementation for OCL expressions.
*
* @author Tim Klinger (tklinger)
* @author Christian W. Damus (cdamus)
*
* @since 1.3
*/
public class EvaluationVisitorImpl
extends AbstractEvaluationVisitor {
private static final Integer UNLIMITED = Integer.valueOf(UnlimitedNaturalLiteralExp.UNLIMITED);
private static int tempCounter = 0;
private EvaluationEnvironment.Enumerations enumerations;
/**
* Constructor
*
* @param env
* an evaluation environment (map of variable names to values)
* @param extentMap
* a map of classes to their instance lists
*/
@SuppressWarnings("unchecked")
public EvaluationVisitorImpl(
Environment env,
EvaluationEnvironment evalEnv,
Map extends CLS, ? extends Set extends E>> extentMap) {
super(env, evalEnv, extentMap);
enumerations = OCLUtil.getAdapter(evalEnv, EvaluationEnvironment.Enumerations.class);
}
private boolean isBooleanOperation(int opCode) {
return opCode == PredefinedType.AND ||
opCode == PredefinedType.OR ||
opCode == PredefinedType.NOT ||
opCode == PredefinedType.XOR ||
opCode == PredefinedType.IMPLIES;
}
/**
*
* Callback for an OperationCallExp visit.
*
*/
@Override
public Object visitOperationCallExp(OperationCallExp oc) {
// check if source type is primitive and handle the
// primitive ops "inline". Otherwise use java reflection
// to invoke the operation (there is currently no means
// to do this directly in EMF).
// Note: Generally the result of an operation invocation on the
// undefined
// object or with an undefined argument is undefined except in the
// following
// cases prescribed by the spec (p. 2-10, sec. 2.4.11)
// 1. true || is true
// 2. false && is false
// 3. false implies is true
// 4. if else has the value
// dictated
// by the condition regardless of the other value.
// all irrespective of the order of the arguments.
OCLExpression source = oc.getSource();
C sourceType = source.getType();
O oper = oc.getReferredOperation();
int opCode = oc.getOperationCode();
List> args = oc.getArgument();
int numArgs = args.size();
// evaluate source
Object sourceVal = safeVisitExpression(source);
OCLExpression body = getOperationBody(oper);
if ((body != null) || opCode <= 0 /* not a pre-defined operation */
|| getEvaluationEnvironment().overrides(oper, opCode)) {
// delegate evaluation to the evaluation environment
// evaluate args
Object[] evalArgs = new Object[numArgs];
int i = 0;
for (Iterator> it = args.iterator(); it.hasNext(); i++) {
OCLExpression arg = it.next();
evalArgs[i] = safeVisitExpression(arg);
}
// ask the environment to evaluate
try {
Object result;
if (body != null) {
// if source is undefined, result is OclInvalid
if (isUndefined(sourceVal)) {
return getInvalid();
}
result = call(oper, body, sourceVal, evalArgs);
} else {
// handle <, <=, >, and >= operators
if (opCode <= 0) {
opCode = inferOperationCode(oper, opCode);
}
result = getEvaluationEnvironment().callOperation(
oper, opCode, sourceVal, evalArgs);
}
return result;
} catch (EvaluationHaltedException e) {
// evaluation stopped on demand, propagate father
throw e;
} catch (UnsupportedOperationException ignore) {
// let the EvaluationVisitor do its thing
} catch (Exception e) {
OCLPlugin
.catching(getClass(), "visitOperationCallExp", e);//$NON-NLS-1$
OCLPlugin.log(
Diagnostic.ERROR,
OCLStatusCodes.IGNORED_EXCEPTION_WARNING,
OCLMessages.bind(
OCLMessages.ErrorMessage_ERROR_,
"visitOperationCallExp", //$NON-NLS-1$
e.getLocalizedMessage()),
e);
return getInvalid();
}
}
// inline primitive and collection operation evaluation for increased
// efficiency
// We handle equals and notEquals separately since they require type
// checking
// The semantics for equality are as follows:
//
// Define primtive(type) := type in {Boolean, String, Integer, Double,
// Void}
//
// For the expression x = y, let t1 = runtimeType(x1), t2 =
// runtimeType(x2)
//
// if primitive(t1) or primitive(t2) then
// we use the java semantics for the corresponding built-in primitive
// types EXCEPT for
// the following cases:
// (1) when one or the type is Void, the result is true just when both x
// and y are undefined.
// (2) when the t1 and t2 are non-conformant (for example t1 = String,
// t2 = Integer) then
// the result is false.
//
// For example,
// "1 = 1.0" evaluates to true (unlike "(new Integer(1)).equals(new
// Double(1.0))" which evalutes to false).
// "1 = 'x'" evalutes to false
// "(1/0) = 1" evaluates to false
// "(1/0) = (1/0)" evaluates to true
//
// otherwise, for non-primitive types, we use the "equals" method to
// determine equality, which is, by default,
// object identity.
//
// The semantics for inequality are dual.
//
if (opCode == PredefinedType.EQUAL) {
if (sourceVal == getInvalid()) {
return getInvalid();
}
// evaluate argument
OCLExpression arg = args.get(0);
Object argVal = safeVisitExpression(arg);
if (argVal == getInvalid()) {
return argVal;
}
if (sourceVal instanceof Number) {
// coerce to Long or Double, if possible, for comparison
sourceVal = higherPrecisionNumber((Number) sourceVal);
}
if (argVal instanceof Number) {
// coerce to Long or Double, if possible, for comparison
argVal = higherPrecisionNumber((Number) argVal);
}
return Boolean.valueOf(ObjectUtil.equal(sourceVal, argVal));
}
else if (opCode == PredefinedType.NOT_EQUAL) {
if (sourceVal == getInvalid()) {
return getInvalid();
}
// notEquals
// evaluate argument
OCLExpression arg = args.get(0);
Object argVal = safeVisitExpression(arg);
if (argVal == getInvalid()) {
return argVal;
}
if (sourceVal instanceof Number) {
// coerce to Long or Double, if possible, for comparison
sourceVal = higherPrecisionNumber((Number) sourceVal);
}
if (argVal instanceof Number) {
// coerce to Long or Double, if possible, for comparison
argVal = higherPrecisionNumber((Number) argVal);
}
return Boolean.valueOf(!ObjectUtil.equal(sourceVal, argVal));
}
if (sourceType instanceof PrimitiveType>
|| sourceType instanceof CollectionType, ?>
|| getUMLReflection().isEnumeration(sourceType)
|| getUMLReflection().isDataType(sourceType)
|| (sourceType instanceof VoidType>) || (sourceType instanceof InvalidType>)) {
if (numArgs == 0) {
//
// unary operations:
//
// if source is undefined and the operation is not
// undefined, then this expression is invalid
if (isUndefined(sourceVal)
&& opCode != PredefinedType.OCL_IS_UNDEFINED
&& opCode != PredefinedType.OCL_IS_INVALID) {
return getInvalid();
}
// evaluate this operation
switch (opCode) {
case PredefinedType.MINUS:
// Integer::minus()
// -* doesn't exist, so evaluate to invalid
if (sourceType == getUnlimitedNatural() && UNLIMITED.equals(sourceVal)) {
return getInvalid();
}
if (sourceVal instanceof Integer) {
return - (Integer) sourceVal;
} else if (sourceVal instanceof Long) {
return - (Long) sourceVal;
}
// Double::minus()
return - (Double) sourceVal;
case PredefinedType.ABS:
if (sourceVal instanceof Integer) {
int sourceInt = (Integer) sourceVal;
if (sourceType == getUnlimitedNatural()) {
// the unlimited value has no absolute
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
return getInvalid();
}
}
// Integer::abs()
return Math.abs(sourceInt);
} else if (sourceVal instanceof Long) {
long sourceInt = (Long) sourceVal;
if (sourceType == getUnlimitedNatural()) {
// the unlimited value has no absolute
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
return getInvalid();
}
}
// Integer::abs()
return Math.abs(sourceInt);
}
// Real::abs()
return Math.abs((Double) sourceVal);
case PredefinedType.FLOOR:
if (sourceVal instanceof Double) {
// Real::floor()
return (int) Math.floor((Double) sourceVal);
}
if (sourceType == getUnlimitedNatural()) {
long sourceInt = (Long) higherPrecisionNumber((Number) sourceVal);
// the unlimited value has no floor
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
return getInvalid();
}
}
// Integer::floor()
return sourceVal;
case PredefinedType.ROUND:
if (sourceVal instanceof Double) {
// Real::round()
return (int) Math.round((Double) sourceVal);
}
if (sourceType == getUnlimitedNatural()) {
long sourceInt = (Long) higherPrecisionNumber((Number) sourceVal);
// the unlimited value can't be rounded
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
return getInvalid();
}
}
// Integer::round()
return sourceVal;
case PredefinedType.NOT:
return (((Boolean) sourceVal).booleanValue()) ? Boolean.FALSE
: Boolean.TRUE;
case PredefinedType.OCL_IS_UNDEFINED:
// OclAny::oclIsUndefined()
return isUndefined(sourceVal)?
Boolean.TRUE : Boolean.FALSE;
case PredefinedType.OCL_IS_INVALID:
// OclAny::oclIsInvalid()
return (sourceVal == getInvalid())?
Boolean.TRUE : Boolean.FALSE;
case PredefinedType.SIZE:
if (sourceType == getString()) {
// String::size()
return new Integer(((String) sourceVal).length());
} else if (sourceType instanceof CollectionType, ?>) {
return new Integer(((Collection>) sourceVal).size());
}
case PredefinedType.TO_INTEGER:
// String::toInteger()
return Integer.valueOf((String) sourceVal);
case PredefinedType.TO_REAL:
// String::toReal()
return Double.valueOf((String) sourceVal);
case PredefinedType.TO_LOWER:
// String::toLower()
return UnicodeSupport.toLowerCase((String) sourceVal);
case PredefinedType.TO_UPPER:
// String::toUpper()
return UnicodeSupport.toUpperCase((String) sourceVal);
case PredefinedType.IS_EMPTY:
// Collection::isEmpty()
return Boolean.valueOf(((Collection>) sourceVal)
.isEmpty());
case PredefinedType.NOT_EMPTY:
// Collection::notEmpty()
return Boolean.valueOf(!((Collection>) sourceVal)
.isEmpty());
case PredefinedType.SUM:
// Collection::sum()
Number num = (Number) CollectionUtil.sum((Collection>) sourceVal);
if (num == null) {
// empty collection
@SuppressWarnings("unchecked")
CollectionType numCollType = (CollectionType) sourceType;
C numType = numCollType.getElementType();
if (numType == getReal()) {
num = new Double(0.0);
} else if (numType == getInteger()) {
num = new Integer(0);
}
}
return num;
case PredefinedType.FLATTEN:
// Set, Bag, Sequence, OrderedSet::flatten()
return CollectionUtil.flatten((Collection>) sourceVal);
case PredefinedType.AS_SET:
// Set, Bag, Sequence, OrderedSet::asSet()
return CollectionUtil.asSet((Collection>) sourceVal);
case PredefinedType.AS_BAG:
// Set, Bag, Sequence, OrderedSet::asBag()
return CollectionUtil.asBag((Collection>) sourceVal);
case PredefinedType.AS_ORDERED_SET:
// Set, Bag, Sequence, OrderedSet::asOrderedSet()
return CollectionUtil.asOrderedSet((Collection>) sourceVal);
case PredefinedType.AS_SEQUENCE:
// Set, Bag, Sequence, OrderedSet::asSequence)
return CollectionUtil.asSequence((Collection>) sourceVal);
case PredefinedType.FIRST:
// OrderedSet::first()
if (((Collection>) sourceVal).isEmpty()) {
return getInvalid();
}
return CollectionUtil.first((Collection>) sourceVal);
case PredefinedType.LAST:
// OrderedSet::last()
if (((Collection>) sourceVal).isEmpty()) {
return getInvalid();
}
return CollectionUtil.last((Collection>) sourceVal);
} // end of unary operation switch
} else if (numArgs == 1) {
//
// binary operations:
//
// evaluate argument
OCLExpression arg = args.get(0);
// get argument type
C argType = arg.getType();
if (isUndefined(sourceVal)) {
switch (opCode) {
case PredefinedType.OCL_IS_TYPE_OF:
case PredefinedType.OCL_IS_KIND_OF:
case PredefinedType.OCL_AS_TYPE:
case PredefinedType.AND:
case PredefinedType.OR:
case PredefinedType.XOR:
case PredefinedType.IMPLIES:
if (isLaxNullHandling()) {
break;
} else {
return getInvalid();
}
default:
return getInvalid();
}
}
// AnyType::oclIsTypeOf(OclType)
if (opCode == PredefinedType.OCL_IS_TYPE_OF) {
Object targetType = arg.accept(getVisitor());
// UnlimitedNatural is represented as Integer, so checking sourceVal's type
// doesn't work. Therefore, UnlimitedNatural needs to be handled here.
if (sourceType == getUnlimitedNatural()) {
return targetType == getUnlimitedNatural();
}
Boolean result = oclIsTypeOf(sourceVal, targetType);
if (result == null) {
return getInvalid();
} else {
return result;
}
} else if (opCode == PredefinedType.OCL_IS_KIND_OF) {
// no special check for Integer representation of UnlimitedNatural necessary
// because UnlimitedNatural is subtype of Integer
Object targetType = arg.accept(getVisitor());
// UnlimitedNatural is represented as Integer, so checking sourceVal's type
// doesn't work. Therefore, UnlimitedNatural needs to be handled here.
if (sourceType == getUnlimitedNatural() && targetType == getUnlimitedNatural()) {
return true; // other combinations properly handled since checked with Integer
}
Boolean result = oclIsKindOf(sourceVal, targetType);
if (result == null) {
return getInvalid();
} else {
return result;
}
} else if (opCode == PredefinedType.OCL_AS_TYPE) {
// Type conversions for the built-in, non-collection
// types are completely checked in the parser. The only
// actual work that
// needs to be done here is to convert from Any/Real to
// Integer
// and back (necessary since in OCL Integers extend
// Reals but this is not true of the java primtives).
// if the source is undefined or the conversion to
// OclVoid so is the result
if (sourceVal == null || (argType instanceof VoidType>)) {
return sourceVal;
}
if (sourceVal == getInvalid() || (argType instanceof InvalidType>)) {
return getInvalid();
}
if (sourceVal instanceof String
&& ((TypeExp) arg).getReferredType() == getString()) {
return sourceVal;
} else if (sourceVal instanceof Double
&& (argType == getInteger())) {
return new Integer(((Double) sourceVal).intValue());
} else if (sourceVal instanceof Boolean
&& ((TypeExp) arg).getReferredType() == getBoolean()) {
return sourceVal;
} else if (sourceVal instanceof Integer
&& (((TypeExp) arg).getReferredType() == getReal())) {
if (sourceType == getUnlimitedNatural()) {
int sourceInt = (Integer) sourceVal;
// the unlimited value is invalid as Real because there
// is no positive infinity defined in the OCL Real type
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
return getInvalid();
}
}
return new Double(((Integer) sourceVal).doubleValue());
} else if (sourceType == getUnlimitedNatural() && sourceVal.equals(UNLIMITED)
&& (((TypeExp) arg).getReferredType() == getInteger())) {
// According to OCL 2.3 (10-11-42) Section 8.2.1, UnlimitedNatural value
// * is an invalid Integer.
return getInvalid();
} else if (((TypeExp) arg).getReferredType() instanceof AnyType>) {
return sourceVal;
} else if ((sourceType == getUnlimitedNatural() && ((TypeExp) arg).getReferredType() == getUnlimitedNatural()) ||
oclIsKindOf(sourceVal, ((TypeExp) arg).getReferredType())) {
return sourceVal;
} else {
return getInvalid();
}
}
// evaluate arg, unless we have a boolean operation
Object argVal = null;
if (!isBooleanOperation(opCode)) {
argVal = safeVisitExpression(arg);
if (argVal == getInvalid()) {
return argVal; // an invalid argument leads to invalid operation call value
} // unless a boolean operation doesn't evaluate the arg
}
if (sourceVal instanceof Number) {
if (argVal == null) {
// one-arg numeric operation is invalid for null / undefined arg
return getInvalid();
}
// we have a numeric operation. Promote to high precision
sourceVal = higherPrecisionNumber((Number) sourceVal);
if (argVal instanceof Number) {
argVal = higherPrecisionNumber((Number) argVal);
}
}
if (sourceVal instanceof Long && argVal instanceof Long) {
//
// source and single arg are both integers
//
long sourceInt = (Long) sourceVal;
long argInt = (Long) argVal;
boolean sourceUnlimited =
sourceType == getUnlimitedNatural()
&& sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED;
boolean argUnlimited =
argType == getUnlimitedNatural()
&& argInt == UnlimitedNaturalLiteralExp.UNLIMITED;
if (sourceUnlimited && argUnlimited) {
switch (opCode) {
case PredefinedType.LESS_THAN:
case PredefinedType.GREATER_THAN:
// See section 11.5.5 of 10-11-42 in OCL 2.3
return Boolean.FALSE;
case PredefinedType.GREATER_THAN_EQUAL:
case PredefinedType.LESS_THAN_EQUAL:
// See section 11.5.5 of 10-11-42 in OCL 2.3
return Boolean.TRUE;
default:
// cannot do arithmetic on the unlimited value
return getInvalid();
}
} else if (sourceUnlimited || argUnlimited) {
switch (opCode) {
case PredefinedType.LESS_THAN:
case PredefinedType.LESS_THAN_EQUAL:
return argUnlimited;
case PredefinedType.GREATER_THAN:
case PredefinedType.GREATER_THAN_EQUAL:
return sourceUnlimited;
default:
// cannot do arithmetic on the unlimited value
return getInvalid();
}
}
switch (opCode) {
// Integer::plus(Integer)
case PredefinedType.PLUS:
return coerceNumber(sourceInt + argInt);
// Integer::minus(Integer)
case PredefinedType.MINUS:
return coerceNumber(sourceInt - argInt);
// Integer::times(Integer)
case PredefinedType.TIMES:
return coerceNumber(sourceInt * argInt);
// Integer::divide(Integer)
case PredefinedType.DIVIDE: {
// denominator of 0 means undefined
double num = sourceInt;
double denom = argInt;
return (denom == 0.0) ? getInvalid() : num / denom;
}
// Integer::div(Integer)
case PredefinedType.DIV:
// denominator of 0 means undefined
return (argInt == 0) ? getInvalid() :
coerceNumber(sourceInt / argInt);
// Integer::mod(Integer)
case PredefinedType.MOD:
return coerceNumber(sourceInt % argInt);
// Integer::max(Integer)
case PredefinedType.MAX:
return coerceNumber(Math.max(sourceInt, argInt));
// Integer::min(Integer)
case PredefinedType.MIN:
return coerceNumber(Math.min(sourceInt, argInt));
// Integer::lessThan(Integer)
case PredefinedType.LESS_THAN:
return sourceInt < argInt;
// Integer::greaterThan(Integer)
case PredefinedType.GREATER_THAN:
return sourceInt > argInt;
// Integer::lessThanEqual(Integer)
case PredefinedType.LESS_THAN_EQUAL:
return sourceInt <= argInt;
// Integer::greaterThanEqual(Integer)
case PredefinedType.GREATER_THAN_EQUAL:
return sourceInt >= argInt;
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
} else if (sourceVal instanceof Long
&& argVal instanceof Double) {
//
// source is an integer and single arg is a real
//
long sourceInt = (Long) sourceVal;
double argReal = (Double) argVal;
if (sourceType == getUnlimitedNatural()) {
if (sourceInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
switch (opCode) {
case PredefinedType.LESS_THAN:
// unlimited is not less than or equal to
// any Real value
return Boolean.FALSE;
case PredefinedType.GREATER_THAN:
case PredefinedType.GREATER_THAN_EQUAL:
// unlimited is greater than
// every Real value
return Boolean.TRUE;
default:
// cannot do arithmetic on the unlimited value
return getInvalid();
}
}
}
switch (opCode) {
// Integer::plus(Real)
case PredefinedType.PLUS:
return coerceNumber(sourceInt + argReal);
// Integer::minus(Real)
case PredefinedType.MINUS:
return coerceNumber(sourceInt - argReal);
// Integer::times(Real)
case PredefinedType.TIMES:
return coerceNumber(sourceInt * argReal);
// Integer::divide(Real)
case PredefinedType.DIVIDE:
// denominator of 0 results in undefined
return (argReal == 0.0) ? getInvalid() : sourceInt / argReal;
// Integer::max(Real)
case PredefinedType.MAX:
return coerceNumber(Math.max(sourceInt, argReal));
// Integer::min(Real)
case PredefinedType.MIN:
return coerceNumber(Math.min(sourceInt, argReal));
// Integer::lessThan(Real)
case PredefinedType.LESS_THAN:
return sourceInt < argReal;
// Integer::greaterThan(Real)
case PredefinedType.GREATER_THAN:
return sourceInt > argReal;
// Integer::lessThanEqual(Real)
case PredefinedType.LESS_THAN_EQUAL:
return sourceInt <= argReal;
// Integer::greaterThanEqual(Real)
case PredefinedType.GREATER_THAN_EQUAL:
return sourceInt >= argReal;
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
}
else if (sourceVal instanceof Double
&& argVal instanceof Long) {
double sourceReal = (Double) sourceVal;
long argInt = (Long) argVal;
//
// source is a real and single arg is an integer
//
if (argType == getUnlimitedNatural()) {
if (argInt == UnlimitedNaturalLiteralExp.UNLIMITED) {
switch (opCode) {
case PredefinedType.LESS_THAN:
// unlimited is greater than
// every Real value
return Boolean.TRUE;
case PredefinedType.GREATER_THAN:
case PredefinedType.GREATER_THAN_EQUAL:
// unlimited is not less than or equal to
// any Real value
return Boolean.FALSE;
default:
// cannot do arithmetic on the unlimited value
return getInvalid();
}
}
}
// for these arithmetic operations, don't need to coerce
// the result to any other precision because OCL Reals are
// represented as Doubles, anyway
switch (opCode) {
// Real::plus(Integer)
case PredefinedType.PLUS:
return sourceReal + argInt;
// Real::minus(Integer)
case PredefinedType.MINUS:
return sourceReal - argInt;
// Real::times(Integer)
case PredefinedType.TIMES:
return sourceReal * argInt;
// Real::divide(Integer)
case PredefinedType.DIVIDE:
// denominator of 0 results in undefined
return (argInt == 0) ? getInvalid() : sourceReal / argInt;
// Real::max(Integer)
case PredefinedType.MAX:
return Math.max(sourceReal, argInt);
// Real::min(Integer)
case PredefinedType.MIN:
return Math.min(sourceReal, argInt);
// Real::lessThan(Integer)
case PredefinedType.LESS_THAN:
return sourceReal < argInt;
// Real::greaterThan(Integer)
case PredefinedType.GREATER_THAN:
return sourceReal > argInt;
// Real::lessThanEqual(Integer)
case PredefinedType.LESS_THAN_EQUAL:
return sourceReal <= argInt;
// Real::greaterThanEqual(Integer)
case PredefinedType.GREATER_THAN_EQUAL:
return sourceReal >= argInt;
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
} else if (sourceVal instanceof Double
&& argVal instanceof Double) {
double sourceReal = (Double) sourceVal;
double argReal = (Double) argVal;
//
// source is a real and single arg is a real
//
switch (opCode) {
// Real::plus(Real)
case PredefinedType.PLUS:
return sourceReal + argReal;
// Real::minus(Real)
case PredefinedType.MINUS:
return sourceReal - argReal;
// Real::times(Real)
case PredefinedType.TIMES:
return sourceReal * argReal;
// Real::divide(Real)
case PredefinedType.DIVIDE:
// denominator of 0 results in undefined
return (argReal == 0.0) ? getInvalid() : sourceReal / argReal;
// Real::max(Real)
case PredefinedType.MAX:
return Math.max(sourceReal, argReal);
// Real::min(Real)
case PredefinedType.MIN:
return Math.min(sourceReal, argReal);
// Real::lessThan(Real)
case PredefinedType.LESS_THAN:
return sourceReal < argReal;
// Real::greaterThan(Real)
case PredefinedType.GREATER_THAN:
return sourceReal > argReal;
// Real::lessThanEqual(Real)
case PredefinedType.LESS_THAN_EQUAL:
return sourceReal <= argReal;
// Real::greaterThanEqual(Real)
case PredefinedType.GREATER_THAN_EQUAL:
return sourceReal >= argReal;
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
} else if (sourceVal instanceof Boolean || isBooleanOperation(opCode)) {
// the logic with an undefined value is basic 3-valued
// logic:
// null represents the undefined value
// boolean source and single boolean arg
switch (opCode) {
// Boolean::or(Boolean)
case PredefinedType.OR:
if (Boolean.TRUE.equals(sourceVal)) {
return Boolean.TRUE;
}
// must evaluate the argument now
argVal = arg.accept(getVisitor());
if (Boolean.TRUE.equals(argVal)) {
return Boolean.TRUE;
}
if (isUndefined(sourceVal) || isUndefined(argVal)) {
return getInvalid();
}
return Boolean.FALSE;
// Boolean::xor(Boolean)
case PredefinedType.XOR:
// XOR does not have a short-circuit
argVal = arg.accept(getVisitor());
if (isUndefined(sourceVal) || isUndefined(argVal)) {
return getInvalid();
}
return (argVal == null) ? sourceVal
: (((Boolean) sourceVal).booleanValue()
^ ((Boolean) argVal).booleanValue() ? Boolean.TRUE
: Boolean.FALSE);
// Boolean::and(Boolean)
case PredefinedType.AND:
if (Boolean.FALSE.equals(sourceVal)) {
return Boolean.FALSE;
}
// must evaluate the argument now
argVal = arg.accept(getVisitor());
if (Boolean.FALSE.equals(argVal)) {
return Boolean.FALSE;
}
if (isUndefined(sourceVal) || isUndefined(argVal)) {
return getInvalid();
}
return Boolean.TRUE;
// Boolean::implies
case PredefinedType.IMPLIES:
if (Boolean.FALSE.equals(sourceVal)) {
return Boolean.TRUE;
}
// must evaluate the argument now
argVal = arg.accept(getVisitor());
if (Boolean.TRUE.equals(argVal)) {
return Boolean.TRUE;
}
if (isUndefined(sourceVal) || isUndefined(argVal)) {
return getInvalid();
}
return argVal;
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
}
else if (sourceVal instanceof String) {
if (isUndefined(argVal)) {
return getInvalid();
}
switch (opCode) {
// String::concat(String)
case PredefinedType.CONCAT:
return ((String) sourceVal).concat((String) argVal);
// Handle < (lessThan)
case PredefinedType.LESS_THAN:
return Boolean.valueOf(((String) sourceVal).compareTo((String) argVal) < 0);
// Handle <= (lessThanEqual)
case PredefinedType.LESS_THAN_EQUAL:
return Boolean.valueOf(((String) sourceVal).compareTo((String) argVal) <= 0);
// Handle > (greaterThan)
case PredefinedType.GREATER_THAN:
return Boolean.valueOf(((String) sourceVal).compareTo((String) argVal) > 0);
// Handle > (greaterThanEqual)
case PredefinedType.GREATER_THAN_EQUAL:
return Boolean.valueOf(((String) sourceVal).compareTo((String) argVal) >= 0);
default: {
String message = OCLMessages.bind(
OCLMessages.UnknownOperation_ERROR_,
getName(oper));
RuntimeException error = new RuntimeException(message);
OCLPlugin.throwing(getClass(),
"visitOperationCallExp", error);//$NON-NLS-1$
throw error;
}
}
} else if (sourceVal instanceof Collection>) {
@SuppressWarnings("unchecked")
Collection