package com.sap.runlet.interpreter.signatureimplementations; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import behavioral.actions.Statement; import com.sap.runlet.abstractinterpreter.Interpreter; import com.sap.runlet.abstractinterpreter.objects.MultiValuedObject; import com.sap.runlet.abstractinterpreter.objects.RunletObject; import com.sap.runlet.abstractinterpreter.util.Bag; import com.sap.runlet.abstractinterpreter.util.HashBag; import com.sap.runlet.interpreter.RunletInterpreter; import com.sap.runlet.interpreter.RunletStackFrame; import com.sap.runlet.interpreter.statements.ReturnInterpreter; import data.classes.Association; import data.classes.AssociationEnd; import data.classes.ClassTypeDefinition; import data.classes.FunctionSignature; import data.classes.FunctionSignatureImplementation; import data.classes.NativeImpl; import data.classes.Parameter; import data.classes.SapClass; import data.classes.Signature; import data.classes.SignatureImplementation; import data.classes.TypeDefinition; import dataaccess.analytics.CellSet; import dataaccess.analytics.Dimension; import dataaccess.expressions.Expression; /** * A {@link CellSet} is considered an implementation of a * {@link FunctionSignature} that takes as arguments the dimension coordinates * and delivers as result the aggregated cell value at that coordinate. The implementation * therefore assumes that the dimension parameters have been pushed onto the stack. They * are identified by position. * * @author Axel Uhl D043530 * */ public class CellSetInterpreter implements Interpreter<CellSet, SapClass, TypeDefinition, ClassTypeDefinition, Association, AssociationEnd, Statement, Expression, SignatureImplementation, RunletStackFrame, NativeImpl, RunletInterpreter> { private CellSet cellSet; public CellSetInterpreter(CellSet cellSet) { this.cellSet = cellSet; } /** * There is no caching in here. For each cell requested this will re-compute all dimension values * and do the aggregation for the cell requested. Just barely enough to demonstrate the intended * semantics of this kind of expression. Don't expect useful performance! * * TODO consider multi-valued coordinates and return a multi-dimensional structure according to the coordinate's multiplicities */ @Override public RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> evaluate(RunletInterpreter interpreter) throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Signature cellSetFunctionSignature = cellSet.getImplementedSignature(); Bag<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> matchingFactsValues = new HashBag<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>>(); Map<Dimension, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> valuesForDimension = new HashMap<Dimension, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>>(); Iterator<Parameter> paramIter = cellSetFunctionSignature.getInput().iterator(); RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> facts = interpreter.getCallstack().peek().getValue(paramIter.next()); Iterator<Dimension> dimensionIter = cellSet.getDimensions().iterator(); while (paramIter.hasNext()) { RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> atValue = interpreter.getCallstack().peek().getValue(paramIter.next()); valuesForDimension.put(dimensionIter.next(), atValue); } FunctionSignature valueFunction = cellSet.getValueFunction(); FunctionSignatureImplementation valueFunctionImpl = null; if (valueFunction != null) { valueFunctionImpl = cellSet.getValueFunction().getImplementation(); } // TODO correct nested multiplicity handling, removing the "flatten()" call again for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> fact : facts.flatten()) { boolean factMatchesAllDimensions = true; for (Dimension dimension : cellSet.getDimensions()) { FunctionSignatureImplementation impl = dimension.getCharacteristicFunction().getImplementation(); RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> dimensionValueForFact = interpreter.convert( callSingleArgBlock(interpreter, fact, impl), dimension.getCharacteristicFunction().getOutput()); if (!valuesForDimension.get(dimension).logicallyEquals(dimensionValueForFact)) { factMatchesAllDimensions = false; break; } } if (factMatchesAllDimensions) { RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> value; if (valueFunctionImpl != null) { value = callSingleArgBlock(interpreter, fact, valueFunctionImpl); } else { value = fact; } matchingFactsValues.add(value); } } TypeDefinition valuesType = cellSet.getAggregationFunction().getInput().get(0).getType(); MultiValuedObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> valuesToAggregate = new MultiValuedObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>( valuesType, matchingFactsValues, valuesType.isOrdered(), valuesType.isUnique()); RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> result = callSingleArgBlock(interpreter, valuesToAggregate, cellSet.getAggregationFunction().getImplementation()); // Wrap the whole thing into a ReturnResult because that's what's expected from // a signature implementation: ReturnInterpreter.ReturnResult returnResult = new ReturnInterpreter.ReturnResult(result); return returnResult; } private RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> callSingleArgBlock(RunletInterpreter interpreter, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> arg, FunctionSignatureImplementation impl) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { RunletStackFrame stackFrame = new RunletStackFrame(); stackFrame.enterValue(impl.getImplementedSignature().getInput().get(0), arg); interpreter.push(stackFrame); RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> dimensionValueForFact = ((ReturnInterpreter.ReturnResult) interpreter.evaluateSignatureImplementation(impl)).getResult(); interpreter.pop(); return dimensionValueForFact; } }