AadlBaUtils.java

/**
 * AADL-BA-FrontEnd
 *
 * Copyright (c) 2011-2021 TELECOM ParisTech and CNRS
 *
 * TELECOM ParisTech/LTCI
 *
 * Authors: see AUTHORS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Eclipse Public License as published by Eclipse,
 * either version 2.0 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Eclipse Public License for more details.
 * You should have received a copy of the Eclipse Public License
 * along with this program.  If not, see
 * https://www.eclipse.org/legal/epl-2.0/
 */

package org.osate.ba.utils;

import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;

import javax.naming.OperationNotSupportedException;

import org.eclipse.emf.common.util.EList;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.AadlBoolean;
import org.osate.aadl2.AadlInteger;
import org.osate.aadl2.AadlReal;
import org.osate.aadl2.AadlString;
import org.osate.aadl2.Abstract;
import org.osate.aadl2.AbstractFeature;
import org.osate.aadl2.AccessSpecification;
import org.osate.aadl2.AccessType;
import org.osate.aadl2.ArrayDimension;
import org.osate.aadl2.ArrayableElement;
import org.osate.aadl2.BasicProperty;
import org.osate.aadl2.BusAccess;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ClassifierValue;
import org.osate.aadl2.ComponentClassifier;
import org.osate.aadl2.ComponentPrototype;
import org.osate.aadl2.ComponentPrototypeActual;
import org.osate.aadl2.ComponentPrototypeBinding;
import org.osate.aadl2.ComponentType;
import org.osate.aadl2.DataAccess;
import org.osate.aadl2.DataClassifier;
import org.osate.aadl2.DataPort;
import org.osate.aadl2.DataPrototype;
import org.osate.aadl2.DataSubcomponent;
import org.osate.aadl2.DataSubcomponentType;
import org.osate.aadl2.DirectedFeature;
import org.osate.aadl2.DirectionType;
import org.osate.aadl2.Element;
import org.osate.aadl2.EnumerationLiteral;
import org.osate.aadl2.EnumerationType;
import org.osate.aadl2.EventDataPort;
import org.osate.aadl2.EventPort;
import org.osate.aadl2.Feature;
import org.osate.aadl2.FeatureGroup;
import org.osate.aadl2.FeatureGroupPrototype;
import org.osate.aadl2.FeatureGroupPrototypeActual;
import org.osate.aadl2.FeatureGroupPrototypeBinding;
import org.osate.aadl2.FeatureGroupType;
import org.osate.aadl2.FeaturePrototype;
import org.osate.aadl2.FeaturePrototypeActual;
import org.osate.aadl2.FeaturePrototypeBinding;
import org.osate.aadl2.IntegerLiteral;
import org.osate.aadl2.ListValue;
import org.osate.aadl2.ModalPropertyValue;
import org.osate.aadl2.ModeTransitionTrigger;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.NamedValue;
import org.osate.aadl2.Parameter;
import org.osate.aadl2.PortCategory;
import org.osate.aadl2.PortSpecification;
import org.osate.aadl2.ProcessClassifier;
import org.osate.aadl2.ProcessorClassifier;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.PropertyExpression;
import org.osate.aadl2.PropertyType;
import org.osate.aadl2.Prototype;
import org.osate.aadl2.PrototypeBinding;
import org.osate.aadl2.StringLiteral;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.SubprogramAccess;
import org.osate.aadl2.SubprogramClassifier;
import org.osate.aadl2.SubprogramGroup;
import org.osate.aadl2.SubprogramGroupAccess;
import org.osate.aadl2.SubprogramGroupSubcomponent;
import org.osate.aadl2.SubprogramSubcomponent;
import org.osate.aadl2.SystemSubcomponent;
import org.osate.aadl2.ThreadGroup;
import org.osate.aadl2.UnitLiteral;
import org.osate.ba.aadlba.BehaviorBooleanLiteral;
import org.osate.ba.aadlba.BehaviorElement;
import org.osate.ba.aadlba.BehaviorFeatureType;
import org.osate.ba.aadlba.BehaviorIntegerLiteral;
import org.osate.ba.aadlba.BehaviorNamedElement;
import org.osate.ba.aadlba.BehaviorPropertyConstant;
import org.osate.ba.aadlba.BehaviorRealLiteral;
import org.osate.ba.aadlba.BehaviorState;
import org.osate.ba.aadlba.BehaviorStringLiteral;
import org.osate.ba.aadlba.BehaviorTime;
import org.osate.ba.aadlba.BehaviorTransition;
import org.osate.ba.aadlba.BehaviorVariable;
import org.osate.ba.aadlba.BehaviorVariableHolder;
import org.osate.ba.aadlba.ClassifierPropertyReference;
import org.osate.ba.aadlba.DataAccessHolder;
import org.osate.ba.aadlba.DataAccessPrototypeHolder;
import org.osate.ba.aadlba.DataComponentReference;
import org.osate.ba.aadlba.DataHolder;
import org.osate.ba.aadlba.DataRepresentation;
import org.osate.ba.aadlba.ElementHolder;
import org.osate.ba.aadlba.Factor;
import org.osate.ba.aadlba.FeaturePrototypeHolder;
import org.osate.ba.aadlba.FeatureType;
import org.osate.ba.aadlba.ForOrForAllStatement;
import org.osate.ba.aadlba.IndexableElement;
import org.osate.ba.aadlba.IntegerValue;
import org.osate.ba.aadlba.IterativeVariable;
import org.osate.ba.aadlba.Otherwise;
import org.osate.ba.aadlba.PortCountValue;
import org.osate.ba.aadlba.PortFreshValue;
import org.osate.ba.aadlba.PropertyElementHolder;
import org.osate.ba.aadlba.PropertyNameHolder;
import org.osate.ba.aadlba.PropertyReference;
import org.osate.ba.aadlba.PrototypeHolder;
import org.osate.ba.aadlba.Relation;
import org.osate.ba.aadlba.SimpleExpression;
import org.osate.ba.aadlba.StructUnionElement;
import org.osate.ba.aadlba.Target;
import org.osate.ba.aadlba.Term;
import org.osate.ba.aadlba.Value;
import org.osate.ba.aadlba.ValueConstant;
import org.osate.ba.aadlba.ValueExpression;
import org.osate.ba.aadlba.ValueVariable;
import org.osate.ba.analyzers.TypeHolder;
import org.osate.utils.internal.Aadl2Utils;
import org.osate.utils.internal.Aadl2Utils.DataAccessRight;
import org.osate.utils.internal.Aadl2Visitors;
import org.osate.utils.internal.PropertyUtils;
import org.osate.utils.internal.names.DataModelProperties;

/**
 * A collection of static utils methods.
 */
public class AadlBaUtils {

	/**
	 * String separator for a component reference name.
	 */
	public static final String STRING_NAME_SEPARATOR = ".";

	/**
	 * Returns the last data representation from the property stack of the given data
	 * classifier or DataRepresentation.UNKNOWN if Data_Model::Data_Representation
	 * property is not set or if the data classifier represents a data structure.
	 *
	 * @param c the given data classifier
	 * @return the data representation or DataRepresentation.UNKNOWN
	 */
	public static DataRepresentation getDataRepresentation(DataClassifier c) {
		DataRepresentation result = null;

		if (c == null) {
			result = DataRepresentation.UNKNOWN;
		} else {

			EList<PropertyExpression> l = PropertyUtils.findPropertyExpression(c,
					DataModelProperties.DATA_REPRESENTATION);
			if (l.size() > 0) {
				// Fetches the last enumeration value from the inheritance stack of
				// properties.
				NamedValue nv = (NamedValue) l.get(l.size() - 1);
				String tmp = ((EnumerationLiteral) nv.getNamedValue()).getName();
				result = DataRepresentation.getByName(tmp);

				if (result == null) {
					result = DataRepresentation.UNKNOWN;
				}
			} else {
				result = DataRepresentation.UNKNOWN;
			}
		}

		return result;
	}

	/**
	 * Returns the data representation of the given BehaviorVariable object
	 * or DataRepresentation.UNKNOWN if Data_Model::Data_Representation
	 * property is not set or if the BehaviorVariable object represents a
	 * data structure.
	 *
	 * @param bv the given BehaviorVariable object
	 * @return the data representation or DataRepresentation.UNKNOWN
	 */
	public static DataRepresentation getDataRepresentation(BehaviorVariable bv) {
		Element el = bv.getDataClassifier();

		return getDataRepresentation((DataClassifier) el);
	}

	/**
	 * Returns the data representation associated to the given PropertyType
	 * object.
	 * <BR><BR>
	 * Note : this method only supports the following property types :
	 * <BR>_ Aadl integer
	 * <BR>_ Aadl real
	 * <BR>_ Aadl string
	 * <BR>_ Aadl boolean
	 * <BR>_ Enumeration
	 * <BR><BR>
	 * @param type the given PropertyType object.
	 * @return the data representation associated to the given PropertyType
	 * object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(PropertyType type) {
		if (type instanceof AadlInteger) {
			return DataRepresentation.INTEGER;
		} else if (type instanceof AadlReal) {
			return DataRepresentation.FLOAT;
		} else if (type instanceof AadlString) {
			return DataRepresentation.STRING;
		} else if (type instanceof AadlBoolean) {
			return DataRepresentation.BOOLEAN;
		} else if (type instanceof EnumerationType) {
			return DataRepresentation.ENUM;
		} else {
			// Not implemented:
			// ClassifierType, EnumerationType, ListType, NonListType, NumberType,
			// RangeType, RecordType, ReferenceType, UnitsType
			String errorMsg = "getDataRepresentation: " + type.getClass().getSimpleName() + " from "
					+ type.eContainer().toString() + " is not supported yet.";
			throw new UnsupportedOperationException(errorMsg);
		}
	}

	/**
	 * Returns the data representation associated to the given PropertyAssociation
	 * object.
	 * <BR><BR>
	 * Note : {@link #getDataRepresentation(PropertyExpression)} to see restrictions.
	 * @param pa the given PropertyAssociation object.
	 * @return the data representation associated to the given PropertyAssociation
	 * object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(PropertyAssociation pa) {
		ModalPropertyValue mpv = pa.getOwnedValues().get(pa.getOwnedValues().size() - 1);
		return getDataRepresentation(mpv.getOwnedValue());
	}

	/**
	 * Returns the data representation associated to the given PropertyExpression
	 * object.
	 * <BR><BR>
	 * Note : this method only supports the following property types :
	 * <BR>_ Boolean literal
	 * <BR>_ Integer literal
	 * <BR>_ Real literal
	 * <BR>_ String literal
	 * <BR><BR>
	 * @param pe the given PropertyExpression object.
	 * @return the data representation associated to the given PropertyExpression
	 * object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(PropertyExpression pe) {
		int type = pe.eClass().getClassifierID();
		switch (type) {
		case Aadl2Package.BOOLEAN_LITERAL:
			return DataRepresentation.BOOLEAN;
		case Aadl2Package.INTEGER_LITERAL:
			return DataRepresentation.INTEGER;
		case Aadl2Package.REAL_LITERAL:
			return DataRepresentation.FLOAT;
		case Aadl2Package.STRING_LITERAL:
			return DataRepresentation.STRING;

		default: {
			// Not implemented:
			// ClassifierValue, ComputedValue, InstanceReferenceValue,
			// ListValue, NamedValue, NumberValue, Operation, PropertyValue,
			// RangeValue, RecordValue, ReferenceValue
			String errorMsg = "getDataRepresentation: " + pe.getClass().getSimpleName() + " from "
					+ pe.eContainer().toString() + " is not supported yet.";
			throw new UnsupportedOperationException(errorMsg);
		}
		}
	}

	/**
	 * Returns the data representation associated to the given
	 * BehaviorPropertyConstant object <BR><BR>
	 * Note : {@link #getDataRepresentation(PropertyType)} to see restrictions.
	 * <BR><BR>
	 * @param pc the given BehaviorPropertyConstant object
	 * @return the data representation associated to the given
	 * BehaviorPropertyConstant object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(BehaviorPropertyConstant pc) {
		return getDataRepresentation((pc.getProperty().getPropertyType()));
	}

	/**
	 * Returns the data representation associated to the given PropertyReference
	 * object <BR><BR>
	 * Note : {@link #getDataRepresentation(PropertyType)} and
	 * {@link #getDataRepresentation(PropertyExpression)} to see restrictions.
	 * <BR><BR>
	 * @param pv the given PropertyReference object
	 * @return the data representation associated to the given
	 * PropertyReference object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(PropertyReference pr) {
		EList<PropertyNameHolder> holders = pr.getProperties();
		PropertyNameHolder last = holders.get(holders.size() - 1);
		Element el = last.getProperty().getElement();

		if (el instanceof PropertyType || el instanceof BasicProperty) {
			return getDataRepresentation(((BasicProperty) el).getPropertyType());
		} else if (el instanceof PropertyAssociation) {
			return getDataRepresentation((PropertyAssociation) el);
		} else if (el instanceof PropertyExpression) {
			return getDataRepresentation((PropertyExpression) el);
		} else if (el instanceof EnumerationLiteral) {
			return DataRepresentation.ENUM_LITERAL;
		} else {
			String errorMsg = "getDataRepresentation: " + el.getClass().getSimpleName() + " is not supported yet.";
			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}
	}

	/**
	 * Returns the PropertyType object associated to the given PropertyElementHolder
	 * given object.
	 * @param holder the PropertyElementHolder given object
	 * @return the associated PropertyType object
	 */
	public static PropertyType getPropertyType(PropertyElementHolder holder) {
		PropertyType result = null;

		Element el = holder.getElement();

		if (el instanceof PropertyType) {
			result = (PropertyType) el;
		} else if (el instanceof BasicProperty) {
			result = ((BasicProperty) el).getPropertyType();
		} else if (el instanceof PropertyAssociation) {
			result = ((PropertyAssociation) el).getProperty().getPropertyType();
		} else if (el instanceof PropertyExpression) {
			result = PropertyUtils.getContainingProperty((PropertyExpression) el).getPropertyType();
		} else if (el instanceof EnumerationLiteral) {
			result = (EnumerationType) el.eContainer();
		}
		return result;
	}

	/**
	 * Returns the data representation associated to the given ValueConstant
	 * object <BR><BR>
	 * Note : {@link #getDataRepresentation(PropertyType)}
	 *                        to see restrictions on property constant and value.
	 * <BR><BR>
	 * @param v the given ValueConstant object
	 * @return the data representation associated to the given ValueConstant
	 * object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(ValueConstant v) {
		if (v instanceof BehaviorPropertyConstant) {
			return getDataRepresentation((BehaviorPropertyConstant) v);
		} else if (v instanceof PropertyReference) {
			return getDataRepresentation((PropertyReference) v);
		} else if (v instanceof BehaviorIntegerLiteral) {
			return DataRepresentation.INTEGER;
		} else if (v instanceof BehaviorRealLiteral) {
			return DataRepresentation.FLOAT;
		} else if (v instanceof BehaviorStringLiteral) {
			return DataRepresentation.STRING;
		} else if (v instanceof BehaviorBooleanLiteral) {
			return DataRepresentation.BOOLEAN;
		} else // Behavior enumeration literal case.
		{
			return DataRepresentation.ENUM;
		}
	}

	/**
	 * Returns the data representation of the given ValueVariable object
	 * or DataRepresentation.UNKNOWN if Data_Model::Data_Representation
	 * property is not set or if the ValueVariable object represents a data
	 * structure.
	 *
	 * @param v the given ValueVariable object
	 * @return the data representation or DataRepresentation.UNKNOWN
	 */
	public static DataRepresentation getDataRepresentation(ValueVariable v) {
		if (v instanceof PortCountValue) {
			return DataRepresentation.INTEGER;
		} else if (v instanceof PortFreshValue) {
			return DataRepresentation.BOOLEAN;
		} else {
			// Either ElementHolder or DataComponentReference object.
			Element el = null;

			if (v instanceof ElementHolder) {
				if (v instanceof PrototypeHolder) {
					PrototypeHolder ph = (PrototypeHolder) v;

					if (ph.getPrototypeBinding() != null) {
						el = ph.getPrototypeBinding();
					} else {
						el = ph.getPrototype();
					}
				} else {
					el = ((ElementHolder) v).getElement();
				}
			} else // DataComponentReference case.
			{
				DataComponentReference dcr = (DataComponentReference) v;

				DataHolder lastElement = dcr.getData().get(dcr.getData().size() - 1);
				el = lastElement.getElement();
			}

			if (el instanceof Abstract) {
				return DataRepresentation.UNKNOWN;
			} else if (el instanceof Feature) {
				Classifier c = ((Feature) el).getClassifier();

				if (c instanceof DataClassifier) {
					return getDataRepresentation((DataClassifier) c);
				} else {
					return DataRepresentation.UNKNOWN;
				}
			} else if (el instanceof DataSubcomponent) {
				final DataSubcomponent subcompo = (DataSubcomponent) el;
				final Classifier classifier = subcompo.getClassifier();

				// fixes 2401: Avoid crashing the editor when subcomponent is not resolved
				if (classifier == null || classifier.eIsProxy()) {
					return DataRepresentation.UNKNOWN;
				}

				return getDataRepresentation((DataClassifier) ((DataSubcomponent) el).getClassifier());
			} else if (el instanceof BehaviorVariable) {
				// Behavior case.
				return getDataRepresentation((BehaviorVariable) el);
			} else {
				// Prototype cases.
				Classifier klass;
				ComponentClassifier baParentComponent = (ComponentClassifier) v.getContainingClassifier();

				klass = AadlBaUtils.getClassifier(el, baParentComponent);
				return getDataRepresentation((DataClassifier) klass);
			}
		}
	}

	/**
	 * Returns the data representation associated to the given Value object.
	 * or DataRepresentation.UNKNOWN if Data_Model::Data_Representation
	 * property is not set or if the Value object represents a data
	 * structure.
	 * <BR><BR>
	 * Note : {@link #getDataRepresentation(PropertyType)}
	 *                        to see restrictions on property constant and value.
	 * <BR><BR>
	 * @param v the given Value object
	 * @return the data representation associated to the given Value object or
	 * DataRepresentation.UNKNOWN
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static DataRepresentation getDataRepresentation(Value v) {
		if (v instanceof ValueVariable) {
			return getDataRepresentation((ValueVariable) v);
		} else {
			return getDataRepresentation((ValueConstant) v);
		}
	}

	/**
	 * Constructs a string base on the name of the given behavior states list,
	 * separated by the given separator symbol.
	 *
	 * @param bsl the given behavior states list
	 * @param separator the name separator symbol
	 * @return the string build on the given behavior state's names and given
	 * separator
	 */
	public static String identifierListToString(EList<BehaviorState> bsl, String separator) {
		String[] ls = new String[bsl.size()];
		int i = 0;

		for (BehaviorState bs : bsl) {
			ls[i] = bs.getName();
			i++;
		}

		return Aadl2Utils.concatenateString(separator, ls);
	}

	/**
	 * Returns the given Element object's classifier.
	 * If the Element object is a prototype, it will try to resolve it as
	 * follow: returns the data prototype binded classifier at first withing the
	 * element's parent container otherwise the constraining classifier.
	 * It returns {@code null} if the prototype is not defined.
	 * <BR><BR>
	 * This method support instances of:<BR>
	 * <BR>_Feature (port, data access, subprogram access, parameter, etc.)
	 * <BR>_Subcomponent (data subcomponent, subprogram subcomponent, etc.)
	 * <BR>_BehaviorVariable
	 * <BR>_IterativeVariable (for/forall's iterative variable)
	 * <BR>_Prototype (all excepted FeatureGroupPrototype)
	 * <BR>_PrototypeBinding (all excepted FeatureGroupPrototypeBinding)
	 * <BR>_ClassifierValue (struct or union data subcomponent)
	 * <BR><BR>
	 * If the given Element object is not one of those types, an
	 * UnsupportedOperationException is thrown.
	 *
	 * @param el the given Element object
	 * @param parentContainer the element's parent component.
	 * @return the given element's classifier or {@code null} if the prototype is
	 * not defined
	 * @exception UnsupportedOperationException for unsupported element
	 * object types.
	 */
	public static Classifier getClassifier(Element el, Classifier parentContainer) {
		Classifier result = null;

		if (el instanceof Feature) {
			Feature f = (Feature) el;

			if (el instanceof FeatureGroup) {
				org.osate.aadl2.FeatureType ft = ((FeatureGroup) el).getFeatureType();

				if (ft != null) {
					if (ft instanceof FeatureGroupType) {
						result = (FeatureGroupType) ft;
					} else // FeatureGroupPrototype case
					{
						result = getClassifier((FeatureGroupPrototype) ft, parentContainer);
					}
				}
			} else {
				// Feature case.
				result = f.getClassifier();

				// Resolves data prototype.
				// Feature without classifier returns null.
				if (result == null && f.getPrototype() != null) {
					result = prototypeResolver(f.getPrototype(), parentContainer);
				}
			}
		} else if (el instanceof Subcomponent) {
			Subcomponent sub = (Subcomponent) el;

			if (el instanceof SubprogramGroupSubcomponent) {
				result = ((SubprogramGroupSubcomponent) el).getClassifier();
			} else {
				// Subcomponent case.
				result = sub.getClassifier();

				// Resolves data prototype.
				// Subcomponent without classifier returns null.
				if (result == null && sub.getPrototype() != null) {
					result = prototypeResolver(sub.getPrototype(), parentContainer);
				}
			}
		} else if (el instanceof BehaviorVariable) {
			// Local variable case (BehaviorVariable).
			BehaviorVariable bv = (BehaviorVariable) el;
			result = bv.getDataClassifier();
		} else if (el instanceof IterativeVariable) {
			// Iterative variable case.
			result = ((IterativeVariable) el).getDataClassifier();
		} else if (el instanceof Prototype) {
			result = prototypeResolver((Prototype) el, parentContainer);
		} else if (el instanceof PrototypeBinding) {
			// Prototype binding case.
			result = prototypeBindingResolver((PrototypeBinding) el);
		} else if (el instanceof ClassifierValue) {
			// struct or union member case (ClassifierValue).
			result = ((ClassifierValue) el).getClassifier();
		} else if (el instanceof StructUnionElement) {
			return ((StructUnionElement) el).getDataClassifier();
		} else {
			// Reports error.
			String errorMsg = "getClassifier : " + el.getClass().getSimpleName() + " is not supported yet.";

			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}

		return result;
	}

	/**
	 * Resolves the given prototype by returning the binded classifier
	 * or if there is no binded classifier, the constraining classifier.
	 * It returns {@code null} if the given prototype is not defined.
	 *
	 * @param prototype the given prototype
	 * @param parentContainer the element's parent component
	 * @return the binded classifier at first then the constraining classifier or
	 * {@code null}
	 */
	public static Classifier prototypeResolver(Prototype prototype, Classifier parentContainer) {
		Classifier result = null;

		// First find a prototype bind as prototype may be binded several times.
		PrototypeBinding pb = Aadl2Visitors.findPrototypeBindingInComponent(parentContainer, prototype.getName());

		if (pb != null) {
			result = prototypeBindingResolver(pb);
		} else {
			// If there is no prototype binding found, returns the constraining
			// classifier from the prototype declaration (it may be null).

			if (prototype instanceof ComponentPrototype) {
				result = ((ComponentPrototype) prototype).getConstrainingClassifier();
			} else if (prototype instanceof FeaturePrototype) {
				result = ((FeaturePrototype) prototype).getConstrainingClassifier();
			} else if (prototype instanceof FeatureGroupPrototype) {
				result = ((FeatureGroupPrototype) prototype).getConstrainingFeatureGroupType();
			} else {
				// Reports error.
				String errorMsg = "prototypeResolver : " + prototype.getClass().getSimpleName()
						+ " is not supported yet.";

				System.err.println(errorMsg);
				throw new UnsupportedOperationException(errorMsg);
			}
		}

		return result;
	}

	/**
	 * Resolves the given prototype binding by returning the binded classifier
	 * It returns {@code null} if the given prototype binding is not defined.
	 *
	 * @param pb the given prototype binding
	 * @return the binded classifier or {@code null}
	 */
	public static Classifier prototypeBindingResolver(PrototypeBinding pb) {
		Classifier result = null;

		if (pb instanceof ComponentPrototypeBinding) {
			ComponentPrototypeBinding cpb;
			cpb = (ComponentPrototypeBinding) pb;

			// Takes the last binding.
			ComponentPrototypeActual cpa = cpb.getActuals().get(cpb.getActuals().size() - 1);

			result = (Classifier) cpa.getSubcomponentType();
		} else if (pb instanceof FeaturePrototypeBinding) {
			FeaturePrototypeBinding fpb;
			fpb = (FeaturePrototypeBinding) pb;
			FeaturePrototypeActual fpa = fpb.getActual();

			if (fpa instanceof AccessSpecification) {
				result = ((AccessSpecification) fpa).getClassifier();
			} else if (fpa instanceof PortSpecification) {
				result = ((PortSpecification) fpa).getClassifier();
			} else {
				// Reports error.
				String errorMsg = "prototypeBindingResolver : " + fpa.getClass().getSimpleName()
						+ " is not supported yet.";

				System.err.println(errorMsg);
				throw new UnsupportedOperationException(errorMsg);
			}
		} else if (pb instanceof FeatureGroupPrototypeBinding) {
			FeatureGroupPrototypeBinding fgpb = (FeatureGroupPrototypeBinding) pb;
			result = (FeatureGroupType) fgpb.getActual().getFeatureType();
		} else {
			// Reports error.
			String errorMsg = "prototypeBindingResolver : " + pb.getClass().getSimpleName() + " is not supported yet.";

			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}

		return result;
	}

	/**
	 * Returns the DataClassifier of the element binded to the given
	 * Value object. A target instance can be given to this method as
	 * Target instance can be cast into ValueVariable reference.
	 * <BR><BR>
	 *  Notes: <BR><BR>
	 *  <BR>_ ValueVariable : {@link #getClassifier(Element, Classifier)}
	 *                                 to see the restrictions.
	 *  <BR>_ ValueConstant : Property constant and property reference are not supported:
	 *  returns {@code null}.
	 *  <BR><BR>
	 *
	 * @param v the given Value object
	 * @return the binded component's DataClassifier object or {@code null} for
	 * the ValueConstant objects
	 * @exception UnsupportedOperationException for unsupported binded
	 * object types.
	 */
	public static DataClassifier getDataClassifier(Value v) {
		return getDataClassifier(v, null);
	}

	/**
	 * Returns the DataClassifier object associated to the given Target object.
	 *
	 * @param t the given Target object
	 * @return the DataClassifier object
	 * @exception UnsupportedOperationException for unsupported cases
	 */
	public static DataClassifier getDataClassifier(Target t) {
		return getDataClassifier(t, null);
	}

	/**
	 * Returns the DataClassifier object associated to the given List of PrototypeBinging objects.
	 *
	 * @param prototypeBindings the list of PrototypeBinging object
	 * @param dp the DataPrototype searched for in prototypeBindings
	 * @return the DataClassifier object (null if not found)
	 */
	private static DataClassifier getDataClassifier(List<PrototypeBinding> prototypeBindings, DataPrototype dp) {
		for (PrototypeBinding pb : prototypeBindings) {
			if (pb instanceof ComponentPrototypeBinding && pb.getFormal().equals(dp)) {
				ComponentPrototypeBinding cpb = (ComponentPrototypeBinding) pb;
				ComponentPrototypeActual cpa = cpb.getActuals().get(cpb.getActuals().size() - 1);
				if (cpa.getSubcomponentType() instanceof DataClassifier) {
					return (DataClassifier) cpa.getSubcomponentType();
				}
			}
		}
		return null;
	}

	/**
	 * Returns the DataClassifier object associated to the given Target object.
	 *
	 * @param t the given Target object
	 * @param parentContainer the object that contains the element binded to the
	 * given Target Object (and not the Target container)
	 * @return the DataClassifier object
	 * @exception UnsupportedOperationException for unsupported cases
	 */
	public static DataClassifier getDataClassifier(Target t, NamedElement parentContainer) {
		NamedElement result;

		if (t instanceof DataHolder) {
			result = ((DataHolder) t).getElement();
		} else if (t instanceof DataComponentReference) {
			DataComponentReference r = (DataComponentReference) t;
			DataHolder h = r.getData().get(r.getData().size() - 1);
			result = h.getElement();
		} else {
			throw new UnsupportedOperationException('\'' + t.getClass().getSimpleName() + "\' is not yet supported");
		}

		if (result instanceof DataClassifier) {
			return (DataClassifier) result;
		} else if (result instanceof DataSubcomponent) {
			DataSubcomponentType dst = ((DataSubcomponent) result).getDataSubcomponentType();
			if (dst instanceof DataPrototype) {
				DataPrototype dp = (DataPrototype) dst;
				if (t instanceof DataComponentReference) {
					DataComponentReference r = (DataComponentReference) t;
					return prototypeBindingResolverForDataComponentReference(r, dp, parentContainer);
				}
			} else if (dst instanceof DataClassifier) {
				return (DataClassifier) dst;
			}
		} else if (result instanceof BehaviorVariable) {
			BehaviorVariable bv = (BehaviorVariable) result;
			return bv.getDataClassifier();
		} else if (result instanceof DataAccess) {
			DataAccess da = (DataAccess) result;
			if (da.getDataFeatureClassifier() instanceof DataClassifier) {
				return (DataClassifier) da.getDataFeatureClassifier();
			} else if (da.getDataFeatureClassifier() instanceof Prototype) {
				return (DataClassifier) getClassifier(da.getDataFeatureClassifier(), (Classifier) parentContainer);
			}
		} else if (result instanceof Parameter) {
			Parameter p = (Parameter) result;
			if (p.getDataFeatureClassifier() instanceof DataClassifier) {
				return (DataClassifier) p.getDataFeatureClassifier();
			} else if (p.getDataFeatureClassifier() instanceof Prototype) {
				return (DataClassifier) getClassifier(p.getDataFeatureClassifier(), (Classifier) parentContainer);
			}
		} else // Abstract components case.
		{
			return null;
		}
		// Prototype case
		return null;
	}

	/**
	 * Returns the DataClassifier object associated to prototype binding in a DataComponentReference
	 *
	 * @param r the DataComponentRefenrence object from which the binding will be resolved
	 * @param dp the DataPrototpye object that enables to identify the targeted prototype binding
	 * @param parentContainer the object that contains the element binded to the
	 * given DataComponentRefenrence
	 * @return the DataClassifier object
	 */
	private static DataClassifier prototypeBindingResolverForDataComponentReference(DataComponentReference r,
			DataPrototype dp, NamedElement parentContainer) {
		if (r.getData().size() > 1) {
			DataHolder h = r.getData().get(r.getData().size() - 2);
			NamedElement parent = h.getElement();
			if (parent instanceof Classifier) {
				Classifier c = (Classifier) parent;
				return getDataClassifier(c.getOwnedPrototypeBindings(), dp);
			}
			if (parent instanceof Subcomponent) {
				Subcomponent s = (Subcomponent) parent;
				return getDataClassifier(s.getOwnedPrototypeBindings(), dp);
			} else if (parent instanceof DataAccess) {
				DataAccess da = (DataAccess) parent;
				DataSubcomponentType parentDst = da.getDataFeatureClassifier();
				Classifier c = (Classifier) parentDst;
				DataClassifier matchedPrototype = getDataClassifier(c.getOwnedPrototypeBindings(), dp);
				if (matchedPrototype == null) {
					if (parentContainer instanceof ComponentType) {
						ComponentType ct = (ComponentType) parentContainer;
						for (Feature f : ct.getOwnedFeatures()) {
							if (f instanceof DataAccess && f.getName().equals(parent.getName())) {
								DataAccess containerDa = (DataAccess) f;
								DataSubcomponentType containerDst = containerDa.getDataFeatureClassifier();
								Classifier c2 = (Classifier) containerDst;
								return getDataClassifier(c2.getOwnedPrototypeBindings(), dp);
							}
						}
					}
				}
			} else if (parent instanceof DataPort) {
				DataPort dataport = (DataPort) parent;
				DataSubcomponentType parentDst = dataport.getDataFeatureClassifier();
				Classifier c = (Classifier) parentDst;
				DataClassifier matchedPrototype = getDataClassifier(c.getOwnedPrototypeBindings(), dp);
				if (matchedPrototype == null) {
					if (parentContainer instanceof ComponentType) {
						ComponentType ct = (ComponentType) parentContainer;
						for (Feature f : ct.getOwnedFeatures()) {
							if (f instanceof DataPort && f.getName().equals(parent.getName())) {
								DataPort containerDp = (DataPort) f;
								DataSubcomponentType containerDst = containerDp.getDataFeatureClassifier();
								Classifier c2 = (Classifier) containerDst;
								return getDataClassifier(c2.getOwnedPrototypeBindings(), dp);
							}
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * Returns the DataClassifier of the element binded to the given
	 * Value object. A target instance can be given to this method as
	 * Target instance can be cast into ValueVariable reference.
	 * <BR><BR>
	 *  Notes: <BR><BR>
	 *  <BR>_ ValueVariable : {@link #getClassifier(Element, Classifier)}
	 *                                 to see the restrictions.
	 *  <BR>_ ValueConstant : Property constant and property reference are not supported:
	 *  returns {@code null}.
	 *  <BR><BR>
	 *
	 * @param v the given Value object
	 * @param parentContainer only for AADLBA declarative objects which have no
	 * parent set, yet
	 * @return the binded component's DataClassifier object or {@code null} for
	 * the ValueConstant objects and for the Abstract components objects.
	 * @exception UnsupportedOperationException for unsupported binded
	 * object types.
	 */
	public static DataClassifier getDataClassifier(Value v, ComponentClassifier parentContainer) {
		Classifier result = null;

		if (v instanceof ValueVariable) {
			// Either ElementHolder or DataComponentReference object.
			Element el = null;

			if (v instanceof ElementHolder) {
				el = ((ElementHolder) v).getElement();
			} else // DataComponentReference case.
			{
				DataComponentReference dcr = (DataComponentReference) v;

				DataHolder lastElement = dcr.getData().get(dcr.getData().size() - 1);
				el = lastElement.getElement();
			}

			if (parentContainer == null) {
				parentContainer = (ComponentClassifier) v.getContainingClassifier();
			}

			result = getClassifier(el, parentContainer);
		} else // Property constant and property reference are not supported.
		{
			result = null;
		}

		if (result instanceof DataClassifier) {
			return (DataClassifier) result;
		} else // Abstract components case.
		{
			return null;
		}
	}

	private static TypeHolder getTypeHolder(DataClassifier klass) {
		TypeHolder result = new TypeHolder();
		result.setKlass(klass);
		result.setDataRep(getDataRepresentation(klass));
		return result;
	}

	/**
	 * Returns the TypeHolder (data representation and component's DataClassifier
	 * if any) of the given Value object.
	 * <BR><BR>
	 * Notes: {@link #getDataRepresentation(PropertyType)} and
	 *        {@link #getDataClassifier(Value)} to see restrictions.
	 * <BR><BR>
	 * @param v the given Value object
	 * @param parentContainer only for AADLBA declarative objects which have no
	 * parent set, yet
	 * @return the type holder of the given Value object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	private static TypeHolder getTypeHolder(Value v, ComponentClassifier parentContainer) {
		TypeHolder result = new TypeHolder();

		try {
			result.setDataRep(getDataRepresentation(v));
		} catch (UnsupportedOperationException e) {
			result.setDataRep(DataRepresentation.UNKNOWN);
		}

		// Port count value and port fresh value are, respectively, universal
		// integer and universal boolean. So their data classifier field must be
		// null.
		if (v instanceof ValueVariable && (!(v instanceof PortCountValue || v instanceof PortFreshValue))) {
			result.setKlass(getDataClassifier(v, parentContainer));
		}
		// else: nothing.
		// getDataClassifier doesn't support property constant and property reference

		return result;
	}

	/**
	 * Returns the TypeHolder (data representation and component's DataClassifier
	 * if any) of the given IterativeVariable object.
	 *
	 * @param uccr the given IterativeVariable object.
	 * @return the type holder of the given IterativeVariable
	 * object.
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	private static TypeHolder getTypeHolder(IterativeVariable iv) {
		TypeHolder result = new TypeHolder();
		result.setKlass(iv.getDataClassifier());
		result.setDataRep(getDataRepresentation(result.getKlass()));

		return result;
	}

	/**
	 * Returns the TypeHolder (data representation and component's DataClassifier
	 * if any) of the given Element object.
	 * <BR><BR>
	 * For now, only the following objects are supported:<BR>
	 * <BR>_ IterativeVariable
	 * <BR>_ DataClassifier
	 * <BR>_ Target
	 * <BR>_ Value
	 * <BR><BR>
	 *
	 * @param el the given Element object.
	 * @return the type holder of the given Element object
	 * @exception UnsupportedOperationException for the unsupported types
	 * or Element instances.
	 * @exception DimensionException in any case of array dimension overflow.
	 */
	public static TypeHolder getTypeHolder(Element el) throws DimensionException {
		return getTypeHolder(el, null);
	}

	/**
	 * Returns the TypeHolder (data representation and component's DataClassifier
	 * if any) of the given Element object.
	 * <BR><BR>
	 * For now, only the following objects are supported:<BR>
	 * <BR>_ IterativeVariable
	 * <BR>_ DataClassifier
	 * <BR>_ Target
	 * <BR>_ Value
	 * <BR><BR>
	 *
	 * @param el the given Element object.
	 * @param parentContainer only for AADLBA declarative objects which have no
	 * parent set, yet
	 * @return the type holder of the given Element object. If the given element
	 * is null, it returns the default type holder object.
	 * @exception UnsupportedOperationException for the unsupported types
	 * or Element instances.
	 * @exception DimensionException in any case of array dimension overflow.
	 */
	public static TypeHolder getTypeHolder(Element el, ComponentClassifier parentContainer) throws DimensionException {
		TypeHolder result = null;

		if (el instanceof IterativeVariable) {
			IterativeVariable iv = (IterativeVariable) el;

			result = getTypeHolder(iv);

			processArrayDataRepresentation(el, result, 0);

			return result;
		} else if (el instanceof DataClassifier) {
			DataClassifier dc = (DataClassifier) el;
			result = getTypeHolder(dc);
			return result;
		} else if (el instanceof ValueConstant) {
			return result = getTypeHolder((ValueConstant) el, parentContainer);
		} else if (el instanceof Target || el instanceof ValueVariable) {
			result = getTypeHolder((Value) el, parentContainer);

			// The declared variable dimension: the number of [] in the element
			// declaration.
			int declaredDim = 0;

			// The expression dimension: the number of [] in the expression.
			// Only targets and values can express dimension.
			int exprDim = 0;

			boolean hasToProcessDataRepresentation = false;

			// Flag in order to process data representation if declared dimension
			// is zero and if the given element is an element values of a
			// for/forall statement.
			if (el.eContainer() instanceof ForOrForAllStatement) {
				hasToProcessDataRepresentation = true;
			}

			// Either ElementHolder or DataComponentReference object.
			Element bindedEl = null;
			ElementHolder elHolder = null;

			if (el instanceof ElementHolder) {
				elHolder = (ElementHolder) el;
				bindedEl = elHolder.getElement();
			} else // DataComponentReference case.
			{
				DataComponentReference dcr = (DataComponentReference) el;

				elHolder = dcr.getData().get(dcr.getData().size() - 1);
			}

			bindedEl = elHolder.getElement();

			if (elHolder instanceof IndexableElement) {
				IndexableElement ie = (IndexableElement) elHolder;

				if (ie.isSetArrayIndexes()) {
					exprDim = ie.getArrayIndexes().size();
				}
			}

			// Set up the declared, expression dimension and dimension sizes.
			if (bindedEl instanceof ArrayableElement) {
				ArrayableElement ae = (ArrayableElement) bindedEl;

				if (false == ae.getArrayDimensions().isEmpty()) {
					EList<ArrayDimension> adl = ae.getArrayDimensions();

					declaredDim = adl.size();

					if (exprDim <= declaredDim) {
						long[] ds = new long[declaredDim - exprDim];
						for (int i = exprDim; i < declaredDim; i++) {
							ds[i - exprDim] = adl.get(i).getSize().getSize();
						}
						result.setDimensionSizes(ds);
					}
				}
			}

			// The given type is not expressed as an array in AADL standard core
			// way.
			// Let's evaluate the array behavior of the given type in the Data
			// Model Annex standard way if any dimension is expressed (exprDim !=0)
			// or if the element is an element values of a for/forall statement.
			if (declaredDim == 0 && (exprDim > 0 || hasToProcessDataRepresentation)) {
				processArrayDataRepresentation(el, result, exprDim);
			} else {
				if (exprDim <= declaredDim) {
					result.setDimension(declaredDim - exprDim);
				} else {
					// The given type is expressed as an array in AADL standard
					// core way. We don't evaluate array behavior expressed in
					// the Data Model Annex standard as we don't want to mix up
					// the two standards.
					String msg = "mixs up AADL standard array and data model array";
					throw new DimensionException(el, msg, true);
				}
			}

			return result;
		} else if (el == null || el instanceof Abstract) {
			// returns the default type holder for untyped prototypes.
			return new TypeHolder();
		} else {
			String errorMsg = "getTypeHolder : " + el.getClass().getSimpleName() + " is not supported yet at line "
					+ Aadl2Utils.getLocationReference(el).getLine() + ".";
			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}
	}

	// Evaluates the array behavior of the given type in the Data Model Annex
	// standard way. Set up dimension and dimension sizes of the given
	// TypeHolder object.
	// The given expression dimension is used as an dimension offset.
	private static void processArrayDataRepresentation(Element el, TypeHolder type, int exprDim)
			throws DimensionException {
		// Treats only type declared as an array. Otherwise returns.
		if (type.getDataRep() == DataRepresentation.ARRAY) {
			// Fetches the array element data type.
			ClassifierValue cv = AadlBaUtils.getBaseType(type.getKlass());

			if (cv != null && cv.getClassifier() instanceof DataClassifier) {
				DataClassifier dc = (DataClassifier) cv.getClassifier();
				type.setKlass(dc);
				type.setDataRep(AadlBaUtils.getDataRepresentation(dc));
			} else {
				type.setKlass(null);
			}

			EList<PropertyExpression> pel = PropertyUtils.findPropertyExpression(type.getKlass(),
					DataModelProperties.DIMENSION);
			int declareDimBT = 0;
			long[] declareDimSizeBT;

			if (false == pel.isEmpty()) {
				// pel has only one element, according to AADL core standard.
				PropertyExpression pe = pel.get(pel.size() - 1);

				if (pe instanceof ListValue) {
					ListValue lv = (ListValue) pe;
					EList<PropertyExpression> lve = lv.getOwnedListElements();
					declareDimBT = lve.size();

					if (declareDimBT >= exprDim) {
						declareDimSizeBT = new long[declareDimBT - exprDim];

						for (int i = exprDim; i < declareDimBT; i++) {
							IntegerLiteral il = (IntegerLiteral) lve.get(i);
							declareDimSizeBT[i - exprDim] = il.getValue();
						}

						type.setDimension(declareDimBT - exprDim);
						type.setDimensionSizes(declareDimSizeBT);
					} else {
						String msg = "must be an array but is resolved as " + type.getKlass().getQualifiedName();

						throw new DimensionException(el, msg, false);
					}
				}
			} else {
				// Returning -1 and null means that the expression is declared as an
				// array but the dimension property is not set.

				type.setDimension(-1);
				type.setDimensionSizes(null);
				return;
//        String msg = "is declared as an array but the dimension property is not set" ;
//        throw new DimensionException(el, msg, true) ;
			}
		} else {
			return;
		}
	}

	/**
	 * Compare a given name to a given list of behavior named
	 * elements. The matching is base on behavior named element's name
	 * (case insensitive).
	 *
	 * @param name the given name
	 * @param lbne the given list of behavior named elements
	 * @return the first behavior named element form the given list which has the
	 * same name as the given name. {@code null} otherwise
	 */
	public static <T extends BehaviorNamedElement> T compareNamedElementList(String name, EList<T> lbne) {
		for (T bne2 : lbne) {
			if (name.equalsIgnoreCase(bne2.getName())) {
				return bne2;
			}
		}

		return null;
	}

	/**
	 * Compare the given Target objects.<BR><BR>
	 *
	 * This comparator is base on name and not on object's hash number, meaning
	 * that two different Target instances with the same name are
	 * considered as equals.
	 *
	 * This comparator doesn't support array indexes comparison, meaning that two
	 * target instances with the same name and different array indexes
	 * are considered as equals.
	 * <BR><BR>
	 *
	 * @param tar0 the first name
	 * @param tar1 the second name
	 * @return {@code true} if the given Target objects are the same.
	 * Otherwise returns {@code false}
	 */
	public static boolean isSameTarget(Target tar0, Target tar1) {
		if (tar0 instanceof ElementHolder && tar1 instanceof DataComponentReference
				|| tar1 instanceof ElementHolder && tar0 instanceof DataComponentReference) {
			return false;
		} else {
			if (tar0 instanceof ElementHolder && tar1 instanceof ElementHolder) {
				return isSameElementHolder((ElementHolder) tar0, (ElementHolder) tar1);
			} else {
				EList<DataHolder> dhl0 = ((DataComponentReference) tar0).getData();
				EList<DataHolder> dhl1 = ((DataComponentReference) tar1).getData();

				if (dhl0.size() == dhl1.size()) {
					ListIterator<DataHolder> it1 = dhl1.listIterator();
					DataHolder dh1 = null;
					boolean result = true;

					for (DataHolder dh0 : dhl0) {
						dh1 = it1.next();
						result &= isSameElementHolder(dh0, dh1);
					}

					return result;
				} else {
					return false;
				}
			}
		}
	}

	private static boolean isSameElementHolder(ElementHolder eh0, ElementHolder eh1) {
		String name0 = eh0.getElement().getName();
		String name1 = eh1.getElement().getName();

		return name0.equalsIgnoreCase(name1);
	}

	/**
	 * Create a behavior time comparator.<BR><BR>
	 *
	 * This comparator supports time units (from AADL property set Time_Units)
	 * comparison. For example, comparing 60 sec and 1 min returns 0.<BR><BR>
	 *
	 * It only supports behavior time objects with integer constant literal.
	 * Otherwise it throws a ClassCastException.
	 *
	 * @return a behavior time comparator
	 * @throws UnsupportedOperationException if behavior time objects are not
	 * integer constant literal
	 */
	public static Comparator<BehaviorTime> createBehaviorTimeComparator() {
		return (behT1, behT2) -> {
			IntegerValue iv1 = behT1.getIntegerValue();
			IntegerValue iv2 = behT2.getIntegerValue();

			if (iv1 instanceof BehaviorIntegerLiteral && iv2 instanceof BehaviorIntegerLiteral) {
				double d1 = new Long(((BehaviorIntegerLiteral) iv1).getValue()).doubleValue();
				double d2 = new Long(((BehaviorIntegerLiteral) iv2).getValue()).doubleValue();

				UnitLiteral unit1 = behT1.getUnit();
				UnitLiteral unit2 = behT2.getUnit();

				double relativeFact = unit1.getAbsoluteFactor(unit2);

				// Convert to the smallest time unit. In order to keep
				// the highest precision.
				if (relativeFact < 1) {
					d2 *= unit2.getAbsoluteFactor(unit1);
				} else {
					d1 *= relativeFact;
				}

				return (d1 < d2 ? -1 : (d1 == d2 ? 0 : 1));
			} else {
				throw new UnsupportedOperationException("Can only compare "
						+ "BehaviorTime with IntegerLiteral at line " + behT1.getLocationReference().getLine() + ".");
			}
		};
	}

	/**
	 * Analyze the given AADL Osate element and return its enumeration type.
	 *
	 * It's an improved version of Osate2 org.osate.parser.AadlSemanticCheckSwitch#getFeatureType
	 *
	 * @param el the given AADL Osate element
	 * @return the given AADL Osate element's type
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	/*
	 * <copyright>
	 * Copyright 2009 by Carnegie Mellon University, all rights reserved.
	 *
	 * Use of the Open Source AADL Tool Environment (OSATE) is subject to the terms of the license set forth
	 * at http://www.eclipse.org/legal/cpl-v10.html.
	 *
	 * NO WARRANTY
	 *
	 * ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER PROPERTY OR RIGHTS GRANTED OR PROVIDED BY
	 * CARNEGIE MELLON UNIVERSITY PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN "AS-IS" BASIS.
	 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING,
	 * BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, INFORMATIONAL CONTENT,
	 * NONINFRINGEMENT, OR ERROR-FREE OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, SPECIAL OR
	 * CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE,
	 * REGARDLESS OF WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. LICENSEE AGREES THAT IT WILL NOT
	 * MAKE ANY WARRANTY ON BEHALF OF CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON CONCERNING THE
	 * APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE DELIVERABLES UNDER THIS LICENSE.
	 *
	 * Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie Mellon University, its trustees, officers,
	 * employees, and agents from all claims or demands made against them (and any related losses, expenses, or
	 * attorney's fees) arising out of, or relating to Licensee's and/or its sub licensees' negligent use or willful
	 * misuse of or negligent conduct or willful misconduct regarding the Software, facilities, or other rights or
	 * assistance granted by Carnegie Mellon University under this License, including, but not limited to, any claims of
	 * product liability, personal injury, death, damage to property, or violation of any laws or regulations.
	 *
	 * Carnegie Mellon University Software Engineering Institute authored documents are sponsored by the U.S. Department
	 * of Defense under Contract F19628-00-C-0003. Carnegie Mellon University retains copyrights in all material produced
	 * under this contract. The U.S. Government retains a non-exclusive, royalty-free license to publish or reproduce these
	 * documents, or allow others to do so, for U.S. Government purposes only pursuant to the copyright license
	 * under the contract clause at 252.227.7013.
	 * </copyright>
	 */
	public static org.osate.ba.aadlba.FeatureType getFeatureType(Element el) {
		if (el instanceof DataPort) {
			switch (((DataPort) el).getDirection()) {
			case IN:
				return FeatureType.IN_DATA_PORT;
			case OUT:
				return FeatureType.OUT_DATA_PORT;
			case IN_OUT:
				return FeatureType.IN_OUT_DATA_PORT;
			}
		} else if (el instanceof EventPort) {
			switch (((EventPort) el).getDirection()) {
			case IN:
				return FeatureType.IN_EVENT_PORT;
			case OUT:
				return FeatureType.OUT_EVENT_PORT;
			case IN_OUT:
				return FeatureType.IN_OUT_EVENT_PORT;
			}
		} else if (el instanceof EventDataPort) {
			switch (((EventDataPort) el).getDirection()) {
			case IN:
				return FeatureType.IN_EVENT_DATA_PORT;
			case OUT:
				return FeatureType.OUT_EVENT_DATA_PORT;
			case IN_OUT:
				return FeatureType.IN_OUT_EVENT_DATA_PORT;
			}
		} else if (el instanceof FeatureGroup) {
			return FeatureType.FEATURE_GROUP;
		} else if (el instanceof DataAccess) {
			switch (((DataAccess) el).getKind()) {
			case PROVIDES:
				return FeatureType.PROVIDES_DATA_ACCESS;
			case REQUIRES:
				return FeatureType.REQUIRES_DATA_ACCESS;
			}
		} else if (el instanceof SubprogramAccess) {
			switch (((SubprogramAccess) el).getKind()) {
			case PROVIDES:
				return FeatureType.PROVIDES_SUBPROGRAM_ACCESS;
			case REQUIRES:
				return FeatureType.REQUIRES_SUBPROGRAM_ACCESS;
			}
		} else if (el instanceof SubprogramGroupAccess) {
			switch (((SubprogramGroupAccess) el).getKind()) {
			case PROVIDES:
				return FeatureType.PROVIDES_SUBPROGRAM_GROUP_ACCESS;
			case REQUIRES:
				return FeatureType.REQUIRES_SUBPROGRAM_GROUP_ACCESS;
			}
		} else if (el instanceof BusAccess) {
			switch (((BusAccess) el).getKind()) {
			case PROVIDES:
				return FeatureType.PROVIDES_BUS_ACCESS;
			case REQUIRES:
				return FeatureType.REQUIRES_BUS_ACCESS;
			}
		} else if (el instanceof AbstractFeature) {
			return FeatureType.ABSTRACT_FEATURE;
		} else if (el instanceof Parameter) {
			switch (((Parameter) el).getDirection()) {
			case IN:
				return FeatureType.IN_PARAMETER;

			case OUT:
				return FeatureType.OUT_PARAMETER;

			case IN_OUT:
				return FeatureType.IN_OUT_PARAMETER;
			}
		} else if (el instanceof Prototype) {
			if (el instanceof ComponentPrototype) {
				switch (el.eClass().getClassifierID()) {
				case Aadl2Package.SUBPROGRAM_PROTOTYPE:
					return FeatureType.SUBPROGRAM_PROTOTYPE;
				case Aadl2Package.SUBPROGRAM_GROUP_PROTOTYPE:
					return FeatureType.SUBPROGRAM_GROUP_PROTOTYPE;
				case Aadl2Package.THREAD_PROTOTYPE:
					return FeatureType.THREAD_PROTOTYPE;
				case Aadl2Package.THREAD_GROUP_PROTOTYPE:
					return FeatureType.THREAD_GROUP_PROTOTYPE;
				default:
					return FeatureType.COMPONENT_PROTOTYPE;
				}
			} else if (el instanceof FeaturePrototype) {
				return getFeaturePrototypeType((FeaturePrototype) el);
			} else if (el instanceof FeatureGroupPrototype) {
				return FeatureType.FEATURE_GROUP_PROTOTYPE;
			}
		} else if (el instanceof PrototypeBinding) {
			if (el instanceof ComponentPrototypeBinding) {
				return FeatureType.COMPONENT_PROTOTYPE_BINDING;
			} else if (el instanceof FeatureGroupPrototypeBinding) {
				return FeatureType.FEATURE_GROUP_PROTOTYPE_BINDING;
			} else // FeaturePrototypeBinding case.
			{
				return FeatureType.FEATURE_PROTOTYPE_BINDING;
			}
		} else if (el instanceof org.osate.aadl2.PropertyConstant) {
			return FeatureType.PROPERTY_CONSTANT;
		} else if (el instanceof org.osate.aadl2.Property) {
			return FeatureType.PROPERTY_VALUE;
		} else if (el instanceof ClassifierValue) {
			return FeatureType.CLASSIFIER_VALUE;
		} else if (el instanceof SubprogramGroup) {
			return FeatureType.SUBPROGRAM_GROUP;
		} else if (el instanceof SubprogramGroupAccess) {
			switch (((SubprogramGroupAccess) el).getKind()) {
			case PROVIDES:
				return FeatureType.PROVIDES_SUBPROGRAM_GROUP_ACCESS;
			case REQUIRES:
				return FeatureType.REQUIRES_SUBPROGRAM_GROUP_ACCESS;
			}
		} else if (el instanceof ThreadGroup) {
			return FeatureType.THREAD_GROUP;
		} else if (el instanceof SystemSubcomponent) {
			return FeatureType.SYSTEM_SUBCOMPONENT;
		} else if (el instanceof SubprogramSubcomponent) {
			return FeatureType.SUBPROGRAM_SUBCOMPONENT;
		} else if (el instanceof SubprogramClassifier) {
			return FeatureType.SUBPROGRAM_CLASSIFIER;
		} else if (el instanceof DataSubcomponent) {
			return FeatureType.DATA_SUBCOMPONENT;
		} else if (el instanceof DataClassifier) {
			return FeatureType.DATA_CLASSIFIER;
		} else if (el instanceof ProcessorClassifier) {
			return FeatureType.PROCESSOR_CLASSIFIER;
		} else if (el instanceof ProcessClassifier) {
			return FeatureType.PROCESS_CLASSIFIER;
		}

		String errorMsg = "getFeatureType : " + el.getClass().getSimpleName() + " is not supported yet at line "
				+ Aadl2Utils.getLocationReference(el).getLine() + ".";
		System.err.println(errorMsg);
		throw new UnsupportedOperationException(errorMsg);
	}

	/**
	 * Analyze the given behavior annex feature and returns its type.
	 *
	 * @param el the given behavior annex feature
	 * @return the given behavior annex feature's type
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static BehaviorFeatureType getBehaviorAnnexFeatureType(BehaviorElement el) {
		if (el instanceof org.osate.ba.aadlba.BehaviorVariable) {
			return BehaviorFeatureType.BEHAVIOR_VARIABLE;
		} else if (el instanceof IterativeVariable) {
			return BehaviorFeatureType.ITERATIVE_VARIABLE;
		}

		String errorMsg = "feature : " + el.getClass().getSimpleName() + " is not supported yet.";
		System.err.println(errorMsg);
		throw new UnsupportedOperationException(errorMsg);
	}

	/**
	 * If the given value expression is composed of an single value, it returns
	 * this value otherwise {@code null}. Recursive method.
	 *
	 * @param ve the given value expression
	 * @return the value or {@code null}
	 */
	public static Value isOnlyOneValue(ValueExpression ve) {
		Relation firstRelation = ve.getRelations().get(0);
		SimpleExpression firstSE = firstRelation.getFirstExpression();
		Term firstTerm = firstSE.getTerms().get(0);
		Factor firstFactor = firstTerm.getFactors().get(0);
		Value firstValue = firstFactor.getFirstValue();

		if (ve.getRelations().size() == 1) {
			if (firstRelation.getSecondExpression() == null) {
				if (firstSE.getTerms().size() == 1) {
					if (firstTerm.getFactors().size() == 1) {
						if (firstFactor.getSecondValue() == null) {
							// Recursive case.
							if (firstValue instanceof ValueExpression) {
								return isOnlyOneValue((ValueExpression) firstValue);
							} else {
								return firstValue;
							}
						}
					}
				}
			}
		}

		return null;
	}

	/**
	 * Translates the given ComponentPrototypeBinding object into a FeatureType
	 * enumeration.
	 *
	 * @param cpb the given ComponentPrototypeBinding
	 * @return the translation in FeatureType object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static FeatureType getCompPrototypeType(ComponentPrototypeBinding cpb) {
		ComponentPrototypeActual cpa = cpb.getActuals().get(cpb.getActuals().size() - 1);
		switch (cpa.getCategory()) {
		case SUBPROGRAM:
			return FeatureType.SUBPROGRAM_PROTOTYPE;
		case SUBPROGRAM_GROUP:
			return FeatureType.SUBPROGRAM_GROUP_PROTOTYPE;
		case THREAD:
			return FeatureType.THREAD_PROTOTYPE;
		case THREAD_GROUP:
			return FeatureType.THREAD_GROUP_PROTOTYPE;

		default: {
			// Reports error.
			String errorMsg = "getCompPrototypeType : " + cpa.getCategory() + " is not supported yet.";
			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}
		}
	}

	/**
	 * Translates the given FeaturePrototypeBinding object into a FeatureType
	 * enumeration.
	 *
	 * @param fpb the given FeaturePrototypeBinding
	 * @return the translation in FeatureType object
	 * @exception UnsupportedOperationException for the unsupported types
	 */
	public static FeatureType getFeatPrototypeType(FeaturePrototypeBinding fpb) {
		FeaturePrototypeActual fpa = fpb.getActual();

		if (fpa instanceof AccessSpecification) {
			AccessSpecification as = (AccessSpecification) fpa;

			boolean isRequired = as.getKind() == AccessType.REQUIRES;

			switch (as.getCategory()) {
			case BUS: {
				return (isRequired) ? FeatureType.REQUIRES_BUS_ACCESS_PROTOTYPE
						: FeatureType.PROVIDES_BUS_ACCESS_PROTOTYPE;
			}

			case DATA: {
				return (isRequired) ? FeatureType.REQUIRES_DATA_ACCESS_PROTOTYPE
						: FeatureType.PROVIDES_DATA_ACCESS_PROTOTYPE;
			}

			case SUBPROGRAM: {
				return (isRequired) ? FeatureType.REQUIRES_SUBPROGRAM_ACCESS_PROTOTYPE
						: FeatureType.PROVIDES_SUBPROGRAM_ACCESS_PROTOTYPE;
			}

			case SUBPROGRAM_GROUP: {
				return (isRequired) ? FeatureType.REQUIRES_SUBPROGRAM_GROUP_ACCESS_PROTOTYPE
						: FeatureType.PROVIDES_SUBPROGRAM_GROUP_ACCESS_PROTOTYPE;
			}

			}
		} else if (fpa instanceof PortSpecification) {
			PortSpecification ps = (PortSpecification) fpa;

			if (ps.getCategory() == PortCategory.DATA) {
				switch (ps.getDirection()) {
				case IN:
					return FeatureType.IN_DATA_PORT_PROTOTYPE;
				case OUT:
					return FeatureType.OUT_DATA_PORT_PROTOTYPE;
				case IN_OUT:
					return FeatureType.IN_OUT_DATA_PORT_PROTOTYPE;
				}
			} else if (ps.getCategory() == PortCategory.EVENT) {
				switch (ps.getDirection()) {
				case IN:
					return FeatureType.IN_EVENT_PORT_PROTOTYPE;
				case OUT:
					return FeatureType.OUT_EVENT_PORT_PROTOTYPE;
				case IN_OUT:
					return FeatureType.IN_OUT_EVENT_PORT_PROTOTYPE;
				}
			} else {
				switch (ps.getDirection()) {
				case IN:
					return FeatureType.IN_EVENT_DATA_PORT_PROTOTYPE;
				case OUT:
					return FeatureType.OUT_EVENT_DATA_PORT_PROTOTYPE;
				case IN_OUT:
					return FeatureType.IN_OUT_EVENT_DATA_PORT_PROTOTYPE;
				}
			}
		} else if (fpa instanceof FeatureGroupPrototypeActual) {
			return FeatureType.FEATURE_GROUP_PROTOTYPE;
		} else {
			// Reports error.
			String errorMsg = "getFeatPrototypeType : " + fpa.getClass().getSimpleName() + " is not supported yet.";
			System.err.println(errorMsg);
			throw new UnsupportedOperationException(errorMsg);
		}

		return FeatureType.NONE;
	}

	private static FeatureType getFeaturePrototypeType(FeaturePrototype fp) {
		switch (fp.getDirection()) {
		case IN:
			return FeatureType.IN_FEATURE_PROTOTYPE;
		case OUT:
			return FeatureType.OUT_FEATURE_PROTOTYPE;
		case IN_OUT:
			return FeatureType.IN_OUT_FEATURE_PROTOTYPE;
		default:
			return FeatureType.FEATURE_PROTOTYPE;
		}
	}

	/**
	 * Returns the direction type of the given Element or {@code null} if the
	 * given Element is not an instance of DirectedFeature excepted
	 * DataSubcomponent.<BR><BR>
	 *
	 * Note: DataSubcomponent returns DirectionType.IN_OUT <BR><BR>
	 *
	 * @param el the given Element
	 * @return the direction type or {@code null}
	 */
	public static DirectionType getDirectionType(Element el) {
		if (el instanceof DirectedFeature) {
			return ((DirectedFeature) el).getDirection();
		} else if (el instanceof DataSubcomponent) {
			return DirectionType.IN_OUT;
		} else {
			return null;
		}
	}

	/**
	 * Returns the last value of the base type property of the given component or
	 * {@code null} if the base type property is not set.
	 *
	 * @param component the given component
	 * @return the last value of the base type property or {@code null}
	 */
	public static ClassifierValue getBaseType(Classifier component) {
		PropertyExpression pe;
		EList<PropertyExpression> le;
		Element el;

		EList<PropertyExpression> lpe = PropertyUtils.findPropertyExpression(component, DataModelProperties.BASE_TYPE);
		if (lpe != null && (!lpe.isEmpty())) {
			pe = lpe.get(lpe.size() - 1);

			// Syntax with ()
			if (pe instanceof ListValue) {
				le = ((ListValue) pe).getOwnedListElements();

				if (le != null && (!le.isEmpty())) {
					el = le.get(le.size() - 1);

					if (el instanceof ClassifierValue) {
						return (ClassifierValue) el;
					}
				}
			}
			// Syntax without ()
			else if (pe instanceof ClassifierValue) {
				return (ClassifierValue) pe;
			}
		}

		return null;
	}

	/**
	 * Returns the direction type of the object binded to the given Target or
	 * {@code null} if the binded object is not an instance of : <BR><BlockQuote>
	 * _ DirectedFeature.<BR>
	 * _ Data subcomponent.<BR>
	 * _ Local variable.<BR></BlockQuote>
	 * (see {@link #getDirectionType(org.osate.aadl2.Element)}).
	 * <BR><BR>
	 * Notes:<BR><BR>
	 * _  Behavior variable always returns DirectionType.IN_OUT as
	 *    behavior variable is a valid Target and ValueVariable.
	 * <BR><BR>
	 * _ Iterative variable always returns DirectionType.IN as iterative variables
	 *   is a valid value variable but not a target.
	 * <BR><BR>
	 * _ Data subcomponent always returns DirectionType.IN_OUT as
	 *   data subcomponent is valid Target and ValueVariable.
	 *
	 * <BR><BR>
	 * @param tar the given Target
	 * @return the direction type or {@code null}
	 */
	public static DirectionType getDirectionType(Target tar) {
		if (tar instanceof IterativeVariable) {
			return DirectionType.IN;
		} else if (tar instanceof BehaviorVariableHolder) {
			return DirectionType.IN_OUT;
		} else if (tar instanceof ElementHolder) // The other ElementHolders.
		{
			return getDirectionType(((ElementHolder) tar).getElement());
		} else // Data component reference.
		{
			EList<DataHolder> dhl = ((DataComponentReference) tar).getData();

			return getDirectionType(dhl.get(dhl.size() - 1).getElement());
		}
	}

	/**
	 * If the given Target object is a DataAccessHolder object or a
	 * DataComponentReference object which the last element is a DataAccessHolder
	 * object, it returns the data access right or "unknown" if the default
	 * data access right is not set.
	 *
	 * @see org.osate.utils.internal.Aadl2Utils#getAccessRight
	 * @param tar the given Target object
	 * @return the data access right or "unknown"
	 */
	public static String getAccessRight(Target tar) {
		ElementHolder el = null;

		if (tar instanceof ElementHolder) // The other ElementHolders.
		{
			el = (ElementHolder) tar;
		} else // Data component reference.
		{
			EList<DataHolder> dhl = ((DataComponentReference) tar).getData();
			el = dhl.get(dhl.size() - 1);
		}

		if (el instanceof DataAccessHolder || el instanceof DataAccessPrototypeHolder
				|| el instanceof FeaturePrototypeHolder) {
			return Aadl2Utils.getAccessRight(el.getElement());
		} else {
			return "unknown";
		}
	}

	/**
	 * Same as {@link #getAccessRight(Target)}, if the given Target object is a
	 * DataAccessHolder object or a DataComponentReference object which the last
	 * element is a DataAccessHolder object, it returns the DataAccessRight enum
	 * reference or {@link org.osate.utils.internal.Aadl2Utils.DataAccessRight#unknown} if the default data access
	 * right is not set.
	 *
	 * @see org.osate.utils.internal.Aadl2Utils#getAccessRight
	 * @param tar the given Target object
	 * @return the data access right or {@link org.osate.utils.internal.Aadl2Utils.DataAccessRight#unknown}
	 */
	public static DataAccessRight getDataAccessRight(Target tar) {
		return DataAccessRight.getDataAccessRight(getAccessRight(tar));
	}

	/**
	 * Returns the name of the given ModeTransitionTrigger object.
	 *
	 * @param mtt the given ModeTransitionTrigger object
	 * @return a name
	 */
	public static String getName(ModeTransitionTrigger mtt) {
		return mtt.getTriggerPort().getName();
	}

	/**
	 * Compares behavior transition priorities.
	 *
	 * Returns {@code true} if bt1 priority is > bt2 or bt2 has otherwise execution
	 * condition and bt1 hasn't. Otherwise returns {@code false}.
	 *
	 * @param bt1 a behavior transition
	 * @param bt2 an other behavior transition
	 * @return  {@code true} if bt1 priority is > bt2 or bt2 has otherwise execution
	 * condition and bt1 hasn't. Otherwise {@code false}.
	 */
	public static boolean compareBehaviorTransitionPriority(BehaviorTransition bt1, BehaviorTransition bt2) {
		boolean o1 = bt1.getCondition() instanceof Otherwise;
		boolean o2 = bt2.getCondition() instanceof Otherwise;

		long p1 = (bt1.getPriority() >= 0) ? (bt1.getPriority()) : (AadlBaVisitors.DEFAULT_TRANSITION_PRIORITY);

		long p2 = (bt2.getPriority() >= 0) ? (bt2.getPriority()) : (AadlBaVisitors.DEFAULT_TRANSITION_PRIORITY);

		boolean p = p1 > p2;

		if (o1 || o2) {
			if (o1 && o2) {
				return p;
			} else {
				return o2;
			}
		} else {
			return p;
		}
	}

	/**
	 * If the given PropertyReference object represents a string literal from
	 * a property string list declared in a classifier (property Enumerators), it
	 * returns the Classifier object and the StringLiteral object
	 * (see DataModelEnumLiteral). Otherwise, it throws an
	 * OperationNotSupportedException.
	 *
	 * @see DataModelEnumLiteral
	 * @param ref the given PropertyReference object
	 * @return a DataModelEnumLiteral object
	 * @throws OperationNotSupportedException
	 */
	public static DataModelEnumLiteral toDataModelEnumLiteral(PropertyReference ref)
			throws OperationNotSupportedException {
		DataModelEnumLiteral result = new DataModelEnumLiteral();

		if (ref instanceof ClassifierPropertyReference) {
			ClassifierPropertyReference cpr = (ClassifierPropertyReference) ref;
			result.classifier = cpr.getClassifier();

			EList<PropertyNameHolder> properties = cpr.getProperties();

			if (properties.size() == 2) {
				// The first property name is supposed to be a property association
				// Enumerators. Don't need to check it.

				result.stringLiteral = extractStringLiteral(properties.get(1));
			} else {
				throw new OperationNotSupportedException("this property reference not supported");
			}

		} else {
			throw new OperationNotSupportedException(ref.getClass().getSimpleName() + " not supported");
		}

		return result;
	}

	// If the given PropertyNameHolder object only contains a StringLiteral,
	// it returns it. Otherwise it throws an OperationNotSupportedException.
	private static StringLiteral extractStringLiteral(PropertyNameHolder pnh) throws OperationNotSupportedException {
		if (pnh.getField() != null) {
			throw new OperationNotSupportedException("property fields not supported");
		} else {
			Element el = pnh.getProperty().getElement();

			if (el instanceof StringLiteral) {
				return (StringLiteral) el;
			} else {
				throw new UnsupportedOperationException(el.getClass().getSimpleName() + " not supported");
			}
		}
	}
}