EMV2Util.java

/**
 * Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file).
 * All Rights Reserved.
 *
 * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
 * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
 * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 *
 * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 * SPDX-License-Identifier: EPL-2.0
 *
 * Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
 *
 * This program includes and/or can make use of certain third party source code, object code, documentation and other
 * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
 * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
 * conditions contained in any such Third Party Software or separate license file distributed with such Third Party
 * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
 * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
 * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
 */
package org.osate.xtext.aadl2.errormodel.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.AadlPackage;
import org.osate.aadl2.AnnexSubclause;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentClassifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.ComponentType;
import org.osate.aadl2.Connection;
import org.osate.aadl2.ContainedNamedElement;
import org.osate.aadl2.ContainmentPathElement;
import org.osate.aadl2.DefaultAnnexSubclause;
import org.osate.aadl2.DirectedFeature;
import org.osate.aadl2.DirectionType;
import org.osate.aadl2.Element;
import org.osate.aadl2.Feature;
import org.osate.aadl2.FeatureGroup;
import org.osate.aadl2.FeatureGroupType;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.ConnectionReference;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.aadl2.instance.FlowSpecificationInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.modelsupport.modeltraversal.ForAllElement;
import org.osate.aadl2.modelsupport.scoping.Aadl2GlobalScopeUtil;
import org.osate.aadl2.modelsupport.util.AadlUtil;
import org.osate.aadl2.util.Aadl2InstanceUtil;
import org.osate.aadl2.util.Aadl2Util;
import org.osate.aadl2.util.OsateDebug;
import org.osate.xtext.aadl2.errormodel.errorModel.AllExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.AndExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.CompositeState;
import org.osate.xtext.aadl2.errormodel.errorModel.ConditionElement;
import org.osate.xtext.aadl2.errormodel.errorModel.ConditionExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2Path;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2PathElement;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorEvent;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorState;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorStateMachine;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorTransition;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorDetection;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorFlow;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelLibrary;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelPackage;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorPath;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorSink;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorSource;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorStateToModeMapping;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorType;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorTypes;
import org.osate.xtext.aadl2.errormodel.errorModel.EventOrPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.FeatureorPPReference;
import org.osate.xtext.aadl2.errormodel.errorModel.OrExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.OrlessExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.OrmoreExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.OutgoingPropagationCondition;
import org.osate.xtext.aadl2.errormodel.errorModel.PropagationPath;
import org.osate.xtext.aadl2.errormodel.errorModel.PropagationPoint;
import org.osate.xtext.aadl2.errormodel.errorModel.QualifiedErrorBehaviorState;
import org.osate.xtext.aadl2.errormodel.errorModel.QualifiedPropagationPoint;
import org.osate.xtext.aadl2.errormodel.errorModel.SConditionElement;
import org.osate.xtext.aadl2.errormodel.errorModel.SubcomponentElement;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeMappingSet;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeSet;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeToken;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeTransformationSet;
import org.osate.xtext.aadl2.errormodel.errorModel.impl.AndExpressionImpl;
import org.osate.xtext.aadl2.errormodel.errorModel.impl.OrExpressionImpl;

public class EMV2Util {

	private EMV2Util() {
	}

	public static final String ErrorModelAnnexName = "EMV2";

	public static String getLibraryName(ErrorModelLibrary lib) {
		AadlPackage root = EcoreUtil2.getContainerOfType(lib, AadlPackage.class);
		if (root != null) {
			return root.getName();
		}
		return lib.getName();
	}

	public static ErrorSource getErrorSource(ComponentInstance ci, ErrorPropagation ep) {
		Collection<ErrorFlow> flows = getAllErrorFlows(ci);
		for (ErrorFlow ef : flows) {
			if (ef instanceof ErrorSource) {
				ErrorSource es = (ErrorSource) ef;
				if (es.getSourceModelElement() == ep) {
					return es;
				}

			}
		}
		return null;
	}

	/**
	 * get ErrorModelSubclause object in the classifier
	 * @param cl classifier
	 * @return ErrorModelSubclause
	 */
	private static void getClassifierEMV2Subclause(ComponentClassifier cl, EList<ErrorModelSubclause> result) {
		ErrorModelSubclause ems = getOwnEMV2Subclause(cl);
		if (ems != null) {
			result.add(ems);
		}
	}

	/**
	 * recursively add all EMV2 subclauses of classifier and its extends ancestors
	 * in case of implementations do not follow to the type
	 * cl can be null or an unresolved proxy.
	 * if cl has a subclause its subclause is added before following the extends hierarhy
	 * @param cl Component Implementation
	 * @param result list of EMV2 subclauses
	 */
	private static void getAllClassifierEMV2Subclause(ComponentClassifier cl, EList<ErrorModelSubclause> result) {
		if (!Aadl2Util.isNull(cl)) {
			getClassifierEMV2Subclause(cl, result);
			getAllClassifierEMV2Subclause((ComponentClassifier) cl.getExtended(), result);
		}
	}

	/**
	 * return the list of EMV2 subclauses of the classifier and
	 * The extends hierarchy and the type in the case of an implementation are searched for the ErrorModelSubcany of its extends ancestors
	 * @param element declarative model element or error annex element or instance object
	 * @return ErrorModelSubclause
	 */
	public static EList<ErrorModelSubclause> getAllContainingClassifierEMV2Subclauses(Element element) {
		Classifier cl = null;
		if (element instanceof InstanceObject) {
			ComponentInstance ci = ((InstanceObject) element).getComponentInstance();
			cl = ci.getComponentClassifier();
		} else if (element != null) {
			cl = getAssociatedClassifier(element);
		}
		EList<ErrorModelSubclause> result = new BasicEList<ErrorModelSubclause>();
		if (cl == null) {
			ErrorModelSubclause localemsc = getContainingErrorModelSubclause(element);
			if (localemsc != null) {
				result.add(localemsc);
			}
			return result;
		}
		if (cl instanceof ComponentImplementation) {
			getAllClassifierEMV2Subclause((ComponentImplementation) cl, result);
			getAllClassifierEMV2Subclause(((ComponentImplementation) cl).getType(), result);
		} else if (cl instanceof ComponentType) {
			getAllClassifierEMV2Subclause((ComponentType) cl, result);
		}
		return result;
	}

	/**
	 * return the classifier that this subclause element belongs to.
	 * The subclause can be embedded or separate.
	 * @param an EObject in a EMV2 subclause
	 * @return Classifier
	 * @since 6.0
	 */
	public static Classifier getAssociatedClassifier(Element emv2Element) {

		Classifier cl = emv2Element.getContainingClassifier();
		if (cl != null) {
			return cl;
		}
		ErrorModelSubclause emsc = getContainingErrorModelSubclause(emv2Element);
		if (emsc == null || !emsc.getName().equalsIgnoreCase("EMV2")) {
			// we are not inside an EMV2 subclause
			return null;
		}
		return Aadl2GlobalScopeUtil.get(emsc, Aadl2Package.eINSTANCE.getComponentClassifier(), emsc.getQualifiedName());
	}

	/**
	 * get the separately stored EMV2 subclause, which is assumed to have the name of the classifier
	 * @param cl Classifier
	 * @return ErrorModelSubclause
	 * @since 6.0
	 */
	public static ErrorModelSubclause getAssociatedEMV2Subclause(Classifier cl) {
		return Aadl2GlobalScopeUtil.get(cl, ErrorModelPackage.eINSTANCE.getErrorModelSubclause(),
				cl.getQualifiedName());
	}

	/**
	 * get the error model subclause for the specified classifier.
	 * Do it for the separately stored first, if not found then embedded subclause
	 * Does not look in the extends hierarchy
	 * @param cl CLassifier
	 * @return
	 */
	public static ErrorModelSubclause getOwnEMV2Subclause(ComponentClassifier cl) {
		if (cl == null) {
			return null;
		}
		// separately stored EMV2 subclause
		ErrorModelSubclause emsc = getAssociatedEMV2Subclause(cl);
		if (emsc != null) {
			return emsc;
		}
		// embedded EMV2 subclause
		return getEmbeddedEMV2Subclause(cl);
	}

	/**
	 * @since 6.0
	 */
	public static ErrorModelSubclause getEmbeddedEMV2Subclause(Classifier cl) {
		// embedded EMV2 subclause
		EList<AnnexSubclause> asl = cl.getOwnedAnnexSubclauses();
		for (AnnexSubclause al : asl) {
			AnnexSubclause actal = ((DefaultAnnexSubclause) al).getParsedAnnexSubclause();
			if (actal instanceof ErrorModelSubclause) {
				return ((ErrorModelSubclause) actal);
			}
		}
		return null;

	}

	/**
	 * find the first subclause on the classifier or its extends hierarchy.
	 * When used on a component implementation this method does not go from an implementation to a type.
	 *
	 * @param cl
	 * @return
	 */
	public static ErrorModelSubclause getExtendsEMV2Subclause(ComponentClassifier cl) {
		if (cl == null) {
			return null;
		}
		ErrorModelSubclause ems = getOwnEMV2Subclause(cl);
		if (ems != null) {
			return ems;
		}
		if (!Aadl2Util.isNull(cl.getExtended())) {
			ems = getExtendsEMV2Subclause((ComponentClassifier) cl.getExtended());
			if (ems != null) {
				return ems;
			}
		}
		return null;
	}

	/**
	 * return first error model subclause that has a use types and return the list of libraries
	 * @param cl
	 * @return EList<ErrorModelLibrary>
	 */
	public static EList<ErrorModelLibrary> getErrorModelSubclauseWithUseTypes(Classifier cl) {
		EList<ErrorModelSubclause> result = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : result) {
			return errorModelSubclause.getUseTypes();
		}
		return null;
	}

	/**
	 * Check to see if all NamedElements in the Elist have a unique name.
	 * Do not account for Error Propagations. They are checked separately.
	 * this method handles error propagations as well, using their printname.
	 *
	 * list can contain object that are not NamedElements.
	 * @param el EList or NamedElements or other objects
	 * @return EList of NameElements that are defining a previously defined name
	 */
	public static List<NamedElement> findDoubleNamedElementsInList(Collection<?> el) {
		List<NamedElement> result = new ArrayList<NamedElement>();
		final Set<String> seen = new HashSet<String>();

		if (el != null) {
			for (final Object obj : el) {
				if (obj instanceof NamedElement && !(obj instanceof ErrorPropagation)) {
					final NamedElement lit = (NamedElement) obj;
					String name = lit.getName();
					if (name != null && !name.isEmpty()) {
						name = name.toLowerCase();
						if (!seen.add(name)) {
							result.add(lit);
						}
					}
				}
			}
		}
		return result;
	}

	/**
	 * find propagation point including those inherited from classifiers being extended
	 * @param element context
	 * @param name String
	 * @return PropagationPoint propagation point
	 */
	public static PropagationPoint findPropagationPoint(Element element, String name) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(element);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<PropagationPoint> eflist = errorModelSubclause.getPoints();
			for (PropagationPoint propPoint : eflist) {
				if (name.equalsIgnoreCase(propPoint.getName())) {
					return propPoint;
				}
			}
		}
		return null;
	}

	public static String getPath(FeatureorPPReference ref) {
		if (ref == null) {
			return "";
		}
		if (ref.getNext() != null) {
			return ref.getFeatureorPP().getName() + "." + getPath(ref.getNext());
		} else {
			return ref.getFeatureorPP().getName();
		}
	}

	/**
	 * Find error propagation in classifier hierarchy
	 * name can be a dotted name
	 * @param elem the context whose containing classifier is searched hierarchically to find the error propagation
	 * @param name
	 * @param dir
	 * @return
	 */
	public static ErrorPropagation findErrorContainment(Element elem, String name, DirectionType dir) {
		Classifier cl = getAssociatedClassifier(elem);
		if (cl != null) {
			return findErrorContainment(cl, name, dir);
		}
		return null;
	}

	/**
	 * Find error propagation  starting with the specified classifier
	 * goes up the inheritance hierarchy
	 * name can be a dotted name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param name
	 * @param dir
	 * @return
	 */
	public static ErrorPropagation findErrorContainment(Classifier cl, String name, DirectionType dir) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorPropagation> eflist = errorModelSubclause.getPropagations();
			for (ErrorPropagation ep : eflist) {
				if (ep.isNot() && ep.getDirection().equals(dir)) {
					if (ep.getKind() != null) {
						if (ep.getKind().equalsIgnoreCase(name)) {
							return ep;
						}
					} else {
						String refname = EMV2Util.getPath(ep.getFeatureorPPRef());
						if (refname.equalsIgnoreCase(name)) {
							return ep;
						}
					}
				}
			}
		}
		return null;
	}

	public static ErrorPropagation findSubcomponentOrIncomingErrorProparation(Element elem, String name) {
		Classifier cl = getAssociatedClassifier(elem);
		if (cl == null) {
			return null;
		}
		EList<Subcomponent> subs;
		int idx = name.indexOf('.');
		boolean foundSub = false;
		boolean findMore = true;
		while (idx != -1 && findMore) {
			String subname = name.substring(0, idx);
			findMore = false;
			if (cl instanceof ComponentImplementation) {
				subs = ((ComponentImplementation) cl).getAllSubcomponents();
				for (Subcomponent sub : subs) {
					if (sub.getName().equalsIgnoreCase(subname)) {
						name = name.substring(idx + 1);
						cl = sub.getClassifier();
						idx = name.indexOf('.');
						foundSub = true;
						findMore = true;
					}
				}
			}
		}

		if (foundSub) {
			return EMV2Util.findErrorPropagation(cl, name, DirectionType.OUT);
		} else {
			return EMV2Util.findErrorPropagation(cl, name, DirectionType.IN);
		}
	}

	/**
	 * Find error propagation in classifier hierarchy
	 * name can be a dotted name
	 * @param elem the context whose containing classifier is searched hierarchically to find the error propagation
	 * @param name
	 * @param dir
	 * @return
	 */
	public static ErrorPropagation findErrorPropagation(Element elem, String name, DirectionType dir) {
		Classifier cl = getAssociatedClassifier(elem);
		if (cl != null) {
			return findErrorPropagation(cl, name, dir);
		}
		return null;
	}

	/**
	 * Find error propagation in classifier hierarchy
	 * name can be a dotted name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param name
	 * @param dir
	 * @return
	 */
	public static ErrorPropagation findErrorPropagation(Classifier cl, String name, DirectionType dir) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorPropagation> eflist = errorModelSubclause.getPropagations();
			for (ErrorPropagation ep : eflist) {
				if (!ep.isNot() && ep.getDirection().equals(dir)) {
					if (ep.getKind() != null) {
						if (ep.getKind().equalsIgnoreCase(name)) {
							return ep;
						}
					} else {
						String refname = EMV2Util.getPath(ep.getFeatureorPPRef());
						if (refname != null && name.equalsIgnoreCase(refname)) {
							return ep;
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * find the incoming error propagation point of the specified name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param eppName Name of error propagation point we are looking for
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation findIncomingErrorPropagation(Classifier cl, String eppName) {
		return findErrorPropagation(cl, eppName, DirectionType.IN);
	}

	/**
	 * find the outgoing error propagation point of the specified name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param eppName Name of error propagation point we are looking for
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation findOutgoingErrorPropagation(Classifier cl, String eppName) {
		return findErrorPropagation(cl, eppName, DirectionType.OUT);
	}

	/**
	 * find the outgoing error containment point of the specified name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param eppName Name of error propagation point we are looking for
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation findOutgoingErrorContainment(Classifier cl, String eppName) {
		return findErrorContainment(cl, eppName, DirectionType.OUT);
	}

	/**
	 * find the incoming error propagation point of the specified name
	 * @param cl the classifier in the context of which we find the error propagation
	 * @param eppName Name of error propagation point we are looking for
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation findIncomingErrorContainment(Classifier cl, String eppName) {
		return findErrorContainment(cl, eppName, DirectionType.IN);
	}

	public static String getFeatureInstancePath(FeatureInstance fi) {
		String res = fi.getName();
		NamedElement current = fi;
		while (current.getOwner() instanceof FeatureInstance) {
			current = (NamedElement) current.getOwner();
			res = current.getName() + "." + res;
		}
		return res;
	}

	/**
	 * Get outgoing error propagation associated with feature instance or its enclosing feature instance
	 * Find it for the current feature instance or any enclosing feature instances
	 * @param fi feature instance
	 * @return error propagation
	 */
	public static ErrorPropagation getOutgoingErrorPropagation(FeatureInstance fi) {
		if (!fi.getFlowDirection().outgoing()) {
			return null;
		}
		ComponentInstance ci = fi.getContainingComponentInstance();
		FeatureInstance current = fi;
		ErrorPropagation res = EMV2Util.findOutgoingErrorPropagation(ci.getComponentClassifier(),
				getFeatureInstancePath(current));
		while (res == null && current.getOwner() instanceof FeatureInstance) {
			current = (FeatureInstance) current.getOwner();
			res = EMV2Util.findOutgoingErrorPropagation(ci.getComponentClassifier(), getFeatureInstancePath(current));
		}
		return res;
	}

	/**
	 * Get incoming error propagation associated with feature instance or its enclosing feature instance
	 * @param fi feature instance
	 * @return error propagation
	 */
	public static ErrorPropagation getIncomingErrorPropagation(FeatureInstance fi) {
		if (!fi.getFlowDirection().incoming()) {
			return null;
		}
		ComponentInstance ci = fi.getContainingComponentInstance();
		FeatureInstance current = fi;
		ErrorPropagation res = EMV2Util.findIncomingErrorPropagation(ci.getComponentClassifier(),
				getFeatureInstancePath(current));
		while (res == null && current.getOwner() instanceof FeatureInstance) {
			current = (FeatureInstance) current.getOwner();
			return EMV2Util.findIncomingErrorPropagation(ci.getComponentClassifier(), getFeatureInstancePath(current));
		}
		return res;
	}

	/**
	 * Get outgoing error propagation associated with feature instance or its enclosing feature instance
	 * @param fi feature instance
	 * @return error propagation
	 */
	public static ErrorPropagation getOutgoingErrorContainment(FeatureInstance fi) {
		if (!fi.getFlowDirection().outgoing()) {
			return null;
		}
		ComponentInstance ci = fi.getContainingComponentInstance();
		FeatureInstance current = fi;
		ErrorPropagation res = EMV2Util.findOutgoingErrorContainment(ci.getComponentClassifier(),
				getFeatureInstancePath(current));
		while (res == null && current.getOwner() instanceof FeatureInstance) {
			current = (FeatureInstance) current.getOwner();
			return EMV2Util.findOutgoingErrorContainment(ci.getComponentClassifier(), getFeatureInstancePath(current));
		}
		return res;
	}

	/**
	 * Get incoming error propagation associated with feature instance or its enclosing feature instance
	 * @param fi feature instance
	 * @return error propagation
	 */
	public static ErrorPropagation getIncomingErrorContainment(FeatureInstance fi) {
		if (!fi.getFlowDirection().incoming()) {
			return null;
		}
		ComponentInstance ci = fi.getContainingComponentInstance();
		FeatureInstance current = fi;
		ErrorPropagation res = EMV2Util.findIncomingErrorContainment(ci.getComponentClassifier(),
				getFeatureInstancePath(current));
		while (res == null && current.getOwner() instanceof FeatureInstance) {
			current = (FeatureInstance) current.getOwner();
			return EMV2Util.findIncomingErrorContainment(ci.getComponentClassifier(), getFeatureInstancePath(current));
		}
		return res;
	}

	/**
	 * Get incoming error propagation associated with component instance access
	 * @param ci component instance
	 * @return error propagation
	 */
	public static ErrorPropagation getIncomingAccessErrorPropagation(ComponentInstance ci) {
		return EMV2Util.findIncomingErrorPropagation(ci.getComponentClassifier(), "access");
	}

	/**
	 * Get outgoing error propagation associated with component instance access
	 * @param ci component instance
	 * @return error propagation
	 */
	public static ErrorPropagation getOutgoingAccessErrorPropagation(ComponentInstance ci) {
		return EMV2Util.findOutgoingErrorPropagation(ci.getComponentClassifier(), "access");
	}

	/**
	 * Get incoming error Containment associated with component instance access
	 * @param ci component instance
	 * @return error propagation
	 */
	public static ErrorPropagation getIncomingAccessErrorContainment(ComponentInstance ci) {
		return EMV2Util.findIncomingErrorContainment(ci.getComponentClassifier(), "access");
	}

	/**
	 * Get outgoing error Containment associated with component instance access
	 * @param ci component instance
	 * @return error propagation
	 */
	public static ErrorPropagation getOutgoingAccessErrorContainment(ComponentInstance ci) {
		return EMV2Util.findOutgoingErrorContainment(ci.getComponentClassifier(), "access");
	}

	/**
	 * find error flow starting with given classifier by looking through all error flows
	 * @param cl
	 * @param name
	 * @return ErrorFlow
	 */
	public static ErrorFlow findErrorFlow(Element el, String name) {
		Classifier cl = getAssociatedClassifier(el);
		if (cl != null) {
			Collection<ErrorFlow> eflist = getAllErrorFlows(cl);
			return (ErrorFlow) AadlUtil.findNamedElementInList(eflist, name);
		}
		return null;
	}

	/**
	 * Find ConnectionErrorSource with given classifier by looking through all connection error sources
	 * @param conni the connection instance whose connection declarations we're using
	 * @return the connection error source or null
	 */

	public static ErrorSource findConnectionErrorSourceForConnection(ConnectionInstance conni) {
		for (ConnectionReference connref : conni.getConnectionReferences()) {
			Connection conn = connref.getConnection();
			Classifier cl = getAssociatedClassifier(conn);
			if (cl != null) {
				Collection<ErrorSource> ceslist = getAllConnectionErrorSources(cl);
				for (ErrorSource ces : ceslist) {
					if (ces.isAll() || ces.getSourceModelElement().getName().equalsIgnoreCase(conn.getName())) {
						return ces;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Find ErrorType with given name by looking through all error types
	 * referenced in all EMV2 subclauses of the supplied element's containing
	 * classifier
	 * @param el the element whose classifier we're using
	 * @param name the name of the Errortype to search for
	 * @return the specified error type, or null, if either the element's classifier is null or no
	 * ErrorType by the specified name was found
	 */
	public static ErrorType findErrorType(Element el, String name) {
		Classifier cl = getAssociatedClassifier(el);
		if (cl != null) {
			for (ErrorModelSubclause currSubclause : getAllContainingClassifierEMV2Subclauses(cl)) {
				for (ErrorModelLibrary currLibrary : currSubclause.getUseTypes()) {
					return (ErrorType) AadlUtil.findNamedElementInList(currLibrary.getTypes(), name);
				}
			}
		}
		return null;
	}

	/**
	 * Find ErrorType set with given name by looking through all error types
	 * referenced in all EMV2 subclauses of the supplied element's containing
	 * classifier
	 * @param el the element whose classifier we're using
	 * @param name the name of the ErrorTypeSet to search for
	 * @return the specified error type set, or null, if either the element's classifier is null or no
	 * ErrorType by the specified name was found
	 */
	public static TypeSet findErrorTypeSet(Element el, String name) {
		Classifier cl = getAssociatedClassifier(el);
		if (cl != null) {
			for (ErrorModelSubclause currSubclause : getAllContainingClassifierEMV2Subclauses(cl)) {
				for (ErrorModelLibrary currLibrary : currSubclause.getUseTypes()) {
					return (TypeSet) AadlUtil.findNamedElementInList(currLibrary.getTypesets(), name);
				}
			}
		}
		return null;
	}

	/**
	 * find the error flow whose incoming error propagation point is flowSource
	 * @param eps List of error propagations
	 * @param flowSource ErrorPropagation
	 * @return ErrorFlow list
	 */
	public static EList<ErrorFlow> findErrorFlowFrom(Collection<ErrorFlow> efs, ErrorPropagation flowSource) {
		EList<ErrorFlow> result = new BasicEList<ErrorFlow>();
		for (ErrorFlow ef : efs) {
			ErrorPropagation eprop = null;
			if (ef instanceof ErrorPath) {
				ErrorPath ep = (ErrorPath) ef;
				eprop = ep.getIncoming();
			} else if (ef instanceof ErrorSink) {
				ErrorSink es = (ErrorSink) ef;
				eprop = es.getIncoming();

			}
			if (eprop != null && eprop == flowSource) {
				result.add(ef);
			}
		}
		return result;
	}

	/**
	 * find the error flow whose incoming error propagation point is flowSource
	 * @param eps List of error propagations
	 * @param flowSource ErrorPropagation
	 * @return ErrorFlow list
	 */
	public static EList<ErrorFlow> findErrorFlow(Collection<ErrorFlow> efs, ErrorPropagation flowSource,
			ErrorTypes sourceType, ErrorPropagation flowTarget, ErrorTypes targetType) {
		EList<ErrorFlow> result = new BasicEList<ErrorFlow>();
		for (ErrorFlow ef : efs) {
			if (ef instanceof ErrorPath && flowSource != null) {
				ErrorPath ep = (ErrorPath) ef;
				if ((!ep.isAllIncoming() && EMV2Util.isSame(flowSource, ep.getIncoming()))
						&& (!ep.isAllOutgoing() && EMV2Util.isSame(flowTarget, ep.getOutgoing()))) {
					if (EMV2TypeSetUtil.contains(ep.getTypeTokenConstraint(), sourceType)
							&& EMV2TypeSetUtil.contains(ep.getTargetToken(), targetType)) {
						result.add(ep);
					}
				}
			} else if (ef instanceof ErrorSource) {
				ErrorSource es = (ErrorSource) ef;
				if (!es.isAll() && EMV2Util.isSame(flowTarget, es.getSourceModelElement())
						&& EMV2TypeSetUtil.contains(es.getTypeTokenConstraint(), targetType)) {
					result.add(es);
				}
			}
		}
		return result;
	}

	public static boolean areEquivalent(ErrorPropagation ep1, ErrorPropagation ep2) {
		String path1 = getPath(ep1.getFeatureorPPRef());
		String path2 = getPath(ep2.getFeatureorPPRef());
		return path1.equalsIgnoreCase(path2) && EMV2TypeSetUtil.contains(ep1.getTypeSet(), ep2.getTypeSet())
				&& EMV2TypeSetUtil.contains(ep2.getTypeSet(), ep1.getTypeSet());
	}

	/**
	 * Return true if a ConditionExpression object contains a reference to a specific error propagation.
	 * In fact, we are looking for an incoming error propagation that may trigger a condition
	 * (for example, switching to a state, propagating an error, etc.
	 *
	 * @param conditionExpression : the conditionExpression that may contain a reference to the error propagation
	 * @param errorPropagation    : the incoming propagation we are looking for as a referenced condition
	 * @return if the conditionExpression contains a reference (either with a or or and) to the error propagation passed as the second parameter.
	 */
	public static boolean errorConditionContainsIncomingPropagation(ConditionExpression conditionExpression,
			ErrorPropagation errorPropagation) {
		boolean result = false;
		if (conditionExpression instanceof ConditionElement) {
			ConditionElement ce = (ConditionElement) conditionExpression;
			NamedElement eop = getErrorModelElement(ce);
			if (eop instanceof ErrorPropagation) {
				if (areEquivalent((ErrorPropagation) eop, errorPropagation)) {
					return true;
				}
			}
		}

		if (conditionExpression instanceof AndExpressionImpl) {
			AndExpressionImpl ae = (AndExpressionImpl) conditionExpression;
			for (ConditionExpression ce : ae.getOperands()) {
				if (errorConditionContainsIncomingPropagation(ce, errorPropagation)) {
					result = true;
				}
			}
		}

		if (conditionExpression instanceof OrExpressionImpl) {
			OrExpressionImpl oe = (OrExpressionImpl) conditionExpression;
			for (ConditionExpression ce : oe.getOperands()) {
				if (errorConditionContainsIncomingPropagation(ce, errorPropagation)) {
					result = true;
				}
			}
		}

		return result;
	}

	/**
	 * For a given component and errorPropagation, it gives all the potential
	 * other outgoingpropagation. When we have a component with
	 * an incoming error propagation, this can be an error sink. On the other
	 * hand, receiving this error may trigger a state that will be used
	 * to propagate an error.
	 *
	 * Consider the model below. In that case, the pedalvalue feature is
	 * an error sink and makes the component to switch to the Failed state.
	 * On the other hand, being in the Failed state sends
	 * the NoValue error on brake and skid.
	 *
	 * In that case, the following method is used to return
	 * the outgoing error propagation related to an specific incoming
	 * error propagation. In the following examples, the incoming error
	 * propagation is pedalvalue, the outgoing error propagation Failed/brake and Failed/skid
	 *
	 * 	component error behavior
	 * 		transitions
	 * 			terrfrompedal 		: Operational -[pedalvalue{NoService}]-> Failed;
	 * 		propagations
	 * 			p1 : Failed -[]-> brake(NoValue);
	 * 			p2 : Failed -[]-> skid(NoValue);
	 * 		end component;
	 *
	 * @param component
	 * @param flowSource
	 * @return
	 */
	public static EList<OutgoingPropagationCondition> getAdditionalOutgoingPropagation(ComponentInstance component,
			ErrorPropagation flowSource) {
		EList<OutgoingPropagationCondition> result = new BasicEList<OutgoingPropagationCondition>();

		for (ErrorBehaviorTransition trans : EMV2Util.getAllErrorBehaviorTransitions(component)) {
			if (errorConditionContainsIncomingPropagation(trans.getCondition(), flowSource)) {

				for (OutgoingPropagationCondition opc : EMV2Util.getAllOutgoingPropagationConditions(component)) {

					if ((!result.contains(opc)) && (opc.getState() == trans.getTarget())) {
						result.add(opc);
					}
				}

			}

		}
		return result;
	}

	public static EList<ErrorFlow> findErrorFlowFromComponentInstance(ComponentInstance component,
			ErrorPropagation flowSource) {
		EList<ErrorFlow> result = new BasicEList<ErrorFlow>();
		Collection<ErrorFlow> efs = EMV2Util.getAllErrorFlows(component.getComponentClassifier());
		ErrorFlow toAdd;
		for (ErrorFlow ef : efs) {
			ErrorPropagation eprop = null;
			toAdd = ef;
			boolean isall = false;
			if (ef instanceof ErrorPath) {
				ErrorPath ep = (ErrorPath) ef;
				eprop = ep.getIncoming();
				isall = ep.isAllIncoming();
			} else if (ef instanceof ErrorSink) {
				ErrorSink es = (ErrorSink) ef;
				eprop = es.getIncoming();
				isall = es.isAllIncoming();
			}
			if ((eprop != null && eprop == flowSource) || isall) {
				result.add(toAdd);
			}
		}
		return result;
	}

	/**
	 * find the error flow whose outgoing error propagation point is flowSource
	 * @param eps List of error propagations
	 * @param flowSource ErrorPropagation
	 * @return ErrorFlow list
	 */
	public static EList<ErrorFlow> findReverseErrorFlowFrom(Collection<ErrorFlow> efs, ErrorPropagation flowSource) {
		EList<ErrorFlow> result = new BasicEList<ErrorFlow>();
		for (ErrorFlow ef : efs) {
			ErrorPropagation eprop = null;
			boolean isall = false;
			if (ef instanceof ErrorPath) {
				ErrorPath ep = (ErrorPath) ef;
				eprop = ep.getOutgoing();
				isall = ep.isAllOutgoing();
			} else if (ef instanceof ErrorSource
					&& ((ErrorSource) ef).getSourceModelElement() instanceof ErrorPropagation) {
				ErrorSource es = (ErrorSource) ef;
				eprop = (ErrorPropagation) es.getSourceModelElement();
				isall = es.isAll();
			}
			if ((eprop != null && eprop == flowSource) || isall) {
				result.add(ef);
			}
		}
		return result;
	}

	public static Collection<ErrorPropagation> getOutgoingPropagationOrAll(ErrorSource errorSource) {
		Collection<ErrorPropagation> eplist = null;
		if (errorSource.getSourceModelElement() != null
				&& errorSource.getSourceModelElement() instanceof ErrorPropagation) {
			eplist = new BasicEList<ErrorPropagation>();
			eplist.add((ErrorPropagation) errorSource.getSourceModelElement());
		} else if (errorSource.isAll()) {
			eplist = EMV2Util.getAllOutgoingErrorPropagations(getAssociatedClassifier(errorSource));
		}
		return eplist;
	}

	public static Collection<ErrorPropagation> getOutgoingPropagationOrAll(ErrorPath errorPath) {
		Collection<ErrorPropagation> eplist = null;
		if (errorPath.getOutgoing() != null) {
			eplist = new BasicEList<ErrorPropagation>();
			eplist.add(errorPath.getOutgoing());
		} else if (errorPath.isAllOutgoing()) {
			eplist = EMV2Util.getAllOutgoingErrorPropagations(getAssociatedClassifier(errorPath));
		}
		return eplist;
	}

	/**
	 * find Error Behavior State utilizing use behavior or within EBSM
	 *
	 * @param context context of the reference to the state
	 * @param name String
	 * @return ErrorBehavior State
	 */
	public static ErrorBehaviorState findErrorBehaviorState(Element context, String name) {
		ErrorBehaviorStateMachine ebsm;
		if (context instanceof QualifiedErrorBehaviorState) {
			// look up state in state machine of subcomponent
			QualifiedErrorBehaviorState qualifiedState = (QualifiedErrorBehaviorState) context;
			Subcomponent sub = qualifiedState.getSubcomponent().getSubcomponent();
			ComponentClassifier subcl = sub.getAllClassifier();
			if (subcl == null) {
				return null;
			}
			return findErrorBehaviorState(subcl, name);
		} else {
//			// first see if it is in type bindings
//			EList<ErrorBehaviorState> typebindings = getErrorBehaviorStateTypeBindings(context);
//			if (typebindings != null){
//				for (ErrorBehaviorState ebs : typebindings) {
//					if (name.equalsIgnoreCase(ebs.getName())){
//						return ebs;
//					}
//				}
//			}
			// resolve in local context, which is assumed to be an EBSM
			ebsm = EMV2Util.getErrorBehaviorStateMachine(context);
			return findErrorBehaviorStateInEBSM(ebsm, name);
		}
	}

//	/**
//	 * need to use this if the error behavior state is in the state machine and it is overwritten by type binding
//	 * @param es Error State whose type we are looking for
//	 * @param context context of reference to error state
//	 * @return TypeSet
//	 */
//	public static TypeSet getErrorTypeSet(ErrorBehaviorState es, Element context){
//		EList<ErrorBehaviorState> typebindings = getErrorBehaviorStateTypeBindings(context);
//		for (ErrorBehaviorState typeBinding : typebindings) {
//			if (es.getName().equalsIgnoreCase(typeBinding.getName())){
//				return typeBinding.getTypeSet();
//			}
//		}
//		return es.getTypeSet();
//	}

//	/**
//	 * get the type bindings list from use behavior
//	 * @param element
//	 * @return
//	 */
//	public static EList<ErrorBehaviorState> getErrorBehaviorStateTypeBindings(Element element){
//		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(element);
//		for (ErrorModelSubclause errorModelSubclause : emslist) {
//			EList<ErrorBehaviorState> result = errorModelSubclause.getStateTypeBindings();
//			if (result!= null){
//				return result;
//			}
//		}
//		return null;
//	}

	/**
	 * find error behavior state in state machine
	 * @param ebsm
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorState findErrorBehaviorStateInEBSM(ErrorBehaviorStateMachine ebsm, String name) {
		if (ebsm != null) {
			EList<ErrorBehaviorState> ebsl = ebsm.getStates();
			for (ErrorBehaviorState ebs : ebsl) {
				if (Aadl2Util.getItemNameWithoutQualification(name).equalsIgnoreCase(ebs.getName())) {
					return ebs;
				}
			}
//			// enable if we support extends on EBSM
//			if (ebsm.getExtends() != null){
//				return findErrorBehaviorStateInEBSM(ebsm.getExtends(), name);
//			}
		}
		return null;
	}

	/**
	 * find error behavior transition in the specified context, i.e., its enclosing classifier subclause or inherited subclauses
	 * we look in the component error behavior sections first, and then in the EBSM of the use behavior.
	 * @param context
	 * @param name
	 * @return
	 */
	public static ErrorBehaviorTransition findErrorBehaviorTransition(Element context, String name) {
		Classifier cl = getAssociatedClassifier(context);
		if (cl != null) {
			// we are not in an error library
			ErrorBehaviorTransition ebt = findErrorBehaviorTransitioninCEB(cl, name);
			if (ebt != null) {
				return ebt;
			}
			// find it in the EBSM from the use behavior
			return findErrorBehaviorTransitioninUseBehavior(cl, name);
		} else {
			// we are inside an error library resolving transition references
			ErrorBehaviorStateMachine ebsm = EMV2Util.getErrorBehaviorStateMachine(context);
			return findErrorBehaviorTransitionInEBSM(ebsm, name);
		}
	}

	/**
	 * find transition in EBSM specified in use behavior
	 * @param cl classifier as context of use behavior
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorTransition findErrorBehaviorTransitioninUseBehavior(Classifier cl, String name) {
		if (cl == null) {
			return null;
		}
		ErrorBehaviorStateMachine ebsm = EMV2Util.getErrorBehaviorStateMachine(cl);
		if (ebsm != null) {
			return findErrorBehaviorTransitionInEBSM(ebsm, name);
		}
		return null;
	}

	/**
	 * find transition in EBSM
	 * @param ebsm
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorTransition findErrorBehaviorTransitionInEBSM(ErrorBehaviorStateMachine ebsm,
			String name) {
		if (ebsm != null) {
			EList<ErrorBehaviorTransition> ebsl = ebsm.getTransitions();
			for (ErrorBehaviorTransition ebs : ebsl) {
				if (Aadl2Util.getItemNameWithoutQualification(name).equalsIgnoreCase(ebs.getName())) {
					return ebs;
				}
			}
//			// enable if we introduce extends of EBSM
//			if (ebsm.getExtends() != null){
//				return findErrorBehaviorTransitionInEBSM(ebsm.getExtends(), name);
//			}
		}
		return null;
	}

	/**
	 * find the error behavior transition in the component error behavior looking in all inherited subclauses according to extends and type of implementation
	 * @param cl
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorTransition findErrorBehaviorTransitioninCEB(Classifier cl, String name) {
		if (cl == null) {
			return null;
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorTransition res = findOwnErrorBehaviorTransition(errorModelSubclause, name);
			if (res != null) {
				return res;
			}
		}
		return null;
	}

	/**
	 * find transition in given subclause component error behavior
	 * @param ems
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorTransition findOwnErrorBehaviorTransition(ErrorModelSubclause ems, String name) {
		if (ems == null) {
			return null;
		}
		EList<ErrorBehaviorTransition> transitions = ems.getTransitions();
		for (ErrorBehaviorTransition errorBehaviorTransition : transitions) {
			if (name.equalsIgnoreCase(errorBehaviorTransition.getName())) {
				return errorBehaviorTransition;
			}
		}
		return null;
	}

	private static ErrorBehaviorEvent findOwnErrorBehaviorEvent(ErrorModelSubclause ems, String name) {
		if (ems == null) {
			return null;
		}
		EList<ErrorBehaviorEvent> events = ems.getEvents();
		for (ErrorBehaviorEvent errorBehaviorEvent : events) {
			if (name.equalsIgnoreCase(errorBehaviorEvent.getName())) {
				return errorBehaviorEvent;
			}
		}
		return null;
	}

	/**
	 * find the error behavior event in the component error behavior looking in all inherited subclauses according to extends and type of implementation
	 * @param cl ComponentClassifier (May be null)
	 * @param name
	 * @return
	 */
	private static ErrorBehaviorEvent findErrorBehaviorEventinCEB(Classifier cl, String name) {
		if (cl == null) {
			return null;
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorEvent res = findOwnErrorBehaviorEvent(errorModelSubclause, name);
			if (res != null) {
				return res;
			}
		}
		return null;
	}

	/**
	 * find error behavior event in the specified context, i.e., its enclosing classifier subclause or inherited subclauses
	 * we look in the component error behavior sections first, and then in the EBSM of the use behavior.
	 * @param context
	 * @param name
	 * @return
	 */
	public static ErrorBehaviorEvent findErrorBehaviorEvent(Element context, String name) {
		Classifier cl = getAssociatedClassifier(context);
		// we are not in an error library
		ErrorBehaviorEvent ebt = findErrorBehaviorEventinCEB(cl, name);
		if (ebt != null) {
			return ebt;
		}
		// find it in the EBSM from the context
		ErrorBehaviorStateMachine ebsm = EMV2Util.getErrorBehaviorStateMachine(context);
		return findErrorBehaviorEventInEBSM(ebsm, name);
	}

	private static ErrorBehaviorEvent findErrorBehaviorEventInEBSM(ErrorBehaviorStateMachine ebsm, String name) {
		if (ebsm != null) {
			EList<ErrorBehaviorEvent> ebsl = ebsm.getEvents();
			for (ErrorBehaviorEvent ebs : ebsl) {
				if (Aadl2Util.getItemNameWithoutQualification(name).equalsIgnoreCase(ebs.getName())) {
					return ebs;
				}
			}
//			// enable if we support extends of EBSM
//			if (ebsm.getExtends() != null){
//				return findErrorBehaviorEventInEBSM(ebsm.getExtends(), name);
//			}
		}
		return null;
	}

	/**
	 * find Error Detection in own subclause component error behavior
	 * @param ems
	 * @param name
	 * @return
	 */
	private static OutgoingPropagationCondition findOwnOutgoingPropagationCondition(ErrorModelSubclause ems,
			ErrorPropagation ep, ErrorTypes type) {
		if (ems == null) {
			return null;
		}
		EList<OutgoingPropagationCondition> outgoingPs = ems.getOutgoingPropagationConditions();
		for (OutgoingPropagationCondition outgoingPropagationCondition : outgoingPs) {
			if (outgoingPropagationCondition.isAllPropagations()
					|| EMV2Util.isSame(outgoingPropagationCondition.getOutgoing(), ep)) {
				if (EMV2TypeSetUtil.contains(outgoingPropagationCondition.getTypeToken(), type)) {
					return outgoingPropagationCondition;
				}
			}
		}
		return null;
	}

	/**
	 * find outgoing propagation condition in the specified context.
	 * we look in the component error behavior sections .
	 * @param context
	 * @param ep
	 * @return
	 */
	public static boolean existsOutgoingPropagationCondition(Element context, ErrorPropagation ep, ErrorTypes type) {
		Classifier cl = getAssociatedClassifier(context);
		if (cl == null) {
			return false;
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			OutgoingPropagationCondition res = findOwnOutgoingPropagationCondition(errorModelSubclause, ep, type);
			if (res != null) {
				return true;
			}
		}
		return false;
	}

	/**
	 * return list of outgoing error propagations including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorPropagation> list of ErrorPropagations excluding duplicates
	 */
	public static Collection<ErrorPropagation> getAllOutgoingErrorPropagations(Classifier cl) {
		return getAllErrorPropagations(cl, DirectionType.OUT);
	}

	/**
	 * return list of error propagations including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorPropagation> list of ErrorPropagations excluding duplicates
	 */
	public static Collection<ErrorPropagation> getAllIncomingErrorPropagations(Classifier cl) {
		return getAllErrorPropagations(cl, DirectionType.IN);
	}

	public static Collection<ErrorPropagation> getAllIncomingErrorPropagations(ComponentInstance ci) {
		return getAllErrorPropagations(ci.getComponentClassifier(), DirectionType.IN);
	}

	/**
	 * return list of error propagations including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorPropagation> list of ErrorPropagation excluding duplicates
	 */
	public static Collection<ErrorPropagation> getAllErrorPropagations(Classifier cl, DirectionType dir) {
		HashMap<String, ErrorPropagation> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorPropagation> eflist = errorModelSubclause.getPropagations();
			for (ErrorPropagation errorProp : eflist) {
				if (!errorProp.isNot() && errorProp.getDirection().equals(dir)) {
					String epname = EMV2Util.getPrintName(errorProp);
					if (!result.containsKey(epname)) {
						result.put(epname, errorProp);
					}
				}
			}
		}
		return result.values();
	}

	/**
	 * return list of error propagations including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return list of ErrorPropagation excluding duplicates
	 * @since 6.3
	 */
	public static List<ErrorPropagation> getAllErrorPropagations(Classifier cl) {
		var result = new ArrayList<ErrorPropagation>();
		var subclauses = getAllContainingClassifierEMV2Subclauses(cl);
		var propagations = subclauses.stream()
				.flatMap(subclause -> subclause.getPropagations().stream())
				.filter(propagation -> !propagation.isNot())
				.collect(Collectors.toList());
		// Filter out propagations in extended classifiers that are overridden.
		for (var propagation : propagations) {
			var propagationName = getPrintName(propagation);
			var alreadyExists = false;
			for (var existing : result) {
				var existingName = getPrintName(existing);
				if (propagation.getDirection() == existing.getDirection()
						&& propagationName.equalsIgnoreCase(existingName)) {
					alreadyExists = true;
					break;
				}
			}
			if (!alreadyExists) {
				result.add(propagation);
			}
		}
		return result;
	}

	/**
	 * return list of ConnectionErrorSource including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ConnectionErrorSource> list of ConnectionErrorSource excluding duplicates
	 */
	public static Collection<ErrorSource> getAllConnectionErrorSources(Classifier cl) {
		HashMap<String, ErrorSource> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorSource> eflist = errorModelSubclause.getConnectionErrorSources();
			for (ErrorSource errorProp : eflist) {
				String epname = EMV2Util.getPrintName(errorProp);
				if (!result.containsKey(epname)) {
					result.put(epname, errorProp);
				}
			}
		}
		return result.values();
	}

	/**
	 * determine if two propagations match by name.
	 * This is useful when we have inherited error propagations and one reference points
	 * to the original and the second one to the one overriding the original.
	 * @param ep1
	 * @param ep2
	 * @return
	 */
	public static boolean isSame(ErrorPropagation ep1, ErrorPropagation ep2) {
		return (getPropagationName(ep1).equalsIgnoreCase(getPropagationName(ep2)));

	}

	/**
	 * compare NamedElements by name.
	 * Useful when dealing with inheritence.
	 * @param errorModelElement1
	 * @param errorModelElement2
	 * @return
	 */
	public static boolean isSame(NamedElement errorModelElement1, NamedElement errorModelElement2) {
		if (errorModelElement1 instanceof ErrorPropagation && errorModelElement2 instanceof ErrorPropagation) {
			return isSame((ErrorPropagation) errorModelElement1, (ErrorPropagation) errorModelElement2);
		}
		return (errorModelElement1.getName().equalsIgnoreCase(errorModelElement2.getName()));
	}

	/**
	 * Determine if the two expressions are the same.
	 * Compare operators and compare referenced elements by pathname
	 * @param ce1
	 * @param ce2
	 * @return
	 */
	public static boolean isEqual(ConditionExpression ce1, ConditionExpression ce2) {
		if ((ce1 == null) && (ce2 == null)) {
			return true;
		}

		if (ce1 == null) {
			return false;
		}

		if (ce2 == null) {
			return false;
		}

		if (ce1.getClass() != ce2.getClass()) {
			return false;
		}
		// ConditionElement
		// AndExpression
		// SAndExpression
		// OrlessExpression
		// SOrExpression
		// SAndExpression
		// OrmoreExpression
		// They are the same class, so we test for only one being a certain class
		if (ce1 instanceof ConditionElement) {
			ConditionElement element1 = (ConditionElement) ce1;
			ConditionElement element2 = (ConditionElement) ce2;
			return (getPathNameWithoutType(element1.getQualifiedErrorPropagationReference().getEmv2Target())
					.equalsIgnoreCase(
							getPathNameWithoutType(element2.getQualifiedErrorPropagationReference().getEmv2Target())));
		}

		if (ce1 instanceof AndExpression) {
			AndExpression expr1 = (AndExpression) ce1;
			AndExpression expr2 = (AndExpression) ce2;
			if (expr1.getOperands().size() != expr2.getOperands().size()) {
				return false;
			}

			for (int i = 0; i < expr1.getOperands().size(); i++) {
				if (!isEqual(expr1.getOperands().get(i), expr2.getOperands().get(i))) {
					return false;
				}
			}
			return true;
		}

		if (ce1 instanceof OrExpression) {
			OrExpression expr1 = (OrExpression) ce1;
			OrExpression expr2 = (OrExpression) ce2;
			if (expr1.getOperands().size() != expr2.getOperands().size()) {
				return false;
			}

			for (int i = 0; i < expr1.getOperands().size(); i++) {
				if (!isEqual(expr1.getOperands().get(i), expr2.getOperands().get(i))) {
					return false;
				}
			}
			return true;
		}
		OsateDebug.osateDebug("[EMV2Util] isEqual does not handled this class type " + ce1 + "|" + ce2);
		return false;
	}

	/**
	 * return list of error containments including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorPropagation> list of ErrorPropagations excluding duplicates
	 */
	public static Collection<ErrorPropagation> getAllErrorContainments(Classifier cl, DirectionType dir) {
		HashMap<String, ErrorPropagation> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorPropagation> eflist = errorModelSubclause.getPropagations();
			for (ErrorPropagation errorProp : eflist) {
				if (errorProp.isNot() && errorProp.getDirection().equals(dir)) {
					String epname = EMV2Util.getPrintName(errorProp);
					if (!result.containsKey(epname)) {
						result.put(epname, errorProp);
					}
				}
			}
		}
		return result.values();
	}

	/**
	 * return list of error flow including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorFlow> list of error flow
	 */
	public static Collection<ErrorFlow> getAllErrorFlows(Classifier cl) {
		HashMap<String, ErrorFlow> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorFlow> eflist = errorModelSubclause.getFlows();
			for (ErrorFlow errorFlow : eflist) {
				if (!result.containsKey(errorFlow.getName())) {
					result.put(errorFlow.getName(), errorFlow);
				}
			}
		}
		return result.values();
	}

	public static Collection<ErrorFlow> getAllErrorFlows(ComponentInstance ci) {
		return getAllErrorFlows(ci.getComponentClassifier());
	}

	/**
	 * return list of error sources including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorSource> list of error flow
	 */
	public static Collection<ErrorSource> getAllErrorSources(Classifier cl) {
		var result = new TreeMap<String, ErrorSource>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorFlow> eflist = errorModelSubclause.getFlows();
			for (ErrorFlow errorFlow : eflist) {
				if (errorFlow instanceof ErrorSource) {
					if (!result.containsKey(errorFlow.getName())) {
						result.put(errorFlow.getName(), (ErrorSource) errorFlow);
					}
				}
			}
		}
		return result.values();
	}

	public static Collection<ErrorSource> getAllErrorSources(ComponentInstance ci) {
		return getAllErrorSources(ci.getComponentClassifier());
	}

	/**
	 * return list of error paths including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorSource> list of error paths declared in the flow section
	 */
	public static Collection<ErrorPath> getAllErrorPaths(Classifier cl) {
		var result = new TreeMap<String, ErrorPath>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorFlow> eflist = errorModelSubclause.getFlows();
			for (ErrorFlow errorFlow : eflist) {
				if (errorFlow instanceof ErrorPath) {
					if (!result.containsKey(errorFlow.getName())) {
						result.put(errorFlow.getName(), (ErrorPath) errorFlow);
					}
				}
			}
		}
		return result.values();
	}

	public static Collection<ErrorPath> getAllErrorPaths(ComponentInstance ci) {
		return getAllErrorPaths(ci.getComponentClassifier());
	}

	/**
	 * return list of error sinks including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorSource> list of error sinks declared in the flow section
	 */
	public static Collection<ErrorSink> getAllErrorSinks(Classifier cl) {
		var result = new TreeMap<String, ErrorSink>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorFlow> eflist = errorModelSubclause.getFlows();
			for (ErrorFlow errorFlow : eflist) {
				if (errorFlow instanceof ErrorSink) {
					if (!result.containsKey(errorFlow.getName())) {
						result.put(errorFlow.getName(), (ErrorSink) errorFlow);
					}
				}
			}
		}
		return result.values();
	}

	public static Collection<ErrorSink> getAllErrorSinks(ComponentInstance ci) {
		return getAllErrorSinks(ci.getComponentClassifier());
	}

	/** return the name for an error propagation **/
	public static String getPropagationName(ErrorPropagation propagation) {
		String res = propagation.getKind();
		if (res != null) {
			return res;
		}
		return getPath(propagation.getFeatureorPPRef());
	}

	/**
	 * return type transformation set to be used for connections.
	 * Looks for use transformations in classifier or inherited from classifiers being extended
	 * @param cl Classifier
	 * @return TypeTransformationSet
	 */
	public static TypeTransformationSet getAllTypeTransformationSet(ComponentInstance ci) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(ci);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			TypeTransformationSet tts = errorModelSubclause.getTypeTransformationSet();
			if (tts != null) {
				return tts;
			}
		}
		return null;
	}

	/**
	 * return type mapping set to be used for bindings other than connection bindings.
	 * Looks for use type equivalence in classifier or inherited from classifiers being extended
	 * Also look in parent component instances, i.e., the equivalence mapping is inherited down the component hierarchy
	 * @param ci Component Instance
	 * @return TypeMappingSet
	 */
	public static TypeMappingSet getAllTypeEquivalenceMapping(ComponentInstance ci) {
		while (ci != null) {
			EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(ci);
			for (ErrorModelSubclause errorModelSubclause : emslist) {
				TypeMappingSet tms = errorModelSubclause.getTypeEquivalence();
				if (tms != null) {
					return tms;
				}
			}
			ci = ci.getContainingComponentInstance();
		}
		return null;
	}

	public static TypeMappingSet getAllTypeEquivalenceMapping(Classifier cl) {

		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			TypeMappingSet tms = errorModelSubclause.getTypeEquivalence();
			if (tms != null) {
				return tms;
			}
		}

		return null;
	}

	/**
	 * return list of propagation points including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<PropagationPoint> list of propagation points as HashMap for quick lookup of names
	 */
	public static Collection<PropagationPoint> getAllPropagationPoints(Classifier cl) {
		HashMap<String, PropagationPoint> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<PropagationPoint> eflist = errorModelSubclause.getPoints();
			for (PropagationPoint propPoint : eflist) {
				if (!result.containsKey(propPoint.getName())) {
					result.put(propPoint.getName(), propPoint);
				}
			}
		}
		return result.values();
	}

	/**
	 * return list of PropagationPaths including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<PropagationPath> list of PropagationPaths as HashMap for quick lookup of names
	 */
	public static Collection<PropagationPath> getAllPropagationPaths(Classifier cl) {
		HashMap<String, PropagationPath> result = new LinkedHashMap<>();
		var unnamedPaths = new ArrayList<PropagationPath>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<PropagationPath> eflist = errorModelSubclause.getPaths();
			for (PropagationPath propPointConn : eflist) {
				if (propPointConn.getName() == null) {
					unnamedPaths.add(propPointConn);
				} else if (!result.containsKey(propPointConn.getName())) {
					result.put(propPointConn.getName(), propPointConn);
				}
			}
		}
		var returnValue = new ArrayList<>(result.values());
		returnValue.addAll(unnamedPaths);
		return returnValue;
	}

	/**
	 * return list of ErrorBehaviorEvents including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorBehaviorEvent> list of ErrorBehaviorEvents as HashMap for quick lookup of names
	 */
	public static Collection<ErrorBehaviorEvent> getAllErrorBehaviorEvents(Classifier cl) {
		var result = new TreeMap<String, ErrorBehaviorEvent>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		boolean foundEBSM = false;
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			EList<ErrorBehaviorEvent> eflist = errorModelSubclause.getEvents();
			for (ErrorBehaviorEvent ebe : eflist) {
				if (!result.containsKey(ebe.getName())) {
					result.put(ebe.getName(), ebe);
				}
			}
			if (!foundEBSM && ebsm != null) {
				foundEBSM = true;
				eflist = ebsm.getEvents();
				for (ErrorBehaviorEvent ebe : eflist) {
					if (!result.containsKey(ebe.getName())) {
						result.put(ebe.getName(), ebe);
					}
				}
			}

		}
		return result.values();
	}

	public static Collection<ErrorBehaviorEvent> getAllErrorBehaviorEvents(ComponentInstance ci) {
		return getAllErrorBehaviorEvents(ci.getComponentClassifier());
	}

	/**
	 * return list of ErrorBehaviorStates including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorBehaviorState> list of ErrorBehaviorStates as HashMap for quick lookup of names
	 */
	public static Collection<ErrorBehaviorState> getAllErrorBehaviorStates(Classifier cl) {
		HashMap<String, ErrorBehaviorState> result = new LinkedHashMap<>();
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			if (ebsm != null) {
				EList<ErrorBehaviorState> eflist = ebsm.getStates();
				for (ErrorBehaviorState ebs : eflist) {
					if (!result.containsKey(ebs.getName())) {
						result.put(ebs.getName(), ebs);
					}
				}
				return result.values();
			}

		}
		return Collections.emptyList();
	}

	public static Collection<ErrorBehaviorState> getAllErrorBehaviorStates(ComponentInstance ci) {
		return getAllErrorBehaviorStates(ci.getComponentClassifier());
	}

	/**
	 * @since 6.3
	 */
	public static ErrorBehaviorStateMachine getAllErrorBehaviorStateMachine(Classifier cl) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			if (ebsm != null) {
				return ebsm;
			}
		}
		return null;
	}

	/**
	 * @since 6.3
	 */
	public static ErrorBehaviorStateMachine getAllErrorBehaviorStateMachine(ComponentInstance ci) {
		return getAllErrorBehaviorStateMachine(ci.getComponentClassifier());
	}

	/**
	 * return list of ErrorBehaviorEvents including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<ErrorBehaviorEvent> list of ErrorBehaviorEvents as HashMap for quick lookup of names
	 */
	public static Collection<ErrorBehaviorTransition> getAllErrorBehaviorTransitions(Classifier cl) {
		BasicEList<ErrorBehaviorTransition> unlist = new BasicEList<ErrorBehaviorTransition>();
		Collection<ErrorBehaviorTransition> res = getAllErrorBehaviorTransitions(cl, unlist).values();

		BasicEList<ErrorBehaviorTransition> result = new BasicEList<ErrorBehaviorTransition>();
		if (cl == null) {
			return result;
		}
		result.addAll(res);
		result.addAll(unlist);
		return result;
	}

	public static Collection<ErrorBehaviorTransition> getAllErrorBehaviorTransitions(ComponentInstance ci) {
		return getAllErrorBehaviorTransitions(ci.getComponentClassifier());
	}

	private static void getAllConditionElementsFromConditionExpression(EList<ConditionElement> propagations,
			ConditionExpression ce) {

		if (ce instanceof ConditionElement) {
			ConditionElement element = (ConditionElement) ce;
			propagations.add(element);
		} else if (ce instanceof AndExpression) {
			AndExpression and = (AndExpression) ce;
			for (ConditionExpression foobar : and.getOperands()) {
				getAllConditionElementsFromConditionExpression(propagations, foobar);
			}
		} else if (ce instanceof OrExpression) {
			OrExpression or = (OrExpression) ce;
			for (ConditionExpression foobar : or.getOperands()) {
				getAllConditionElementsFromConditionExpression(propagations, foobar);
			}
		} else if (ce instanceof OrmoreExpression) {
			OrmoreExpression or = (OrmoreExpression) ce;
			for (ConditionExpression foobar : or.getOperands()) {
				getAllConditionElementsFromConditionExpression(propagations, foobar);
			}
		} else if (ce instanceof OrlessExpression) {
			OrlessExpression or = (OrlessExpression) ce;
			for (ConditionExpression foobar : or.getOperands()) {
				getAllConditionElementsFromConditionExpression(propagations, foobar);
			}
		} else if (ce instanceof AllExpression) {
			AllExpression or = (AllExpression) ce;
			for (ConditionExpression foobar : or.getOperands()) {
				getAllConditionElementsFromConditionExpression(propagations, foobar);
			}
		}
	}

	/**
	 * return the list of all ConditionElement associated with an ErrorBehaviorTransition.
	 * In fact, for a transition, we can have several condition element enclosed in and and or branches.
	 * This methods returns all the condition element referenced inside this constructs.
	 * @param ebt the behavior transition that contains all the condition elements.
	 * @return all the condition element related to the behavior transition
	 */
	public static Collection<ConditionElement> getAllConditionElementsFromConditionExpression(
			ErrorBehaviorTransition ebt) {
		EList<ConditionElement> result = new BasicEList<ConditionElement>();
		getAllConditionElementsFromConditionExpression(result, ebt.getCondition());
		return result;
	}

	public static Collection<ConditionElement> getAllConditionElementsFromConditionExpression(
			OutgoingPropagationCondition ebt) {
		EList<ConditionElement> result = new BasicEList<ConditionElement>();
		getAllConditionElementsFromConditionExpression(result, ebt.getCondition());
		return result;
	}

	public static Collection<ConditionElement> getAllConditionElementsFromConditionExpression(CompositeState cs) {
		EList<ConditionElement> result = new BasicEList<ConditionElement>();
		getAllConditionElementsFromConditionExpression(result, cs.getCondition());
		return result;
	}

	public static Collection<ConditionElement> getAllConditionElementsFromConditionExpression(ConditionExpression ce) {
		EList<ConditionElement> result = new BasicEList<ConditionElement>();
		getAllConditionElementsFromConditionExpression(result, ce);
		return result;
	}

	/**
	 * return list of ErrorBehaviorTransition including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @param unnamed Collection of unnamed ErrorBehaviorTransition
	 * @return Collection<ErrorBehaviorTransition> list of ErrorBehaviorTransition as HashMap for quick lookup of names
	 */
	private static Map<String, ErrorBehaviorTransition> getAllErrorBehaviorTransitions(Classifier cl,
			Collection<ErrorBehaviorTransition> unnamed) {
		var result = new TreeMap<String, ErrorBehaviorTransition>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		boolean foundEBSM = false;
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			EList<ErrorBehaviorTransition> eflist = errorModelSubclause.getTransitions();
			for (ErrorBehaviorTransition ebt : eflist) {
				if (ebt.getName() == null) {
					unnamed.add(ebt);
				} else {
					if (!result.containsKey(ebt.getName())) {
						result.put(ebt.getName(), ebt);
					}
				}
			}
			if (!foundEBSM && ebsm != null) {
				foundEBSM = true;
				eflist = ebsm.getTransitions();
				for (ErrorBehaviorTransition ebt : eflist) {
					if (ebt.getName() == null) {
						unnamed.add(ebt);
					} else {
						if (!result.containsKey(ebt.getName())) {
							result.put(ebt.getName(), ebt);
						}
					}
				}
			}

		}
		return result;
	}

	/**
	 * return list of OutgoingPropagationCondition including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<OutgoingPropagationCondition> list of OutgoingPropagationCondition
	 */
	public static Collection<OutgoingPropagationCondition> getAllOutgoingPropagationConditions(Classifier cl) {
		BasicEList<OutgoingPropagationCondition> unlist = new BasicEList<OutgoingPropagationCondition>();
		Collection<OutgoingPropagationCondition> res = getAllOutgoingPropagationConditions(cl, unlist).values();
		unlist.addAll(res);
		return unlist;
	}

	public static Collection<OutgoingPropagationCondition> getAllOutgoingPropagationConditions(ComponentInstance ci) {
		return getAllOutgoingPropagationConditions(ci.getComponentClassifier());
	}

	/**
	 * return list of OutgoingPropagationCondition including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @param unnamed Collection of unnamed OutgoingPropagationCondition
	 * @return Collection<ErrorBehaviorTransition> list of OutgoingPropagationCondition as HashMap for quick lookup of names
	 */
	private static Map<String, OutgoingPropagationCondition> getAllOutgoingPropagationConditions(Classifier cl,
			Collection<OutgoingPropagationCondition> unnamed) {
		var result = new TreeMap<String, OutgoingPropagationCondition>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<OutgoingPropagationCondition> eflist = errorModelSubclause.getOutgoingPropagationConditions();
			for (OutgoingPropagationCondition ebt : eflist) {
				if (ebt.getName() == null) {
					unnamed.add(ebt);
				} else {
					if (!result.containsKey(ebt.getName())) {
						result.put(ebt.getName(), ebt);
					}
				}
			}
		}
		return result;
	}

	/**
	 * return list of CompositeState including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<CompositeState> list of CompositeState
	 */
	public static EList<CompositeState> getAllCompositeStates(Classifier cl) {
		EList<CompositeState> unlist = new BasicEList<CompositeState>();
		Collection<CompositeState> res = getAllCompositeStates(cl, unlist).values();
		unlist.addAll(res);
		return unlist;
	}

	public static EList<CompositeState> getAllCompositeStates(ComponentInstance ci) {
		return getAllCompositeStates(ci.getComponentClassifier());
	}

	/**
	 * return list of CompositeState including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @param unnamed Collection of unnamed CompositeState
	 * @return Collection<CompositeState> list of CompositeState as HashMap for quick lookup of names
	 */
	private static Map<String, CompositeState> getAllCompositeStates(Classifier cl,
			Collection<CompositeState> unnamed) {
		var result = new TreeMap<String, CompositeState>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<CompositeState> eslist = errorModelSubclause.getStates();
			for (CompositeState es : eslist) {
				if (es.getName() == null) {
					unnamed.add(es);
				} else {
					if (!result.containsKey(es.getName())) {
						result.put(es.getName(), es);
					}
				}
			}
		}
		return result;
	}

	/**
	 * return list of OutgoingPropagationCondition including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @return Collection<OutgoingPropagationCondition> list of OutgoingPropagationCondition
	 */
	public static Collection<ErrorDetection> getAllErrorDetections(Classifier cl) {
		BasicEList<ErrorDetection> unlist = new BasicEList<ErrorDetection>();
		Collection<ErrorDetection> res = getAllErrorDetections(cl, unlist).values();
		unlist.addAll(res);
		return unlist;
	}

	/**
	 * return list of OutgoingPropagationCondition including those inherited from classifiers being extended
	 * @param cl Classifier
	 * @param unnamed Collection of unnamed OutgoingPropagationCondition
	 * @return Collection<ErrorBehaviorTransition> list of OutgoingPropagationCondition as HashMap for quick lookup of names
	 * @since 7.0
	 */
	public static Map<String, ErrorDetection> getAllErrorDetections(Classifier cl,
			Collection<ErrorDetection> unnamed) {
		var result = new TreeMap<String, ErrorDetection>(String.CASE_INSENSITIVE_ORDER);
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorDetection> eflist = errorModelSubclause.getErrorDetections();
			for (ErrorDetection ebt : eflist) {
				if (ebt.getName() == null) {
					unnamed.add(ebt);
				} else {
					if (!result.containsKey(ebt.getName())) {
						result.put(ebt.getName(), ebt);
					}
				}
			}
		}
		return result;
	}

	/**
	 * @since 7.0
	 */
	public static List<ErrorStateToModeMapping> getAllModeMappings(Classifier classifier) {
		var result = new ArrayList<ErrorStateToModeMapping>();
		for (var subclause : getAllContainingClassifierEMV2Subclauses(classifier)) {
			result.addAll(subclause.getErrorStateToModeMappings());
		}
		return result;
	}

	/**
	 * return list of NamedElements in error model subclause
	 * @param ems error model subclause
	 * @return Collection<NamedElement> list of defined named elements
	 */
	public static Collection<NamedElement> getAllNamedElements(ErrorModelSubclause ems) {
		Collection<NamedElement> result = new BasicEList<NamedElement>();
		result.addAll(ems.getPropagations());
		result.addAll(ems.getFlows());
		result.addAll(ems.getEvents());
		result.addAll(ems.getTransitions());
		result.addAll(ems.getOutgoingPropagationConditions());
		result.addAll(ems.getErrorDetections());
		return result;
	}

	public static void getAllNamedElements(ErrorBehaviorStateMachine ebsm, Collection<NamedElement> result) {
		if (ebsm == null) {
			return;
		}
		result.addAll(ebsm.getEvents());
		result.addAll(ebsm.getStates());
		result.addAll(ebsm.getTransitions());
	}

	/**
	 * get Error Behavior State Machine (ebsm) in context  of the element
	 * This means the ebsm is either an enclosing container or the ebsm is referenced by an enclosing use behavior declaration.
	 * @param element
	 * @return
	 */
	public static ErrorBehaviorStateMachine getErrorBehaviorStateMachine(Element element) {
		EObject container = element;
		// find enclosing ebsm
		while (container != null) {
			if (container instanceof ErrorBehaviorStateMachine) {
				return (ErrorBehaviorStateMachine) container;
			}
			container = container.eContainer();
		}
		// now find it in use behavior clause
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(element);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			if (ebsm != null) {
				return ebsm;
			}
		}
		return null;
	}

	/**
	 * get the enclosing error model library.
	 * Returns null if the element is not in a error model library
	 * @param element
	 * @return ErrorModelLibrary or null
	 */
	public static ErrorModelLibrary getContainingErrorModelLibrary(Element element) {
		EObject container = element;
		while (container != null && !(container instanceof ErrorModelLibrary)) {
			container = container.eContainer();
		}
		return (ErrorModelLibrary) container;
	}

	/**
	 * get the enclosing error model subclause.
	 * Returns null if the element is not in a error model subclause
	 * @param element
	 * @return ErrorModelSubclause or null
	 */
	public static ErrorModelSubclause getContainingErrorModelSubclause(EObject element) {
		EObject container = element;
		while (container != null && !(container instanceof ErrorModelSubclause)) {
			container = container.eContainer();
		}
		return (ErrorModelSubclause) container;
	}

	/**
	 * return the feature the error propagation is pointing to or null
	 * @param ep
	 * @return Feature
	 */
	public static Feature getFeature(ErrorPropagation ep) {
		FeatureorPPReference forppref = ep.getFeatureorPPRef();
		if (forppref == null) {
			return null;
		}
		while (forppref.getNext() != null) {
			forppref = forppref.getNext();
		}
		if (forppref.getFeatureorPP() instanceof Feature) {
			return (Feature) forppref.getFeatureorPP();
		}
		return null;
	}

	/**
	 * return true if error propagation points to an access point
	 * @param ep
	 * @return Feature
	 */
	public static boolean isAccess(ErrorPropagation ep) {
		String s = ep.getKind();
		return (s != null) && (s.equalsIgnoreCase("access"));
	}

	/**
	 * return true if error propagation points to generic (functional) binding
	 * @param ep
	 * @return Feature
	 */
	public static boolean isBinding(ErrorPropagation ep) {
		String s = ep.getKind();
		return (s != null) && (s.equalsIgnoreCase("bindings"));
	}

	public static String getPrintName(Element el) {
		if (el instanceof ErrorPropagation) {
			ErrorPropagation ep = (ErrorPropagation) el;
			return getPrintName(ep);
		}
		if (el instanceof TypeSet) {
			TypeSet ts = (TypeSet) el;
			return getPrintName(ts);
		}
		if (el instanceof NamedElement) {
			NamedElement ne = (NamedElement) el;
			if (ne.getName() != null) {
				return ne.getName();
			}
		}
		return "";
	}

	public static String getDirectionName(Element el) {
		if (el instanceof ErrorPropagation) {
			ErrorPropagation ep = (ErrorPropagation) el;
			return getDirectionName(ep);
		}
		if (el instanceof TypeSet) {
			TypeSet ts = (TypeSet) el;
			return getPrintName(ts);
		}
		if (el instanceof NamedElement) {
			NamedElement ne = (NamedElement) el;
			if (ne.getName() != null) {
				return ne.getName();
			}
		}
		return "";
	}

	public static String getName(EObject el) {
		if (el instanceof ErrorPropagation) {
			ErrorPropagation ep = (ErrorPropagation) el;
			return getPropagationName(ep);
		}
		if (el instanceof TypeSet) {
			TypeSet ts = (TypeSet) el;
			return getName(ts);
		}
		if (el instanceof NamedElement) {
			NamedElement ne = (NamedElement) el;
			if (ne.getName() != null) {
				return ne.getName();
			}
		}
		return "";
	}

	/**
	 * get printName of Error Propagation
	 * @param ep
	 * @return
	 */
	public static String getPrintName(ErrorPropagation ep) {
		return getPropagationName(ep);
	}

	public static String getDirectionName(ErrorPropagation ep) {
		return ep.getDirection().getLiteral() + "-" + getPropagationName(ep);
	}

	/**
	 * @deprecated Unused. Will be removed in 2.8.1
	 */
	@Deprecated
	public static String getPrintNameWithoutType(EMV2Path ep) {
		if (ep == null) {
			return "";
		}
		EMV2PathElement epe = ep.getEmv2Target();
		if (epe == null) {
			return "";
		}
		ContainmentPathElement cpe = ep.getContainmentPath();
		if (cpe == null) {
			return getPathNameWithoutType(epe);
		}
		String prefix = "^" + cpe.getNamedElement().getName();
		while (cpe.getPath() != null) {
			cpe = cpe.getPath();
			prefix = prefix + "." + cpe.getNamedElement().getName();
		}
		if (!prefix.isEmpty()) {
			prefix = prefix + '@';
		}
		if (epe.getEmv2PropagationKind() != null) {
			return prefix + epe.getEmv2PropagationKind();
		} else {
			return getPathNameWithoutType(epe);
		}
	}

	public static String getPrintName(EMV2Path ep) {
		if (ep == null) {
			return "";
		}
		EMV2PathElement epe = ep.getEmv2Target();
		if (epe == null) {
			return "";
		}
		ContainmentPathElement cpe = ep.getContainmentPath();
		String prefix;
		if (cpe == null) {
			prefix = "";
		} else {
			prefix = "^" + cpe.getNamedElement().getName();
			cpe = cpe.getPath();
		}
		while (cpe != null) {
			prefix = prefix + "." + cpe.getNamedElement().getName();
			cpe = cpe.getPath();
		}
		if (!prefix.isEmpty()) {
			prefix = prefix + '@';
		}
		if (epe.getEmv2PropagationKind() != null) {
			return prefix + epe.getEmv2PropagationKind()
					+ (epe.getErrorType() != null ? "." + epe.getErrorType().getName() : "");
		} else {
			return getPathName(epe);
		}
	}

	/**
	 * **********************************
	 * methods for retrieving elements in an EMV2Path
	 */

	/**
	 * This method returns the error type if referenced in the containment path
	 * This applies only to paths in EMV2PropertionAssociation
	 * In ConditionElement the type or type set is
	 * @param ep
	 * @return
	 */
	public static ErrorTypes getErrorType(EMV2Path ep) {
		EMV2PathElement last = getLast(ep.getEmv2Target());
		if (last.getNamedElement() instanceof ErrorTypes) {
			return (ErrorTypes) last.getNamedElement();
		}
		if (last.getErrorType() != null) {
			return last.getErrorType();
		}
		return null;
	}

	/**
	 * Get the error model element pointed to by the EMV2Path.
	 * An error model element can be ErrorSource, ErrorSink, ErrorPath, ErrorPropagaiton, ErrorState,
	 * ErrorBehaviorEvent (ErrorEvent, RecoverEvent, RepairEvent), ErrorBehaviorTransition
	 * This works for condition elements (ConditionElement, SConditionElement)
	 * @param ce ConditionElement
	 * @return NamedElement
	 */
	public static NamedElement getErrorModelElement(ConditionElement ce) {
		return getErrorModelElement(ce.getQualifiedErrorPropagationReference());
	}

	/**
	 * get the error propagation pointed to by the EMV2Path or null if not an error propagation.
	 * This method calls getErrorModelElement
	 * @param ce ConditionElement
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation getErrorPropagation(SConditionElement ce) {
		return getErrorPropagation(ce.getQualifiedErrorPropagationReference());
	}

	/**
	 * get the error propagation or error behavior event pointed to by the EMV2Path or null if not an error propagation.
	 * This method calls getErrorModelElement
	 * @param ce ConditionElement
	 * @return EventOrPropagation
	 */
	public static EventOrPropagation getErrorEventOrPropagation(ConditionElement ce) {
		return getErrorEventOrPropagation(ce.getQualifiedErrorPropagationReference());
	}

	/**
	 * get the error propagation pointed to by the EMV2Path or null if not an error propagation.
	 * This method calls getErrorModelElement
	 * @param epath EMV2Path
	 * @return ErrorPropagation
	 */
	public static ErrorPropagation getErrorPropagation(EMV2Path epath) {
		NamedElement res = getErrorModelElement(epath);
		if (res instanceof ErrorPropagation) {
			return (ErrorPropagation) res;
		}
		return null;
	}

	/**
	 * get the error propagation or error behavior event pointed to by the EMV2Path or null if not an error propagation.
	 * This method calls getErrorModelElement
	 * @param epath EMV2Path
	 * @return EventOrPropagation
	 */
	public static EventOrPropagation getErrorEventOrPropagation(EMV2Path epath) {
		if (epath == null) {
			return null;
		}
		NamedElement res = getErrorModelElement(epath);
		if (res instanceof ErrorPropagation || res instanceof ErrorBehaviorEvent) {
			return (EventOrPropagation) res;
		}
		return null;
	}

	/**
	 * Get the error model element pointed to by the EMV2Path.
	 * An error model element can be ErrorSource, ErrorSink, ErrorPath, ErrorPropagaiton, ErrorState,
	 * ErrorBehaviorEvent (ErrorEvent, RecoverEvent, RepairEvent), ErrorBehaviorTransition
	 * This works for condition elements (ConditionElement, SConditionElement)
	 * @param epath EMV2Path
	 * @return NamedElement
	 */
	public static NamedElement getErrorModelElement(EMV2Path epath) {
		if (epath == null) {
			return null;
		}
		EMV2PathElement target = getLast(epath.getEmv2Target());
		if (target.getNamedElement() instanceof ErrorTypes) {
			EObject prev = target.eContainer();
			if (prev instanceof EMV2PathElement) {
				target = (EMV2PathElement) prev;
			} else {
				return null;
			}
		}
		NamedElement prop = target.getNamedElement();
		if (prop != null) {
			return prop;
		}
		String kind = target.getEmv2PropagationKind();
		Classifier cxtcl = EMV2Util.getAssociatedClassifier(epath);
		if (target.eContainer() instanceof EMV2PathElement) {
			// should be a subcomponent reference
			NamedElement cxt = ((EMV2PathElement) target.eContainer()).getNamedElement();
			if (cxt instanceof Subcomponent) {
				cxtcl = ((Subcomponent) cxt).getAllClassifier();
			}
		} else {
			if (epath.getContainmentPath() != null) {
				ContainmentPathElement last = getLast(epath.getContainmentPath());
				if (last.getNamedElement() instanceof Subcomponent) {
					cxtcl = ((Subcomponent) last.getNamedElement()).getAllClassifier();
				}
			}
		}
		ErrorPropagation ep = findErrorPropagation(cxtcl, kind, DirectionType.IN);
		if (ep == null) {
			ep = findErrorPropagation(cxtcl, kind, DirectionType.OUT);
		}
		return ep;
	}

	public static EMV2PathElement getLast(EMV2PathElement ep) {
		EMV2PathElement result = ep;
		while (result.getPath() != null) {
			result = result.getPath();
		}
		return result;
	}

	/**
	 * return the last subcomponent in the EMV2Path
	 * @param epath EMV2Path
	 * @return Subcomponent
	 */
	public static Subcomponent getLastSubcomponent(EMV2Path epath) {
		if (epath == null) {
			return null;
		}
		if (epath.getContainmentPath() != null) {
			// handle paths that come from the EMV2PropertyAssociation with the new syntax for the core path
			ContainmentPathElement last = getLast(epath.getContainmentPath());
			if (last.getNamedElement() instanceof Subcomponent) {
				return (Subcomponent) last.getNamedElement();
			}
			return null;
		}
		EMV2PathElement epe = epath.getEmv2Target();
		Subcomponent result = null;
		while (epe != null) {
			if (epe.getNamedElement() instanceof Subcomponent) {
				result = (Subcomponent) epe.getNamedElement();
			}
			epe = epe.getPath();
		}
		return result;
	}

	/**
	 * get the last component instance in the epath relative to the component instance root
	 * Returns root if the path does not include subcomponents.
	 * Returns null if the component instance is not found, i.e., the path subcomponent references cannot be found in the
	 * component instance hierarchy.
	 * @param epath EMV2Path that includes EMV2PathElements pointing to subcomponents.
	 * @param root ComponentInstance that is the root of the subcomponent section of the path
	 * @return ComponentInstance
	 */
	public static ComponentInstance getLastComponentInstance(EMV2Path epath, ComponentInstance root) {
		ComponentInstance result = root;
		if (epath.getContainmentPath() != null) {
			// handle paths that come from the EMV2PropertyAssociation with the new syntax for the core path
			ContainmentPathElement ce = epath.getContainmentPath();
			while (ce != null && result != null) {
				if (ce.getNamedElement() instanceof Subcomponent) {
					Subcomponent sub = (Subcomponent) ce.getNamedElement();
					result = result.findSubcomponentInstance(sub);
				}
				ce = ce.getPath();
			}
			return result;
		}
		EMV2PathElement epe = epath.getEmv2Target();
		while (epe != null && result != null) {
			if (epe.getNamedElement() instanceof Subcomponent) {
				Subcomponent sub = (Subcomponent) epe.getNamedElement();
				result = result.findSubcomponentInstance(sub);
			}
			epe = epe.getPath();
		}
		return result;
	}

	public static ComponentInstance getLastComponentInstance(QualifiedErrorBehaviorState qs, ComponentInstance root) {
		ComponentInstance referencedInstance;
		referencedInstance = root;
		while (qs != null && referencedInstance != null) {
			referencedInstance = referencedInstance.findSubcomponentInstance(qs.getSubcomponent().getSubcomponent());
			qs = qs.getNext();
		}
		return referencedInstance;
	}

	public static ContainmentPathElement getLast(ContainmentPathElement ep) {
		if (ep == null) {
			return null;
		}
		ContainmentPathElement result = ep;
		while (result.getPath() != null) {
			result = result.getPath();
		}
		return result;
	}

	public static String getPathNameWithoutType(EMV2PathElement ep) {
		if (ep == null || ep.getNamedElement() instanceof ErrorTypes) {
			return "";
		}
		String path = getPathNameWithoutType(ep.getPath());
		String myname = getPrintName(ep.getNamedElement());
		if (myname == null) {
			return path;
		}
		if (!path.isEmpty()) {
			return myname + "." + path;
		} else {
			return myname;
		}
	}

	public static String getPathName(EMV2PathElement ep) {
		if (ep == null) {
			return "";
		}
		String path = getPathName(ep.getPath());
		String myname = getPrintName(ep.getNamedElement());
		if (myname == null) {
			return path;
		}
		if (!path.isEmpty()) {
			return myname + "." + path;
		} else {
			return myname;
		}
	}

	public static String getPrintName(TypeSet ts) {
		if (ts == null) {
			return "";
		}
		EList<TypeToken> tel = ts.getTypeTokens();
		return getPrintName(tel);
	}

	public static String getName(TypeSet ts) {
		if (ts == null) {
			return "";
		}
		if (ts.getName() != null) {
			return ts.getName();
		}
		EList<TypeToken> tel = ts.getTypeTokens();
		return getName(tel);
	}

	public static String getPrintName(ErrorModelLibrary eml) {
		return AadlUtil.getContainingPackage(eml).getName();
	}

	public static String getPrintName(TypeToken tu) {
		if (tu == null) {
			return "";
		}
		return "{" + getName(tu) + "}";
	}

	public static String getName(TypeToken tu) {
		if (tu == null) {
			return "";
		}
		String res = "";
		for (ErrorTypes et : tu.getType()) {
			if (res.isEmpty()) {
				res = et.getName();
			} else {
				res = res + '*' + et.getName();
			}
		}
		return res;
	}

	public static String getName(EList<TypeToken> tul) {
		boolean docomma = false;
		String res = "";
		for (TypeToken typeSetElement : tul) {
			EList<ErrorTypes> et = typeSetElement.getType();
			if (docomma) {
				res = res + ',';
			} else {
				docomma = true;
			}
			if (et != null && !et.isEmpty()) {
				boolean doproduct = false;
				for (ErrorTypes errorType : et) {
					if (doproduct) {
						res = res + "*";
					} else {
						doproduct = true;
					}
					res = res + errorType.getName();
				}
			}
		}
		return res;
	}

	public static String getPrintName(EList<TypeToken> tul) {
		boolean docomma = false;
		String res = "{";
		for (TypeToken typeSetElement : tul) {
			EList<ErrorTypes> et = typeSetElement.getType();
			if (docomma) {
				res = res + ',';
			} else {
				docomma = true;
			}
			if (et != null && !et.isEmpty()) {
				boolean doproduct = false;
				for (ErrorTypes errorType : et) {
					if (doproduct) {
						res = res + "*";
					} else {
						doproduct = true;
					}
					res = res + errorType.getName();
				}
			}
		}
		return res + "}";
	}

	/**
	 * resolve the errortype if it is an alias, otherwise return the error type
	 * @param et ErrorType that may be an alias
	 * @return ErrorType without alias
	 */
	public static ErrorType resolveAlias(ErrorType et) {
		if (Aadl2Util.isNull(et)) {
			return null;
		}
		HashSet<ErrorType> result = new HashSet<ErrorType>();
		while (!Aadl2Util.isNull(et.getAliasedType())) {
			result.add(et);
			et = et.getAliasedType();
			if (result.contains(et)) {
				return et;
			}
		}
		return et;
	}

	/**
	 * resolve the typeset if it is an alias, otherwise return the typeset
	 * @param typeset TypeSet
	 * @return TypeSet resolved TypeSet
	 */
	public static TypeSet resolveAlias(TypeSet typeset) {
		if (Aadl2Util.isNull(typeset)) {
			return null;
		}
		HashSet<TypeSet> result = new HashSet<TypeSet>();
		while (!Aadl2Util.isNull(typeset.getAliasedType())) {
			result.add(typeset);
			typeset = typeset.getAliasedType();
			if (result.contains(typeset)) {
				return typeset;
			}
		}
		return typeset;
	}

	/**
	 * find the actual ErrorTypes following the alias link
	 * @param et
	 * @return
	 */
	public static ErrorTypes resolveAlias(ErrorTypes et) {
		if (Aadl2Util.isNull(et)) {
			return null;
		}
		return (et instanceof ErrorType) ? EMV2Util.resolveAlias((ErrorType) et) : EMV2Util.resolveAlias((TypeSet) et);
	}

	/**
	 *  figure out the target typetoken based on the source and type mappings
	 * Path can be a connection instance, a flow spec instance, or an error flow
	 * If null or no mapping found, then use source type token
	 * @param path connection instance, flow spec instance, error flow
	 * @param path path of mapping
	 * @return TypeToken
	 */
	public static TypeToken mapToken(TypeToken sourceToken, EObject path) {
		TypeToken result = sourceToken;
		if (path instanceof ConnectionInstance) {
			if (sourceToken != null) {
				// TODO lookup type transformations for connections and use them to determine target type
				ConnectionReference connref = Aadl2InstanceUtil.getTopConnectionReference((ConnectionInstance) path);
				ComponentInstance parentci = connref.getContext();
				TypeTransformationSet tts = getAllTypeTransformationSet(parentci);
				result = EMV2TypeSetUtil.mapTypeToken(sourceToken, tts);
			}
		} else if (path instanceof ErrorPath) {
			ErrorPath epath = (ErrorPath) path;
			// map the token
			TypeSet ttup = epath.getTargetToken();
			if (ttup == null) {
				// map token via tms
				TypeMappingSet tms = getUseMappings(epath);
				if (tms != null) {
					result = EMV2TypeSetUtil.mapTypeToken(sourceToken, tms);
				}
			} else {
				result = ttup.getTypeTokens().get(0);
			}
		} else if (path instanceof FlowSpecificationInstance) {
			// pass on source token
		}
		return result;
	}

	/**
	 * get TypeMappingSet listed in Use Mappings
	 * @param context Type use context
	 * @return EList<ErrorModelLibrary>
	 */
	public static TypeMappingSet getUseMappings(ErrorPath context) {
		// handle local type mapping set
		if (context.getTypeMappingSet() != null) {
			return context.getTypeMappingSet();
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(context);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			TypeMappingSet tms = errorModelSubclause.getTypeMappingSet();
			if (tms != null) {
				return tms;
			}
		}
		return null;
	}

	public static final EList<ErrorModelLibrary> EmptyElist = new BasicEList<ErrorModelLibrary>();

	/**
	 * get list of ErrorModelLibraries listed in UseTypes
	 * @param context Type use context
	 * @return EList<ErrorModelLibrary>
	 */
	public static EList<ErrorModelLibrary> getUseTypes(Element context) {
		EObject useTypesContainer = context;
		while (useTypesContainer != null && !(useTypesContainer instanceof ErrorModelLibrary
				|| useTypesContainer instanceof ErrorModelSubclause
				|| useTypesContainer instanceof TypeTransformationSet || useTypesContainer instanceof TypeMappingSet
				|| useTypesContainer instanceof ErrorBehaviorStateMachine)) {
			useTypesContainer = useTypesContainer.eContainer();
		}
		if (useTypesContainer instanceof ErrorModelSubclause) {
			EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(context);
			for (ErrorModelSubclause errorModelSubclause : emslist) {
				EList<ErrorModelLibrary> eml = errorModelSubclause.getUseTypes();
				if (!eml.isEmpty()) {
					return eml;
				}
			}
			return EmptyElist;
		}
		if (useTypesContainer instanceof TypeTransformationSet) {
			return ((TypeTransformationSet) useTypesContainer).getUseTypes();
		}
		if (useTypesContainer instanceof TypeMappingSet) {
			return ((TypeMappingSet) useTypesContainer).getUseTypes();
		}
		if (useTypesContainer instanceof ErrorBehaviorStateMachine) {
			return ((ErrorBehaviorStateMachine) useTypesContainer).getUseTypes();
		}
		if (useTypesContainer instanceof ErrorModelLibrary) {
			return ((ErrorModelLibrary) useTypesContainer).getUseTypes();
		}
		return EmptyElist;
	}

	/**
	 * find the model object that contains the condition expression
	 * @param ce Condition Expression
	 * @return EObject
	 */
	public static EObject getConditionExpressionContext(EObject ce) {
		EObject res = ce;
		while (res != null && res instanceof ConditionExpression) {
			res = res.eContainer();
		}
		return res;
	}

	/**
	 * get error type (last element of containment path, if present
	 * Otherwise return null
	 * @param containedNamedElement Containment path
	 * @return EList<ErrorType>
	 */
	public static ErrorType getContainmentErrorType(ContainedNamedElement containedNamedElement) {
		EList<ContainmentPathElement> cpes = containedNamedElement.getContainmentPathElements();
		if (!cpes.isEmpty()) {
			ContainmentPathElement cpe = cpes.get(cpes.size() - 1);
			NamedElement appliestome = cpe.getNamedElement();
			if (appliestome instanceof ErrorType) {
				return ((ErrorType) appliestome);
			}
		}
		return null;
	}

	/**
	 * take inheritance into account
	 * @param ci
	 * @return
	 */
	public static boolean hasEMV2Subclause(ComponentInstance ci) {
		return hasEMV2Subclause(ci.getComponentClassifier());
	}

	public static boolean hasErrorPropagations(ComponentInstance ci) {
		return hasErrorPropagations(ci.getComponentClassifier());
	}

	public static boolean hasErrorPropagations(ComponentClassifier cl) {
		if (cl == null) {
			return false;
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<ErrorPropagation> eflist = errorModelSubclause.getPropagations();
			if (!eflist.isEmpty()) {
				return true;
			}
		}
		return false;
	}

	/**
	 *
	 * @param cl 	The component classifier that is under test to have
	 * 				an error-annex subclause.
	 * @return		True is the component classifier has an error annex
	 * 				subclause. False otherwise.
	 */
	public static boolean hasEMV2Subclause(ComponentClassifier cl) {
		if (cl == null) {
			return false;
		}
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		return (!emslist.isEmpty());
	}

	public static boolean hasComponentErrorBehaviorTransitions(ComponentInstance ci) {
		return hasComponentErrorBehaviorTransitions(ci.getComponentClassifier());
	}

	public static boolean hasComponentErrorBehaviorTransitions(ComponentClassifier cl) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {

			if (!errorModelSubclause.getTransitions().isEmpty()) {
				return true;
			}
			if (errorModelSubclause.getUseBehavior() != null
					&& !errorModelSubclause.getUseBehavior().getTransitions().isEmpty()) {
				return true;
			}
		}
		return false;
	}

	public static boolean hasComponentErrorBehaviorStates(ComponentInstance ci) {
		return hasComponentErrorBehaviorStates(ci.getComponentClassifier());
	}

	public static boolean hasComponentErrorBehaviorStates(ComponentClassifier cl) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine cet = errorModelSubclause.getUseBehavior();
			if (cet != null) {
				return true;
			}
		}
		return false;
	}

	public static boolean hasCompositeErrorBehavior(ComponentInstance ci) {
		return hasCompositeErrorBehavior(ci.getComponentClassifier());
	}

	public static boolean hasCompositeErrorBehavior(ComponentClassifier cl) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<CompositeState> ceb = errorModelSubclause.getStates();
			if (!ceb.isEmpty()) {
				return true;
			}
		}
		return false;
	}

	public static boolean hasOutgoingPropagations(ComponentInstance ci) {
		Collection<ErrorPropagation> ceb = getAllOutgoingErrorPropagations(ci.getComponentClassifier());
		return !ceb.isEmpty();
	}

	public static boolean hasErrorBehaviorStates(ComponentInstance ci) {
		Collection<ErrorBehaviorState> ceb = getAllErrorBehaviorStates(ci);
		return !ceb.isEmpty();
	}

	public static boolean hasOutgoingPropagationCondition(ComponentInstance ci) {
		return hasOutgoingPropagationCondition(ci.getComponentClassifier());
	}

	public static boolean hasOutgoingPropagationCondition(ComponentClassifier cl) {
		EList<ErrorModelSubclause> emslist = getAllContainingClassifierEMV2Subclauses(cl);
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			EList<OutgoingPropagationCondition> ceb = errorModelSubclause.getOutgoingPropagationConditions();
			if (!ceb.isEmpty()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * retrieve list of component instances that have EMV2 clauses
	 * @param ci ComponentInstance
	 * @return EList of leaf component instances
	 */
	@SuppressWarnings("unchecked")
	public static EList<ComponentInstance> getComponentInstancesWithEMV2Subclause(ComponentInstance ci) {
		EList<?> result = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				return (obj instanceof ComponentInstance && (EMV2Util.hasEMV2Subclause((ComponentInstance) obj)));
			}
		}.processPreOrderComponentInstance(ci);
		return (EList<ComponentInstance>) result;
	}

	/**
	 * retrieve list of component instances that have error propagations specified
	 * @param ci ComponentInstance
	 * @return EList of leaf component instances
	 */
	@SuppressWarnings("unchecked")
	public static EList<ComponentInstance> getComponentInstancesWithErrorPropagations(ComponentInstance ci) {
		EList<?> result = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				return (obj instanceof ComponentInstance && (EMV2Util.hasErrorPropagations((ComponentInstance) obj)));
			}
		}.processPreOrderComponentInstance(ci);
		return (EList<ComponentInstance>) result;
	}

	/**
	 * retrieve list of component instances that have component error behavior specified
	 * @param ci ComponentInstance
	 * @return EList of leaf component instances
	 */
	@SuppressWarnings("unchecked")
	public static EList<ComponentInstance> getComponentInstancesWithComponentErrorBehaviorStates(ComponentInstance ci) {
		EList<?> result = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				return (obj instanceof ComponentInstance
						&& (EMV2Util.hasComponentErrorBehaviorStates((ComponentInstance) obj)));
			}
		}.processPreOrderComponentInstance(ci);
		return (EList<ComponentInstance>) result;
	}

	/**
	 * retrieve list of component instances that have component error behavior specified
	 * @param ci ComponentInstance
	 * @return EList of leaf component instances
	 */
	@SuppressWarnings("unchecked")
	public static EList<ComponentInstance> getComponentInstancesWithComponentErrorBehaviorTransitions(
			ComponentInstance ci) {
		EList<?> result = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				return (obj instanceof ComponentInstance
						&& (EMV2Util.hasComponentErrorBehaviorTransitions((ComponentInstance) obj)));
			}
		}.processPreOrderComponentInstance(ci);
		return (EList<ComponentInstance>) result;
	}

	/**
	 * retrieve list of component instances that have composite error behavior specified
	 * @param ci ComponentInstance
	 * @return EList of leaf component instances
	 */
	@SuppressWarnings("unchecked")
	public static EList<ComponentInstance> getComponentInstancesWithCompositeErrorBehavior(ComponentInstance ci) {
		EList<?> result = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				return (obj instanceof ComponentInstance
						&& (EMV2Util.hasCompositeErrorBehavior((ComponentInstance) obj)));
			}
		}.processPreOrderComponentInstance(ci);
		return (EList<ComponentInstance>) result;
	}

	/**
	 * returns the feature instance in the component instance that is referenced by the Error Propagation (or Containment)
	 * @param ep
	 * @param ci
	 * @return
	 */
	public static FeatureInstance findFeatureInstance(ErrorPropagation ep, ComponentInstance ci) {
		if (ep == null) {
			return null;
		}
		FeatureorPPReference forppref = ep.getFeatureorPPRef();
		if (forppref == null || forppref.getFeatureorPP() instanceof PropagationPoint) {
			return null;
		}
		FeatureInstance container = ci.findFeatureInstance((Feature) forppref.getFeatureorPP());
		while (forppref.getNext() != null) {
			forppref = forppref.getNext();
			NamedElement ne = forppref.getFeatureorPP();
			if (ne instanceof Feature) {
				Feature fe = (Feature) ne;
				FeatureInstance fi = container.findFeatureInstance(fe);
				if (fi != null) {
					container = fi;
				} else {
					return null;
				}
			} else {
				return null;
			}
		}
		return container;
	}

	/**
	 * returns the feature instance in the component instance that is referenced by the Error Propagation (or Containment)
	 * @param ep
	 * @param ci
	 * @return
	 */
	public static NamedElement getErrorPropagationFeature(ErrorPropagation ep, ComponentInstance ci) {
		Feature f = getFeature(ep);
		if (f == null) {
			return ci;
		}
		return f;
	}

	/**
	 * returns the feature instance in the component instance that is referenced by the Error Propagation (or Containment)
	 * @param ep
	 * @param ci
	 * @return
	 */
	public static DirectionType getErrorPropagationFeatureDirection(ErrorPropagation ep) {
		FeatureorPPReference fref = ep.getFeatureorPPRef();
		boolean inverse = false;
		NamedElement f = null;
		DirectionType featuredir = DirectionType.IN_OUT;
		while (fref != null) {
			f = fref.getFeatureorPP();
			fref = fref.getNext();
			if (f instanceof FeatureGroup && fref != null) {
				FeatureGroup fg = (FeatureGroup) f;
				FeatureGroupType fgt = fg.getAllFeatureGroupType();
				if (fg.isInverse()) {
					inverse = !inverse;
				}
				if (fgt != null && fgt.getInverse() != null
						&& !fgt.getOwnedFeatures().contains(fref.getFeatureorPP())) {
					inverse = !inverse;
				}
			}
		}

		if (f instanceof DirectedFeature) {
			featuredir = ((DirectedFeature) f).getDirection();
			if (inverse) {
				return featuredir.getInverseDirection();
			} else {
				return featuredir;
			}
		}
		return featuredir;
	}

	/**
	 * return true if error propagation points to feature instance
	 * @param ep Error Propagation (or Containment)
	 * @param fi Feature Instance
	 * @return Boolean
	 */
	public static boolean isErrorPropagationOf(ErrorPropagation ep, FeatureInstance fi) {
		if (Aadl2Util.isNull(fi.getFeature())) {
			return false; // not to a feature
		}
		Feature f = getFeature(ep);
		return f.getName().equalsIgnoreCase(fi.getFeature().getName());
	}

	public static PropagationPoint getPropagationPoint(ErrorPropagation ep) {
		FeatureorPPReference ref = ep.getFeatureorPPRef();
		if (ref instanceof PropagationPoint) {
			return (PropagationPoint) ref;
		}
		return null;
	}

	/**
	 * for compatibility reasons.
	 * The path is now right recursive and most code interprets that path.
	 */
	public static EList<FeatureorPPReference> getFeatureorPPRefs(ErrorPropagation errorPropagation) {
		final EList<FeatureorPPReference> list = new BasicEList<>();
		for (FeatureorPPReference current = errorPropagation.getFeatureorPPRef(); current != null; current = current
				.getNext()) {
			list.add(current);
		}
		return list;
	}

	public static EList<SubcomponentElement> getSubcomponents(QualifiedPropagationPoint propagationPoint) {
		final EList<SubcomponentElement> list = new BasicEList<>();
		for (QualifiedPropagationPoint current = propagationPoint; current.getSubcomponent() != null;) {
			list.add(current.getSubcomponent());
			current = current.getNext();
		}
		return list;
	}

	public static NamedElement getEndPoint(QualifiedPropagationPoint propagationPointPath) {
		QualifiedPropagationPoint current = propagationPointPath;
		while (current != null && current.getPropagationPoint() == null) {
			current = current.getNext();
		}
		return current == null ? null : current.getPropagationPoint();
	}

	public static EList<SubcomponentElement> getSubcomponents(SConditionElement conditionElement) {
		final EList<SubcomponentElement> list = new BasicEList<>();
		for (QualifiedErrorBehaviorState current = conditionElement.getQualifiedState(); current
				.getSubcomponent() != null;) {
			list.add(current.getSubcomponent());
			current = current.getNext();
		}
		return list;
	}

	public static ErrorBehaviorState getState(SConditionElement conditionElement) {
		for (QualifiedErrorBehaviorState current = conditionElement
				.getQualifiedState(); current != null; current = current.getNext()) {
			if (current.getState() != null) {
				return current.getState();
			}
		}
		return null;
	}

	public static boolean checkCyclicExtends(ErrorModelLibrary etl) {
		if (etl.getExtends() == null) {
			return false;
		}
		HashSet<ErrorModelLibrary> result = new HashSet<ErrorModelLibrary>();
		return recursiveCheckCyclicExtends(etl, result);
	}

	private static boolean recursiveCheckCyclicExtends(ErrorModelLibrary etl, HashSet<ErrorModelLibrary> shetl) {
		boolean result = false;
		if (etl.getExtends() != null) {
			shetl.add(etl);
			EList<ErrorModelLibrary> etllist = etl.getExtends();
			for (ErrorModelLibrary xetl : etllist) {
				if (shetl.contains(xetl)) {
					result = true;
				} else {
					result = result || recursiveCheckCyclicExtends(xetl, shetl);
				}
			}
			shetl.remove(etl);
		}
		return result;
	}

	public static boolean isProcessor(ErrorPropagation ep) {
		return (ep != null) && (ep.getKind() != null) && (ep.getKind().equalsIgnoreCase("processor"));
	}

	public static String stripUnderScore(String value) {
		if (value == null) {
			return value;
		}
		return value.replaceAll("_", "");
	}

}