ErrorModelValidator.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.validation;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

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.eclipse.xtext.validation.Check;
import org.eclipse.xtext.validation.CheckType;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.AbstractFeature;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.ComponentType;
import org.osate.aadl2.Connection;
import org.osate.aadl2.ConnectionEnd;
import org.osate.aadl2.ContainedNamedElement;
import org.osate.aadl2.ContainmentPathElement;
import org.osate.aadl2.Context;
import org.osate.aadl2.DefaultAnnexSubclause;
import org.osate.aadl2.DirectionType;
import org.osate.aadl2.Element;
import org.osate.aadl2.FeatureGroupType;
import org.osate.aadl2.InternalFeature;
import org.osate.aadl2.ModalPropertyValue;
import org.osate.aadl2.ModeTransition;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Port;
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.PropertyType;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.modelsupport.util.AadlUtil;
import org.osate.aadl2.util.Aadl2Util;
import org.osate.xtext.aadl2.errormodel.errorModel.CompositeState;
import org.osate.xtext.aadl2.errormodel.errorModel.ConditionElement;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2Path;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2PathElement;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2PropertyAssociation;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2Root;
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.ErrorEvent;
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.ErrorType;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorTypes;
import org.osate.xtext.aadl2.errormodel.errorModel.EventOrPropagation;
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.RecoverEvent;
import org.osate.xtext.aadl2.errormodel.errorModel.ReportingPortReference;
import org.osate.xtext.aadl2.errormodel.errorModel.SConditionElement;
import org.osate.xtext.aadl2.errormodel.errorModel.TransitionBranch;
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.TypeTransformation;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeTransformationSet;
import org.osate.xtext.aadl2.errormodel.util.EMV2Properties;
import org.osate.xtext.aadl2.errormodel.util.EMV2TypeSetUtil;
import org.osate.xtext.aadl2.errormodel.util.EMV2Util;

/**
 * @since 5.0
 */
public class ErrorModelValidator extends AbstractErrorModelValidator {

	@Override
	protected boolean isResponsible(Map<Object, Object> context, EObject eObject) {
		return (eObject.eClass().getEPackage() == ErrorModelPackage.eINSTANCE || eObject instanceof Connection
				|| eObject instanceof PropertyAssociation);
	}

	// copy of casePropertyAssociation
	@Check(CheckType.FAST)
	public void caseEMV2PropertyAssociation(EMV2PropertyAssociation pa) {
		checkPropertyAssociation(pa);
	}

	protected void checkPropertyAssociation(EMV2PropertyAssociation pa) {
		// type check value against type
		Property pdef = pa.getProperty();
		if (Aadl2Util.isNull(pdef)) {
			return;
		}

		PropertyType pt = pdef.getPropertyType();
		if (Aadl2Util.isNull(pt)) {
			return;
		}

		EList<ModalPropertyValue> pvl = pa.getOwnedValues();
		for (ModalPropertyValue modalPropertyValue : pvl) {
			typeCheckPropertyValues(pt, modalPropertyValue.getOwnedValue(), pa, pdef.getQualifiedName(), 0);
		}
		checkAssociationAppliesTo(pa);
	}

	private void checkAssociationAppliesTo(final EMV2PropertyAssociation pa) {
		final Property pn = pa.getProperty();
		final EList<EMV2Path> appliesTo = pa.getEmv2Path();
		if (appliesTo == null || appliesTo.size() == 0) {
			Element element = pa.getOwner();
			if (element instanceof NamedElement) {
				final boolean applies = ((NamedElement) element).acceptsProperty(pn);
				if (!applies) {
					error(pa, "Property " + pa.getProperty().getQualifiedName() + " does not apply to "
							+ ((NamedElement) element).getName());
					// error(pa,
					// "Property " + pa.getQualifiedName() +
					// " does not apply to " + element.eClass().getName());
				}
			}
		} else {
			for (EMV2Path cna : appliesTo) {
				EMV2PathElement path = cna.getEmv2Target();
				if (path != null) {
					// only the last value is interesting to us
					final EMV2PathElement ph = EMV2Util.getLast(path);
					NamedElement ne = ph.getNamedElement();
					if (ne instanceof ErrorTypes) {
						ErrorTypes et = (ErrorTypes) ne;
						EObject prev = ph.eContainer();
						if (prev instanceof EMV2PathElement) {
							ne = ((EMV2PathElement) prev).getNamedElement();
							boolean noMatch = false;
							if (ne instanceof ErrorBehaviorState) {
								TypeSet ts = ((ErrorBehaviorState) ne).getTypeSet();
								noMatch = ts != null && !EMV2TypeSetUtil.contains(ts, et);
							} else if (ne instanceof ErrorPropagation) {
								String epname = EMV2Util.getPrintName((ErrorPropagation) ne);
								EList<ErrorPropagation> eplist = EMV2Util.getContainingErrorModelSubclause(ne)
										.getPropagations();
								Boolean foundType = false;
								for (ErrorPropagation ep : eplist) {
									if (epname.equalsIgnoreCase(EMV2Util.getPrintName(ep))) {
										TypeSet ts = ep.getTypeSet();
										if (EMV2TypeSetUtil.contains(ts, et)) {
											foundType = true;
											break;
										}
									}
								}
								noMatch = !foundType;
							} else if (ne instanceof ErrorEvent) {
								TypeSet ts = ((ErrorEvent) ne).getTypeSet();
								noMatch = ts != null && !EMV2TypeSetUtil.contains(ts, et);
							}
							if (noMatch) {
								error(pa,
										"Property " + pa.getProperty().getQualifiedName()
												+ " applies to refers to type " + EMV2Util.getPrintName(et)
												+ " not contained in type set of error propagation "
												+ EMV2Util.getPrintName(ne));
							}
						}
					}
					if (!Aadl2Util.isNull(ne)) {
						final boolean applies = ph.getNamedElement().acceptsProperty(pn);
						if (!applies) {
							error(pa, "Property " + pa.getProperty().getQualifiedName() + " does not apply to "
									+ EMV2Util.getPrintName(cna));
						}
					}
				}
			}
		}
	}

	@Check(CheckType.FAST)
	public void caseErrorPropagation(ErrorPropagation errorPropagation) {
		checkDirectionType(errorPropagation);
		checkTypePropagationAndContainment(errorPropagation);
	}

	@Check(CheckType.FAST)
	public void casePropagationPoint(PropagationPoint propagationPoint) {
		checkUniquePropagationPointorPath(propagationPoint);
	}

	/**
	 * @since 6.1
	 */
	@Check(CheckType.FAST)
	public void caseTypeTransformationSet(TypeTransformationSet typeTransformation) {
		checkTypeTransformationSet(typeTransformation);
	}

	@Override
	@Check(CheckType.FAST)
	public void casePropertyAssociation(PropertyAssociation propertyAssociation) {
		// check that error type is contained in type set of target element
		EList<ContainedNamedElement> apto = propertyAssociation.getAppliesTos();
		for (ContainedNamedElement containedNamedElement : apto) {
			EList<ContainmentPathElement> cpe = containedNamedElement.getContainmentPathElements();
			if (cpe.size() > 1) {
				ContainmentPathElement obj = cpe.get(cpe.size() - 1);
				if (obj.getNamedElement() instanceof ErrorType) {
					ErrorType et = (ErrorType) obj.getNamedElement();
					ContainmentPathElement target = cpe.get(cpe.size() - 2);
					NamedElement ne = target.getNamedElement();
					TypeSet tts = null;
					if (ne instanceof ErrorBehaviorState) {
						tts = ((ErrorBehaviorState) ne).getTypeSet();
					} else if (ne instanceof ErrorPropagation) {
						tts = ((ErrorPropagation) ne).getTypeSet();
					} else if (ne instanceof ErrorEvent) {
						tts = ((ErrorEvent) ne).getTypeSet();
					}
					if (!EMV2TypeSetUtil.contains(tts, et)) {
						error(propertyAssociation, "Property " + propertyAssociation.getProperty().getQualifiedName()
								+ " applies to refers to type " + EMV2Util.getPrintName(et)
								+ " not contained in type set of error propagation " + EMV2Util.getPrintName(ne));
					}
				}
			}
		}
	}

	@Check(CheckType.FAST)
	public void casePropagationPointConnection(PropagationPath opc) {
		checkUniquePropagationPointorPath(opc);
	}

	@Check(CheckType.FAST)
	public void caseErrorType(ErrorType et) {
		checkCyclicExtends(et);
		checkCyclicRenames(et);
	}

	@Check(CheckType.FAST)
	public void caseTypeSet(TypeSet ts) {
		checkTypeSetUniqueTypes(ts);
		checkCyclicRenames(ts);

	}

	@Check(CheckType.FAST)
	public void caseTypeToken(TypeToken tt) {
		checkTypeTokenUniqueTypes(tt);
		checkTypeTokenSingleTypeSet(tt);
	}

	@Check(CheckType.FAST)
	public void caseRecoverEvent(RecoverEvent recoverEvent) {
		checkRecoverEventTriggerType(recoverEvent);
	}

	@Check(CheckType.FAST)
	public void caseRepairEvent(RecoverEvent recoverEvent) {
		checkRecoverEventTriggerType(recoverEvent);
	}

	@Check(CheckType.FAST)
	public void caseConditionElement(ConditionElement conditionElement) {
		checkConditionElementDirection(conditionElement);
		checkConditionElementType(conditionElement);
	}

	@Check(CheckType.FAST)
	public void caseSConditionElement(SConditionElement conditionElement) {
		checkSConditionElementType(conditionElement);
	}

	@Check(CheckType.FAST)
	public void caseErrorModelSubclause(ErrorModelSubclause subclause) {
		if (EcoreUtil2.getContainerOfType(subclause, FeatureGroupType.class) != null) {
			error("Error model subclauses are not permitted in feature group types.",
					EcoreUtil2.getContainerOfType(subclause, DefaultAnnexSubclause.class),
					Aadl2Package.eINSTANCE.getNamedElement_Name());
			return;
		}
		checkSubclauseAssociationToClassifier(subclause);
		checkDuplicateSubclause(subclause);
		checkOnePropagationAndContainmentPoint(subclause);
		Collection<NamedElement> names = EMV2Util.getAllNamedElements(subclause);
		List<NamedElement> doubles = EMV2Util.findDoubleNamedElementsInList(names);
		for (NamedElement namedElement : doubles) {
			if (!(namedElement instanceof ErrorPropagation)) {
				error(namedElement,
						"Subclause has more than one element with the name '" + namedElement.getName() + "'.");
			}
		}
		Collection<ErrorPropagation> ins = EMV2Util
				.getAllIncomingErrorPropagations(subclause.getContainingClassifier());
		for (ErrorPropagation errorPropagation : ins) {
			checkTypePropagationAndContainment(errorPropagation);
		}
		Collection<ErrorPropagation> outs = EMV2Util
				.getAllOutgoingErrorPropagations(subclause.getContainingClassifier());
		for (ErrorPropagation errorPropagation : outs) {
			checkTypePropagationAndContainment(errorPropagation);
		}
		checkUseBehavior(subclause);
	}

	@Check(CheckType.FAST)
	public void caseTypeMappingSet(TypeMappingSet tms) {
		checkTypeMappingSet(tms);
		// checkElementRuleConsistency(tms);
	}

	@Check(CheckType.FAST)
	public void caseErrorModelLibrary(ErrorModelLibrary errorModelLibrary) {
		if (errorModelLibrary.getName() == null) {
			errorModelLibrary.setName("emv2");
		}
		boolean cyclicextends = checkCyclicExtends(errorModelLibrary);
		checkUniqueDefiningIdentifiers(errorModelLibrary, cyclicextends);
	}

	@Check(CheckType.FAST)
	public void caseErrorBehaviorStateMachine(ErrorBehaviorStateMachine ebsm) {
		checkUniqueEBSMElements(ebsm);
	}

	@Check(CheckType.FAST)
	public void caseErrorBehaviorTransition(ErrorBehaviorTransition ebt) {
		checkTransitionSourceTypes(ebt);
		checkTransitionTargetTypes(ebt);
		checkBranches(ebt);
		checkTransitionTargetTriggerTypes(ebt);
	}

	@Check(CheckType.FAST)
	public void caseTransitionBranch(TransitionBranch ebt) {
		checkTransitionTargetTypes(ebt);
	}

	@Check(CheckType.FAST)
	public void caseErrorDetection(ErrorDetection ebt) {
		checkDetectionSourceTypes(ebt);
	}

	/**
	 * @since 3.0
	 */
	@Check
	public void typeCheckDetectionReportingPort(ErrorDetection detection) {
		ReportingPortReference portReference = detection.getDetectionReportingPort();
		if (portReference != null) {
			NamedElement element = portReference.getElement();
			if (!element.eIsProxy() && !(element instanceof AbstractFeature || element instanceof Port
					|| element instanceof InternalFeature)) {
				error("'" + element.getName() + "' is not a port, abstract feature, or internal feature.",
						portReference, ErrorModelPackage.eINSTANCE.getReportingPortReference_Element());
			}
		}
	}

	@Check(CheckType.FAST)
	public void caseErrorSource(ErrorSource ef) {
		checkErrorSourceTypes(ef);
		checkFlowDirection(ef);
	}

	@Check(CheckType.FAST)
	public void caseOutgoingPropagationCondition(OutgoingPropagationCondition opc) {
		checkOutgoingConditionSourceTypes(opc);
		checkOutgoingTypes(opc);
		checkHasConditionOrTypeToken(opc);
	}

	@Check(CheckType.FAST)
	public void caseErrorSink(ErrorSink ef) {
		checkErrorSinkTypes(ef);
		checkFlowDirection(ef);
	}

	@Check(CheckType.FAST)
	public void caseErrorPath(ErrorPath ef) {
		checkErrorPathTypes(ef);
		checkFlowDirection(ef);
		if (ef.getTypeMappingSet() != null) {
			warning(ef, "Legacy support: please declare 'use mappings' at subclause level.");
		}
	}

	@Check(CheckType.FAST)
	public void caseConnection(Connection conn) {
		checkConnectionErrorTypes(conn);
	}

	private void checkRecoverEventTriggerType(RecoverEvent recoverEvent) {
		EList<NamedElement> cl = recoverEvent.getEventInitiator();
		for (NamedElement namedElement : cl) {
			if (!(namedElement instanceof ModeTransition || namedElement instanceof Port
					|| namedElement instanceof InternalFeature)) {
				error(recoverEvent, "Recover event trigger reference '" + namedElement.getName()
						+ "' is not a port, component internal self event, or mode transition.");
			}
		}
	}

	private void checkUseBehavior(ErrorModelSubclause subclause) {
		// now find it in use behavior clause
		EList<ErrorModelSubclause> emslist = EMV2Util.getAllContainingClassifierEMV2Subclauses(subclause);
		ErrorBehaviorStateMachine foundEBMS = null;
		Classifier foundcl = null;
		for (ErrorModelSubclause errorModelSubclause : emslist) {
			ErrorBehaviorStateMachine ebsm = errorModelSubclause.getUseBehavior();
			if (ebsm != null) {
				if (foundEBMS != null && foundEBMS != ebsm) {
					Classifier cl = EMV2Util.getAssociatedClassifier(errorModelSubclause);
					if (cl instanceof ComponentImplementation && foundcl instanceof ComponentType) {
						error(foundcl,
								"use behavior '" + foundEBMS.getName() + "' of '" + foundcl.getQualifiedName()
										+ "' is not the same as use behavior '" + ebsm.getName() + "' of '"
										+ cl.getQualifiedName() + "'");
						return;
					} else {
						warning(foundcl,
								"use behavior '" + foundEBMS.getName() + "' of '" + foundcl.getQualifiedName()
										+ "' is not the same as use behavior '" + ebsm.getName() + "' of '"
										+ cl.getQualifiedName() + "'");
					}
				}
				foundEBMS = ebsm;
				foundcl = EMV2Util.getAssociatedClassifier(errorModelSubclause);
			}
		}
	}

	private void checkSConditionElementType(SConditionElement conditionElement) {
		// this method handles state part of composite. The incoming propagation is handled by the ConditionElement method.
		ErrorBehaviorState es = EMV2Util.getState(conditionElement);
		if (es == null) {
			return;
		}

		CompositeState compState;
		EObject eo = conditionElement;
		while (eo.eContainer() != null) {
			eo = eo.eContainer();
			if (eo instanceof CompositeState) {
				break;
			}
		}
		if (eo instanceof CompositeState) {
			compState = (CompositeState) eo;
			EList<TypeToken> targetTKs = compState.getTypedToken().getTypeTokens();
			// marks error if target state has a typeset or multiple errors associated with it
			if ((targetTKs != null && targetTKs.size() > 1) || (targetTKs.get(0).getType() != null
					&& !(targetTKs.get(0).getType().get(0) instanceof ErrorType))) {
				error(compState, "Target error type may only have a single error type");
			}
		}


		TypeSet triggerTS = null;
		String triggerName = "";
		triggerTS = es.getTypeSet();
		triggerName = "state " + es.getName();
		TypeSet condTS = conditionElement.getConstraint();

		if (condTS == null) {
			return;
		}
		if (triggerTS == null && condTS != null) {
			// it is ok for a state not to have a type set.
			error(conditionElement, "Condition has type constraint but referenced " + triggerName + " does not.");
		} else if (!EMV2TypeSetUtil.isNoError(condTS) && !EMV2TypeSetUtil.contains(triggerTS, condTS)) {
			error(conditionElement,
					"Condition type constraint " + EMV2Util.getPrintName(condTS) + "is not contained in type set "
							+ EMV2Util.getPrintName(triggerTS) + "of referenced " + triggerName);
		}
	}

	private void checkConditionElementDirection(ConditionElement conditionElement) {
		if (conditionElement.getQualifiedErrorPropagationReference() == null) {
			return;
		}
		ErrorPropagation ep = EMV2Util.getErrorPropagation(conditionElement.getQualifiedErrorPropagationReference());
		Subcomponent sub = EMV2Util.getLastSubcomponent(conditionElement.getQualifiedErrorPropagationReference());
		if (ep == null) {
			return;
		}
		if (sub != null) {
			if (ep.getDirection() != DirectionType.OUT) {

				error(conditionElement,
						"Referenced subcomponent error propagation " + sub.getName() + '.' + EMV2Util.getPrintName(ep)
						+ " must be an out propagation");
			}
		} else if (ep.getDirection() != DirectionType.IN) {
			error(conditionElement,
					"Referenced local error propagation " + EMV2Util.getPrintName(ep) + " must be an in propagation");
		}
	}

	private void checkConditionElementType(ConditionElement conditionElement) {
		EventOrPropagation ep = EMV2Util.getErrorEventOrPropagation(conditionElement);
		if (ep == null) {
			return;
		}
		TypeSet triggerTS = null;
		String triggerName = "";
		if (ep instanceof ErrorPropagation) {
			triggerTS = ((ErrorPropagation) ep).getTypeSet();
			triggerName = "propagation " + EMV2Util.getPrintName((ErrorPropagation) ep);
		} else if (ep instanceof ErrorEvent) {
			triggerTS = ((ErrorEvent) ep).getTypeSet();
			triggerName = "event " + ((ErrorBehaviorEvent) ep).getName();
		}
		TypeSet condTS = conditionElement.getConstraint();
		if (condTS == null) {
			return;
		}
		if (triggerTS == null && condTS != null) {
			// it is ok for a state not to have a type set.
			error(conditionElement, "Condition has type constraint but referenced " + triggerName + " does not.");
		} else if (!EMV2TypeSetUtil.isNoError(condTS) && !EMV2TypeSetUtil.contains(triggerTS, condTS)) {
			error(conditionElement,
					"Condition type constraint " + EMV2Util.getPrintName(condTS) + "is not contained in type set "
							+ EMV2Util.getPrintName(triggerTS) + "of referenced " + triggerName);
		}
	}

	private void checkDirectionType(ErrorPropagation errorPropagation) {
		DirectionType pd = errorPropagation.getDirection();
		DirectionType portd = EMV2Util.getErrorPropagationFeatureDirection(errorPropagation);
		if (!(pd.getName().equalsIgnoreCase(portd.getName()) || portd == DirectionType.IN_OUT)) {
			error(errorPropagation, "Propagation '" + EMV2Util.getPropagationName(errorPropagation)
					+ "' direction does not match feature direction.");
		}
	}

	private void checkTypePropagationAndContainment(ErrorPropagation ep) {
		ErrorPropagation epopposite;
		if (ep.isNot()) {
			return; // do it only for propagation, so we do not get double messages.
		}
		epopposite = EMV2Util.findErrorContainment(ep.getContainingClassifier(), EMV2Util.getPrintName(ep),
				ep.getDirection());
		if (epopposite == null) {
			return;
		}
		BasicEList<TypeToken> res = EMV2TypeSetUtil.getTypeSetIntersection(ep.getTypeSet(), epopposite.getTypeSet());
		if (!res.isEmpty()) {
			error(ep, "Error propagation and containment " + EMV2Util.getPrintName(ep)
					+ " have a common error type or type product " + EMV2Util.getPrintName(res));
		}
	}

	private void checkSubclauseAssociationToClassifier(ErrorModelSubclause emsc) {
		if (emsc.eContainer() instanceof EMV2Root) {
			Classifier cl = EMV2Util.getAssociatedClassifier(emsc);
			if (cl == null) {
				warning(emsc, "EMV2 subclause name '" + emsc.getName() + "' does not identify a component classifier.");
			}
		}
	}

	private void checkDuplicateSubclause(ErrorModelSubclause emsc) {
		ErrorModelSubclause duplicate = null;
		Classifier cl = null;
		if (emsc.eContainer() instanceof EMV2Root) {
			cl = EMV2Util.getAssociatedClassifier(emsc);
			duplicate = EMV2Util.getEmbeddedEMV2Subclause(cl);
		} else {
			cl = emsc.getContainingClassifier();
			duplicate = EMV2Util.getAssociatedEMV2Subclause(cl);
		}
		if (duplicate != null) {
			error(emsc, "EMV2 subclause for component '" + cl.getName()
					+ "' is both embedded in classifier and declared separately.");
		}
	}

	private void checkOnePropagationAndContainmentPoint(ErrorModelSubclause ems) {
		EList<ErrorPropagation> eps = ems.getPropagations();
		int epssize = eps.size();
		for (int i = 0; i < epssize - 1; i++) {
			ErrorPropagation ep1 = eps.get(i);
			for (int k = i + 1; k < epssize; k++) {
				ErrorPropagation ep2 = eps.get(k);
				if (EMV2Util.getPrintName(ep1).equalsIgnoreCase(EMV2Util.getPrintName(ep2))) {
					// uniqueness needs to take into account direction
					if (((ep1.isNot() && ep2.isNot()) || (!ep1.isNot() && !ep2.isNot()))
							&& ep1.getDirection() == ep2.getDirection()) {
						error(ep2, (ep1.isNot() ? "Error containment " : "Error propagation ")
								+ EMV2Util.getPrintName(ep2) + " can only be declared once [E.7.1 (L1)].");
					}
				}
			}
		}
	}

	private void checkFlowDirection(ErrorSource errorSource) {
		NamedElement ne = errorSource.getSourceModelElement();
		if (ne instanceof ErrorPropagation) {
			if (!Aadl2Util.isNull(ne) && ne instanceof ErrorPropagation) {
				ErrorPropagation ep = (ErrorPropagation) ne;
				DirectionType epd = ep.getDirection();
				if (!(epd.equals(DirectionType.OUT))) {
					error(errorSource,
							EMV2Util.getPrintName(ep) + " of error source is not an outgoing propagation point.");
				}
			}
		}
	}

	private void checkFlowDirection(ErrorSink errorSink) {
		ErrorPropagation ep = errorSink.getIncoming();
		if (!Aadl2Util.isNull(ep)) {
			DirectionType epd = ep.getDirection();
			if (!(epd.equals(DirectionType.IN))) {
				error(errorSink, EMV2Util.getPrintName(ep) + " of error sink is not an incoming propagation point.");
			}
		}
	}

	private void checkFlowDirection(ErrorPath errorPath) {
		ErrorPropagation ep = errorPath.getIncoming();
		if (!Aadl2Util.isNull(ep)) {
			DirectionType epd = ep.getDirection();
			if (!(epd.equals(DirectionType.IN))) {
				error(errorPath,
						"Source " + EMV2Util.getPrintName(ep) + " of error path is not an incoming propagation point.");
			}
		}
		ep = errorPath.getOutgoing();
		if (!Aadl2Util.isNull(ep)) {
			DirectionType epd = ep.getDirection();
			if (!(epd.equals(DirectionType.OUT))) {
				error(errorPath,
						"Target " + EMV2Util.getPrintName(ep) + " of error path is not an outgoing propagation point.");
			}
		}
	}

	private void checkTypeSetUniqueTypes(TypeSet ts) {
		EList<TypeToken> etlist = ts.getTypeTokens();
		int size = etlist.size();
		for (int i = 0; i < size - 1; i++) {
			TypeToken tok = etlist.get(i);
			for (int k = i + 1; k < size; k++) {
				TypeToken tok2 = etlist.get(k);
				if (EMV2TypeSetUtil.contains(tok, tok2) || EMV2TypeSetUtil.contains(tok2, tok)) {
					error(ts, "Typeset elements " + EMV2Util.getPrintName(tok) + " and " + EMV2Util.getPrintName(tok2)
							+ " are not disjoint.");
				}
			}
		}
	}

	private void checkTypeTokenUniqueTypes(TypeToken ts) {
		if (ts.getType().size() < 2) {
			return;
		}
		Collection<ErrorType> matchTypes = new ArrayList<ErrorType>();
		for (ErrorTypes et : ts.getType()) {
			if (et instanceof ErrorType) {
				ErrorType match = sameRoot(matchTypes, (ErrorType) et);
				if (match != null) {
					error(ts, "Element type " + match.getName() + " and " + et.getName() + " have same root type");
				} else {
					matchTypes.add((ErrorType) et);
				}
			}
		}
	}

	private ErrorType sameRoot(Collection<ErrorType> matchTypes, ErrorType et) {
		ErrorType etroot = EMV2TypeSetUtil.rootType(et);
		for (ErrorType match : matchTypes) {
			if (etroot == EMV2TypeSetUtil.rootType(match)) {
				return match;
			}

		}
		return null;
	}

	private void checkTypeTokenSingleTypeSet(TypeToken ts) {
		EList<ErrorTypes> ets = ts.getType();
		if (ets.size() > 1) {
			ErrorTypes first = ets.get(0);
			if (first instanceof TypeSet) {
				error(ts, "Type product contains type set " + first.getName());
			}
		}
	}

	private void checkUniqueEBSMElements(ErrorBehaviorStateMachine ebsm) {
		HashMap<String, EObject> etlset = new HashMap<String, EObject>(10, 10);
		for (ErrorBehaviorEvent oep : ebsm.getEvents()) {
			if (etlset.containsKey(oep.getName())) {
				error(oep, "error behavior event " + oep.getName() + " defined more than once");
			} else {
				etlset.put(oep.getName(), oep);
			}
		}
		for (ErrorBehaviorState oep : ebsm.getStates()) {
			if (etlset.containsKey(oep.getName())) {
				error(oep, "error behavior state " + oep.getName() + " previously defined as "
						+ etlset.get(oep.getName()).eClass().getName());
			} else {
				etlset.put(oep.getName(), oep);
			}
		}
		for (ErrorBehaviorTransition oep : ebsm.getTransitions()) {
			if (etlset.containsKey(oep.getName())) {
				error(oep, "error behavior transition " + oep.getName() + " previously defined as "
						+ etlset.get(oep.getName()).eClass().getName());
			} else {
				etlset.put(oep.getName(), oep);
			}
		}
	}

	private void checkUniquePropagationPointorPath(NamedElement ep) {
		Collection<PropagationPoint> tab = EMV2Util.getAllPropagationPoints(ep.getContainingClassifier());
		for (PropagationPoint oep : tab) {
			if (oep != ep && oep.getName().equalsIgnoreCase(ep.getName())) {
				error(ep, "Propagation point " + (ep instanceof PropagationPath ? "path " : "") + ep.getName()
						+ " conflicts with propagation point.");
			}
		}
		for (PropagationPath oep : EMV2Util.getAllPropagationPaths(ep.getContainingClassifier())) {
			if (oep != ep && oep.getName() != null && oep.getName().equalsIgnoreCase(ep.getName())) {
				error(ep, "Propagation point " + (ep instanceof PropagationPath ? "path " : "") + ep.getName()
						+ "' conflicts with propagation path.");
			}
		}
		EObject searchResult = null;
		Classifier cl = AadlUtil.getContainingClassifier(ep);
		if (cl instanceof ComponentImplementation) {
			searchResult = ((ComponentImplementation) cl).getType().findNamedElement(ep.getName());
		} else {
			searchResult = AadlUtil.getContainingClassifier(ep).findNamedElement(ep.getName());
		}
		if (searchResult != null) {
			error(ep,
					"Propagation point " + ep.getName() + " conflicts with feature in component type " + cl.getName());
		}
	}

	private void checkUniqueDefiningIdentifiers(ErrorModelLibrary etl, boolean cyclicextends) {
		HashMap<String, EObject> types = new HashMap<String, EObject>(10, 10);
		checkUniqueDefiningEBSMMappingsTransformations(etl, types);
		if (cyclicextends) {
			return;
		}
		for (ErrorModelLibrary xetl : etl.getExtends()) {
			checkUniqueInheritedDefiningErrorTypes(etl, xetl, types);
		}
	}

	private void checkUniqueDefiningEBSMMappingsTransformations(ErrorModelLibrary etl,
			HashMap<String, EObject> types) {
		for (ErrorBehaviorStateMachine ebsm : etl.getBehaviors()) {
			if (types.containsKey(ebsm.getName())) {
				error(ebsm, "Error behavior state machine identifier " + ebsm.getName()
						+ " is not unique in error model library");
			}
			types.put(ebsm.getName(), ebsm);
		}
		for (TypeMappingSet tms : etl.getMappings()) {
			if (types.containsKey(tms.getName())) {
				error(tms, "Type mapping set identifier " + tms.getName() + " is not unique in error model library");
			}
			types.put(tms.getName(), tms);
		}
		for (TypeTransformationSet tts : etl.getTransformations()) {
			if (types.containsKey(tts.getName())) {
				error(tts, "Type transformation set identifier " + tts.getName()
						+ " is not unique in error model library");
			}
			types.put(tts.getName(), tts);
		}
		for (ErrorTypes ets : etl.getTypes()) {
			if (types.containsKey(ets.getName())) {
				error(ets, "Error type or type set (alias) identifier " + ets.getName()
						+ " is not unique in error model library");
			}
			types.put(ets.getName(), ets);
		}
	}

	private void checkUniqueInheritedDefiningErrorTypes(ErrorModelLibrary lib, ErrorModelLibrary etl,
			HashMap<String, EObject> types) {
		for (ErrorTypes et : etl.getTypes()) {
			if (types.containsKey(et.getName())) {
				EObject source = types.get(et.getName());
				ErrorModelLibrary eml = EMV2Util.getContainingErrorModelLibrary(et);
				EObject errObj = EMV2Util.getContainingErrorModelLibrary((Element) source) == lib ? source : lib;
				error(errObj,
						"Error type or type set (alias) " + et.getName() + " inherited from "
								+ EMV2Util.getPrintName(eml)
								+ " conflicts with another identifier in error type library");
			}
			types.put(et.getName(), et);
		}
		for (ErrorModelLibrary xetl : etl.getExtends()) {
			checkUniqueInheritedDefiningErrorTypes(lib, xetl, types);
		}
	}

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

	private 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)) {
					error(xetl, "Cycle in extends of error type library " + etl.getName());
					result = true;
				} else {
					result = result || recursiveCheckCyclicExtends(xetl, shetl);
				}
			}
			shetl.remove(etl);
		}
		return result;
	}

	private void checkCyclicExtends(ErrorType origet) {
		ErrorType et = EMV2Util.resolveAlias(origet);
		if (et.getSuperType() == null) {
			return;
		}
		HashSet<ErrorType> result = new HashSet<ErrorType>();
		while (et.getSuperType() != null) {
			result.add(et);
			ErrorType last = et;
			et = EMV2Util.resolveAlias(et.getSuperType());
			if (result.contains(et)) {
				error(origet, "Cycle in supertype hierarchy of error type " + origet.getName() + " at type "
						+ last.getName());
				return;
			}
		}
	}

	private void checkCyclicRenames(ErrorType origet) {
		ErrorType et = origet;
		if (et.getAliasedType() == null) {
			return;
		}
		HashSet<ErrorType> result = new HashSet<ErrorType>();
		while (et.getAliasedType() != null) {
			result.add(et);
			ErrorType last = et;
			et = et.getAliasedType();
			if (result.contains(et)) {
				error(origet, "Cycle in renames of error type " + origet.getName() + " at type " + last.getName());
				return;
			}
		}
	}

	private void checkCyclicRenames(TypeSet origet) {
		TypeSet et = origet;
		if (et.getAliasedType() == null) {
			return;
		}
		HashSet<TypeSet> result = new HashSet<TypeSet>();
		while (et.getAliasedType() != null) {
			result.add(et);
			TypeSet last = et;
			et = et.getAliasedType();
			if (result.contains(et)) {
				error(origet, "Cycle in renames of type set " + origet.getName() + " at type set " + last.getName());
				return;
			}
		}
	}

	private void checkOutgoingTypes(OutgoingPropagationCondition opc) {
		if (opc.getTypeToken() == null) {
			return;
		}
		ErrorPropagation ep = opc.getOutgoing();
		if (opc.getTypeToken() != null
				&& (opc.getTypeToken().getTypeTokens().size() > 1
						|| opc.getTypeToken().getTypeTokens().get(0).getType().size() > 0 && !(opc.getTypeToken()
								.getTypeTokens().get(0).getType().get(0) instanceof ErrorType))) {
			error(opc, "Outgoing error propagation may only have a single error type");
		}
		if (ep != null) {
			if (!EMV2TypeSetUtil.isNoError(opc.getTypeToken())
					&& !EMV2TypeSetUtil.contains(ep.getTypeSet(), opc.getTypeToken())) {
				error(opc,
						"Outgoing error type " + EMV2Util.getPrintName(opc.getTypeToken())
								+ " is not contained in type set of outgoing error propagation specification \'"
								+ EMV2Util.getPrintName(ep) + "\'");
			}
		} else {
			if (opc.isAllPropagations()) {
				// check containment for all of the outgoing propagation points
				Classifier cl = opc.getContainingClassifier();
				Collection<ErrorPropagation> eps = EMV2Util.getAllOutgoingErrorPropagations(cl);
				for (ErrorPropagation errorPropagation : eps) {
					if (opc.getTypeToken() != null) {
						if (!EMV2TypeSetUtil.isNoError(opc.getTypeToken())
								&& !EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), opc.getTypeToken())) {
							error(opc,
									"Outgoing error type " + EMV2Util.getPrintName(opc.getTypeToken())
											+ " is not contained in type set of outgoing propagation "
											+ EMV2Util.getPrintName(errorPropagation)
											+ EMV2Util.getPrintName(errorPropagation.getTypeSet()));
						}
					}
				}
			}
		}
	}

	private void checkHasConditionOrTypeToken(OutgoingPropagationCondition opc) {
		if (opc.getCondition() == null && opc.getTypeToken() == null) {
			error(opc,
					"Propagation condition must have at least a condition within brackets or a type set for the outgoing propagation");
		}
	}

	private void checkTransitionTargetTypes(ErrorBehaviorTransition ebt) {
		if (ebt.isSteadyState()) {
			return;
		}
		ErrorBehaviorState ebs = ebt.getTarget();
		if (ebs != null) {
			TypeSet ebsTS = ebs.getTypeSet();
			TypeSet tt = ebt.getTargetToken();
			if (tt == null || tt.getTypeTokens().isEmpty()) {
				return;
			}
			if (tt.getTypeTokens().size() > 1) {
				error(ebt,
						"Target state " + ebs.getName()
								+ " may only have a single error type");
			}
			TypeToken ebtargetTS = tt.getTypeTokens().get(0);
			if (ebtargetTS == null) {
				return;
			}
			if (ebtargetTS.getType() != null && !(ebtargetTS.getType().get(0) instanceof ErrorType)) {
				error(ebt, "Target state " + ebs.getName()
						+ " may only have a single error type");
			}
			if (ebsTS == null && ebtargetTS != null) {
				error(ebt,
						"Target state " + ebs.getName()
								+ " does not have a type set declared but the transition target specifies "
								+ EMV2Util.getPrintName(ebtargetTS));
			} else if (!EMV2TypeSetUtil.contains(ebsTS, ebtargetTS)) {
				error(ebt, "Target type " + EMV2Util.getPrintName(ebt.getTargetToken())
						+ " is not contained in type set of error behavior state \'" + ebs.getName() + "\'");
			}
		}
	}

	private void checkTransitionTargetTriggerTypes(ErrorBehaviorTransition ebt) {
		if (ebt.isSteadyState()) {
			return;
		}
		ErrorBehaviorState targetstate = ebt.getTarget();
		if (targetstate != null) {
			TypeSet targetTS = targetstate.getTypeSet();
			if (targetTS == null) {
				return;
			}
			TypeSet tt = ebt.getTargetToken();
			if (tt != null) {
				return;
			}
			// state requires a type
			if (ebt.getCondition() instanceof ConditionElement) {
				// either the event must be typed or the source state must be typed
				EventOrPropagation ep = EMV2Util.getErrorEventOrPropagation((ConditionElement) ebt.getCondition());
				if (ep instanceof ErrorEvent) {
					ErrorEvent ev = (ErrorEvent) ep;
					TypeSet evTS = ev.getTypeSet();
					if (evTS == null) {
						TypeSet srcTS = ebt.getSource().getTypeSet();
						if (srcTS == null) {
							error(ebt,
									"Target state " + targetstate.getName()
											+ " requires type but the triggering error event "
											+ EMV2Util.getPrintName(ev) + " or source state "
											+ EMV2Util.getPrintName(ebt.getSource()) + " does not have a type");
						} else {
							// source typeset must be contained in target type set
							if (!EMV2TypeSetUtil.contains(targetTS, srcTS)) {
								error(ebt,
										"Target state " + targetstate.getName()
												+ " does not contain types of source state "
												+ EMV2Util.getPrintName(ebt.getSource()));
							}
						}
					} else {
						// error event has type. It must be consistent with the expected state type
						if (!EMV2TypeSetUtil.contains(targetTS, evTS)) {
							error(ebt, "Target state " + targetstate.getName()
									+ " does not contain types of error event " + EMV2Util.getPrintName(ev));
						}
					}
				} else if (ep instanceof ErrorPropagation) {
					ErrorPropagation eprop = (ErrorPropagation) ep;
					// we have an error propagation
					// we can check type compatibility
					if (!EMV2TypeSetUtil.contains(targetTS, eprop.getTypeSet())) {
						error(ebt, "Target state " + targetstate.getName()
								+ " does not contain types of error propagation " + EMV2Util.getPrintName(eprop));
					}
				}
			} else {
				// full condition expression
				// type transformation & events must be typed
				ErrorBehaviorStateMachine ebsm = (ErrorBehaviorStateMachine) targetstate.eContainer();
				if (ebsm.getUseTransformation().isEmpty()) {
					error(ebt, "Target state " + targetstate.getName()
							+ " does not include a target type but requires types. For conditions on multiple elements a target type must be assigned explicitly or a type transformation must be specified in the error behavior state machine"
							+ EMV2Util.getPrintName(ebsm));
				}
			}
		}
	}

	private void checkBranches(ErrorBehaviorTransition ebt) {
		EList<TransitionBranch> branches = ebt.getDestinationBranches();
		boolean foundsteady = false;
		boolean foundothers = false;
		BigDecimal prob = new BigDecimal(0.0, MathContext.UNLIMITED);
		if (branches.isEmpty()) {
			return;
		}
		for (TransitionBranch transitionBranch : branches) {
			if (transitionBranch.isSteadyState()) {
				if (foundsteady) {
					error(ebt, "More than one same state branch");
				} else {
					foundsteady = true;
				}
			}
			if (transitionBranch.getValue().isOthers()) {
				if (foundothers) {
					error(ebt, "More than one other branch");
				} else {
					foundothers = true;
				}
			}
			String bv = EMV2Util.stripUnderScore(transitionBranch.getValue().getRealvalue());
			Property sl = transitionBranch.getValue().getSymboliclabel();
			if (bv != null) {
				prob = prob.add(new BigDecimal(bv, MathContext.UNLIMITED));
			} else if (sl != null) {
				Classifier cl = EMV2Util.getAssociatedClassifier(ebt);
				List<EMV2PropertyAssociation> pa = EMV2Properties.getProperty(sl.getQualifiedName(), cl, ebt, null);
				for (EMV2PropertyAssociation emv2PropertyAssociation : pa) {
					prob = prob.add(new BigDecimal(EMV2Properties.getRealValue(emv2PropertyAssociation),
							MathContext.UNLIMITED));
				}
			}
		}
		if (!foundothers && prob.compareTo(new BigDecimal(1.0)) != 0) {
			error(ebt, "Sum of branch probabilities must be 1");
		}
		if (foundothers && prob.compareTo(new BigDecimal(1.0)) >= 0) {
			error(ebt, "Sum of branch probabilities must be less than 1 due to 'others'");
		}
	}

	private void checkTransitionTargetTypes(TransitionBranch ebt) {
		if (ebt.isSteadyState()) {
			return;
		}
		ErrorBehaviorState ebs = ebt.getTarget();
		if (ebs != null) {
			TypeSet ebsTS = ebs.getTypeSet();
			TypeSet tt = ebt.getTargetToken();
			if (tt == null || tt.getTypeTokens().isEmpty()) {
				return;
			}
			TypeToken ebtargetTS = tt.getTypeTokens().get(0);
			if (ebtargetTS == null) {
				return;
			}
			if (ebsTS == null && ebtargetTS != null) {
				error(ebt,
						"Target state " + ebs.getName()
								+ " does not have a type set declared but the transition target specifies "
								+ EMV2Util.getPrintName(ebtargetTS));
			} else if (!EMV2TypeSetUtil.contains(ebsTS, ebtargetTS)) {
				error(ebt, "Target type " + EMV2Util.getPrintName(ebt.getTargetToken())
						+ " is not contained in type set of error behavior state \'" + ebs.getName() + "\'");
			}
		}
	}

	private void checkOutgoingConditionSourceTypes(OutgoingPropagationCondition opc) {
		ErrorBehaviorState ebs = opc.getState();
		if (ebs == null) {
			return;
		}
		TypeSet ebsTS = ebs.getTypeSet();
		TypeSet srcTS = opc.getTypeTokenConstraint();
		if (srcTS == null) {
			return;
		}
		if (ebsTS == null && srcTS != null) {
			error(opc, "Error state " + ebs.getName()
					+ " does not have a type set declared but the outgoing propagation condition has type token "
					+ EMV2Util.getPrintName(srcTS));
		} else if (!EMV2TypeSetUtil.contains(ebsTS, srcTS)) {
			error(opc, "Outgoing condition state type set " + EMV2Util.getPrintName(srcTS)
					+ " is not contained in type set of error behavior state \'" + ebs.getName() + "\'");
		}
	}

	private void checkDetectionSourceTypes(ErrorDetection ebt) {
		ErrorBehaviorState ebs = ebt.getState();
		if (ebs == null) {
			return;
		}
		TypeSet ebsTS = ebs.getTypeSet();
		TypeSet srcTS = ebt.getTypeTokenConstraint();
		if (srcTS == null) {
			return;
		}
		if (ebsTS == null && srcTS != null) {
			error(ebt,
					"Source state " + ebs.getName()
							+ " does not have a type set declared but the detection source specifies "
							+ EMV2Util.getPrintName(srcTS));
		} else if (!EMV2TypeSetUtil.contains(ebsTS, srcTS)) {
			error(ebt, "Source type " + EMV2Util.getPrintName(srcTS)
					+ " is not contained in type set of error behavior state \'" + ebs.getName() + "\'");
		}
	}

	private void checkTransitionSourceTypes(ErrorBehaviorTransition ebt) {
		ErrorBehaviorState ebs = ebt.getSource();
		if (ebs == null) {
			return;
		}
		TypeSet ebsTS = ebs.getTypeSet();
		TypeSet srcTS = ebt.getTypeTokenConstraint();
		if (srcTS == null) {
			return;
		}
		if (ebsTS == null && srcTS != null) {
			error(ebt,
					"Source state " + ebs.getName()
							+ " does not have a type set declared but the transition source specifies "
							+ EMV2Util.getPrintName(srcTS));
		} else if (!EMV2TypeSetUtil.contains(ebsTS, srcTS)) {
			error(ebt, "Source type " + EMV2Util.getPrintName(srcTS)
					+ " is not contained in type set of error behavior state \'" + ebs.getName() + "\'");
		}
	}

	private void checkErrorSourceTypes(ErrorSource ef) {
		if (ef.getTypeTokenConstraint() == null) {
			return;
		}
		NamedElement ne = ef.getSourceModelElement();
		if (ne instanceof Connection) {
			return;
		}
		if (ne instanceof ErrorPropagation) {
			ErrorPropagation epout = (ErrorPropagation) ne;
			if (!EMV2TypeSetUtil.contains(epout.getTypeSet(), ef.getTypeTokenConstraint())) {
				error(ef,
						"Error source type constraint " + EMV2Util.getPrintName(ef.getTypeTokenConstraint())
								+ " is not contained in type set of outgoing propagation "
								+ EMV2Util.getPrintName(epout) + EMV2Util.getPrintName(epout.getTypeSet()));
			}
		} else if (ef.isAll()) {
			if (EMV2Util.getContainingErrorModelSubclause(ef).getConnectionErrorSources().contains(ef)) {
				return;
			}
			// check containment for all of the outgoing propagation points
			Classifier cl = ef.getContainingClassifier();
			Collection<ErrorPropagation> eps = EMV2Util.getAllOutgoingErrorPropagations(cl);
			for (ErrorPropagation errorPropagation : eps) {
				if (!EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), ef.getTypeTokenConstraint())) {
					error(ef,
							"Error source type constraint " + EMV2Util.getPrintName(ef.getTypeTokenConstraint())
									+ " is not contained in type set of outgoing propagation "
									+ EMV2Util.getPrintName(errorPropagation)
									+ EMV2Util.getPrintName(errorPropagation.getTypeSet()));
				}
			}
		}
	}

	private void checkErrorSinkTypes(ErrorSink ef) {
		if (ef.getTypeTokenConstraint() == null) {
			return;
		}
		ErrorPropagation ep = ef.getIncoming();
		if (ep != null) {
			if (!EMV2TypeSetUtil.contains(ep.getTypeSet(), ef.getTypeTokenConstraint())) {
				error(ef, "Type token constraint is not contained in type set of incoming propagation \""
						+ EMV2Util.getPrintName(ep) + "\"");
			}
		} else {
			// check containment for all of the incoming propagation points
			Classifier cl = ef.getContainingClassifier();
			Collection<ErrorPropagation> eps = EMV2Util.getAllIncomingErrorPropagations(cl);
			for (ErrorPropagation errorPropagation : eps) {
				if (!EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), ef.getTypeTokenConstraint())) {
					error(ef, "Type token constraint is not contained in type set of incoming propagation \""
							+ EMV2Util.getPrintName(errorPropagation) + "\"");
				}
			}
		}
	}

	private void checkErrorPathTypes(ErrorPath ef) {
		ErrorPropagation epin = ef.getIncoming();
		ErrorPropagation epout = ef.getOutgoing();
		if (ef.getTypeTokenConstraint() != null) {
			// check that incoming flow type constraint is contained in that of
			// in prop
			if (epin != null) {
				if (!EMV2TypeSetUtil.contains(epin.getTypeSet(), ef.getTypeTokenConstraint())) {
					error(ef, "Type token constraint is not contained in type set of incoming propagation "
							+ EMV2Util.getPrintName(epin));
				}
			} else {
				// check containment for all of the incoming propagation points
				Classifier cl = ef.getContainingClassifier();
				Collection<ErrorPropagation> eps = EMV2Util.getAllIncomingErrorPropagations(cl);
				for (ErrorPropagation errorPropagation : eps) {
					if (!EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), ef.getTypeTokenConstraint())) {
						error(ef, "Type token constraint is not contained in type set of incoming propagation "
								+ EMV2Util.getPrintName(errorPropagation));
					}
				}
			}
		}
		if (ef.getTargetToken() != null) {
			// check that outoging flow type token is contained in that of prop
			if (epout != null) {
				if (!EMV2TypeSetUtil.contains(epout.getTypeSet(), ef.getTargetToken())) {
					error(ef, "Target token is not contained in type set of outgoing propagation "
							+ EMV2Util.getPrintName(epout));
				}
			} else {
				// check containment for all of the outgoing propagation points
				Classifier cl = ef.getContainingClassifier();
				Collection<ErrorPropagation> eps = EMV2Util.getAllOutgoingErrorPropagations(cl);
				for (ErrorPropagation errorPropagation : eps) {
					if (!EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), ef.getTargetToken())) {
						error(ef, "Target token is not contained in type set of outgoing propagation "
								+ EMV2Util.getPrintName(errorPropagation));
					}
				}
			}
			// ensures only one error type is associated with the target error type
			TypeSet ts = ef.getTargetToken();
			if (ts.getTypeTokens().size() > 1) {
				error(ef, "Outgoing propagation may only have a single error type");
			}
			TypeToken tt = ts.getTypeTokens().get(0);
			if (tt != null && tt.getType() != null && !(tt.getType().get(0) instanceof ErrorType)) {
				error(ef, "Outgoing propagation may only have a single error type");
			}
		} else {
			// no outgoing path type token
			if (ef.getTypeTokenConstraint() != null) {
				// match the incoming path constraint
				if (epout != null) {
					// need to handle use mappings
					if (!EMV2TypeSetUtil.contains(epout.getTypeSet(), ef.getTypeTokenConstraint())) {
						error(ef, "Incoming path type constraint is not contained in type set of outgoing propagation "
								+ EMV2Util.getPrintName(epout));
					}
				} else {
					// check containment for all of the outgoing propagation
					// points
					Classifier cl = ef.getContainingClassifier();
					Collection<ErrorPropagation> eps = EMV2Util.getAllOutgoingErrorPropagations(cl);
					for (ErrorPropagation errorPropagation : eps) {
						if (!EMV2TypeSetUtil.contains(errorPropagation.getTypeSet(), ef.getTypeTokenConstraint())) {
							error(ef,
									"Incoming path type constraint is not contained in type set of outgoing propagation "
											+ EMV2Util.getPrintName(errorPropagation));
						}
					}
				}
			} else {
				// no incoming or outgoing ef type constraint or token
				// check in to out type set of propagations for the flow
				if (epout != null && epin != null) {
					if (!EMV2TypeSetUtil.contains(epout.getTypeSet(), epin.getTypeSet())) {
						error(ef,
								"Incoming error propagation " + EMV2Util.getPrintName(epin)
										+ " constraint is not contained in type set of outgoing propagation "
										+ EMV2Util.getPrintName(epout));
					}
				} else if (epout == null && epin != null) {
					// check containment for all of the outgoing propagation
					// points against one incoming
					Classifier cl = ef.getContainingClassifier();
					Collection<ErrorPropagation> eps = EMV2Util.getAllOutgoingErrorPropagations(cl);
					for (ErrorPropagation allepout : eps) {
						if (!EMV2TypeSetUtil.contains(allepout.getTypeSet(), epin.getTypeSet())) {
							error(ef,
									"Incoming error propagation " + EMV2Util.getPrintName(epin)
											+ " constraint is not contained in type set of outgoing propagation "
											+ EMV2Util.getPrintName(allepout));
						}
					}
				} else if (epout != null && epin == null) {
					// check containment for all of the outgoing propagation
					// points against one incoming
					Classifier cl = ef.getContainingClassifier();
					Collection<ErrorPropagation> eps = EMV2Util.getAllIncomingErrorPropagations(cl);
					for (ErrorPropagation errorPropagation : eps) {
						if (!EMV2TypeSetUtil.contains(epout.getTypeSet(), errorPropagation.getTypeSet())) {
							error(ef,
									"Incoming error propagation " + EMV2Util.getPrintName(errorPropagation)
											+ " constraint is not contained in type set of outgoing propagation "
											+ EMV2Util.getPrintName(epout));
						}
					}
				} else if (epout == null && epin == null) {
					// check containment for all of the outgoing propagation
					// points against one incoming
					Classifier cl = ef.getContainingClassifier();
					Collection<ErrorPropagation> epsin = EMV2Util.getAllIncomingErrorPropagations(cl);
					Collection<ErrorPropagation> epsout = EMV2Util.getAllOutgoingErrorPropagations(cl);
					for (ErrorPropagation allepout : epsout) {
						for (ErrorPropagation allepin : epsin) {
							if (!EMV2TypeSetUtil.contains(allepout.getTypeSet(), allepin.getTypeSet())) {
								error(ef,
										"Incoming error propagation " + EMV2Util.getPrintName(allepin)
												+ " constraint is not contained in type set of outgoing propagation "
												+ EMV2Util.getPrintName(allepout));
							}
						}
					}
				}
			}
		}

	}

	private void checkConnectionErrorTypes(Connection conn) {
		ComponentImplementation cimpl = conn.getContainingComponentImpl();
		ConnectionEnd src = conn.getAllSource();
		Context srcCxt = conn.getAllSourceContext();
		ErrorPropagation srcprop = null;
		ErrorPropagation srccontain = null;
		Classifier srccl = null;
		String srcname = (src instanceof Subcomponent) ? "access" : src.getName();
		if (srcCxt instanceof Subcomponent) {
			srccl = ((Subcomponent) srcCxt).getClassifier();
		} else if (src instanceof Subcomponent) {
			srccl = ((Subcomponent) src).getClassifier();
		} else {
			srccl = cimpl;
		}
		if (srccl != null) {
			if (AadlUtil.isIncomingConnection(conn)) {
				srcprop = EMV2Util.findIncomingErrorPropagation(srccl, srcname);
				srccontain = EMV2Util.findIncomingErrorContainment(srccl, srcname);
			} else {
				srcprop = EMV2Util.findOutgoingErrorPropagation(srccl, srcname);
				srccontain = EMV2Util.findOutgoingErrorContainment(srccl, srcname);
			}
		}
		ConnectionEnd dst = conn.getAllDestination();
		Context dstCxt = conn.getAllDestinationContext();
		Classifier dstcl = null;
		ErrorPropagation dstprop = null;
		ErrorPropagation dstcontain = null;
		String dstname = (dst instanceof Subcomponent) ? "access" : dst.getName();
		if (dstCxt instanceof Subcomponent) {
			dstcl = ((Subcomponent) dstCxt).getClassifier();
		} else if (dst instanceof Subcomponent) {
			dstcl = ((Subcomponent) dst).getClassifier();
		} else {
			dstcl = cimpl;
		}
		if (dstcl != null) {
			if (AadlUtil.isOutgoingConnection(conn)) {
				dstprop = EMV2Util.findOutgoingErrorPropagation(dstcl, dstname);
				dstcontain = EMV2Util.findOutgoingErrorContainment(dstcl, dstname);
			} else {
				dstprop = EMV2Util.findIncomingErrorPropagation(dstcl, dstname);
				dstcontain = EMV2Util.findIncomingErrorContainment(dstcl, dstname);
			}
		}
		if (srcprop != null && dstprop != null) {
			if (!EMV2TypeSetUtil.contains(dstprop.getTypeSet(), srcprop.getTypeSet())) {

				TypeMappingSet typeEquivalence = EMV2Util.getAllTypeEquivalenceMapping(cimpl);

				if (typeEquivalence != null) {
					TypeToken mappedtt = EMV2TypeSetUtil.mapTypeToken(srcprop.getTypeSet(), typeEquivalence);
					if (mappedtt != null) {
						if (!EMV2TypeSetUtil.contains(dstprop.getTypeSet(), mappedtt)) {
							error(conn, "Source propagation  " + EMV2Util.getPrintName(srcprop)
									+ EMV2Util.getPrintName(srcprop.getTypeSet())
									+ " has error types not handled by destination propagation "
									+ EMV2Util.getPrintName(dstprop) + EMV2Util.getPrintName(dstprop.getTypeSet()));
						}
					}
				} else {
					error(conn,
							"Source propagation  " + EMV2Util.getPrintName(srcprop)
									+ EMV2Util.getPrintName(srcprop.getTypeSet())
									+ " has error types not handled by destination propagation "
									+ EMV2Util.getPrintName(dstprop) + EMV2Util.getPrintName(dstprop.getTypeSet()));
				}
			}
		}
		if (srccontain != null && dstcontain != null) {
			if (!EMV2TypeSetUtil.contains(srcprop.getTypeSet(), dstprop.getTypeSet())) {
				error(conn,
						"Source containment  " + EMV2Util.getPrintName(srcprop)
								+ EMV2Util.getPrintName(srcprop.getTypeSet())
								+ " does not contain error types listed by Source containment "
								+ EMV2Util.getPrintName(dstprop) + EMV2Util.getPrintName(dstprop.getTypeSet()));
			}
		}
		// TODO comment out once we handle this consistency check at the
		// instance level
		if (srcCxt instanceof Subcomponent && dstCxt instanceof Subcomponent) {
			// only when going across
			if (srccontain == null && dstcontain != null) {
				EList<ErrorModelSubclause> srcsubclauses = EMV2Util.getAllContainingClassifierEMV2Subclauses(srccl);
				if (!srcsubclauses.isEmpty()) {
					warning(conn,
							"No outgoing containment from " + srcCxt.getName() + " for incoming containment "
									+ EMV2Util.getPrintName(dstcontain) + EMV2Util.getPrintName(dstcontain.getTypeSet())
									+ ". Check for Unhandled Faults.");
				}
			}
			if (srcprop != null && dstprop == null) {
				EList<ErrorModelSubclause> dstsubclauses = EMV2Util.getAllContainingClassifierEMV2Subclauses(dstcl);
				if (!dstsubclauses.isEmpty()) {
					warning(conn,
							"No incoming error propagation from " + dstCxt.getName() + " for outgoing propagation "
									+ EMV2Util.getPrintName(srcprop) + EMV2Util.getPrintName(srcprop.getTypeSet())
									+ ". Check for Unhandled Faults.");
				}
			}
		}

		if (conn.isAllBidirectional()) {
			// check for error flow in the opposite direction
			if (srccl != null) {
				if (AadlUtil.isIncomingConnection(conn)) {
					dstprop = EMV2Util.findOutgoingErrorPropagation(srccl, srcname);
					dstcontain = EMV2Util.findOutgoingErrorContainment(srccl, srcname);
				} else {
					dstprop = EMV2Util.findIncomingErrorPropagation(srccl, srcname);
					dstcontain = EMV2Util.findIncomingErrorContainment(srccl, srcname);
				}
			} else {
				dstprop = null;
				dstcontain = null;
			}
			if (dstcl != null) {
				if (AadlUtil.isOutgoingConnection(conn)) {
					srcprop = EMV2Util.findIncomingErrorPropagation(dstcl, dstname);
					srccontain = EMV2Util.findIncomingErrorContainment(dstcl, dstname);
				} else {
					srcprop = EMV2Util.findOutgoingErrorPropagation(dstcl, dstname);
					srccontain = EMV2Util.findOutgoingErrorContainment(dstcl, dstname);
				}
			} else {
				srcprop = null;
				srccontain = null;
			}

			if (srcprop != null && dstprop != null) {
				if (!EMV2TypeSetUtil.contains(dstprop.getTypeSet(), srcprop.getTypeSet())) {
					error(conn,
							"Reverse direction: Destination propagation  " + EMV2Util.getPrintName(srcprop)
									+ EMV2Util.getPrintName(srcprop.getTypeSet())
									+ " has error types not handled by source propagation "
									+ EMV2Util.getPrintName(dstprop) + EMV2Util.getPrintName(dstprop.getTypeSet()));
				}
			}
			if (srccontain != null && dstcontain != null) {
				if (!EMV2TypeSetUtil.contains(srcprop.getTypeSet(), dstprop.getTypeSet())) {
					error(conn,
							"Reverse direction: Destination containment  " + EMV2Util.getPrintName(srcprop)
									+ EMV2Util.getPrintName(srcprop.getTypeSet())
									+ " does not contain error types listed by source containment "
									+ EMV2Util.getPrintName(dstprop) + EMV2Util.getPrintName(dstprop.getTypeSet()));
				}
			}
			// TODO comment out once we handle this consistency check at the
			// instance level
			if (srcCxt instanceof Subcomponent && dstCxt instanceof Subcomponent) {
				// only when going across
				if (srccontain == null && dstcontain != null) {
					warning(conn, "No outgoing containment for incoming containment "
							+ EMV2Util.getPrintName(dstcontain) + EMV2Util.getPrintName(dstcontain.getTypeSet()));
				}
				if (srcprop != null && dstprop == null) {
					warning(conn, "No incoming error propagation for outgoing propagation "
							+ EMV2Util.getPrintName(srcprop) + EMV2Util.getPrintName(srcprop.getTypeSet()));
				}
			}

		}
	}

	/**
	 * @since 6.1
	 */
	public void checkTypeMappingSet(TypeMappingSet tms) {
		int size = tms.getMapping().size();
		for(int i =0; i<size; i++) {
			TypeSet ts = tms.getMapping().get(i).getTarget();
			TypeToken tt = ts.getTypeTokens().get(0);
			if (tt != null && tt.getType() != null
					&& (!(tt.getType().get(0) instanceof ErrorType) || ts.getTypeTokens().size() > 1)) {
				error(tms.getMapping().get(i), "Target error type may only have a single error type");
			}
		}
	}

	/**
	 * @since 6.0
	 */
	private void checkTypeTransformationSet(TypeTransformationSet typeTransformation) {
		for (TypeTransformation trans : typeTransformation.getTransformation()) {
			TypeSet ts = trans.getTarget();
			TypeToken tt = ts.getTypeTokens().get(0);
			if (tt != null && tt.getType() != null
					&& (!(tt.getType().get(0) instanceof ErrorType) || ts.getTypeTokens().size() > 1)) {
				error(trans, "Target error type may only have a single error type");
			}
		}

	}

}