/*
 *
 * This source file belongs to the MDD4SOA suite developed at the Institute of 
 * Computer Science, Programming and Software Engineering, of the Ludwig-Maximilians-
 * Universität München, Germany. 
 * 
 * http://www.pst.ifi.lmu.de/
 * http://www.mdd4soa.eu/
 *
 * See the enclosed LICENSE file for licensing information.
 *
 */
package de.lmu.ifi.pst.mdd4soa.transform.impl.iom;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.eclipse.emf.common.util.Enumerator;

import de.lmu.ifi.pst.mdd4soa.transform.exception.TransformationException;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.BooleanType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.CollectionType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.IntegerType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.NullType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.PrimitiveType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.RealType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.StringType;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.Type;
import de.lmu.ifi.pst.mdd4soa.transform.impl.iom.type.TypeFactory;

/**
 * <!-- begin-user-doc --> A representation of the literals of the enumeration '
 * <em><b>Operation Type</b></em>', and utility methods for working with them.
 * <!-- end-user-doc -->
 * @see de.lmu.ifi.pst.mdd4soa.transform.impl.iom.IOMPackage#getOperationType()
 * @model
 * @generated
 */
public enum OperationType implements Enumerator {
	/**
	 * The '<em><b>ADDITION</b></em>' literal object.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #ADDITION_VALUE
	 * @generated
	 * @ordered
	 */
	ADDITION(0, "ADDITION", "ADDITION"),

	/**
	 * The '<em><b>SUBTRACTION</b></em>' literal object.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #SUBTRACTION_VALUE
	 * @generated
	 * @ordered
	 */
	SUBTRACTION(0, "SUBTRACTION", "SUBTRACTION"),

	/**
	 * The '<em><b>MULTIPLICATION</b></em>' literal object.
	 * <!-- begin-user-doc
	 * --> <!-- end-user-doc -->
	 * @see #MULTIPLICATION_VALUE
	 * @generated
	 * @ordered
	 */
	MULTIPLICATION(0, "MULTIPLICATION", "MULTIPLICATION"),

	/**
	 * The '<em><b>DIVISION</b></em>' literal object.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #DIVISION_VALUE
	 * @generated
	 * @ordered
	 */
	DIVISION(0, "DIVISION", "DIVISION"), /**
	 * The '<em><b>MODULO</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #MODULO_VALUE
	 * @generated
	 * @ordered
	 */
	MODULO(0, "MODULO", "MODULO"), /**
	 * The '<em><b>AND</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #AND_VALUE
	 * @generated
	 * @ordered
	 */
	AND(0, "AND", "AND"), /**
	 * The '<em><b>OR</b></em>' literal object. <!--
	 * begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @see #OR_VALUE
	 * @generated
	 * @ordered
	 */
	OR(0, "OR", "OR"), /**
	 * The '<em><b>NOT</b></em>' literal object. <!--
	 * begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @see #NOT_VALUE
	 * @generated
	 * @ordered
	 */
	NOT(0, "NOT", "NOT"), /**
	 * The '<em><b>NEGATION</b></em>' literal object. <!--
	 * begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @see #NEGATION_VALUE
	 * @generated
	 * @ordered
	 */
	NEGATION(0, "NEGATION", "NEGATION"), /**
	 * The '<em><b>EQUALS</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #EQUALS_VALUE
	 * @generated
	 * @ordered
	 */
	EQUALS(0, "EQUALS", "EQUALS"), /**
	 * The '<em><b>GREATER</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #GREATER_VALUE
	 * @generated
	 * @ordered
	 */
	GREATER(0, "GREATER", "GREATER"), /**
	 * The '<em><b>GREATER EQUAL</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #GREATER_EQUAL_VALUE
	 * @generated
	 * @ordered
	 */
	GREATER_EQUAL(0, "GREATER_EQUAL", "GREATER_EQUAL"), /**
	 * The '<em><b>LESS</b></em>' literal object.
	 * <!-- begin-user-doc --> <!--
	 * end-user-doc -->
	 * @see #LESS_VALUE
	 * @generated
	 * @ordered
	 */
	LESS(0, "LESS", "LESS"), /**
	 * The '<em><b>LESS EQUAL</b></em>' literal object.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #LESS_EQUAL_VALUE
	 * @generated
	 * @ordered
	 */
	LESS_EQUAL(0, "LESS_EQUAL", "LESS_EQUAL"), /**
	 * The '<em><b>NOT EQUALS</b></em>' literal object.
	 * <!-- begin-user-doc --> <!--
	 * end-user-doc -->
	 * @see #NOT_EQUALS_VALUE
	 * @generated
	 * @ordered
	 */
	NOT_EQUALS(0, "NOT_EQUALS", "NOT_EQUALS");

	/**
	 * The '<em><b>ADDITION</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>ADDITION</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #ADDITION
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int ADDITION_VALUE= 0;

	/**
	 * The '<em><b>SUBTRACTION</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>SUBTRACTION</b></em>' literal object isn't
	 * clear, there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #SUBTRACTION
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int SUBTRACTION_VALUE= 0;

	/**
	 * The '<em><b>MULTIPLICATION</b></em>' literal value.
	 * <!-- begin-user-doc
	 * -->
	 * <p>
	 * If the meaning of '<em><b>MULTIPLICATION</b></em>' literal object isn't
	 * clear, there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #MULTIPLICATION
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int MULTIPLICATION_VALUE= 0;

	/**
	 * The '<em><b>DIVISION</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>DIVISION</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #DIVISION
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int DIVISION_VALUE= 0;

	/**
	 * The '<em><b>MODULO</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>MODULO</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #MODULO
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int MODULO_VALUE= 0;

	/**
	 * The '<em><b>AND</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>AND</b></em>' literal object isn't clear, there
	 * really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #AND
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int AND_VALUE= 0;

	/**
	 * The '<em><b>OR</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>OR</b></em>' literal object isn't clear, there
	 * really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #OR
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int OR_VALUE= 0;

	/**
	 * The '<em><b>NOT</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>NOT</b></em>' literal object isn't clear, there
	 * really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #NOT
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int NOT_VALUE= 0;

	/**
	 * The '<em><b>NEGATION</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>NEGATION</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #NEGATION
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int NEGATION_VALUE= 0;

	/**
	 * The '<em><b>EQUALS</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>EQUALS</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #EQUALS
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int EQUALS_VALUE= 0;

	/**
	 * The '<em><b>GREATER</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>GREATER</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #GREATER
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int GREATER_VALUE= 0;

	/**
	 * The '<em><b>GREATER EQUAL</b></em>' literal value.
	 * <!-- begin-user-doc
	 * -->
	 * <p>
	 * If the meaning of '<em><b>GREATER EQUAL</b></em>' literal object isn't
	 * clear, there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #GREATER_EQUAL
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int GREATER_EQUAL_VALUE= 0;

	/**
	 * The '<em><b>LESS</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>LESS</b></em>' literal object isn't clear,
	 * there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #LESS
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int LESS_VALUE= 0;

	/**
	 * The '<em><b>LESS EQUAL</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>LESS EQUAL</b></em>' literal object isn't
	 * clear, there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #LESS_EQUAL
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int LESS_EQUAL_VALUE= 0;

	/**
	 * The '<em><b>NOT EQUALS</b></em>' literal value.
	 * <!-- begin-user-doc -->
	 * <p>
	 * If the meaning of '<em><b>NOT EQUALS</b></em>' literal object isn't
	 * clear, there really should be more of a description here...
	 * </p>
	 * <!-- end-user-doc -->
	 * @see #NOT_EQUALS
	 * @model
	 * @generated
	 * @ordered
	 */
	public static final int NOT_EQUALS_VALUE= 0;

	/**
	 * An array of all the '<em><b>Operation Type</b></em>' enumerators. <!--
	 * begin-user-doc --> <!-- end-user-doc -->
	 * 
	 * @generated
	 */
	private static final OperationType[] VALUES_ARRAY= new OperationType[] {
			ADDITION,
			SUBTRACTION,
			MULTIPLICATION,
			DIVISION,
			MODULO,
			AND,
			OR,
			NOT,
			NEGATION,
			EQUALS,
			GREATER,
			GREATER_EQUAL,
			LESS,
			LESS_EQUAL,
			NOT_EQUALS,
		};

	/**
	 * A public read-only list of all the '<em><b>Operation Type</b></em>' enumerators.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public static final List<OperationType> VALUES= Collections.unmodifiableList(Arrays.asList(VALUES_ARRAY));

	/**
	 * Returns the '<em><b>Operation Type</b></em>' literal with the specified literal value.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public static OperationType get(String literal) {
		for (int i = 0; i < VALUES_ARRAY.length; ++i) {
			OperationType result = VALUES_ARRAY[i];
			if (result.toString().equals(literal)) {
				return result;
			}
		}
		return null;
	}

	/**
	 * Returns the '<em><b>Operation Type</b></em>' literal with the specified name.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public static OperationType getByName(String name) {
		for (int i = 0; i < VALUES_ARRAY.length; ++i) {
			OperationType result = VALUES_ARRAY[i];
			if (result.getName().equals(name)) {
				return result;
			}
		}
		return null;
	}

	/**
	 * Returns the '<em><b>Operation Type</b></em>' literal with the specified integer value.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public static OperationType get(int value) {
		switch (value) {
			case ADDITION_VALUE: return ADDITION;
		}
		return null;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	private final int value;

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	private final String name;

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	private final String literal;

	/**
	 * Only this class can construct instances.
	 * <!-- begin-user-doc --> <!--
	 * end-user-doc -->
	 * @generated
	 */
	private OperationType(int value, String name, String literal) {
		this.value = value;
		this.name = name;
		this.literal = literal;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public int getValue() {
	  return value;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public String getName() {
	  return name;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public String getLiteral() {
	  return literal;
	}

	/**
	 * Returns the literal value of the enumerator, which is its string representation.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		return literal;
	}

	/**
	 * 
	 * @param operand1
	 * @param operand2
	 * @return
	 * @throws TransformationException
	 */
	private Type getBinaryResultType(Type operand1, Type operand2) throws TransformationException {
		boolean collectionOperand1= getIsCollection(operand1);
		boolean collectionOperand2= getIsCollection(operand2);

		if (operand1 instanceof NullType) {
			throw new TransformationException("You cannot use null as first argument in operations.");
		}
		if (operand2 instanceof NullType && ! (this.equals(EQUALS) || this.equals(NOT_EQUALS))) {
			throw new TransformationException("You can NOT use \"null\" in operation " + getName() + ". Only comparison with == or != is supported.");
		}

		// All operations except NOT and NEGATION have two operands
		switch (this) {
			case ADDITION:
				// we can alter the element, which is appended, but do not want
				// to alter the entire collection
				if (collectionOperand1) {
					// we have done the type checks before
					// see #validateSecondOperandType
					return operand1;
				} else if (!collectionOperand1 && !collectionOperand2) {
					// two simple types get added:
					// one of them is a string:
					if (operand1 instanceof StringType) {
						return operand1;
					} else if (operand2 instanceof StringType) {
						return operand2;
					}
					// none of them are string:
					if (operand1 instanceof BooleanType || operand2 instanceof BooleanType) {
						// Boolean is not supported for Additions
						throw new TransformationException("Addition (+) of boolean expressions is not supported.");
					}
					// Only double and integer remaining:
					if (operand1 instanceof RealType) {
						return operand1;
					} else if (operand2 instanceof RealType) {
						return operand2;
					}
					// only integer remaining:
					return operand1;
				}
				throw new TransformationException("Please do not add collections to simple values, try it the other way round.");

				// remaining arithmetic operators:
			case MODULO:
				// Modulo only allows integers, otherwise continue with
				// arithmetic expression logic
				if (! (operand1 instanceof IntegerType) || ! (operand2 instanceof IntegerType)) {
					throw new TransformationException("You can only use the modulo operator (%) on integer expressions.");
				}
			case MULTIPLICATION:
			case DIVISION:
			case SUBTRACTION:
				// remaining arithmetic operators:
				if (collectionOperand1 || collectionOperand2) {
					throw new TransformationException("You cannot use arithmetic operators (*,/,-,%) on collections.");
				}
				if (operand1 instanceof BooleanType || operand2 instanceof BooleanType || operand1 instanceof StringType
						|| operand2 instanceof StringType) {
					throw new TransformationException("You can only use arithmetic operators (*,/,-,%) on numeric expressions.");
				}
				if (operand1 instanceof RealType) {
					return operand1;
				} else if (operand2 instanceof RealType) {
					return operand2;
				}
				// only integer remaining:
				return operand1;

			case GREATER:
			case GREATER_EQUAL:
			case LESS:
			case LESS_EQUAL:
			case EQUALS:
			case NOT_EQUALS:
				// NOTE: we do not check for undetermined expressions like:
				// "myString" <= 5.321
				// this is either true or false depending on the underlying
				// system
				BooleanType booleanType= TypeFactory.eINSTANCE.createBooleanType();
				booleanType.setName("Boolean");
				return booleanType;

			case AND:
			case OR:
				// boolean operations work only on boolean operands
				if (collectionOperand1 || collectionOperand2) {
					throw new TransformationException("Cannot use boolean operators on collections!");
				}
				if (! (operand1 instanceof BooleanType) || ! (operand2 instanceof BooleanType)) {
					throw new TransformationException("Cannot use boolean operators on other than boolean expressions!");
				}
				return operand1;

			default:
				throw new TransformationException("Unsupported operation type: " + this.getLiteral());
		}
	}

	/**
	 * Handles result types for unary operations.
	 * 
	 * @param operand1
	 * @return
	 * @throws TransformationException
	 */
	private Type getUnaryResultType(Type operand1) throws TransformationException {
		boolean collection= (operand1 instanceof CollectionType);

		switch (this) {
			// handle NOT and NEGATION
			case NOT:
				if (collection) {
					throw new TransformationException("Cannot use not operator (!) on collections!");
				}
				if (! (operand1 instanceof BooleanType)) {
					throw new TransformationException("Cannot use the not (!) operator on other than boolean expressions!");
				}
				return operand1;
			case NEGATION:
				if (collection) {
					throw new TransformationException("Cannot use negation (-) operator on collections!");
				}
				if (operand1 instanceof StringType || operand1 instanceof BooleanType) {
					throw new TransformationException("Cannot use the negation (-) operator on other than numeric expressions!");
				}
				return operand1;
				// only one operand (type won't be changed)
		}
		throw new TransformationException("Supported unary operators are only NOT(!) on boolean types and NEGATION(-) on number types.\n"
				+ this.toString() + " is not supported.");
	}

	/**
	 * 
	 * This method is used to determine the resulting type of an operation.
	 * 
	 * Example: "street name "(String) + 4(Integer) would evaluate to
	 * "street name 4"(String).
	 * 
	 * Also: 4.5(Real) - 2(Integer) evaluates to 2.5 (Real) A programmer is also
	 * used to 3.999 / 2 resulting in 1
	 * 
	 * So depending on the type of the second operand the result will vary.
	 * 
	 * It is necessary to determine the type of an expression, to be able to
	 * determine the type of a variable, to which it is assigned for instance.
	 * 
	 * @param operands list of expressions type resolving is done recursively
	 *        for operation chains
	 * @return {@link Type} the resulting Type of the operation
	 * @throws TransformationException for operations on unsupported types
	 */
	public Type getResultType(List<Expression> operands) throws TransformationException {
		Type firstType= null;
		Type secondType= null;

		try{
			if (operands.size() > 0) {
				firstType= operands.get(0).getType();
				checkForNullType(operands.get(0));
	
				validateFirstOperandType(firstType);
	
				if (operands.size() == 2) {
					secondType= operands.get(1).getType();
					checkForNullType(operands.get(1));
					validateSecondOperandType(firstType, secondType);
					return getBinaryResultType(firstType, secondType);
				}
	
				return getUnaryResultType(firstType);
			}
	
			throw new TransformationException(ERR_ILLEGAL_ARGUMENT_COUNT);
		} catch (TransformationException tre){
			String opName = this.name+" (";
			for (Expression e : operands){
				opName += e.toString()+",";
			}
			opName+= ")";
			throw new TransformationException("Error in operation: "+opName,tre);
		}
		// (this should also never happen, because the parser should not
		// construct such a malformed operation)
	}

	private void checkForNullType(Expression expression) throws TransformationException {
		if (expression.getType() == null) {
			throw new TransformationException("Type of " + expression.toString()
					+ " is not defined. You can only use previously declared variables or previously received message variables.");
		}
	}

	/** Some error messages */
	private static final String ERR_NO_CUSTOM_TYPES_ALLOWED_IN_OPERATIONS= "You cannot use operations on custom types. "
			+ "Please refrain from using anything else than primitive types (string, double, integer, boolean, date). You used ";

	private static final String ERR_ILLEGAL_ARGUMENT_COUNT= "You cannot operate on zero arguments / more than two arguments at once.";

	private static final String ERR_COLLECTION_TYPE_INCOMPATIBLE= "Operations on collections are only supported, "
			+ "if the operand has the same type as the elements in the collection";

	private static final String ERR_NULLTYPE_ONLY_ALLOWED_AS_SECOND_COMPARISON_OPERATOR= "\"null\" is only allowed as the second argument in comparison operations or as assignment.";

	/**
	 * The first operand can either be a primitive type, or a collection type
	 * 
	 * @param firstType
	 * @throws TransformationException
	 */
	private void validateFirstOperandType(Type firstType) throws TransformationException {
		if (getIsNullType(firstType)) {
			throw new TransformationException(ERR_NULLTYPE_ONLY_ALLOWED_AS_SECOND_COMPARISON_OPERATOR);
		}
		if (!getIsPrimitiveType(firstType) && !getIsCollection(firstType)) {
			throw new TransformationException(ERR_NO_CUSTOM_TYPES_ALLOWED_IN_OPERATIONS + "'" + firstType.getName() + "'.");
		}
	}

	/**
	 * Validates the second operand in an operation. Normally only primitive
	 * types are supported in operations. But, if adding or removing elements of
	 * a collection, custom types are also permitted. But the collection's
	 * elements must be compatible to the second operand.
	 * 
	 * @param firstType the first operand's {@link Type}
	 * @param secondType the second operand's {@link Type}
	 * @throws TransformationException if types are not matching / not allowed
	 *         in simple operations
	 */
	private void validateSecondOperandType(Type firstType, Type secondType) throws TransformationException {
		if (getIsCollection(firstType)) {
			Type collectionType= ((CollectionType) firstType).getElemType();
			if (!secondType.getName().equals(collectionType.getName())) {
				throw new TransformationException(ERR_COLLECTION_TYPE_INCOMPATIBLE);
			}
		} else if (! (getIsPrimitiveType(secondType) || getIsNullType(secondType))) {
			throw new TransformationException(ERR_NO_CUSTOM_TYPES_ALLOWED_IN_OPERATIONS + "'" + secondType.getName() + "'");
		}
	}

	private boolean getIsNullType(Type type) {
		return type instanceof NullType;
	}

	private boolean getIsCollection(Type type) {
		return type instanceof CollectionType;
	}

	private boolean getIsPrimitiveType(Type type) {
		return type instanceof PrimitiveType;
	}

} // OperationType
