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