Ptidej-v5.2-master/POM Tests/data/ArgoUML_0.28/src/org/argouml/profile/internal/ocl/EvaluateExpression.java
// $Id: EvaluateExpression.java 16439 2008-12-24 02:38:21Z maurelio1234 $
// 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 syntacally 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).booleanValue() == false) {
                    val = false;
                } else if (right != null && right instanceof Boolean && ((Boolean)right).booleanValue() == false) {
                    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).booleanValue() == true) {
                    val = true;
                } else if (right != null && right instanceof Boolean && ((Boolean)right).booleanValue() == true) {
                    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);
        }
        {

            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();
    }

}
