/* $Id: EvaluateExpression.java 17836 2010-01-12 19:06:55Z linus $ ***************************************************************************** * Copyright (c) 2009 Contributors - see below * 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: * euluis ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 2008 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.profile.internal.ocl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.argouml.profile.internal.ocl.uml14.Bag; import org.argouml.profile.internal.ocl.uml14.HashBag; import org.argouml.profile.internal.ocl.uml14.OclEnumLiteral; import tudresden.ocl.parser.analysis.DepthFirstAdapter; import tudresden.ocl.parser.node.AActualParameterList; import tudresden.ocl.parser.node.AAdditiveExpressionTail; import tudresden.ocl.parser.node.AAndLogicalOperator; import tudresden.ocl.parser.node.ABooleanLiteral; import tudresden.ocl.parser.node.ADeclaratorTail; import tudresden.ocl.parser.node.ADivMultiplyOperator; import tudresden.ocl.parser.node.AEmptyFeatureCallParameters; import tudresden.ocl.parser.node.AEnumLiteral; import tudresden.ocl.parser.node.AEqualRelationalOperator; import tudresden.ocl.parser.node.AExpressionListOrRange; import tudresden.ocl.parser.node.AFeatureCall; import tudresden.ocl.parser.node.AFeatureCallParameters; import tudresden.ocl.parser.node.AFeaturePrimaryExpression; import tudresden.ocl.parser.node.AGtRelationalOperator; import tudresden.ocl.parser.node.AGteqRelationalOperator; import tudresden.ocl.parser.node.AIfExpression; import tudresden.ocl.parser.node.AImpliesLogicalOperator; import tudresden.ocl.parser.node.AIntegerLiteral; import tudresden.ocl.parser.node.AIterateDeclarator; import tudresden.ocl.parser.node.ALetExpression; import tudresden.ocl.parser.node.AListExpressionListOrRangeTail; import tudresden.ocl.parser.node.ALiteralCollection; import tudresden.ocl.parser.node.ALogicalExpressionTail; import tudresden.ocl.parser.node.ALtRelationalOperator; import tudresden.ocl.parser.node.ALteqRelationalOperator; import tudresden.ocl.parser.node.AMinusAddOperator; import tudresden.ocl.parser.node.AMinusUnaryOperator; import tudresden.ocl.parser.node.AMultMultiplyOperator; import tudresden.ocl.parser.node.AMultiplicativeExpressionTail; import tudresden.ocl.parser.node.ANEqualRelationalOperator; import tudresden.ocl.parser.node.ANotUnaryOperator; import tudresden.ocl.parser.node.AOrLogicalOperator; import tudresden.ocl.parser.node.APlusAddOperator; import tudresden.ocl.parser.node.APostfixExpressionTail; import tudresden.ocl.parser.node.ARealLiteral; import tudresden.ocl.parser.node.ARelationalExpressionTail; import tudresden.ocl.parser.node.AStandardDeclarator; import tudresden.ocl.parser.node.AStringLiteral; import tudresden.ocl.parser.node.AUnaryUnaryExpression; import tudresden.ocl.parser.node.AXorLogicalOperator; import tudresden.ocl.parser.node.PActualParameterListTail; import tudresden.ocl.parser.node.PDeclaratorTail; import tudresden.ocl.parser.node.PExpression; import tudresden.ocl.parser.node.PExpressionListTail; /** * Evaluates OCL expressions, this class should not depend on the model * subsystem. This adapter assumes the ocl expression is syntatically and * semantically correct. * * @author maurelio1234 */ public class EvaluateExpression extends DepthFirstAdapter { /** * Logger. */ private static final Logger LOG = Logger .getLogger(EvaluateExpression.class); /** * The Variable Table */ private Map<String, Object> vt = null; /** * Keeps the return value of the visitor */ private Object val = null; /** * Keeps a forward propagated value */ private Object fwd = null; /** * The model interpreter */ private ModelInterpreter interp = null; /** * Constructor * * @param modelElement self * @param mi model interpreter */ public EvaluateExpression(Object modelElement, ModelInterpreter mi) { reset(modelElement, mi); } /** * Constructor * * @param variableTable the variable table * @param modelInterpreter model interpreter */ public EvaluateExpression(Map<String, Object> variableTable, ModelInterpreter modelInterpreter) { reset(variableTable, modelInterpreter); } /** * Resets the internal state of this adapter * * @param mi the model interpreter * @param element the model element */ public void reset(Object element, ModelInterpreter mi) { vt = new HashMap<String, Object>(); vt.put("self", element); reset(vt, mi); } /** * @param newVT the variable table * @param mi the model interpreter */ public void reset(Map<String, Object> newVT, ModelInterpreter mi) { this.interp = mi; this.val = null; this.fwd = null; this.vt = newVT; } /** * @return is the invariant ok? */ public Object getValue() { return val; } /** Interpreter Code * */ /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAIfExpression(tudresden.ocl.parser.node.AIfExpression) */ public void caseAIfExpression(AIfExpression node) { boolean test = false; boolean ret = false; inAIfExpression(node); if (node.getTIf() != null) { node.getTIf().apply(this); } if (node.getIfBranch() != null) { node.getIfBranch().apply(this); test = asBoolean(val, node.getIfBranch()); val = null; } if (node.getTThen() != null) { node.getTThen().apply(this); } if (node.getThenBranch() != null) { node.getThenBranch().apply(this); if (test) { ret = asBoolean(val, node.getThenBranch()); val = null; } } if (node.getTElse() != null) { node.getTElse().apply(this); } if (node.getElseBranch() != null) { node.getElseBranch().apply(this); if (!test) { ret = asBoolean(val, node.getThenBranch()); val = null; } } if (node.getEndif() != null) { node.getEndif().apply(this); } val = ret; outAIfExpression(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseALogicalExpressionTail(tudresden.ocl.parser.node.ALogicalExpressionTail) */ public void caseALogicalExpressionTail(ALogicalExpressionTail node) { Object left = val; val = null; inALogicalExpressionTail(node); if (node.getLogicalOperator() != null) { node.getLogicalOperator().apply(this); } if (node.getRelationalExpression() != null) { node.getRelationalExpression().apply(this); } Object op = node.getLogicalOperator(); Object right = val; val = null; if (op != null) { if (op instanceof AAndLogicalOperator) { if (left != null && left instanceof Boolean && !((Boolean) left)) { val = false; } else if (right != null && right instanceof Boolean && !((Boolean) right)) { val = false; } else { val = asBoolean(left, node) && asBoolean(right, node); } } else if (op instanceof AImpliesLogicalOperator) { val = !asBoolean(left, node) || asBoolean(right, node); } else if (op instanceof AOrLogicalOperator) { if (left != null && left instanceof Boolean && ((Boolean) left)) { val = true; } else if (right != null && right instanceof Boolean && ((Boolean) right)) { val = true; } else { val = asBoolean(left, node) || asBoolean(right, node); } } else if (op instanceof AXorLogicalOperator) { val = !asBoolean(left, node) ^ asBoolean(right, node); } else { error(node); } } else { error(node); } outALogicalExpressionTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseARelationalExpressionTail(tudresden.ocl.parser.node.ARelationalExpressionTail) */ public void caseARelationalExpressionTail(ARelationalExpressionTail node) { Object left = val; val = null; inARelationalExpressionTail(node); if (node.getRelationalOperator() != null) { node.getRelationalOperator().apply(this); } if (node.getAdditiveExpression() != null) { node.getAdditiveExpression().apply(this); } Object op = node.getRelationalOperator(); Object right = val; val = null; if (left != null && op != null && right != null) { if (op instanceof AEqualRelationalOperator) { val = left.equals(right); } else if (op instanceof AGteqRelationalOperator) { val = asInteger(left, node) >= asInteger(right, node); } else if (op instanceof AGtRelationalOperator) { val = asInteger(left, node) > asInteger(right, node); } else if (op instanceof ALteqRelationalOperator) { val = asInteger(left, node) <= asInteger(right, node); } else if (op instanceof ALtRelationalOperator) { val = asInteger(left, node) < asInteger(right, node); } else if (op instanceof ANEqualRelationalOperator) { val = !left.equals(right); } else { error(node); } } else { // if one side is null, compare with the equality operator if (op instanceof AEqualRelationalOperator) { val = (left == right); } else if (op instanceof ANEqualRelationalOperator) { val = (left != right); } else { error(node); val = null; } } outARelationalExpressionTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAAdditiveExpressionTail(tudresden.ocl.parser.node.AAdditiveExpressionTail) */ @Override public void caseAAdditiveExpressionTail(AAdditiveExpressionTail node) { Object left = val; val = null; inAAdditiveExpressionTail(node); if (node.getAddOperator() != null) { node.getAddOperator().apply(this); } if (node.getMultiplicativeExpression() != null) { node.getMultiplicativeExpression().apply(this); } Object op = node.getAddOperator(); Object right = val; val = null; if (left != null && op != null && right != null) { if (op instanceof AMinusAddOperator) { val = asInteger(left, node) - asInteger(right, node); } else if (op instanceof APlusAddOperator) { val = asInteger(left, node) + asInteger(right, node); } else { error(node); } } else { error(node); } outAAdditiveExpressionTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAMultiplicativeExpressionTail(tudresden.ocl.parser.node.AMultiplicativeExpressionTail) */ public void caseAMultiplicativeExpressionTail( AMultiplicativeExpressionTail node) { Object left = val; val = null; inAMultiplicativeExpressionTail(node); if (node.getMultiplyOperator() != null) { node.getMultiplyOperator().apply(this); } if (node.getUnaryExpression() != null) { node.getUnaryExpression().apply(this); } Object op = node.getMultiplyOperator(); Object right = val; val = null; if (left != null && op != null && right != null) { if (op instanceof ADivMultiplyOperator) { val = asInteger(left, node) / asInteger(right, node); } else if (op instanceof AMultMultiplyOperator) { val = asInteger(left, node) * asInteger(right, node); } else { error(node); } } else { error(node); } outAMultiplicativeExpressionTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAUnaryUnaryExpression(tudresden.ocl.parser.node.AUnaryUnaryExpression) */ public void caseAUnaryUnaryExpression(AUnaryUnaryExpression node) { inAUnaryUnaryExpression(node); if (node.getUnaryOperator() != null) { node.getUnaryOperator().apply(this); } if (node.getPostfixExpression() != null) { val = null; node.getPostfixExpression().apply(this); } Object op = node.getUnaryOperator(); if (op instanceof AMinusUnaryOperator) { val = -asInteger(val, node); } else if (op instanceof ANotUnaryOperator) { val = !asBoolean(val, node); } outAUnaryUnaryExpression(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAPostfixExpressionTail(tudresden.ocl.parser.node.APostfixExpressionTail) */ public void caseAPostfixExpressionTail(APostfixExpressionTail node) { inAPostfixExpressionTail(node); if (node.getPostfixExpressionTailBegin() != null) { node.getPostfixExpressionTailBegin().apply(this); } if (node.getFeatureCall() != null) { fwd = node.getPostfixExpressionTailBegin(); node.getFeatureCall().apply(this); // XXX: hypotheses for AFeatureCall: fwd = op, val = head } outAPostfixExpressionTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAFeaturePrimaryExpression(tudresden.ocl.parser.node.AFeaturePrimaryExpression) */ @Override public void caseAFeaturePrimaryExpression(AFeaturePrimaryExpression node) { Object subject = val; Object feature = null; List parameters = null; inAFeaturePrimaryExpression(node); if (node.getPathName() != null) { // TODO support other name kinds node.getPathName().apply(this); feature = node.getPathName().toString().trim(); } if (node.getTimeExpression() != null) { // hypotheses no time expression (only invariants) node.getTimeExpression().apply(this); } if (node.getQualifiers() != null) { // XXX: hypotheses no qualifiers (I don't know) node.getQualifiers().apply(this); } if (node.getFeatureCallParameters() != null) { val = null; node.getFeatureCallParameters().apply(this); parameters = (List) val; } if (subject == null) { val = vt.get(feature); if (val == null) { val = this.interp.getBuiltInSymbol(feature.toString().trim()); } } else { val = runFeatureCall(subject, feature, fwd, parameters); } outAFeaturePrimaryExpression(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outAEmptyFeatureCallParameters(tudresden.ocl.parser.node.AEmptyFeatureCallParameters) */ @Override public void outAEmptyFeatureCallParameters(AEmptyFeatureCallParameters node) { val = new ArrayList(); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAFeatureCallParameters(tudresden.ocl.parser.node.AFeatureCallParameters) */ @SuppressWarnings("unchecked") @Override public void caseAFeatureCallParameters(AFeatureCallParameters node) { inAFeatureCallParameters(node); if (node.getLPar() != null) { node.getLPar().apply(this); } boolean hasDeclarator = false; if (node.getDeclarator() != null) { node.getDeclarator().apply(this); hasDeclarator = true; } if (node.getActualParameterList() != null) { List<String> vars = null; if (hasDeclarator) { List ret = new ArrayList(); vars = (List) val; final PExpression exp = ((AActualParameterList) node .getActualParameterList()).getExpression(); /* * For a iterator call we should provide: (a) the variables (b) * the expression to be evaluated on each step (c) the * lambda-evaluator to evaluate it */ ret.add(vars); ret.add(exp); ret.add(new LambdaEvaluator() { /** * @see org.argouml.profile.internal.ocl.LambdaEvaluator#evaluate(java.util.Map, * java.lang.Object) */ public Object evaluate(Map<String, Object> vti, Object expi) { Object state = EvaluateExpression.this.saveState(); EvaluateExpression.this.vt = vti; EvaluateExpression.this.val = null; EvaluateExpression.this.fwd = null; ((PExpression) expi).apply(EvaluateExpression.this); Object reti = EvaluateExpression.this.val; EvaluateExpression.this.loadState(state); return reti; } }); val = ret; } else { node.getActualParameterList().apply(this); } } if (node.getRPar() != null) { node.getRPar().apply(this); } outAFeatureCallParameters(node); } @SuppressWarnings("unchecked") private void loadState(Object state) { Object[] stateArr = (Object[]) state; this.vt = (Map<String, Object>) stateArr[0]; this.val = stateArr[1]; this.fwd = stateArr[2]; } private Object saveState() { return new Object[] {vt, val, fwd}; } /* * @param node * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAStandardDeclarator(tudresden.ocl.parser.node.AStandardDeclarator) */ @Override public void caseAStandardDeclarator(AStandardDeclarator node) { inAStandardDeclarator(node); List<String> vars = new ArrayList<String>(); if (node.getName() != null) { node.getName().apply(this); vars.add(node.getName().toString().trim()); } { Object temp[] = node.getDeclaratorTail().toArray(); for (int i = 0; i < temp.length; i++) { ((PDeclaratorTail) temp[i]).apply(this); vars.add(((ADeclaratorTail) temp[i]).getName() .toString().trim()); } val = vars; } if (node.getDeclaratorTypeDeclaration() != null) { // TODO check types! node.getDeclaratorTypeDeclaration().apply(this); } if (node.getBar() != null) { node.getBar().apply(this); } outAStandardDeclarator(node); } @Override public void outAIterateDeclarator(AIterateDeclarator node) { // TODO support iterate declarator val = new ArrayList<String>(); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseALetExpression(tudresden.ocl.parser.node.ALetExpression) */ @Override public void caseALetExpression(ALetExpression node) { // TODO support nested let expressions ! Object name = null; Object value = null; inALetExpression(node); if (node.getTLet() != null) { node.getTLet().apply(this); } if (node.getName() != null) { node.getName().apply(this); name = node.getName().toString().trim(); } if (node.getLetExpressionTypeDeclaration() != null) { // TODO: check type! node.getLetExpressionTypeDeclaration().apply(this); } if (node.getEqual() != null) { node.getEqual().apply(this); } if (node.getExpression() != null) { node.getExpression().apply(this); value = val; } if (node.getTIn() != null) { node.getTIn().apply(this); } vt.put(("" + name).trim(), value); val = null; outALetExpression(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outAStringLiteral(tudresden.ocl.parser.node.AStringLiteral) */ public void outAStringLiteral(AStringLiteral node) { String text = node.getStringLit().getText(); val = text.substring(1, text.length() - 1); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outARealLiteral(tudresden.ocl.parser.node.ARealLiteral) */ public void outARealLiteral(ARealLiteral node) { // TODO support real types val = (int) Double.parseDouble(node.getReal().getText()); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outAIntegerLiteral(tudresden.ocl.parser.node.AIntegerLiteral) */ public void outAIntegerLiteral(AIntegerLiteral node) { val = Integer.parseInt(node.getInt().getText()); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outABooleanLiteral(tudresden.ocl.parser.node.ABooleanLiteral) */ public void outABooleanLiteral(ABooleanLiteral node) { val = Boolean.parseBoolean(node.getBool().getText()); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#outAEnumLiteral(tudresden.ocl.parser.node.AEnumLiteral) */ public void outAEnumLiteral(AEnumLiteral node) { val = new OclEnumLiteral(node.getName().toString().trim()); defaultOut(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseALiteralCollection(tudresden.ocl.parser.node.ALiteralCollection) */ @SuppressWarnings("unchecked") public void caseALiteralCollection(ALiteralCollection node) { Collection<Object> col = null; inALiteralCollection(node); if (node.getCollectionKind() != null) { node.getCollectionKind().apply(this); String kind = node.getCollectionKind().toString().trim(); if (kind.equalsIgnoreCase("Set")) { col = new HashSet<Object>(); } else if (kind.equalsIgnoreCase("Sequence")) { col = new ArrayList<Object>(); } else if (kind.equalsIgnoreCase("Bag")) { col = new HashBag<Object>(); } } if (node.getLBrace() != null) { node.getLBrace().apply(this); } if (node.getExpressionListOrRange() != null) { val = null; node.getExpressionListOrRange().apply(this); col.addAll((Collection<Object>) val); } if (node.getRBrace() != null) { node.getRBrace().apply(this); } val = col; outALiteralCollection(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAExpressionListOrRange(tudresden.ocl.parser.node.AExpressionListOrRange) */ @Override public void caseAExpressionListOrRange(AExpressionListOrRange node) { List ret = new ArrayList(); inAExpressionListOrRange(node); if (node.getExpression() != null) { val = null; node.getExpression().apply(this); ret.add(val); } if (node.getExpressionListOrRangeTail() != null) { val = null; node.getExpressionListOrRangeTail().apply(this); ret.addAll((Collection) val); } val = ret; outAExpressionListOrRange(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAListExpressionListOrRangeTail(tudresden.ocl.parser.node.AListExpressionListOrRangeTail) */ @Override public void caseAListExpressionListOrRangeTail( AListExpressionListOrRangeTail node) { // TODO support other kinds of tail inAListExpressionListOrRangeTail(node); { List ret = new ArrayList(); Object temp[] = node.getExpressionListTail().toArray(); for (int i = 0; i < temp.length; i++) { val = null; ((PExpressionListTail) temp[i]).apply(this); ret.add(val); } val = ret; } outAListExpressionListOrRangeTail(node); } /* * @see tudresden.ocl.parser.analysis.DepthFirstAdapter#caseAFeatureCall(tudresden.ocl.parser.node.AFeatureCall) */ @Override public void caseAFeatureCall(AFeatureCall node) { Object subject = val; Object feature = null; Object type = fwd; List parameters = null; inAFeatureCall(node); if (node.getPathName() != null) { // TODO support other name kinds node.getPathName().apply(this); feature = node.getPathName().toString().trim(); } if (node.getTimeExpression() != null) { // XXX hypothesis: no time expression (inv) node.getTimeExpression().apply(this); } if (node.getQualifiers() != null) { // TODO understand qualifiers node.getQualifiers().apply(this); } if (node.getFeatureCallParameters() != null) { val = null; node.getFeatureCallParameters().apply(this); parameters = (List) val; } else { parameters = new ArrayList(); } val = runFeatureCall(subject, feature, type, parameters); outAFeatureCall(node); } @Override public void caseAActualParameterList(AActualParameterList node) { List list = new ArrayList(); inAActualParameterList(node); if (node.getExpression() != null) { val = null; node.getExpression().apply(this); list.add(val); } { // TODO: why is this inside a block? Forgotten else branch?!? // Question by euluis @ 2009-08-16. Object temp[] = node.getActualParameterListTail().toArray(); for (int i = 0; i < temp.length; i++) { val = null; ((PActualParameterListTail) temp[i]).apply(this); list.add(val); } } val = list; outAActualParameterList(node); } /** HELPER METHODS * */ private boolean asBoolean(Object value, Object node) { if (value instanceof Boolean) { return (Boolean) value; } else { errorNotType(node, "Boolean", false); return false; } } private int asInteger(Object value, Object node) { if (value instanceof Integer) { return (Integer) value; } else { errorNotType(node, "integer", 0); return 0; } } private Object runFeatureCall(Object subject, Object feature, Object type, List parameters) { // LOG.debug("OCL FEATURE CALL: " + subject + ""+ type +""+ feature + "" // + parameters); if (parameters == null) { parameters = new ArrayList<Object>(); } // XXX this should be done in CollectionsModelInterpreter // but it can't trigger another invokeFeature... if ((subject instanceof Collection) && type.toString().trim().equals(".")) { Collection col = (Collection) subject; Bag res = new HashBag(); for (Object obj : col) { res.add(interp.invokeFeature(vt, obj, feature.toString().trim(), ".", parameters.toArray())); } return res; } else { return interp.invokeFeature(vt, subject, feature.toString().trim(), type.toString().trim(), parameters.toArray()); } } /** Error Handling * */ private void errorNotType(Object node, String type, Object dft) { LOG.error("OCL does not evaluate to a " + type + " expression!! Exp: " + node + " Val: " + val); val = dft; // TODO: We need a specific exception type here. throw new RuntimeException(); } private void error(Object node) { LOG.error("Unknown error processing OCL exp!! Exp: " + node + " Val: " + val); val = null; // TODO: We need a specific exception type here. throw new RuntimeException(); } }