All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ocl.EvaluationVisitorImpl Maven / Gradle / Ivy

/**
 * 
 *
 * 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> 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 sourceColl = (Collection) sourceVal;

                    // bug 183144:  inputting OclInvalid should result in OclInvalid
                    if (argVal == getInvalid()) {
                        return argVal;
                    }
                    
					switch (opCode) {
						case PredefinedType.INCLUDES:
							// Collection::includes(T)
							return CollectionUtil.includes(sourceColl,
								argVal) ? Boolean.TRUE : Boolean.FALSE;

						case PredefinedType.EXCLUDES:
							// Collection::excludes(T)
							return CollectionUtil.excludes(sourceColl,
								argVal) ? Boolean.TRUE : Boolean.FALSE;

						case PredefinedType.COUNT:
							// Collection::count(T)
							return new Integer(CollectionUtil.count(
								sourceColl, argVal));

						case PredefinedType.INCLUDES_ALL:
							// Collection::includesAll(T)
							return CollectionUtil.includesAll(sourceColl,
								(Collection) argVal) ? Boolean.TRUE
								: Boolean.FALSE;

						case PredefinedType.EXCLUDES_ALL:
							// Collection::excludesAll(T)
							return CollectionUtil.excludesAll(sourceColl,
								(Collection) argVal) ? Boolean.TRUE
								: Boolean.FALSE;

						case PredefinedType.PRODUCT: {
							// Collection::product(Collection(T2))
							@SuppressWarnings("unchecked")
							CollectionType collType = (CollectionType) oc.getType();
							
							return CollectionUtil.product(
									getEvaluationEnvironment(),
									getEnvironment(),
									sourceColl,
									(Collection) argVal,
									collType.getElementType());
						}
						case PredefinedType.UNION: {
							// Set, Bag::union(Set, Bag)
							Collection argColl = (Collection) argVal;
							return CollectionUtil.union(sourceColl, argColl);
						}

						case PredefinedType.INTERSECTION: {
							// Set, Bag::intersection(Set, Bag)
							Collection argColl = (Collection) argVal;
							return CollectionUtil.intersection(sourceColl,
								argColl);
						}

						case PredefinedType.MINUS:
							// Set::minus(Set)
							return CollectionUtil.minus((Set) sourceColl,
								(Set) argVal);

						case PredefinedType.INCLUDING:
							// Set, Bag, Sequence::including(T)
							return CollectionUtil.including(sourceColl,
								argVal);

						case PredefinedType.EXCLUDING:
							// Set, Bag, Sequence::excluding(T)
							return CollectionUtil.excluding(sourceColl,
								argVal);

						case PredefinedType.SYMMETRIC_DIFFERENCE:
							// Set::symmetricDifference(Set)
							return CollectionUtil.symmetricDifference(
								(Set) sourceColl, (Set) argVal);

						case PredefinedType.APPEND:
							// OrderedSet, Sequence::append(T)
							return CollectionUtil.append(sourceColl, argVal);

						case PredefinedType.PREPEND:
							// OrderedSet, Sequence::prepend(T)
							return CollectionUtil.prepend(sourceColl,
								argVal);

						case PredefinedType.AT: {
							// OrderedSet, Sequence::at(Integer)
							if (!(argVal instanceof Integer)) {
								return getInvalid();
							}
							int indexVal = ((Integer) argVal).intValue();
							return CollectionUtil.at(sourceColl, indexVal);
						}

						case PredefinedType.INDEX_OF:
							// OrderedSet, Sequence::indexOf(T)
							Object indexOf = CollectionUtil.indexOf(sourceColl,
								argVal);
							if (indexOf == null) {
								// according to OCL spec, precondition is violated, resulting in invalid
								indexOf = getInvalid();
							}
							return indexOf;
					} // end of collection type switch
				} else if (sourceVal instanceof Comparable) {

					// Handle < (lessThan)
					if (opCode == PredefinedType.LESS_THAN) {
						@SuppressWarnings("unchecked")
						Comparable comp = (Comparable) sourceVal;
						return Boolean.valueOf(comp.compareTo(argVal) < 0);
					}

					//	Handle <= (lessThanEqual)
					else if (opCode == PredefinedType.LESS_THAN_EQUAL) {
						@SuppressWarnings("unchecked")
						Comparable comp = (Comparable) sourceVal;
						return Boolean.valueOf(comp.compareTo(argVal) <= 0);
					}

					// Handle > (greaterThan)
					else if (opCode == PredefinedType.GREATER_THAN) {
						@SuppressWarnings("unchecked")
						Comparable comp = (Comparable) sourceVal;
						return Boolean.valueOf(comp.compareTo(argVal) > 0);
					}

					// Handle > (greaterThanEqual)
					else if (opCode == PredefinedType.GREATER_THAN_EQUAL) {
						@SuppressWarnings("unchecked")
						Comparable comp = (Comparable) sourceVal;
						return Boolean.valueOf(comp.compareTo(argVal) >= 0);
					}
				}
			} else {
				//
				// ternary operations
				//

				// check if undefined
				if (isUndefined(sourceVal)) {
                    return getInvalid();
                }

				// evaluate arg1
				Object arg1 = args.get(0).accept(getVisitor());

				// check if invalid
				if (arg1 == getInvalid()) {
                    return getInvalid();
                }

				// evaluate arg2
				Object arg2 = args.get(1).accept(getVisitor());

				// check if invalid
				if (arg2 == getInvalid()) {
                    return getInvalid();
                }

				if (sourceVal instanceof String) {
					// just one ternary string operation
					// String::substring(Integer, Integer)
					// index orgin 1 for OCL
					if (isUndefined(arg1) || isUndefined(arg2)) {
						return getInvalid();
					}
					int lower = ((Integer) arg1).intValue();
					int upper = ((Integer) arg2).intValue();
					if (!(1 <= lower &&
							  lower <= upper &&
							  upper <= ((String) sourceVal).length())) {
						return getInvalid();
					}
					return ((String) sourceVal).substring(lower-1, upper);
				} else if (sourceVal instanceof Collection) {
					@SuppressWarnings("unchecked")
					Collection sourceColl = (Collection) sourceVal;
					if (opCode == PredefinedType.INSERT_AT) {
						if (isUndefined(arg1)) {
							return getInvalid();
						}
						// OrderedSet, Sequence::insertAt(Integer, T)
						int index = ((Integer) arg1).intValue();
						return CollectionUtil.insertAt(sourceColl, index,
							arg2);
					} else if (opCode == PredefinedType.SUB_ORDERED_SET) {
						if (isUndefined(arg1) || isUndefined(arg2)) {
							return getInvalid();
						}
						// OrderedSet, Sequence::subOrderedSet(Integer, Integer)
						int lower = ((Integer) arg1).intValue();
						int upper = ((Integer) arg2).intValue();
						return CollectionUtil.subOrderedSet(sourceColl,
							lower, upper);
					} else if (opCode == PredefinedType.SUB_SEQUENCE) {
						if (isUndefined(arg1) || isUndefined(arg2)) {
							return getInvalid();
						}
						// Sequence::subSequence(Integer, Integer)
						int lower = ((Integer) arg1).intValue();
						int upper = ((Integer) arg2).intValue();
						return CollectionUtil.subSequence(sourceColl,
							lower, upper);
					}
				}
			}
		} else {
			
			// Handle allInstances
			if (opCode == PredefinedType.ALL_INSTANCES) {
				// can assume classifier type, otherwise the expression would
				//    not have parsed (or validated)
				@SuppressWarnings("unchecked")
				C classifier = (C) sourceVal; 
				
				if (getUMLReflection().isEnumeration(classifier)) {
					// the instances are the literals
					return new java.util.HashSet(
                            getUMLReflection().getEnumerationLiterals(classifier));
				} else if (sourceVal instanceof VoidType) {
					// OclVoid has a single instance: null
					Set result = new java.util.HashSet();
					result.add(null);
					return result;
				} else if (getUMLReflection().isClass(classifier)) {
					return getExtentMap().get(sourceVal);
				} else {
					// other types do not have numerable instances
					return Collections.EMPTY_SET;
				}
			}

			if (opCode == PredefinedType.OCL_IS_UNDEFINED) {
				return isUndefined(sourceVal)?
						Boolean.TRUE : Boolean.FALSE;
			}

			if (opCode == PredefinedType.OCL_IS_INVALID) {
				return (sourceVal == getInvalid())?
						Boolean.TRUE : Boolean.FALSE;
			}

			// result is invalid if source is undefined
			if (isUndefined(sourceVal)) {
			    switch (opCode) {
                case PredefinedType.OCL_IS_TYPE_OF:
                case PredefinedType.OCL_IS_KIND_OF:
                case PredefinedType.OCL_AS_TYPE:
                    if (isLaxNullHandling()) {
                        break;
                    } else {
                        return getInvalid();
                    }
                default:
                    return getInvalid();
			    }
			}

			// Handle type check and conversion:

			// AnyType::oclIsTypeOf(OclType)
			if (opCode == PredefinedType.OCL_IS_TYPE_OF) {
				OCLExpression arg = args.get(0);
				Boolean result = oclIsTypeOf(sourceVal, arg.accept(getVisitor()));
				if (result == null) {
					return getInvalid();
				} else {
					return result;
				}
			}

			// AnyType::oclIsKindOf(OclType)
			else if (opCode == PredefinedType.OCL_IS_KIND_OF) {
				OCLExpression arg = args.get(0);
				Boolean result = oclIsKindOf(sourceVal, arg.accept(getVisitor()));
				if (result == null) {
					return getInvalid();
				} else {
					return result;
				}
			}

			// AnyType::oclAsType(OclType)
			else if (opCode == PredefinedType.OCL_AS_TYPE) {
				// Check if the source object type is really
				// conformant to the arg type. Note that
				// it is not possible to do this check 100%
				// at parse time because we only have the
				// declared type of the source to check
				// against the arg type and it may happen
				// that the declared type is not conformant
				// but a subtype of it is. For example,
				// if there are four types A, B, C, and D;
				// B is subtype of both A and C; D is a subtype of A;
				// and x is a variable of type A; then it is impossible
				// to know at parse time whether x.oclAsType(C)
				// is a valid conversion. If x is an object of
				// type B then it is; if x is an object of type D
				// then it isn't; and this cannot be determined
				// until runtime.				
				OCLExpression arg = args.get(0);
				
				@SuppressWarnings("unchecked")
				C type = (C) arg.accept(getVisitor());
				if (Boolean.TRUE.equals(oclIsKindOf(sourceVal, type))) {
					return sourceVal;
				} else {
					return getInvalid();
				}
			}

			// Handle < (lessThan)
			else if ((opCode == PredefinedType.LESS_THAN) && (sourceVal instanceof Comparable)) {
				@SuppressWarnings("unchecked")
				Comparable compContext = (Comparable) sourceVal;
				OCLExpression arg = args.get(0);
				
				@SuppressWarnings("unchecked")
				Comparable evalArg = (Comparable) arg.accept(getVisitor());
				return Boolean.valueOf(compContext.compareTo(evalArg) < 0);
			}

			//	Handle <= (lessThanEqual)
			else if ((opCode == PredefinedType.LESS_THAN_EQUAL) && (sourceVal instanceof Comparable)) {
				@SuppressWarnings("unchecked")
				Comparable compContext = (Comparable) sourceVal;
				OCLExpression arg = args.get(0);
				
				@SuppressWarnings("unchecked")
				Comparable evalArg = (Comparable) arg.accept(getVisitor());
				return Boolean.valueOf(compContext.compareTo(evalArg) <= 0);
			}

			// Handle > (greaterThan)
			else if ((opCode == PredefinedType.GREATER_THAN) && (sourceVal instanceof Comparable)) {
				@SuppressWarnings("unchecked")
				Comparable compContext = (Comparable) sourceVal;
				OCLExpression arg = args.get(0);
				
				Comparable evalArg = (Comparable) arg.accept(getVisitor());
				return Boolean.valueOf(compContext.compareTo(evalArg) > 0);
			}

			// Handle > (greaterThanEqual)
			else if ((opCode == PredefinedType.GREATER_THAN_EQUAL) && (sourceVal instanceof Comparable)) {
				@SuppressWarnings("unchecked")
				Comparable compContext = (Comparable) sourceVal;
				OCLExpression arg = args.get(0);
				
				@SuppressWarnings("unchecked")
				Comparable evalArg = (Comparable) arg.accept(getVisitor());
				return Boolean.valueOf(compContext.compareTo(evalArg) >= 0);
			}
			
			//
			// unknown operation (shouldn't have gotten this far if we
			//   successfully parsed and/or validated the expression
			//

			return getInvalid();
		}

		return getInvalid();
	}
	
	/**
	 * Infers a standard operation code from the name of a user-defined
	 * operation.  This applies for cases where a standard operation is not
	 * defined by the OCL Standard Library, but is implemented nonetheless by
	 * the interpreter.
	 * 
	 * @param operation the operation
     * @param opcode the original operation code from the AST
	 * @return the appropriate operation code, or the original opcode
     *     if there is no matching standard operation
	 */
	private int inferOperationCode(O operation, int opcode) {
	    int result = opcode;
	    String opName = getName(operation);
	    
        if (PredefinedType.LESS_THAN_NAME.equals(opName)) {
            result = PredefinedType.LESS_THAN;
        } else if (PredefinedType.GREATER_THAN_NAME.equals(opName)) {
            result = PredefinedType.GREATER_THAN;
        } else if (PredefinedType.LESS_THAN_EQUAL_NAME.equals(opName)) {
            result = PredefinedType.LESS_THAN_EQUAL;
        } else if (PredefinedType.GREATER_THAN_EQUAL_NAME.equals(opName)) {
            result = PredefinedType.GREATER_THAN_EQUAL;
        }
        
        return result;
    }

	/**
	 * Callback for an IterateExp visit.
	 */
	@Override
    public Object visitIterateExp(IterateExp ie) {
		// get the variable declaration for the result
		Variable vd = ie.getResult();
		String resultName = (String) vd.accept(getVisitor());

		try {
			// get the list of ocl iterators
			List> iterators = ie.getIterator();
	
			// evaluate the source collection
			Object sourceValue = ie.getSource().accept(getVisitor());
	
			// value of iteration expression is undefined if the source is
			//   null or OclInvalid
			if (isUndefined(sourceValue)) {
				return getInvalid();
			}
			
			Collection coll = (Collection) sourceValue;
			
			// get the body expression
			OCLExpression body = ie.getBody();
	
			// construct an iteration template to evaluate the iterator
			IterationTemplate is =
				IterationTemplate.getInstance(getVisitor());
	
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result variable from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	/**
	 * Callback for an IteratorExp visit.
	 */
	@Override
    public Object visitIteratorExp(IteratorExp ie) {
		C sourceType = ie.getSource().getType();
		
		if (sourceType instanceof PredefinedType) {
			Object sourceValue = ie.getSource().accept(getVisitor());
			
			// value of iteration expression is undefined if the source is
			//   null or OclInvalid
			if (isUndefined(sourceValue)) {
				return getInvalid();
			}
			
			Collection sourceCollection = (Collection) sourceValue;
			
			switch (OCLStandardLibraryUtil.getOperationCode(ie.getName())) {
			case PredefinedType.EXISTS:
				return evaluateExistsIterator(ie, sourceCollection);
			case PredefinedType.FOR_ALL:
				return evaluateForAllIterator(ie, sourceCollection);
			case PredefinedType.SELECT:
				return evaluateSelectIterator(ie, sourceCollection);
			case PredefinedType.REJECT:
				return evaluateRejectIterator(ie, sourceCollection);
			case PredefinedType.COLLECT:
				return evaluateCollectIterator(ie, sourceCollection);
			case PredefinedType.COLLECT_NESTED:
				return evaluateCollectNestedIterator(ie, sourceCollection);
			case PredefinedType.ONE:
				return evaluateOneIterator(ie, sourceCollection);
			case PredefinedType.ANY:
				return evaluateAnyIterator(ie, sourceCollection);
			case PredefinedType.SORTED_BY:
				return evaluateSortedByIterator(ie, sourceCollection);
			case PredefinedType.IS_UNIQUE:
				return evaluateIsUnique(ie, sourceCollection);
			case PredefinedType.CLOSURE:
				return evaluateClosure(ie, sourceCollection);
			}
		}
		
		String message = OCLMessages.bind(
				OCLMessages.IteratorNotImpl_ERROR_, ie.getName());
		UnsupportedOperationException ex = new UnsupportedOperationException(
			message);
		OCLPlugin.throwing(getClass(), "visitIteratorExp", ex);//$NON-NLS-1$
		throw ex;
	}

	private static synchronized String generateName() {
		return "__result__" + tempCounter++;//$NON-NLS-1$
	}

	private Object evaluateExistsIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateExists.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, Boolean.FALSE);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove the result variable from the environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateForAllIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateForAll.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, Boolean.TRUE);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from the environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateCollectNestedIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get initial result value based on the source type
		@SuppressWarnings("unchecked")
		CollectionType collType = (CollectionType) ie.getSource().getType();
		
		Object initResultVal = null;
		if (collType instanceof SetType || collType instanceof BagType) {
            // collection on a Bag or a Set yields a Bag
			initResultVal = CollectionUtil.createNewBag();
        } else {
            // Sequence or Ordered Set yields a Sequence
			initResultVal = CollectionUtil.createNewSequence();
        }
		
		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateCollectNested.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, initResultVal);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {		
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateCollectIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get initial result value based on the source type
		@SuppressWarnings("unchecked")
		CollectionType collType = (CollectionType) ie.getSource().getType();
		
		Object initResultVal = null;
		if (collType instanceof SetType || collType instanceof BagType) {
            // collection on a Bag or a Set yields a Bag
			initResultVal = CollectionUtil.createNewBag();
        } else {
            // Sequence or Ordered Set yields a Sequence
			initResultVal = CollectionUtil.createNewSequence();
        }

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateCollect.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, initResultVal);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateSelectIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get initial result value based on the source type
		@SuppressWarnings("unchecked")
		CollectionType collType = (CollectionType) ie.getSource().getType();
		
		Object initResultVal = null;
		if (collType instanceof SetType) {
            // Set
			initResultVal = CollectionUtil.createNewSet();
        } else if (collType instanceof BagType) {
            // Bag
			initResultVal = CollectionUtil.createNewBag();
        } else if (collType instanceof SequenceType) {
            // Sequence
			initResultVal = CollectionUtil.createNewSequence();
        } else {
            // OrderedSet
			initResultVal = CollectionUtil.createNewOrderedSet();
        }

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateSelect.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, initResultVal);
		
		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateRejectIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get initial result value based on the source type
		@SuppressWarnings("unchecked")
		CollectionType collType = (CollectionType) ie.getSource().getType();
		
		Object initResultVal = null;
		if (collType instanceof SetType) {
            // Set
			initResultVal = CollectionUtil.createNewSet();
        } else if (collType instanceof BagType) {
            // Bag
			initResultVal = CollectionUtil.createNewBag();
        } else if (collType instanceof SequenceType) {
            // Sequence
			initResultVal = CollectionUtil.createNewSequence();
        } else {
            // OrderedSet
			initResultVal = CollectionUtil.createNewOrderedSet();
        }

		//	get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateReject.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, initResultVal);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateOneIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateOne.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, Boolean.FALSE);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateAnyIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateAny.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, null);

		try {
			// evaluate
			return is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	private Object evaluateSortedByIterator(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		//		int numIters = iterators.size();

		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateSortedBy.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		
		final Map> map =
			new HashMap>();
		getEvaluationEnvironment().add(resultName, map);
		try {
			// evaluate
			// TODO: find an efficient way to do this.
			Object evaluationResult = is.evaluate(coll, iterators, body, resultName);
			
			if (evaluationResult == getInvalid()) {
				// handle the OclInvalid result
				return evaluationResult;
			}
			
			is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
		// sort the source collection based on the natural ordering of the
		// body expression evaluations
		List result = new ArrayList(coll);

		Collections.sort(result, getComparatorForSortedBy(map, ie));

		// create result
		// type is Sequence if source is a sequence or a Bag,
		// SortedSet if source is a SortedSet or a Set
		C collType = ie.getSource().getType();
		if (collType instanceof SetType || collType instanceof OrderedSetType) {
            return CollectionUtil.createNewOrderedSet(result);
        } else {
            return CollectionUtil.createNewSequence(result);
        }
	}

	private Comparator getComparatorForSortedBy(
			final Map> map, IteratorExp ie) {
		// special case: UnlimitedNatural::UNLIMITED is greater than
		// everything except for itself
		if (ie.getBody().getType() == getUnlimitedNatural()) {
			return new Comparator() {
				public int compare(Object o1, Object o2) {
					Comparable b1 = map.get(o1);
					Comparable b2 = map.get(o2);
					return (b1.equals(UNLIMITED) ?
						       b2.equals(UNLIMITED) ?
						    	   0 : // both are UNLIMITED
						           1 : // b1 is UNLIMITED, b2 not, so b1>b2
						       b2.equals(UNLIMITED) ?
						    	   -1 : // b2 is UNLIMITED, b1 not, so b1 < b2
						    	   b1.compareTo(b2));
				}
			};
		} else {
			return new Comparator() {

				public int compare(Object o1, Object o2) {
					Comparable b1 = map.get(o1);
					Comparable b2 = map.get(o2);
					return (b1.compareTo(b2));
				}
			};
		}
	}

	private Object evaluateIsUnique(IteratorExp ie, Collection coll) {
		// get the list of ocl iterators
		List> iterators = ie.getIterator();
		
		// get the body expression
		OCLExpression body = ie.getBody();

		// get an iteration template to evaluate the iterator
		IterationTemplate is =
			IterationTemplateIsUnique.getInstance(getVisitor());

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, new HashSet());

		try {
			// evaluate
			is.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
		
		return is.isDone() ? Boolean.FALSE : Boolean.TRUE;
	}

	private Object evaluateClosure(IteratorExp ie, Collection coll) {

		// get the list of ocl iterators
		List> iterators = ie.getIterator();

		// get the body expression
		OCLExpression body = ie.getBody();
		C type = ie.getType();
		// create initial result value
		Object initResultVal = type instanceof OrderedSetType ? CollectionUtil.createNewOrderedSet() : CollectionUtil.createNewSet();

		// get an iteration template to evaluate the iterator
		IterationTemplate template =
			IterationTemplateClosure.getInstance(getVisitor(), body);

		// generate a name for the result variable and add it to the environment
		String resultName = generateName();
		getEvaluationEnvironment().add(resultName, initResultVal);

		try {
			// evaluate
			return template.evaluate(coll, iterators, body, resultName);
		} finally {
			// remove result name from environment
			getEvaluationEnvironment().remove(resultName);
		}
	}

	/**
	 * Callback for an EnumLiteralExp visit. Get the referred enum literal and
	 * return it as an Integer.
	 * 
	 * @param el
	 *            the enumeration literal expresion
	 * @return the enumeration literal as an Integer
	 */
	@Override
    public Object visitEnumLiteralExp(EnumLiteralExp el) {
		return (enumerations == null) ? el.getReferredEnumLiteral()
			: enumerations.getValue(el.getReferredEnumLiteral());
	}

	/**
	 * Callback for a VariableExp visit.
	 * 
	 * @param v
	 *            the variable expression
	 * @return the value of the variable
	 */
	@Override
    public Object visitVariableExp(VariableExp v) {

		// get the referred variable name
		Variable vd = v.getReferredVariable();
		String varName = vd.getName();

		// evaluate the variable in the current environment
		return getEvaluationEnvironment().getValueOf(varName);
	}

	/**
	 * Callback for a PropertyCallExp visit. Evaluates the source of the
	 * expression and then reflectively gets the value of the property on the
	 * result. For example, in "self.foo", "self" is the source and would be
	 * evaluated first, then the value of the property "foo" would be accessed
	 * on that object.
	 */
	@Override
    public Object visitPropertyCallExp(PropertyCallExp pc) {
		P property = pc.getReferredProperty();
		OCLExpression source = pc.getSource();

		// evaluate source
		Object context = source.accept(getVisitor());

		// if source is undefined, result is OclInvalid
		if (isUndefined(context)) {
            return getInvalid();
        }

		OCLExpression derivation = getPropertyBody(property);
		if (derivation != null) {
			// this is an additional property
			
			return navigate(property, derivation, context);
		}
		
		List qualifiers;
		
		if (pc.getQualifier().isEmpty()) {
			qualifiers = Collections.emptyList();
		} else {
			// handle qualified association navigation
			qualifiers = new java.util.ArrayList();
			
			for (OCLExpression q : pc.getQualifier()) {
				qualifiers.add(q.accept(getVisitor()));
			}
		}
		
		Object result = getEvaluationEnvironment().navigateProperty(property, qualifiers, context);
		
		if ((pc.getType() instanceof CollectionType) && !(result instanceof Collection)) {
			// this was an XSD "unspecified multiplicity".  Now that we know what
			//    the multiplicity is, we can coerce it to a collection value
			@SuppressWarnings("unchecked")
			CollectionKind kind = ((CollectionType) pc.getType()).getKind();
			
			Collection collection = CollectionUtil.createNewCollection(kind);
			
			collection.add(result);
			result = collection;
		}
		
		return result;
	}

	/**
	 * Callback for an AssociationClassCallExp visit. Evaluates the source of the
	 * expression and then reflectively gets the value of the reference on the
	 * result. For example, in "self.foo", "self" is the source and would be
	 * evaluated first, then the value of the reference "foo" would be derived
	 * on that object.
	 */
	@Override
    public Object visitAssociationClassCallExp(AssociationClassCallExp ae) {
		Object context = ae.getSource().accept(getVisitor());
		
		if (isUndefined(context)) {
			return getInvalid();
		}
		
		// evaluate attribute on source value
		return getEvaluationEnvironment().navigateAssociationClass(
			ae.getReferredAssociationClass(),
			ae.getNavigationSource(),
			context);
	}

	/**
	 * Callback for a VariableDeclaration visit.
	 */
	@Override
    public Object visitVariable(Variable vd) {
		// add the variable to the environment, initialized to
		// its initial expression (if it has one). return the name
		// of the variable.
		String varName = vd.getName();
		OCLExpression initExp = vd.getInitExpression();
		Object initVal = null;
		if (initExp != null) {
			// if an unpropagated runtime exception is thrown, assign invalid to
			// variable, allowing an oclIsInvalid() to detect it later
			initVal = safeVisitExpression(initExp);
        }
		getEvaluationEnvironment().add(varName, initVal);
		return varName;
	}

	/**
	 * Callback for an IfExp visit.
	 */
	@Override
    public Object visitIfExp(IfExp ie) {
		// get condition
		OCLExpression condition = ie.getCondition();

		// evaluate condition
		Object condVal = condition.accept(getVisitor());
		if (isUndefined(condVal)) {
			return getInvalid();
		}
		Boolean condValBool = (Boolean) condVal;

		if (condValBool.booleanValue()) {
            return ie.getThenExpression().accept(getVisitor());
        }
		return ie.getElseExpression().accept(getVisitor());
	}

	/**
	 * Callback for a TypeExp visiy.
	 */
	@Override
    public Object visitTypeExp(TypeExp t) {
		return t.getReferredType();
	}
	
	@Override
    public Object visitStateExp(StateExp s) {
		return s.getReferredState();
	}
	
	@Override
    public Object visitMessageExp(MessageExp m) {
		throw new UnsupportedOperationException("evaluation of MessageExp"); //$NON-NLS-1$
	}
	
	/**
	 * Callback for an UnspecifiedValueExp visit.
	 */
	@Override
    public Object visitUnspecifiedValueExp(UnspecifiedValueExp uv) {
		// TODO: return a "random instance of the type of the expression"
		throw new UnsupportedOperationException("evaluation of UnspecifiedValueExp"); //$NON-NLS-1$
	}

	/**
	 * Callback for an IntegerLiteralExp visit.
	 * 
	 * @return the value of the integer literal as a java.lang.Integer.
	 */
	@Override
    public Object visitIntegerLiteralExp(IntegerLiteralExp il) {
		return il.getIntegerSymbol();
	}
    
    /**
     * Callback for an UnlimitedNaturalLiteralExp visit.
     * 
     * @return the value of the natural literal as a java.lang.Integer.
     */
    @Override
    public Object visitUnlimitedNaturalLiteralExp(
            UnlimitedNaturalLiteralExp literalExp) {
        return literalExp.getIntegerSymbol();
    }

	/**
	 * Callback for a RealLiteralExp visit.
	 * 
	 * @return the value of the real literal as a java.lang.Double.
	 */
	@Override
    public Object visitRealLiteralExp(RealLiteralExp rl) {
		return rl.getRealSymbol();
	}

	/**
	 * Callback for a StringLiteralExp visit.
	 * 
	 * @return the value of the string literal as a java.lang.String.
	 */
	@Override
    public Object visitStringLiteralExp(StringLiteralExp sl) {
		return sl.getStringSymbol();
	}

	/**
	 * Callback for a BooleanLiteralExp visit.
	 * 
	 * @return the value of the boolean literal as a java.lang.Boolean.
	 */
	@Override
    public Object visitBooleanLiteralExp(BooleanLiteralExp bl) {
		return bl.getBooleanSymbol();
	}

	@Override
    public Object visitInvalidLiteralExp(InvalidLiteralExp il) {
		// just make up some object to take the place of the OclInvalid literal
		return getInvalid();
	}

	@Override
    public Object visitNullLiteralExp(NullLiteralExp il) {
		// the single OclVoid instance is equivalent to Java null
		return null;
	}

	/**
	 * Callback for LetExp visit.
	 */
	@Override
    public Object visitLetExp(LetExp l) {
		// get variable decl for let variable
		Variable vd = l.getVariable();
		String name = (String) vd.accept(getVisitor());
		
		try {
			// evaluate the "in" part of the let
			OCLExpression inExp = l.getIn();
			// return the value of the "in"			
			return inExp.accept(getVisitor());
			
		} finally {
			// remove the variable-init expression binding from the environment
			getEvaluationEnvironment().remove(name);
		}
	}

	/**
	 * Callback for a CollectionLiteralExp visit.
	 */
	@Override
    public Object visitCollectionLiteralExp(CollectionLiteralExp cl) {
		// construct the appropriate collection from the parts
		// based on the collection kind.
		CollectionKind kind = cl.getKind();
		List> parts = cl.getPart();
		Collection result = CollectionUtil.createNewCollection(kind);

		if ((kind == CollectionKind.SEQUENCE_LITERAL) && cl.isSimpleRange()) {
			// literal is of the form: Sequence{first..last}.
			// construct a list with a lazy iterator for it.
			CollectionRange collRange = (CollectionRange) parts.get(0);
			OCLExpression first = collRange.getFirst();
			OCLExpression last = collRange.getLast();

			// evaluate first value
			Integer firstVal = (Integer) first.accept(getVisitor());
			if (firstVal == null) {
				result.add(null);
				return result;
			}
			// evaluate last value
			Integer lastVal = (Integer) last.accept(getVisitor());
			if (lastVal == null) {
				result.add(null);
				return result;
			}

			int firstInt = firstVal.intValue();
			int lastInt = lastVal.intValue();
			if (firstInt > lastInt) {
                return result;
            }

			// construct a lazy integer list for the range
			return new IntegerRangeList(firstInt, lastInt);
		} else {
			// not a sequence or not a simple range
			for (CollectionLiteralPart part : parts) {
				if (part instanceof CollectionItem) {
					// CollectionItem part
					CollectionItem item = (CollectionItem) part;
					OCLExpression itemExp = item.getItem();
					Object itemVal = itemExp.accept(getVisitor());
					if (itemVal == getInvalid()) {
						return getInvalid(); // can't have an invalid element in a collection
					}
					// add it to the result set, even if null, except it's the only item in a Set
					// literal; otherwise, the implicit set conversion of an undefined value
					// would return false for isEmpty(). See also Section 7.5.3 in the OCL 2.3
					// specification (10-11-42) on implicit set conversion by the -> operator
					if (itemVal != null || parts.size() > 1 || !isImplicitSetConversion(cl)) {
						result.add(itemVal);
					}
				} else {
					// Collection range
					CollectionRange range = (CollectionRange) part;
					OCLExpression first = range.getFirst();
					OCLExpression last = range.getLast();

					// evaluate first value
					Integer firstVal = (Integer) first.accept(getVisitor());
					Integer lastVal = (Integer) last.accept(getVisitor());
					if (!((firstVal == null) || (lastVal == null))) {
						// TODO: enhance IntegerRangeList to support multiple ranges
						// add values between first and last inclusive
						int firstInt = firstVal.intValue();
						int lastInt = lastVal.intValue();
						for (int i = firstInt; i <= lastInt; i++) {
                            result.add(new Integer(i));
                        }
					}
				} // end of collection range

			} // end of parts iterator

		} // end of not-simple range case

		return result;
	} // end of Set, OrderedSet, Bag Literals

	private boolean isImplicitSetConversion(CollectionLiteralExp cl) {
		boolean result = false;
		if (cl instanceof EModelElement) {
			EAnnotation implicitSetConversionAnnotation = ((EModelElement) cl)
				.getEAnnotation(AbstractOCLAnalyzer.OCL_ANNOTATIONS_URI);
			if (implicitSetConversionAnnotation != null) {
				String implicitSetConversionDetail = implicitSetConversionAnnotation
					.getDetails().get(AbstractOCLAnalyzer.IMPLICIT_SET_CONVERSION);
				if (implicitSetConversionDetail != null &&
						Boolean.valueOf(implicitSetConversionDetail)) {
					result = true;
				}
			}
		}
		return result;
	}

	// private static inner class for lazy lists over an integer range
	private static final class IntegerRangeList
		extends AbstractList {

//		public IntegerRangeList() {
//			super();
//		}

		public IntegerRangeList(int first, int last) {
			super();
			this.first = first;
			this.last = last;
		}

//		public int getFirst() {
//			return first;
//		}

//		public int getLast() {
//			return last;
//		}

		@Override
        public int size() {
			return last - first + 1;
		}

		@Override
        public Integer get(int index) {
			if (index < 0 || index >= size()) {
				String message = OCLMessages.bind(
						OCLMessages.IndexOutOfRange_ERROR_,
						new Object[] {
								Integer.toString(index),
								Integer.toString(first),
								Integer.toString(last)});
				IllegalArgumentException error = new IllegalArgumentException(
					message);
				OCLPlugin.throwing(getClass(), "get", error);//$NON-NLS-1$
				throw error;
			}
			return new Integer(first + index);
		}

		@Override
        public Iterator iterator() {
			// local iterator class that provides
			// hasNext() and next() methods appropriate
			// for this range set
			class IntegerRangeIterator
				implements Iterator {

				public IntegerRangeIterator() {
					curr = first;
					initialized = false;
				}

				public Integer next() {
					if (!initialized) {
						curr = first - 1;
						initialized = true;
					}
					if (hasNext()) {
                        return new Integer(++curr);
                    }
					throw new NoSuchElementException();
				}

				public boolean hasNext() {
					return (curr < last) || !initialized;
				}

				public void remove() {
					throw new UnsupportedOperationException();
				}

				private int curr;

				private boolean initialized;
			}

			return new IntegerRangeIterator();
		}

		private int first;

		private int last;
	}

	/**
	 * Callback for a TupleLiteralExp visit.
	 * 
	 * @param tl
	 *            tuple literal expression
	 * @return String
	 */
	@Override
    public Object visitTupleLiteralExp(TupleLiteralExp tl) {
		C type = tl.getType();
		List> tp = tl.getPart();
		
		Map propertyValues = new HashMap();
		
		for (TupleLiteralPart part : tp) {
			// Set the tuple field with the value of the init expression
			propertyValues.put(part.getAttribute(), part.accept(getVisitor()));
		}

		return getEvaluationEnvironment().createTuple(type, propertyValues);

	}
	
	@Override
    public Object visitTupleLiteralPart(TupleLiteralPart tp) {
		return tp.getValue().accept(getVisitor());
	}
} //EvaluationVisitorImpl