package com.sap.finex.interpreter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import structure.Field; import structure.FinexClass; import structure.Type; import behavior.expressions.Alias; import behavior.expressions.Expression; import behavior.expressions.FilterExpression; import behavior.expressions.NamedValueExpression; import com.sap.runlet.abstractinterpreter.objects.EmptyObject; import com.sap.runlet.abstractinterpreter.objects.MultiValuedObject; import com.sap.runlet.abstractinterpreter.objects.RunletObject; import com.sap.tc.moin.repository.shared.util.Tuple.Pair; /** * {@link Expression}s can define {@link Alias}es. When an expression is evaluated, one or more {@link RunletObject}s result, * where multiple objects are wrapped by a {@link MultiValuedObject}. For subsequent expression evaluations, the bindings of the * aliases to the objects resulting from the expression evaluation need to be recorded. This class maintains record of the alias * assignments. * <p> * * When an expression is evaluated, it may evaluate operand expressions on which the evaluation result depends. The operand * expressions in turn may define aliases for themself and/or for their direct or transitive operand expressions. When the * operands are evaluated, the alias definitions that happen during this evaluation are also recorded in an instance of this * class. * <p> * * Depending on an expression's semantics, aliases computed by one operand may be visible by other operand expressions. This then * implies an evaluation order on the operands. Vice versa, if an expression defines semantics such that one operand depends on * another operand (e.g., for a {@link FilterExpression}), the condition operand expression uses the result of the operand * expression as its implicit context, implying an evaluation order), this enables the dependent operand to use the aliases * defined by the operand it depends on.<p> * * Before an expression is evaluated, all aliases that the expression may access using {@link NamedValueExpression}s * need to be entered into the stack frame. The respective values can be obtained from an instance of this class.<p> * * @author Axel Uhl D043530 * */ public class AliasValues { private Map<Expression, Map<Pair<RunletObject<Field, Type, FinexClass>, Alias>, RunletObject<Field, Type, FinexClass>>> map = new HashMap<Expression, Map<Pair<RunletObject<Field, Type, FinexClass>, Alias>, RunletObject<Field, Type, FinexClass>>>(); private Map<Expression, Map<RunletObject<Field, Type, FinexClass>, Set<Alias>>> definedAliases = new HashMap<Expression, Map<RunletObject<Field, Type, FinexClass>, Set<Alias>>>(); public void enterAliasValue(Expression e, RunletObject<Field, Type, FinexClass> resultOfE, Alias a, RunletObject<Field, Type, FinexClass> aliasValue) { Map<Pair<RunletObject<Field, Type, FinexClass>, Alias>, RunletObject<Field, Type, FinexClass>> mapForExpression = map.get(e); if (mapForExpression == null) { mapForExpression = new HashMap<Pair<RunletObject<Field, Type, FinexClass>, Alias>, RunletObject<Field, Type, FinexClass>>(); map.put(e, mapForExpression); } mapForExpression.put(new Pair<RunletObject<Field, Type, FinexClass>, Alias>(resultOfE, a), aliasValue); Map<RunletObject<Field, Type, FinexClass>, Set<Alias>> mapForDefinedAliases = definedAliases.get(e); if (mapForDefinedAliases == null) { mapForDefinedAliases = new HashMap<RunletObject<Field, Type, FinexClass>, Set<Alias>>(); definedAliases.put(e, mapForDefinedAliases); } Set<Alias> aliasesSet = mapForDefinedAliases.get(resultOfE); if (aliasesSet == null) { aliasesSet = new HashSet<Alias>(); mapForDefinedAliases.put(resultOfE, aliasesSet); } aliasesSet.add(a); } /** * The expression <tt>e</tt> evaluates to zero, one or more objects. For each of these result objects, a different set of * alias values may be defined which may have to be entered into the stack frame for further processing in the context of a * particular result of <tt>e</tt>. This method tells for each alias defined when evaluating <tt>e</tt> (see also * {@link #getAllAliasesDefinedFor(Expression, RunletObject)}) what their values are in the context of <tt>e</tt> and the * specific evaluation result <tt>resultOfE</tt>. */ public RunletObject<Field, Type, FinexClass> getAliasValue(Expression e, RunletObject<Field, Type, FinexClass> resultOfE, Alias a) { RunletObject<Field, Type, FinexClass> result = null; Map<Pair<RunletObject<Field, Type, FinexClass>, Alias>, RunletObject<Field, Type, FinexClass>> mapForExpression = map.get(e); if (mapForExpression != null) { result = mapForExpression.get(new Pair<RunletObject<Field, Type, FinexClass>, Alias>(resultOfE, a)); } return result; } /** * To be called by an interpreter computing the value of <tt>inExpression</tt> that computed a result object * <tt>computedResult</tt> using a single object (<tt>usedSingleObject</tt>) from the operand expression <tt>fromOperand</tt>. * This announcement manipulates this object such that all aliases computed for the combination * <tt>fromOperand/usedSingleObject</tt> are considered having been computed for <tt>inExpression/computedResult</tt> as well. * In other words, the alias definitions computed by the operand are copied to the alias definitions of the expression using * the operand, for a single result object of that expression (<tt>computeResult</tt>). */ public void used(RunletObject<Field, Type, FinexClass> usedSingleObject, Expression fromOperand, RunletObject<Field, Type, FinexClass> computedResult, Expression inExpression) { for (Alias a : getAllAliasesDefinedFor(fromOperand, usedSingleObject)) { addAliasValue(inExpression, computedResult, a, getAliasValue(fromOperand, usedSingleObject, a)); } } /** * To be called by an interpreter computing the value of <tt>inExpression</tt> that computed a result object * <tt>computedResult</tt> using all objects (<tt>usedCompletObject</tt>) computed by the operand expression * <tt>fromOperand</tt>. This announcement manipulates this object such that all aliases computed for the combination * <tt>fromOperand/o</tt> for all <tt>o</tt> from the flattened <tt>usedCompleteObject</tt> are considered having been * computed for <tt>inExpression/computedResult</tt> as well. In other words, the alias definitions computed by the operand * are aggregated for all objects resulting from the operand and copied to the alias definitions of the expression using the * operand, for a single result object of that expression (<tt>computeResult</tt>).<p> * * This method would typically be used for aggregation expressions that somehow aggregate all results from * the operands. */ public void usedAllOf(RunletObject<Field, Type, FinexClass> usedCompletObject, Expression fromOperand, RunletObject<Field, Type, FinexClass> computedResult, Expression inExpression) { for (RunletObject<Field, Type, FinexClass> o : usedCompletObject.flatten()) { used(o, fromOperand, computedResult, inExpression); } } /** * If a value for the alias <tt>a</tt> is not yet defined for <tt>e/resultOfE</tt>, the * <tt>aliasValue</tt> will simply be stored for this combination such that a call to * {@link #getAliasValue}<tt>(inExpression, computeResult, a)</tt> will return <tt>aliasValue</tt>. * If there already is an entry for <tt>a</tt> for <tt>inExpression/computeResult</tt>, a new * multi-valued object will be constructed which contains the flattened elements of the previous * value of {@link #getAliasValue}<tt>(inExpression, computeResult, a)</tt> to which the * flattened <tt>aliasValue</tt> objects will be appended. This multi-valued object will then be * entered for <tt>e/resultOfE/a</tt>. */ private void addAliasValue(Expression e, RunletObject<Field, Type, FinexClass> resultOfE, Alias a, RunletObject<Field, Type, FinexClass> aliasValue) { RunletObject<Field, Type, FinexClass> oldAliasValue = getAliasValue(e, resultOfE, a); enterAliasValue(e, resultOfE, a, extendValue(oldAliasValue, aliasValue)); } /** * If <tt>oldAliasValue</tt> is <tt>null</tt> or empty, <tt>aliasValue</tt> is the result. If <tt>aliasValue</tt> * is <tt>null</tt> or empty, <tt>oldAliasValue</tt> will result. If both are <tt>null</tt> or empty, an * {@link EmptyObject} will result. Otherwise, a new {@link MultiValuedObject} will result to which all * flattened objects of <tt>oldAliasValue</tt> and <tt>aliasValue</tt> are added. */ private RunletObject<Field, Type, FinexClass> extendValue( RunletObject<Field, Type, FinexClass> oldAliasValue, RunletObject<Field, Type, FinexClass> aliasValue) { if (oldAliasValue == null) { return aliasValue; } else if (aliasValue == null) { return oldAliasValue; } else if (aliasValue.size() == 0) { return oldAliasValue; } else if (oldAliasValue.size() == 0) { return aliasValue; } else { return combineIntoMultiObject(oldAliasValue, aliasValue); } } private RunletObject<Field, Type, FinexClass> combineIntoMultiObject( RunletObject<Field, Type, FinexClass> oldAliasValue, RunletObject<Field, Type, FinexClass> aliasValue) { Collection<RunletObject<Field, Type, FinexClass>> concatenated = new ArrayList<RunletObject<Field, Type, FinexClass>>(2); concatenated.add(oldAliasValue); concatenated.add(aliasValue); return new MultiValuedObject<Field, Type, FinexClass>(oldAliasValue.getType(), new ConcatRunletObjectIterable(concatenated), /* ordered */ false, /* unique */ false); } /** * Removes all alias entries stored for <tt>e</tt>. After this method returns, all queries * of the kind {@link #getAliasValue}<tt>(e, o, a)</tt> for any <tt>o</tt> and <tt>a</tt> will * return <tt>null</tt>.<p> * * Call this method for each operand expression <tt>e1<tt> of some other expression <tt>e2</tt> whose evaluation * has finished and before returning <tt>e2</tt>'s value from the interpreter. Expressions using <tt>e2</tt> * as operand won't be able to see <tt>e1</tt> and therefore won't ask for alias values in the scope of * <tt>e1</tt>. Therefore, those alias associations are then no longer needed. */ public void forget(Expression e) { map.remove(e); definedAliases.remove(e); } /** * Determines all aliases for which values have been defined by evaluating <tt>e</tt> to the * particular result object <tt>resultOfE</tt>. Always returns a non-<tt>null</tt> but potentially empty set. */ public Collection<Alias> getAllAliasesDefinedFor(Expression e, RunletObject<Field, Type, FinexClass> resultOfE) { Set<Alias> result = Collections.emptySet(); Map<RunletObject<Field, Type, FinexClass>, Set<Alias>> aliasMap = definedAliases.get(e); if (aliasMap != null) { Set<Alias> set = aliasMap.get(resultOfE); if (set != null) { result = Collections.unmodifiableSet(set); } } return result; } }