[505805] Missing validation message on sequence calls

Bug: 505805
Change-Id: I7adeafe71adce3f1a9925f15563aa47244740adc
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/ServicesValidationResult.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/ServicesValidationResult.java
index 07799f4..c32ad25 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/ServicesValidationResult.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/runtime/impl/ServicesValidationResult.java
@@ -33,6 +33,11 @@
 public class ServicesValidationResult {
 
 	/**
+	 * The current line separator which will be used by the tooling in order to compute the description.
+	 */
+	private static final String LS = System.getProperty("line.separator");
+
+	/**
 	 * The {@link IReadOnlyQueryEnvironment}.
 	 */
 	private final IReadOnlyQueryEnvironment queryEnvironment;
@@ -122,14 +127,12 @@
 			for (Set<IType> resultTypes : map.values()) {
 				Set<IType> flattenedTypes = new LinkedHashSet<IType>();
 				for (IType resultType : resultTypes) {
-					if (!(resultType instanceof NothingType)) {
-						// flatten
-						if (resultType instanceof ICollectionType) {
-							flattenedTypes.add(new SetType(queryEnvironment, ((ICollectionType)resultType)
-									.getCollectionType()));
-						} else {
-							flattenedTypes.add(new SetType(queryEnvironment, resultType));
-						}
+					// flatten
+					if (resultType instanceof ICollectionType) {
+						flattenedTypes.add(new SetType(queryEnvironment, ((ICollectionType)resultType)
+								.getCollectionType()));
+					} else {
+						flattenedTypes.add(new SetType(queryEnvironment, resultType));
 					}
 				}
 				resultTypes.clear();
@@ -146,14 +149,12 @@
 			for (Set<IType> resultTypes : map.values()) {
 				Set<IType> flattenedTypes = new LinkedHashSet<IType>();
 				for (IType resultType : resultTypes) {
-					if (!(resultType instanceof NothingType)) {
-						// flatten
-						if (resultType instanceof ICollectionType) {
-							flattenedTypes.add(new SequenceType(queryEnvironment,
-									((ICollectionType)resultType).getCollectionType()));
-						} else {
-							flattenedTypes.add(new SequenceType(queryEnvironment, resultType));
-						}
+					// flatten
+					if (resultType instanceof ICollectionType) {
+						flattenedTypes.add(new SequenceType(queryEnvironment, ((ICollectionType)resultType)
+								.getCollectionType()));
+					} else {
+						flattenedTypes.add(new SequenceType(queryEnvironment, resultType));
 					}
 				}
 				resultTypes.clear();
@@ -170,16 +171,57 @@
 	public Set<IType> getResultingTypes() {
 		final Set<IType> result = new LinkedHashSet<IType>();
 
+		final Set<IType> aggregated = new LinkedHashSet<IType>();
 		if (!typesPerService.isEmpty()) {
 			for (Entry<IService, Map<List<IType>, Set<IType>>> entry : typesPerService.entrySet()) {
 				final IService service = entry.getKey();
 				final Map<List<IType>, Set<IType>> types = entry.getValue();
 				Set<IType> validatedTypes = service.validateAllType(validationServices, queryEnvironment,
 						types);
-				result.addAll(validatedTypes);
+				aggregated.addAll(validatedTypes);
 			}
 		} else {
-			result.addAll(serviceNotFoundMessages.values());
+			aggregated.addAll(serviceNotFoundMessages.values());
+		}
+
+		final StringBuilder builderSequenceNothing = new StringBuilder();
+		final StringBuilder builderSetNothing = new StringBuilder();
+		final StringBuilder builderNothing = new StringBuilder();
+		for (IType type : aggregated) {
+			if (type instanceof ICollectionType) {
+				if (((ICollectionType)type).getCollectionType() instanceof NothingType) {
+					final NothingType collectionType = (NothingType)((ICollectionType)type)
+							.getCollectionType();
+					if (type instanceof SequenceType) {
+						builderSequenceNothing.append(collectionType.getMessage());
+						builderSequenceNothing.append(LS);
+					} else if (type instanceof SetType) {
+						builderSetNothing.append(collectionType.getMessage());
+						builderSetNothing.append(LS);
+					} else {
+						throw new IllegalStateException("new collection type ?");
+					}
+				} else {
+					result.add(type);
+				}
+			} else if (type instanceof NothingType) {
+				builderNothing.append(((NothingType)type).getMessage());
+				builderNothing.append(LS);
+			} else {
+				result.add(type);
+			}
+		}
+
+		if (builderSequenceNothing.length() != 0) {
+			result.add(new SequenceType(queryEnvironment, new NothingType(builderSequenceNothing.substring(0,
+					builderSequenceNothing.length() - 1))));
+		}
+		if (builderSetNothing.length() != 0) {
+			result.add(new SetType(queryEnvironment, new NothingType(builderSetNothing.substring(0,
+					builderSetNothing.length() - 1))));
+		}
+		if (builderNothing.length() != 0) {
+			result.add(new NothingType(builderNothing.substring(0, builderNothing.length() - 1)));
 		}
 
 		return result;
diff --git a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/services/AnyServices.java b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/services/AnyServices.java
index 5a1ea08..35467a9 100644
--- a/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/services/AnyServices.java
+++ b/query/plugins/org.eclipse.acceleo.query/src/org/eclipse/acceleo/query/services/AnyServices.java
@@ -18,6 +18,8 @@
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 import org.eclipse.acceleo.annotations.api.documentation.Documentation;
@@ -35,7 +37,11 @@
 import org.eclipse.acceleo.query.runtime.impl.ValidationServices;
 import org.eclipse.acceleo.query.validation.type.ClassType;
 import org.eclipse.acceleo.query.validation.type.EClassifierType;
+import org.eclipse.acceleo.query.validation.type.ICollectionType;
 import org.eclipse.acceleo.query.validation.type.IType;
+import org.eclipse.acceleo.query.validation.type.NothingType;
+import org.eclipse.acceleo.query.validation.type.SequenceType;
+import org.eclipse.acceleo.query.validation.type.SetType;
 import org.eclipse.emf.common.util.Enumerator;
 import org.eclipse.emf.ecore.EClass;
 import org.eclipse.emf.ecore.EClassifier;
@@ -399,6 +405,7 @@
 	}
 
 	private static class OCLAsTypeService extends FilterService {
+
 		public OCLAsTypeService(Method publicMethod, Object serviceInstance) {
 			super(publicMethod, serviceInstance);
 		}
@@ -422,17 +429,58 @@
 					result.add(services.nothing("Unknown type %s", "null"));
 				}
 			} else {
-				if (receiverType instanceof EClassifierType && !environment.getEPackageProvider()
-						.isRegistered(((EClassifierType)receiverType).getType())) {
+				if (receiverType instanceof EClassifierType
+						&& !environment.getEPackageProvider().isRegistered(
+								((EClassifierType)receiverType).getType())) {
 					result.add(services.nothing("%s is not registered within the current environment.",
 							receiverType));
-				} else if (filterType instanceof EClassifierType && !environment.getEPackageProvider()
-						.isRegistered(((EClassifierType)filterType).getType())) {
+				} else if (filterType instanceof EClassifierType
+						&& !environment.getEPackageProvider().isRegistered(
+								((EClassifierType)filterType).getType())) {
 					result.add(services.nothing("%s is not registered within the current environment.",
 							filterType));
 				} else {
-					result.add(services.nothing("%s is not compatible with type %s", receiverType,
-							filterType));
+					result.add(services
+							.nothing("%s is not compatible with type %s", receiverType, filterType));
+				}
+			}
+
+			return result;
+		}
+
+		@Override
+		public Set<IType> validateAllType(ValidationServices services,
+				IReadOnlyQueryEnvironment queryEnvironment, Map<List<IType>, Set<IType>> allTypes) {
+			final Set<IType> result = new LinkedHashSet<IType>();
+			final StringBuilder builder = new StringBuilder();
+
+			for (Entry<List<IType>, Set<IType>> entry : allTypes.entrySet()) {
+				for (IType possibleType : entry.getValue()) {
+					final IType rawType;
+					if (possibleType instanceof ICollectionType) {
+						rawType = ((ICollectionType)possibleType).getCollectionType();
+					} else {
+						rawType = possibleType;
+					}
+					if (rawType instanceof NothingType) {
+						builder.append("\n");
+						builder.append(((NothingType)rawType).getMessage());
+					} else {
+						result.add(possibleType);
+					}
+				}
+			}
+
+			if (result.isEmpty()) {
+				final IType resultType = allTypes.entrySet().iterator().next().getValue().iterator().next();
+				final NothingType nothing = services.nothing("Nothing will be left after calling %s:"
+						+ builder.toString(), getName());
+				if (resultType instanceof SequenceType) {
+					result.add(new SequenceType(queryEnvironment, nothing));
+				} else if (resultType instanceof SetType) {
+					result.add(new SetType(queryEnvironment, nothing));
+				} else {
+					result.add(nothing);
 				}
 			}
 
diff --git a/query/tests/org.eclipse.acceleo.query.tests/src/org/eclipse/acceleo/query/parser/tests/ValidationTest.java b/query/tests/org.eclipse.acceleo.query.tests/src/org/eclipse/acceleo/query/parser/tests/ValidationTest.java
index 9ea70f9..7418577 100644
--- a/query/tests/org.eclipse.acceleo.query.tests/src/org/eclipse/acceleo/query/parser/tests/ValidationTest.java
+++ b/query/tests/org.eclipse.acceleo.query.tests/src/org/eclipse/acceleo/query/parser/tests/ValidationTest.java
@@ -186,6 +186,214 @@
 	}
 
 	@Test
+	public void flattenSequenceNothingFeatureNotExistingAccessTest() {
+		final IValidationResult validationResult = engine.validate("self->asSequence().notExisting",
+				variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof NothingType);
+		assertEquals("Feature notExisting not found in EClass EClass", ((NothingType)((SequenceType)type)
+				.getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(validationResult.getMessages().get(0), ValidationMessageLevel.INFO,
+				"Empty collection: Feature notExisting not found in EClass EClass", 18, 30);
+	}
+
+	@Test
+	public void flattenSetNothingFeatureNotExistingAccessTest() {
+		final IValidationResult validationResult = engine
+				.validate("self->asSet().notExisting", variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SetType);
+		assertTrue(((SetType)type).getCollectionType() instanceof NothingType);
+		assertEquals("Feature notExisting not found in EClass EClass", ((NothingType)((SetType)type)
+				.getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(validationResult.getMessages().get(0), ValidationMessageLevel.INFO,
+				"Empty collection: Feature notExisting not found in EClass EClass", 13, 25);
+	}
+
+	@Test
+	public void flattenSequenceNothingOclAsTypeTest() {
+		final IValidationResult validationResult = engine.validate(
+				"self->asSequence().oclAsType(ecore::EPackage)", variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof NothingType);
+		assertEquals(
+				"Nothing will be left after calling oclAsType:\nEClassifier=EClass is not compatible with type EClassifierLiteral=EPackage",
+				((NothingType)((SequenceType)type).getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(
+				validationResult.getMessages().get(0),
+				ValidationMessageLevel.INFO,
+				"Empty collection: Nothing will be left after calling oclAsType:\nEClassifier=EClass is not compatible with type EClassifierLiteral=EPackage",
+				18, 45);
+	}
+
+	@Test
+	public void flattenSetNothingOclAsTypeTest() {
+		final IValidationResult validationResult = engine.validate(
+				"self->asSequence().oclAsType(ecore::EPackage)", variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof NothingType);
+		assertEquals(
+				"Nothing will be left after calling oclAsType:\nEClassifier=EClass is not compatible with type EClassifierLiteral=EPackage",
+				((NothingType)((SequenceType)type).getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(
+				validationResult.getMessages().get(0),
+				ValidationMessageLevel.INFO,
+				"Empty collection: Nothing will be left after calling oclAsType:\nEClassifier=EClass is not compatible with type EClassifierLiteral=EPackage",
+				18, 45);
+	}
+
+	@Test
+	public void flattenSequenceNothingMultipleTypesNotExistingOnAnyAccessTest() {
+		final Set<IType> selfTypes = new LinkedHashSet<IType>();
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEClass()));
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEPackage()));
+		variableTypes.put("multiType", selfTypes);
+
+		final IValidationResult validationResult = engine.validate("multiType->asSequence().notExisting",
+				variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof NothingType);
+		assertEquals(
+				"Feature notExisting not found in EClass EClass\nFeature notExisting not found in EClass EPackage",
+				((NothingType)((SequenceType)type).getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(
+				validationResult.getMessages().get(0),
+				ValidationMessageLevel.INFO,
+				"Empty collection: Feature notExisting not found in EClass EClass\nFeature notExisting not found in EClass EPackage",
+				23, 35);
+	}
+
+	@Test
+	public void flattenSetNothingMultipleTypesNotExistingOnAnyAccessTest() {
+		final Set<IType> selfTypes = new LinkedHashSet<IType>();
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEClass()));
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEPackage()));
+		variableTypes.put("multiType", selfTypes);
+
+		final IValidationResult validationResult = engine.validate("multiType->asSet().notExisting",
+				variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(1, possibleTypes.size());
+		final IType type = possibleTypes.iterator().next();
+		assertTrue(type instanceof SetType);
+		assertTrue(((SetType)type).getCollectionType() instanceof NothingType);
+		assertEquals(
+				"Feature notExisting not found in EClass EClass\nFeature notExisting not found in EClass EPackage",
+				((NothingType)((SetType)type).getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(
+				validationResult.getMessages().get(0),
+				ValidationMessageLevel.INFO,
+				"Empty collection: Feature notExisting not found in EClass EClass\nFeature notExisting not found in EClass EPackage",
+				18, 30);
+	}
+
+	@Test
+	public void flattenSequenceNothingMultipleTypesNotExistingOnOneAccessTest() {
+		final Set<IType> selfTypes = new LinkedHashSet<IType>();
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEClass()));
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEPackage()));
+		variableTypes.put("multiType", selfTypes);
+
+		final IValidationResult validationResult = engine.validate("multiType->asSequence().eClassifiers",
+				variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(2, possibleTypes.size());
+		final Iterator<IType> it = possibleTypes.iterator();
+		IType type = it.next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof EClassifierType);
+		assertEquals(EcorePackage.eINSTANCE.getEClassifier(), ((EClassifierType)((SequenceType)type)
+				.getCollectionType()).getType());
+		type = it.next();
+		assertTrue(type instanceof SequenceType);
+		assertTrue(((SequenceType)type).getCollectionType() instanceof NothingType);
+		assertEquals("Feature eClassifiers not found in EClass EClass", ((NothingType)((SequenceType)type)
+				.getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(validationResult.getMessages().get(0), ValidationMessageLevel.INFO,
+				"Empty collection: Feature eClassifiers not found in EClass EClass", 23, 36);
+	}
+
+	@Test
+	public void flattenSetNothingMultipleTypesNotExistingOnOneAccessTest() {
+		final Set<IType> selfTypes = new LinkedHashSet<IType>();
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEClass()));
+		selfTypes.add(new EClassifierType(queryEnvironment, EcorePackage.eINSTANCE.getEPackage()));
+		variableTypes.put("multiType", selfTypes);
+
+		final IValidationResult validationResult = engine.validate("multiType->asSet().eClassifiers",
+				variableTypes);
+		final Expression ast = validationResult.getAstResult().getAst();
+
+		Set<IType> possibleTypes = validationResult.getPossibleTypes(ast);
+		assertEquals(0, stripNothingTypes(possibleTypes).size());
+		assertEquals(2, possibleTypes.size());
+		final Iterator<IType> it = possibleTypes.iterator();
+		IType type = it.next();
+		assertTrue(type instanceof SetType);
+		assertTrue(((SetType)type).getCollectionType() instanceof EClassifierType);
+		assertEquals(EcorePackage.eINSTANCE.getEClassifier(), ((EClassifierType)((SetType)type)
+				.getCollectionType()).getType());
+		type = it.next();
+		assertTrue(type instanceof SetType);
+		assertTrue(((SetType)type).getCollectionType() instanceof NothingType);
+		assertEquals("Feature eClassifiers not found in EClass EClass", ((NothingType)((SetType)type)
+				.getCollectionType()).getMessage());
+
+		assertEquals(1, validationResult.getMessages().size());
+		assertValidationMessage(validationResult.getMessages().get(0), ValidationMessageLevel.INFO,
+				"Empty collection: Feature eClassifiers not found in EClass EClass", 18, 31);
+	}
+
+	@Test
 	public void intliteralTest() {
 		final IValidationResult validationResult = engine.validate("2", variableTypes);
 		final Expression ast = validationResult.getAstResult().getAst();