[432885] - Prototype implicit collect for resolve expressions
diff --git a/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/ast/parser/QvtOperationalVisitorCS.java b/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/ast/parser/QvtOperationalVisitorCS.java
index 0772cf0..7fe973f 100644
--- a/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/ast/parser/QvtOperationalVisitorCS.java
+++ b/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/ast/parser/QvtOperationalVisitorCS.java
@@ -7,8 +7,8 @@
*
* Contributors:
* Borland Software Corporation - initial API and implementation
- * Christopher Gerking - bugs 302594, 310991, 289982, 391289, 425634, 427237,
- * 433585, 433919
+ * Christopher Gerking - bugs 302594, 310991, 289982, 391289, 425634, 432885,
+ * 427237, 433585, 433919
* Alex Paperno - bugs 272869, 268636, 404647, 414363, 414363, 401521,
* 419299, 414619, 403440, 415024, 420970, 413391,
* 424584, 424869
@@ -243,6 +243,7 @@
import org.eclipse.ocl.types.OrderedSetType;
import org.eclipse.ocl.types.SequenceType;
import org.eclipse.ocl.types.TypeType;
+import org.eclipse.ocl.util.CollectionUtil;
import org.eclipse.ocl.util.OCLStandardLibraryUtil;
import org.eclipse.ocl.util.OCLUtil;
import org.eclipse.ocl.util.TypeUtil;
@@ -1535,6 +1536,154 @@
return result;
}
+
+ /**
+ * Creates an implicit <code>collect</code> iterator expression for a
+ * resolve on a collection-type source expression.
+ *
+ * @param source
+ * the resolve source expression
+ * @param resolve
+ * the resolve expression
+ * @param env
+ * the current environment
+ *
+ * @return the collect expression
+ */
+ protected IteratorExp<EClassifier, EParameter> createImplicitCollect(OCLExpression<EClassifier> source,
+ ResolveExp resolve,
+ Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env,
+ CSTNode cstNode) {
+
+ @SuppressWarnings("unchecked")
+ EClassifier sourceElementType = ((CollectionType<EClassifier, EOperation>) source.getType())
+ .getElementType();
+
+ IteratorExp<EClassifier, EParameter> result = oclFactory.createIteratorExp();
+ initASTMapping(env, result, cstNode, null);
+ Variable<EClassifier, EParameter> itervar = genVariableDeclaration(cstNode,
+ "modelPropertyCallCS", env,//$NON-NLS-1$
+ null, sourceElementType, null, false, true, false);
+
+ List<Variable<EClassifier, EParameter>> iters = result.getIterator();
+ iters.add(itervar);
+ result.setBody(resolve);
+ result.setName("collect");//$NON-NLS-1$
+ VariableExp<EClassifier, EParameter> vexp = createVariableExp(env, cstNode, itervar);
+
+ /*
+ * adjust the source variable for the body expression to be the newly
+ * generated implicit iterator variable
+ */
+ resolve.setSource(vexp);
+
+
+ // the overall start and end positions are the property positions
+ resolve.setStartPosition(resolve
+ .getPropertyStartPosition());
+ resolve.setEndPosition(resolve.getPropertyEndPosition());
+
+ result.setSource(source);
+
+ // the result of a collect() is flattened, so if the value
+ // that we are collecting is a Collection type, the resulting
+ // type must be flattened by taking its element type (recursively)
+ EClassifier bodyType = resolve.getType();
+ if (bodyType instanceof CollectionType<?, ?>) {
+ @SuppressWarnings("unchecked")
+ CollectionType<EClassifier, EOperation> ct = (CollectionType<EClassifier, EOperation>) bodyType;
+
+ bodyType = CollectionUtil.getFlattenedElementType(ct);
+ }
+
+ if (source.getType() instanceof SequenceType<?, ?>
+ || source.getType() instanceof OrderedSetType<?, ?>
+ || source.getType() instanceof ListType) {
+ EClassifier c = resolveCollectionType(
+ env,
+ CollectionKind.SEQUENCE_LITERAL,
+ bodyType);
+ result.setType(c);
+ } else {
+ EClassifier c = resolveCollectionType(
+ env,
+ CollectionKind.BAG_LITERAL,
+ bodyType);
+ result.setType(c);
+ }
+
+ env.deleteElement(itervar.getName());
+
+ return result;
+ }
+
+ /**
+ * Creates an implicit <code>xcollect</code> iterator expression for a
+ * resolve on a collection-type source expression.
+ *
+ * @param source the resolve source expression
+ * @param resolve the resolve expression
+ * @param fEnv the current environment
+ *
+ * @return the xcollect expression
+ * @throws TerminateException
+ */
+ protected ImperativeIterateExp createImplicitXCollect(
+ OCLExpression<EClassifier> source,
+ ResolveExp resolve,
+ Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env,
+ CSTNode cstNode) {
+
+ @SuppressWarnings("unchecked")
+ EClassifier sourceElementType = ((CollectionType<EClassifier, EOperation>) source.getType())
+ .getElementType();
+
+ ImperativeIterateExp result = ImperativeOCLFactory.eINSTANCE.createImperativeIterateExp();
+ initASTMapping(env, result, cstNode);
+ Variable<EClassifier, EParameter> itervar =
+ genVariableDeclaration(cstNode, "modelPropertyCallCS", env,//$NON-NLS-1$
+ null, sourceElementType, null, false, true, false);
+
+ List<Variable<EClassifier, EParameter>> iters = result.getIterator();
+ iters.add(itervar);
+ result.setBody(resolve);
+ result.setName("xcollect");//$NON-NLS-1$
+ VariableExp<EClassifier, EParameter> vexp = oclFactory.createVariableExp();
+ initASTMapping(env, vexp, cstNode);
+ vexp.setType(itervar.getType());
+ vexp.setReferredVariable(itervar);
+ vexp.setName(itervar.getName());
+
+ /* adjust the source variable for the body expression to be the
+ newly generated implicit iterator variable */
+ resolve.setSource(vexp);
+
+ // the overall start and end positions are the property positions
+ resolve.setStartPosition(resolve.getPropertyStartPosition());
+ resolve.setEndPosition(resolve.getPropertyEndPosition());
+
+ result.setSource(source);
+
+ EClassifier bodyType = resolve.getType();
+
+ if (source.getType() instanceof SequenceType<?, ?>
+ || source.getType() instanceof OrderedSetType<?, ?>
+ || source.getType() instanceof ListType) {
+ EClassifier c = resolveCollectionType(
+ env,
+ CollectionKind.SEQUENCE_LITERAL,
+ bodyType);
+ result.setType(c);
+ } else {
+ EClassifier c = resolveCollectionType(
+ env,
+ CollectionKind.BAG_LITERAL,
+ bodyType);
+ result.setType(c);
+ }
+
+ return result;
+ }
private boolean isArrowAccessToCollection(CallExpCS callExpCS, OCLExpression<EClassifier> source) {
if (source == null) {
@@ -4775,7 +4924,21 @@
DeprecatedImplicitSourceCallHelper.validateCallExp(resolveExpCS, resolveExp, env);
- return resolveExp;
+ OCLExpression<EClassifier> source = resolveExp.getSource();
+ org.eclipse.ocl.ecore.OCLExpression result = resolveExp;
+
+ // FIXME - we should ask MDT OCL for a support to handle this in a better way
+ /*
+ * If the source type is a collection, then need there is an implicit COLLECT
+ * or imperative COLLECT operator.
+ */
+ if ((source != null) && (source.getType() instanceof CollectionType<?,?>)) {
+ result = isArrowAccessToCollection(resolveExpCS, source) ?
+ createImplicitXCollect(source, resolveExp, env, resolveExpCS)
+ : (org.eclipse.ocl.ecore.OCLExpression) createImplicitCollect(source, resolveExp, env, resolveExpCS);
+ }
+
+ return result;
}
private void validateResolveExp(ResolveExp resolveExp,
diff --git a/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/library/QvtResolveUtil.java b/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/library/QvtResolveUtil.java
index 206eb07..9e11da3 100644
--- a/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/library/QvtResolveUtil.java
+++ b/plugins/org.eclipse.m2m.qvt.oml/src/org/eclipse/m2m/internal/qvt/oml/library/QvtResolveUtil.java
@@ -8,7 +8,7 @@
*
* Contributors:
* Borland Software Corporation - initial API and implementation
- * Christopher Gerking - bugs 358709, 433292
+ * Christopher Gerking - bugs 358709, 432885, 433292
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.library;
@@ -35,6 +35,8 @@
import org.eclipse.m2m.internal.qvt.oml.trace.TraceRecord;
import org.eclipse.m2m.internal.qvt.oml.trace.VarParameterValue;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssignExp;
+import org.eclipse.ocl.ecore.CallExp;
+import org.eclipse.ocl.ecore.IteratorExp;
import org.eclipse.ocl.ecore.OperationCallExp;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.expressions.PropertyCallExp;
@@ -90,6 +92,11 @@
return true;
}
+ if(rightValue instanceof IteratorExp) {
+ IteratorExp iterator = (IteratorExp) rightValue;
+ return isLateResolveImplicitCollect(iterator);
+ }
+
if(rightValue instanceof OperationCallExp) {
OperationCallExp operCall = (OperationCallExp) rightValue;
return isLateResolveResultConversion(operCall);
@@ -122,9 +129,9 @@
* @param resolveExp
* a resolve expression
* @return the assignment receiving the late resolve result, if the resolve
- * expression is deferred and its result is assigned to it directly
- * or by using a collection type conversion. Otherwise,
- * <code>null</code> is returned.
+ * expression is deferred and its result is assigned to it either directly,
+ * by using a collection type conversion, or by using an implicit collect.
+ * Otherwise, <code>null</code> is returned.
*/
public static AssignExp getDeferredAssignmentFor(ResolveExp resolveExp) {
if(!resolveExp.isIsDeferred()) {
@@ -137,17 +144,20 @@
if(assignExp.getLeft() instanceof PropertyCallExp) {
return assignExp;
}
- } else if(resolveContainer instanceof OperationCallExp) {
- OperationCallExp operCall = (OperationCallExp) resolveContainer;
- if(!isLateResolveResultConversion(operCall)) {
+ } else if(resolveContainer instanceof CallExp) {
+ CallExp call = (CallExp) resolveContainer;
+ if(call instanceof OperationCallExp && !isLateResolveResultConversion((OperationCallExp) call)) {
+ return null;
+ }
+ if(call instanceof IteratorExp && !isLateResolveImplicitCollect((IteratorExp) call)) {
return null;
}
// lookup the closest outer assignment node
- EObject parent = operCall.eContainer();
+ EObject parent = call.eContainer();
while(parent != null) {
if(parent instanceof AssignExp) {
AssignExp assignExp = (AssignExp)parent;
- if(assignExp.getValue().indexOf(operCall) >= 0) {
+ if(call != assignExp.getLeft()) {
return assignExp;
}
}
@@ -156,21 +166,41 @@
}
return null;
- }
-
+ }
+
/**
* Indicate whether a operation call expression is supported collection
* type conversion for late resolve results.
*/
private static boolean isLateResolveResultConversion(OperationCallExp operCall) {
+
+ if (!isCollectionConversionCall(operCall)) {
+ return false;
+ }
+
if(operCall.getSource() instanceof ResolveExp) {
ResolveExp resolveExp = (ResolveExp) operCall.getSource();
- if(!resolveExp.isIsDeferred()) {
- return false;
- }
-
- return isCollectionConversionCall(operCall) && resolveExp.getType() instanceof CollectionType<?, ?>;
+ return isCollectionTypeLateResolve(resolveExp);
}
+
+ if(operCall.getSource() instanceof IteratorExp) {
+ IteratorExp iteratorExp = (IteratorExp) operCall.getSource();
+ return isLateResolveImplicitCollect(iteratorExp);
+ }
+
+ return false;
+ }
+
+ private static boolean isCollectionTypeLateResolve(ResolveExp resolveExp) {
+ return resolveExp.isIsDeferred() && resolveExp.getType() instanceof CollectionType<?, ?>;
+ }
+
+ private static boolean isLateResolveImplicitCollect(IteratorExp iterator) {
+ if(iterator.getBody() instanceof ResolveExp) {
+ ResolveExp resolveExp = (ResolveExp) iterator.getBody();
+ return isCollectionTypeLateResolve(resolveExp);
+ }
+
return false;
}
@@ -337,22 +367,7 @@
}
}
else {
- // Remark: Should be removed as soon as implict collect is support on resolve too
- if(declaredSourceType instanceof CollectionType<?, ?> && source instanceof Collection<?>) {
- Collection<?> srcCol = (Collection<?>)source;
- for (Object nextSrc : srcCol) {
- EList<TraceRecord> nextPart = source2RecordMap.get(nextSrc);
-
- if(nextPart != null) {
- if(result == null) {
- result = new BasicEList<TraceRecord>();
- }
- result.addAll(nextPart);
- }
- }
- } else {
- result = source2RecordMap.get(source);
- }
+ result = source2RecordMap.get(source);
}
return (result != null) ? Collections.unmodifiableList(result) : Collections.<TraceRecord>emptyList();
diff --git a/tests/org.eclipse.m2m.tests.qvt.oml/parserTestData/models/lateresolve_many/lateresolve_many.qvto b/tests/org.eclipse.m2m.tests.qvt.oml/parserTestData/models/lateresolve_many/lateresolve_many.qvto
index 4875c2c..eda0b74 100644
--- a/tests/org.eclipse.m2m.tests.qvt.oml/parserTestData/models/lateresolve_many/lateresolve_many.qvto
+++ b/tests/org.eclipse.m2m.tests.qvt.oml/parserTestData/models/lateresolve_many/lateresolve_many.qvto
@@ -94,7 +94,7 @@
-- expect warn -> condition of late resolve evaluated in the end, checked it works as non-late;
t.late invresolveone() = self);
}
- )->collect(self).late resolveone(UML::Type);
+ )->collect(self).late resolveone(UML::Type)->any(true);
};
}