package com.sap.runlet.interpreter.expressions; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import behavioral.actions.Iterator; import behavioral.actions.Statement; import com.sap.ap.metamodel.utils.MetamodelUtils; import com.sap.runlet.abstractinterpreter.Interpreter; import com.sap.runlet.abstractinterpreter.objects.EmptyObject; import com.sap.runlet.abstractinterpreter.objects.MultiValuedObject; import com.sap.runlet.abstractinterpreter.objects.RunletObject; import com.sap.runlet.abstractinterpreter.util.Fraction; import com.sap.runlet.interpreter.RunletInterpreter; import com.sap.runlet.interpreter.RunletStackFrame; import com.sap.runlet.interpreter.objects.NativeObject; import data.classes.Association; import data.classes.AssociationEnd; import data.classes.ClassTypeDefinition; import data.classes.NativeImpl; import data.classes.SapClass; import data.classes.SignatureImplementation; import data.classes.TypeDefinition; import dataaccess.expressions.Expression; import dataaccess.query.Selection; /** * Evaluates the <tt>object</tt> expression. If the selection expression is of * boolean type then it will be applied as a filter predicate. If the selection * expression is of numeric type then it is assumed that the <tt>object</tt> * expression evaluates to an ordered object, and the result of the selection * expression will be iterated (it could contain more than one numeric single * object), and for each number the element of the <tt>object</tt> evaluation * result at the respective position is added to the result. This allows for * array-like access to ordered multi objects. * * @author Axel Uhl D043530 * */ public class SelectionInterpreter implements Interpreter<Selection, SapClass, TypeDefinition, ClassTypeDefinition, Association, AssociationEnd, Statement, Expression, SignatureImplementation, RunletStackFrame, NativeImpl, RunletInterpreter> { private Selection selection; public SelectionInterpreter(Selection selection) { this.selection = selection; } @Override public RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> evaluate(RunletInterpreter interpreter) throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> source = interpreter.evaluate(selection.getObject()); // add a new stack frame to place fresh "self" iterator on it that is // local to this Selection expression Collection<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> result; boolean isNumericIndex = ((ClassTypeDefinition) selection.getSelectionExpr().getType()).getClazz().equals( MetamodelUtils.findClass(interpreter.getResourceSet(), "Number")); if (isNumericIndex) { result = selectAtIndices(interpreter, source, interpreter.evaluate(selection.getSelectionExpr())); } else { result = selectMatches(interpreter, source, selection.getSelectionExpr()); } if (result.size() > 0) { return new MultiValuedObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>( selection.getType(), result, selection.getType().isOrdered(), selection.getType().isUnique()); } else { if (result.isEmpty()) { return new EmptyObject<AssociationEnd, SapClass, TypeDefinition, ClassTypeDefinition>(selection.getType(), interpreter .getModelAdapter()); } else { return result.iterator().next(); } } } @SuppressWarnings("unchecked") private Collection<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> selectAtIndices(RunletInterpreter interpreter, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> source, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> atIndices) throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { boolean uniqueResult = source.getType().isUnique() && atIndices.getType().isUnique(); Collection<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> result = RunletObject.createCollection( /* ordered */true, uniqueResult); boolean indicesAreOrdered = atIndices.getType().isOrdered(); if (indicesAreOrdered) { // in this case preserve index order in result int i = 0; Map<Integer, Set<Integer>> mapSourceIndexToResultIndices = new HashMap<Integer, Set<Integer>>(); for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> index : interpreter.evaluate(selection .getSelectionExpr())) { Fraction indexAsFraction = (Fraction) ((NativeObject) index).getNativeObject(); int indexAsInt = indexAsFraction.intValue(); Set<Integer> resultIndices = mapSourceIndexToResultIndices.get(indexAsInt); if (resultIndices == null) { resultIndices = new HashSet<Integer>(); mapSourceIndexToResultIndices.put(indexAsInt, resultIndices); } resultIndices.add(i++); } // i now holds the number of indices specified in the selection // expression RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>[] resultAsArray = (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>[]) new RunletObject<?, ?, ?>[i]; int j = 0; for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> sourceObject : source.flatten()) { Set<Integer> resultPositions = mapSourceIndexToResultIndices.get(j++); if (resultPositions != null) { for (int resultPosition : resultPositions) { resultAsArray[resultPosition] = sourceObject; } } } for (int k = 0; k < resultAsArray.length; k++) { if (resultAsArray[k] == null) { result.add(new EmptyObject<AssociationEnd, SapClass, TypeDefinition, ClassTypeDefinition>(source.getType(), interpreter .getModelAdapter())); } else { result.add(resultAsArray[k]); } } } else { // indices are unordered; preserve source order in results; but // multiple occurrences of // the same index may be possible if indices are non-unique Map<Integer, Integer> occurrencesForSourceIndex = new HashMap<Integer, Integer>(); for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> index : interpreter.evaluate(selection .getSelectionExpr())) { Fraction indexAsFraction = (Fraction) ((NativeObject) index).getNativeObject(); int indexAsInt = indexAsFraction.intValue(); Integer occurrences = occurrencesForSourceIndex.get(indexAsInt); if (occurrences == null) { occurrencesForSourceIndex.put(indexAsInt, 1); } else { occurrencesForSourceIndex.put(indexAsInt, occurrences + 1); } } int j = 0; for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> sourceObject : source.flatten()) { Integer occurrences = occurrencesForSourceIndex.get(j++); if (occurrences != null) { for (int i = 0; i < occurrences; i++) { result.add(sourceObject); } } } } return result; } private Collection<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> selectMatches(RunletInterpreter interpreter, RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> source, Expression expression) throws SecurityException, IllegalArgumentException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Collection<RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition>> result = RunletObject.createCollection(selection .getType().isOrdered(), selection.getType().isUnique()); RunletStackFrame frame = new RunletStackFrame(interpreter.getCallstack().peek()); interpreter.push(frame); try { Iterator iter = selection.getIterator(); frame.enterValue(iter, new EmptyObject<AssociationEnd, SapClass, TypeDefinition, ClassTypeDefinition>( iter.getType(), interpreter.getModelAdapter())); // create // entry in // local // stack // frame // TODO optionally parallelize because condition is guaranteed to be // side effect free for (RunletObject<AssociationEnd, TypeDefinition, ClassTypeDefinition> o : source) { frame.setValue(iter, interpreter.convert(o, iter.getType())); if ((Boolean) ((NativeObject) interpreter.evaluate(selection.getSelectionExpr())).getNativeObject()) { result.add(o); } } return result; } finally { interpreter.pop(); } } }