EMV2AnnexInstantiator.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.aadl2.errormodel.instance.instantiator;

import static org.eclipse.xtext.EcoreUtil2.getContainerOfType;

import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.EcoreUtil2;
import org.osate.aadl2.Aadl2Factory;
import org.osate.aadl2.Feature;
import org.osate.aadl2.InternalFeature;
import org.osate.aadl2.ListValue;
import org.osate.aadl2.ModeTransition;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.NamedValue;
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.PropertyConstant;
import org.osate.aadl2.PropertyExpression;
import org.osate.aadl2.RangeValue;
import org.osate.aadl2.RecordValue;
import org.osate.aadl2.ReferenceValue;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.contrib.deployment.DeploymentProperties;
import org.osate.aadl2.errormodel.instance.AccessPropagation;
import org.osate.aadl2.errormodel.instance.AllPropagations;
import org.osate.aadl2.errormodel.instance.AllPropagationsNoError;
import org.osate.aadl2.errormodel.instance.AllSources;
import org.osate.aadl2.errormodel.instance.AnonymousTypeSet;
import org.osate.aadl2.errormodel.instance.BindingPropagation;
import org.osate.aadl2.errormodel.instance.BindingType;
import org.osate.aadl2.errormodel.instance.Branch;
import org.osate.aadl2.errormodel.instance.BranchSameState;
import org.osate.aadl2.errormodel.instance.BranchStateReference;
import org.osate.aadl2.errormodel.instance.Branches;
import org.osate.aadl2.errormodel.instance.CompositeConditionExpression;
import org.osate.aadl2.errormodel.instance.ConditionExpressionInstance;
import org.osate.aadl2.errormodel.instance.ConditionPropagationReference;
import org.osate.aadl2.errormodel.instance.ConnectionEndPropagation;
import org.osate.aadl2.errormodel.instance.ConstantCode;
import org.osate.aadl2.errormodel.instance.CountExpression;
import org.osate.aadl2.errormodel.instance.CountExpressionOperation;
import org.osate.aadl2.errormodel.instance.DestinationPropagationReference;
import org.osate.aadl2.errormodel.instance.DestinationStateReference;
import org.osate.aadl2.errormodel.instance.DetectionInstance;
import org.osate.aadl2.errormodel.instance.EMV2AnnexInstance;
import org.osate.aadl2.errormodel.instance.EMV2InstanceFactory;
import org.osate.aadl2.errormodel.instance.EMV2InstanceObject;
import org.osate.aadl2.errormodel.instance.ErrorCodeInstance;
import org.osate.aadl2.errormodel.instance.ErrorEventInstance;
import org.osate.aadl2.errormodel.instance.ErrorPropagationInstance;
import org.osate.aadl2.errormodel.instance.EventInstance;
import org.osate.aadl2.errormodel.instance.EventReference;
import org.osate.aadl2.errormodel.instance.FeaturePropagation;
import org.osate.aadl2.errormodel.instance.IntegerCode;
import org.osate.aadl2.errormodel.instance.NoErrorPropagationReference;
import org.osate.aadl2.errormodel.instance.OthersExpression;
import org.osate.aadl2.errormodel.instance.OutgoingPropagationConditionDestination;
import org.osate.aadl2.errormodel.instance.PointPropagation;
import org.osate.aadl2.errormodel.instance.PropagationPointInstance;
import org.osate.aadl2.errormodel.instance.RecoverEventInstance;
import org.osate.aadl2.errormodel.instance.RepairEventInstance;
import org.osate.aadl2.errormodel.instance.SameState;
import org.osate.aadl2.errormodel.instance.SourceStateReference;
import org.osate.aadl2.errormodel.instance.StateInstance;
import org.osate.aadl2.errormodel.instance.StateReference;
import org.osate.aadl2.errormodel.instance.StateSource;
import org.osate.aadl2.errormodel.instance.StringCode;
import org.osate.aadl2.errormodel.instance.TransitionDestination;
import org.osate.aadl2.errormodel.instance.TypeInstance;
import org.osate.aadl2.errormodel.instance.TypeProductInstance;
import org.osate.aadl2.errormodel.instance.TypeSetElement;
import org.osate.aadl2.errormodel.instance.TypeSetInstance;
import org.osate.aadl2.instance.AnnexInstance;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.aadl2.instance.InstanceFactory;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.PropertyAssociationInstance;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.aadl2.modelsupport.errorreporting.AnalysisErrorReporterManager;
import org.osate.aadl2.properties.InvalidModelException;
import org.osate.annexsupport.AnnexInstantiator;
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.EMV2PropertyAssociation;
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.ErrorCodeValue;
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.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.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.RecoverEvent;
import org.osate.xtext.aadl2.errormodel.errorModel.RepairEvent;
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.TypeSet;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeToken;
import org.osate.xtext.aadl2.errormodel.util.EMV2Util;

import com.google.common.collect.Lists;

public class EMV2AnnexInstantiator implements AnnexInstantiator {
	@Override
	public void instantiateAnnex(ComponentInstance instance, String annexName,
			AnalysisErrorReporterManager errorManager) {
		var subclauses = EMV2Util.getAllContainingClassifierEMV2Subclauses(instance);
		var stateMachine = EMV2Util.getAllErrorBehaviorStateMachine(instance);

		EMV2AnnexInstance emv2AI = EMV2InstanceFactory.eINSTANCE.createEMV2AnnexInstance();
		emv2AI.setName("EMV2");
		emv2AI.getSubclauses().addAll(subclauses);
		instance.getAnnexInstances().add(emv2AI);

		Collection<PropagationPoint> pps = EMV2Util.getAllPropagationPoints(instance.getComponentClassifier());
		for (PropagationPoint pp : pps) {
			instantiatePropagationPoint(pp, emv2AI);
		}

		var eps = EMV2Util.getAllErrorPropagations(instance.getComponentClassifier());
		instantiateErrorPropagations(eps, instance, emv2AI);

		Collection<ErrorBehaviorEvent> events = EMV2Util.getAllErrorBehaviorEvents(instance);
		for (ErrorBehaviorEvent ev : events) {
			instantiateEvent(ev, instance, emv2AI);
		}

		for (var source : EMV2Util.getAllErrorSources(instance)) {
			instantiateErrorSource(source, instance, emv2AI);
		}

		for (var sink : EMV2Util.getAllErrorSinks(instance)) {
			instantiateErrorSink(sink, instance, emv2AI);
		}

		for (var path : EMV2Util.getAllErrorPaths(instance)) {
			instantiateErrorPath(path, instance, emv2AI);
		}

		if (stateMachine != null) {
			for (var state : stateMachine.getStates()) {
				instantiateState(state, instance, emv2AI);
			}
			instantiateProperties(stateMachine, emv2AI, instance);
		}

		Collection<ErrorBehaviorTransition> transitions = EMV2Util.getAllErrorBehaviorTransitions(instance);
		for (ErrorBehaviorTransition tr : transitions) {
			instantiateTransition(tr, instance, emv2AI);
		}

		Collection<CompositeState> compstates = EMV2Util.getAllCompositeStates(instance);
		for (CompositeState cs : compstates) {
			instantiateCompositeState(cs, instance, emv2AI);
		}

		Collection<OutgoingPropagationCondition> OPCs = EMV2Util.getAllOutgoingPropagationConditions(instance);
		for (OutgoingPropagationCondition opc : OPCs) {
			instantiateOutgoingPropagationCondition(opc, instance, emv2AI);
		}

		Collection<ErrorDetection> eds = EMV2Util.getAllErrorDetections(instance.getComponentClassifier());
		for (ErrorDetection ed : eds) {
			instantiateDetection(ed, instance, emv2AI);
		}

		for (var modeMapping : EMV2Util.getAllModeMappings(instance.getComponentClassifier())) {
			instantiateModeMapping(modeMapping, instance, emv2AI);
		}

		Collection<PropagationPath> ppaths = EMV2Util.getAllPropagationPaths(instance.getComponentClassifier());
		for (PropagationPath ppath : ppaths) {
			instantiateUserDefinedPath(ppath, emv2AI, instance);
		}

		instantiateProperties(subclauses, emv2AI, instance);
	}

	@Override
	public void instantiateAnnex(SystemInstance instance, String annexName, AnalysisErrorReporterManager errorManager) {
		EcoreUtil2.eAllOfType(instance, ComponentInstance.class).forEach(component -> {
			component.getConnectionInstances().forEach(connection -> instantiateConnectionPath(connection, component));
		});
		instantiateBindingPaths(instance);
	}

	private void instantiateConnectionPath(ConnectionInstance connection, ComponentInstance component) {
		if (connection.isComplete()) {
			// The connection is an across connection.
			var sourcePropagations = new ArrayList<ConnectionEndPropagation>();
			var destinationPropagations = new ArrayDeque<ConnectionEndPropagation>();
			var encounteredAcross = false;
			for (var ref : connection.getConnectionReferences()) {
				if (!encounteredAcross) {
					ConnectionEndPropagation propagation;
					if (ref.getSource() instanceof FeatureInstance source) {
						propagation = findFeaturePropagation(source);
					} else if (ref.getSource() instanceof ComponentInstance source) {
						propagation = findAccessPropagation(source);
					} else {
						throw new RuntimeException("Unexpected connection end: " + ref.getSource());
					}
					if (propagation != null && propagation.getDirection().outgoing()) {
						sourcePropagations.add(propagation);
					}
				}
				if (ref.getConnection().isAcross()) {
					encounteredAcross = true;
				}
				if (encounteredAcross) {
					ConnectionEndPropagation propagation;
					if (ref.getDestination() instanceof FeatureInstance destination) {
						propagation = findFeaturePropagation(destination);
					} else if (ref.getDestination() instanceof ComponentInstance destination) {
						propagation = findAccessPropagation(destination);
					} else {
						throw new RuntimeException("Unexpected connection end: " + ref.getDestination());
					}
					if (propagation != null && propagation.getDirection().incoming()) {
						destinationPropagations.addFirst(propagation);
					}
				}
			}
			if (!sourcePropagations.isEmpty() && !destinationPropagations.isEmpty()) {
				var connectionPath = EMV2InstanceFactory.eINSTANCE.createConnectionPath();
				connectionPath.setName(connection.getName());
				connectionPath.setConnection(connection);
				connectionPath.getSourcePropagations().addAll(sourcePropagations);
				connectionPath.getDestinationPropagations().addAll(destinationPropagations);
				getOrCreateEMV2AnnexInstance(component).getPropagationPaths().add(connectionPath);
			}
		} else if (EcoreUtil2.getContainerOfType(connection.getDestination(),
				ComponentInstance.class) instanceof SystemInstance) {
			// Check if the connection is an up connection that reaches out of the top-level SystemInstance.
			var sourcePropagations = new ArrayList<ConnectionEndPropagation>();
			for (var ref : connection.getConnectionReferences()) {
				ConnectionEndPropagation propagation;
				if (ref.getSource() instanceof FeatureInstance source) {
					propagation = findFeaturePropagation(source);
				} else if (ref.getSource() instanceof ComponentInstance source) {
					propagation = findAccessPropagation(source);
				} else {
					throw new RuntimeException("Unexpected connection end: " + ref.getSource());
				}
				if (propagation != null && propagation.getDirection().outgoing()) {
					sourcePropagations.add(propagation);
				}
			}
			if (!sourcePropagations.isEmpty()) {
				var connectionPath = EMV2InstanceFactory.eINSTANCE.createConnectionPath();
				connectionPath.setName(connection.getName());
				connectionPath.setConnection(connection);
				connectionPath.getSourcePropagations().addAll(sourcePropagations);
				getOrCreateEMV2AnnexInstance(component).getPropagationPaths().add(connectionPath);
			}
		} else {
			// Check if the connection is a down connection that comes in from the top-level SystemInstance.
			var destinationPropagations = new ArrayDeque<ConnectionEndPropagation>();
			for (var ref : connection.getConnectionReferences()) {
				ConnectionEndPropagation propagation;
				if (ref.getDestination() instanceof FeatureInstance destination) {
					propagation = findFeaturePropagation(destination);
				} else if (ref.getDestination() instanceof ComponentInstance destination) {
					propagation = findAccessPropagation(destination);
				} else {
					throw new RuntimeException("Unexpected connection end: " + ref.getDestination());
				}
				if (propagation != null && propagation.getDirection().incoming()) {
					destinationPropagations.addFirst(propagation);
				}
			}
			if (!destinationPropagations.isEmpty()) {
				var connectionPath = EMV2InstanceFactory.eINSTANCE.createConnectionPath();
				connectionPath.setName(connection.getName());
				connectionPath.setConnection(connection);
				connectionPath.getDestinationPropagations().addAll(destinationPropagations);
				getOrCreateEMV2AnnexInstance(component).getPropagationPaths().add(connectionPath);
			}
		}
	}

	/*
	 * The instantiation of BindingPath objects is a bit complicated. The idea is that there should be one BindingPath
	 * object per declarative PropertyAssociation for that binding. However, a single declarative PropertyAssociation
	 * can be instantiated into multiple PropertyAssociationInstance objects if the property is inherit and it is
	 * applied to a container. This method collects all of the values from every PropertyAssociationInstance and groups
	 * them by their singular declarative PropertyAssociation.
	 *
	 * For example, suppose that an Actual_Processor_Binding is applied to a process and that process contains multiple
	 * threads. Now also suppose that the process and all of the threads have in processor propagations and the
	 * processor that is the target of the binding has an out bindings propagation. This will result in one
	 * BindingPath object being created in which the source is the bindings propagation on the processor and the
	 * destination propagations are all of the processor propagations on the process and threads.
	 *
	 * The collection of all bindings for a given type must not only be grouped by a common singular declarative
	 * PropertyAssociation, but also by their instance property values. There are some situations in which a single
	 * declarative binding PropertyAssociation should result in multiple BindingPath objects. If a declarative binding
	 * PropertyAssociation is not at the top system, but is instead in a child component, and the classifier of that
	 * child component is used in multiple subcomponents, then the InstanceReferenceValue objects would be different for
	 * the different subtrees of the instance model and we would want to create multiple BindingPath objects. The record
	 * UniqueBindingKey fulfills this requirement by allowing a grouping by declarative PropertyAssociation and the
	 * instance value objects.
	 *
	 * In addition to grouping by inherited properties, a BindingPath can also have a list of sources or list of
	 * destinations if the binding property lists multiple targets. For example, if a thread is bound to three
	 * processors, then a BindingPath will be created which lists the propagations for all three processors. Memory
	 * binding works the same way, but connection binding is different. For connection binding, the list represents a
	 * sequence of components that data flows through. Therefore, a BindingPath is created from the out connection
	 * propagation on the binding source to the in bindings propagation on the first binding target and another
	 * BindingPath is created from the out bindings propagation on the last binding target to the in connection
	 * propagation on the binding source.
	 */
	private void instantiateBindingPaths(SystemInstance instance) {
		// Key has the binding targets and the values are the binding sources.
		var commonProcessorBindings = new LinkedHashMap<UniqueBindingKey, List<ComponentInstance>>();
		var commonMemoryBindings = new LinkedHashMap<UniqueBindingKey, List<ComponentInstance>>();
		var commonConnectionBindings = new LinkedHashMap<UniqueBindingKey, List<ComponentInstance>>();
		EcoreUtil2.eAllOfType(instance, ComponentInstance.class).forEach(source -> {
			collectCommonBindings(source, commonProcessorBindings, DeploymentProperties::getActualProcessorBinding,
					DeploymentProperties::getActualProcessorBinding_EObject);
			collectCommonBindings(source, commonMemoryBindings, DeploymentProperties::getActualMemoryBinding,
					DeploymentProperties::getActualMemoryBinding_EObject);
			collectCommonBindings(source, commonConnectionBindings, DeploymentProperties::getActualConnectionBinding,
					DeploymentProperties::getActualConnectionBinding_EObject);
		});
		commonProcessorBindings.forEach((key, sources) -> {
			var sourcePropagations = sources.stream()
					.map(source -> findBindingPropagation(source, BindingType.PROCESSOR))
					.filter(Objects::nonNull)
					.toList();
			var targetPropagations = key.bindingTargets()
					.stream()
					.map(target -> findBindingPropagation(target, BindingType.BINDINGS))
					.filter(Objects::nonNull)
					.toList();
			instantiateBindingPath(sourcePropagations, targetPropagations, BindingType.PROCESSOR);
			instantiateBindingPath(targetPropagations, sourcePropagations, BindingType.PROCESSOR);
		});
		commonMemoryBindings.forEach((key, sources) -> {
			var sourcePropagations = sources.stream()
					.map(source -> findBindingPropagation(source, BindingType.MEMORY))
					.filter(Objects::nonNull)
					.toList();
			var targetPropagations = key.bindingTargets()
					.stream()
					.map(target -> findBindingPropagation(target, BindingType.BINDINGS))
					.filter(Objects::nonNull)
					.toList();
			instantiateBindingPath(sourcePropagations, targetPropagations, BindingType.MEMORY);
			instantiateBindingPath(targetPropagations, sourcePropagations, BindingType.MEMORY);
		});
		commonConnectionBindings.forEach((key, sources) -> {
			var sourcePropagations = sources.stream()
					.map(source -> findBindingPropagation(source, BindingType.CONNECTION))
					.filter(Objects::nonNull)
					.toList();
			var targets = key.bindingTargets();
			var firstTargetPropagation = findBindingPropagation(targets.get(0), BindingType.BINDINGS);
			BindingPropagation lastTargetPropagation;
			if (targets.size() == 1) {
				lastTargetPropagation = firstTargetPropagation;
			} else {
				lastTargetPropagation = findBindingPropagation(targets.get(targets.size() - 1), BindingType.BINDINGS);
			}
			if (firstTargetPropagation != null) {
				instantiateBindingPath(sourcePropagations, List.of(firstTargetPropagation), BindingType.CONNECTION);
			}
			if (lastTargetPropagation != null) {
				instantiateBindingPath(List.of(lastTargetPropagation), sourcePropagations, BindingType.CONNECTION);
			}
		});
	}

	private record UniqueBindingKey(PropertyAssociation propertyAssociation, List<ComponentInstance> bindingTargets) {
	}

	private static void collectCommonBindings(ComponentInstance source,
			Map<UniqueBindingKey, List<ComponentInstance>> commonBindings,
			Function<ComponentInstance, Optional<List<InstanceObject>>> getProperty,
			Function<ComponentInstance, PropertyExpression> getExpression) {
		getProperty.apply(source).ifPresent(targets -> {
			var targetComponents = targets.stream()
					.filter(ComponentInstance.class::isInstance)
					.map(ComponentInstance.class::cast)
					.toList();
			if (!targetComponents.isEmpty()) {
				var expression = getExpression.apply(source);
				var instanceAssociation = getContainerOfType(expression, PropertyAssociationInstance.class);
				var key = new UniqueBindingKey(instanceAssociation.getPropertyAssociation(), targetComponents);
				commonBindings.computeIfAbsent(key, k -> new ArrayList<>()).add(source);
			}
		});
	}

	private void instantiateBindingPath(List<BindingPropagation> sourcePropagations,
			List<BindingPropagation> destinationPropagations, BindingType bindingType) {
		var outgoingSources = sourcePropagations.stream()
				.filter(propagation -> propagation.getDirection().outgoing())
				.toList();
		var incomingDestinations = destinationPropagations.stream()
				.filter(propagation -> propagation.getDirection().incoming())
				.toList();
		if (!outgoingSources.isEmpty() && !incomingDestinations.isEmpty()) {
			var commonContainer = Stream.concat(outgoingSources.stream(), incomingDestinations.stream())
					.map(propagation -> getContainerOfType(propagation, ComponentInstance.class))
					.reduce(EMV2AnnexInstantiator::getCommonContainer)
					.get();
			var substringIndex = commonContainer.getInstanceObjectPath().length() + 1;
			var sourcePaths = outgoingSources.stream()
					.map(propagation -> propagation.getInstanceObjectPath().substring(substringIndex))
					.collect(Collectors.joining(", "));
			if (outgoingSources.size() > 1) {
				sourcePaths = '(' + sourcePaths + ')';
			}
			var destinationPaths = incomingDestinations.stream()
					.map(propagation -> propagation.getInstanceObjectPath().substring(substringIndex))
					.collect(Collectors.joining(", "));
			if (incomingDestinations.size() > 1) {
				destinationPaths = '(' + destinationPaths + ')';
			}
			var bindingPath = EMV2InstanceFactory.eINSTANCE.createBindingPath();
			bindingPath.setName(sourcePaths + " -> " + destinationPaths);
			bindingPath.setType(bindingType);
			bindingPath.getSourcePropagations().addAll(sourcePropagations);
			bindingPath.getDestinationPropagations().addAll(destinationPropagations);
			getOrCreateEMV2AnnexInstance(commonContainer).getPropagationPaths().add(bindingPath);
		}
	}

	private static ComponentInstance getCommonContainer(ComponentInstance a, ComponentInstance b) {
		if (EcoreUtil.isAncestor(a, b)) {
			return a;
		} else {
			for (var container : EcoreUtil2.getAllContainers(a)) {
				if (container instanceof ComponentInstance containerComponent
						&& EcoreUtil.isAncestor(containerComponent, b)) {
					return containerComponent;
				}
			}
			return null;
		}
	}

	private EMV2AnnexInstance getOrCreateEMV2AnnexInstance(ComponentInstance component) {
		var annex = findEMV2AnnexInstance(component);
		if (annex == null) {
			annex = EMV2InstanceFactory.eINSTANCE.createEMV2AnnexInstance();
			annex.setName("EMV2");
			component.getAnnexInstances().add(annex);
		}
		return annex;
	}

	private void instantiatePropagationPoint(PropagationPoint g, EMV2AnnexInstance annex) {
		PropagationPointInstance gi = EMV2InstanceFactory.eINSTANCE.createPropagationPointInstance();
		gi.setName(g.getName());
		gi.setPropagationPoint(g);
		annex.getPropagationPoints().add(gi);
	}

	private void instantiateEvent(ErrorBehaviorEvent event, ComponentInstance component, EMV2AnnexInstance annex) {
		if (event instanceof ErrorEvent errorEvent) {
			annex.getEvents().add(createErrorEventInstance(errorEvent, component));
		} else if (event instanceof RecoverEvent recoverEvent) {
			annex.getEvents().add(createRecoverEventInstance(recoverEvent, annex));
		} else if (event instanceof RepairEvent repairEvent) {
			annex.getEvents().add(createRepairEventInstance(repairEvent, annex));
		} else {
			throw new RuntimeException("Unexpected event: " + event);
		}
	}

	private ErrorEventInstance createErrorEventInstance(ErrorEvent event, ComponentInstance component) {
		var eventInstance = EMV2InstanceFactory.eINSTANCE.createErrorEventInstance();
		eventInstance.setName(event.getName());
		eventInstance.setErrorEvent(event);
		if (event.getTypeSet() != null) {
			eventInstance.setTypeSet(createAnonymousTypeSet(event.getTypeSet()));
			instantiateProperties(eventInstance.getTypeSet(), event, component);
		}
		instantiateProperties(eventInstance, event, component);
		return eventInstance;
	}

	private RecoverEventInstance createRecoverEventInstance(RecoverEvent event, EMV2AnnexInstance annex) {
		var eventInstance = EMV2InstanceFactory.eINSTANCE.createRecoverEventInstance();
		eventInstance.setName(event.getName());
		eventInstance.setRecoverEvent(event);
		var component = getContainerOfType(annex, ComponentInstance.class);
		for (var initiator : event.getEventInitiator()) {
			if (initiator instanceof Feature feature) {
				eventInstance.getEventInitiators().add(component.findFeatureInstance(feature));
			} else if (initiator instanceof ModeTransition transition) {
				eventInstance.getEventInitiators().add(component.findModeTransitionInstance(transition));
			}
		}
		instantiateProperties(eventInstance, event, component);
		return eventInstance;
	}

	private RepairEventInstance createRepairEventInstance(RepairEvent event, EMV2AnnexInstance annex) {
		var eventInstance = EMV2InstanceFactory.eINSTANCE.createRepairEventInstance();
		eventInstance.setName(event.getName());
		eventInstance.setRepairEvent(event);
		var component = getContainerOfType(annex, ComponentInstance.class);
		for (var initiator : event.getEventInitiator()) {
			if (initiator instanceof Feature feature) {
				eventInstance.getEventInitiators().add(component.findFeatureInstance(feature));
			} else if (initiator instanceof ModeTransition transition) {
				eventInstance.getEventInitiators().add(component.findModeTransitionInstance(transition));
			}
		}
		instantiateProperties(eventInstance, event, component);
		return eventInstance;
	}

	private void instantiateState(ErrorBehaviorState state, ComponentInstance component, EMV2AnnexInstance annex) {
		var stateInstance = EMV2InstanceFactory.eINSTANCE.createStateInstance();
		stateInstance.setName(state.getName());
		stateInstance.setState(state);
		if (state.getTypeSet() != null) {
			stateInstance.setTypeSet(createAnonymousTypeSet(state.getTypeSet()));
			instantiateProperties(stateInstance.getTypeSet(), state, component);
		}
		instantiateProperties(stateInstance, state, component);
		annex.getStates().add(stateInstance);
		if (state.isIntial()) {
			annex.setInitialState(stateInstance);
		}
	}

	private void instantiateTransition(ErrorBehaviorTransition transition, ComponentInstance component,
			EMV2AnnexInstance annex) {
		try {
			var transitionInstance = EMV2InstanceFactory.eINSTANCE.createTransitionInstance();
			transitionInstance.setTransition(transition);
			transitionInstance.setSource(createStateSource(transition, annex));
			transitionInstance
					.setCondition(createConditionExpressionInstance(transition.getCondition(), component, annex));
			transitionInstance.setDestination(createTransitionDestination(transition, transitionInstance.getSource(),
					transitionInstance.getCondition(), annex));
			if (transition.getName() == null) {
				var sourceName = transitionInstance.getSource().getName();
				var conditionName = transitionInstance.getCondition().getName();
				var destinationName = transitionInstance.getDestination().getName();
				transitionInstance.setName(sourceName + " -[" + conditionName + "]-> " + destinationName);
			} else {
				transitionInstance.setName(transition.getName());
				instantiateProperties(transitionInstance, transition, component);
			}
			annex.getTransitions().add(transitionInstance);
		} catch (InternalFeatureEncounteredException e) {
			// Abort instantiation of transition.
		}
	}

	private StateSource createStateSource(ErrorBehaviorTransition transition, EMV2AnnexInstance annex) {
		return createStateSource(transition.isAllStates(), transition.getSource(), transition.getTypeTokenConstraint(),
				annex);
	}

	private StateSource createStateSource(OutgoingPropagationCondition condition, EMV2AnnexInstance annex) {
		return createStateSource(condition.isAllStates(), condition.getState(), condition.getTypeTokenConstraint(),
				annex);
	}

	private StateSource createStateSource(ErrorDetection detection, EMV2AnnexInstance annex) {
		return createStateSource(detection.isAllStates(), detection.getState(), detection.getTypeTokenConstraint(),
				annex);
	}

	private StateSource createStateSource(boolean allStates, ErrorBehaviorState state, TypeSet typeSet,
			EMV2AnnexInstance annex) {
		if (allStates) {
			return createAllSources();
		} else {
			return createSourceStateReference(state, typeSet, annex);
		}
	}

	private AllSources createAllSources() {
		var allSources = EMV2InstanceFactory.eINSTANCE.createAllSources();
		allSources.setName("all");
		return allSources;
	}

	private SourceStateReference createSourceStateReference(ErrorBehaviorState state, TypeSet typeSet,
			EMV2AnnexInstance annex) {
		var stateReference = EMV2InstanceFactory.eINSTANCE.createSourceStateReference();
		stateReference.setState(findStateInstance(annex, state));
		if (typeSet != null) {
			stateReference.setTypeSet(createAnonymousTypeSet(typeSet));
		} else if (state.getTypeSet() != null) {
			stateReference.setTypeSet(createAnonymousTypeSet(state.getTypeSet()));
		}
		var name = stateReference.getState().getName();
		if (stateReference.getTypeSet() != null) {
			name += ' ' + stateReference.getTypeSet().getName();
		}
		stateReference.setName(name);
		return stateReference;
	}

	private ConditionExpressionInstance createConditionExpressionInstance(ConditionExpression condition,
			ComponentInstance component, EMV2AnnexInstance annex) throws InternalFeatureEncounteredException {
		if (condition instanceof OrExpression orExpression) {
			return createCountExpression(orExpression.getOperands(), CountExpressionOperation.GREATER_EQUAL, 1,
					component, annex);
		} else if (condition instanceof AndExpression andExpression) {
			return createCountExpression(andExpression.getOperands(), CountExpressionOperation.EQUALS, 2, component,
					annex);
		} else if (condition instanceof AllExpression allExpression) {
			return createCountExpression(allExpression.getOperands(), CountExpressionOperation.EQUALS,
					allExpression.getOperands().size() - allExpression.getCount(), component, annex);
		} else if (condition instanceof OrmoreExpression orMoreExpression) {
			return createCountExpression(orMoreExpression.getOperands(), CountExpressionOperation.GREATER_EQUAL,
					orMoreExpression.getCount(), component, annex);
		} else if (condition instanceof OrlessExpression orLessExpression) {
			return createCountExpression(orLessExpression.getOperands(), CountExpressionOperation.LESS_EQUAL,
					orLessExpression.getCount(), component, annex);
		} else if (condition instanceof SConditionElement conditionElement
				&& conditionElement.getQualifiedState() != null) {
			return createStateReference(component, conditionElement.getQualifiedState(),
					conditionElement.getConstraint());
		} else if (condition instanceof ConditionElement conditionElement) {
			var path = conditionElement.getQualifiedErrorPropagationReference().getEmv2Target();
			if (path.getNamedElement() instanceof ErrorBehaviorEvent event) {
				return createEventReference(event, conditionElement.getConstraint(), annex);
			} else {
				return createConditionPropagationReference(component, path, conditionElement.getConstraint());
			}
		} else {
			return null;
		}
	}

	private StateReference createStateReference(ComponentInstance component, QualifiedErrorBehaviorState path,
			TypeSet constraint) {
		var name = new StringBuilder();
		while (path.getNext() != null) {
			component = component.findSubcomponentInstance(path.getSubcomponent().getSubcomponent());
			name.append(component.getName());
			name.append('.');
			path = path.getNext();
		}
		component = component.findSubcomponentInstance(path.getSubcomponent().getSubcomponent());
		name.append(component.getName());
		name.append('.');
		name.append(path.getState().getName());

		var stateReference = EMV2InstanceFactory.eINSTANCE.createStateReference();
		var state = path.getState();
		var annex = findEMV2AnnexInstance(component);
		if (annex == null) {
			// Shouldn't happen if the declarative model is valid.
			return null;
		}
		stateReference.setState(findStateInstance(annex, state));
		if (constraint != null) {
			stateReference.setTypeSet(createAnonymousTypeSet(constraint));
		} else if (state.getTypeSet() != null) {
			stateReference.setTypeSet(createAnonymousTypeSet(state.getTypeSet()));
		}
		if (stateReference.getTypeSet() != null) {
			name.append(' ');
			name.append(stateReference.getTypeSet().getName());
		}
		stateReference.setName(name.toString());
		return stateReference;
	}

	private EventReference createEventReference(ErrorBehaviorEvent event, TypeSet constraint, EMV2AnnexInstance annex) {
		var eventReference = EMV2InstanceFactory.eINSTANCE.createEventReference();
		eventReference.setEvent(findEventInstance(annex, event));
		/*
		 * In OSATE, it is possible to refer to an event and use the keyword 'noerror'. This is not permitted by the
		 * syntax in the standard and 'noerror' should only be used with propagations. An OSATE issue has been filed
		 * stating that the validator should complain in such cases: https://github.com/osate/osate2/issues/2817
		 *
		 * I consider it to be undefined behavior if an event is used with 'noerror', therefore, I have decided to treat
		 * such references as if the event was untyped.
		 */
		if (constraint != null && !constraint.getTypeTokens().isEmpty()
				&& !constraint.getTypeTokens().get(0).isNoError()) {
			eventReference.setTypeSet(createAnonymousTypeSet(constraint));
		} else if (constraint == null && event instanceof ErrorEvent errorEvent && errorEvent.getTypeSet() != null) {
			eventReference.setTypeSet(createAnonymousTypeSet(errorEvent.getTypeSet()));
		}
		var name = event.getName();
		if (eventReference.getTypeSet() != null) {
			name += ' ' + eventReference.getTypeSet().getName();
		}
		eventReference.setName(name);
		return eventReference;
	}

	private ConditionExpressionInstance createConditionPropagationReference(ComponentInstance component,
			EMV2PathElement path, TypeSet constraint) throws InternalFeatureEncounteredException {
		var currentComponent = component;
		var namePrefix = new StringBuilder();
		while (path.getNamedElement() instanceof Subcomponent subcomponent) {
			currentComponent = currentComponent.findSubcomponentInstance(subcomponent);
			namePrefix.append(currentComponent.getName());
			namePrefix.append('.');
			path = path.getPath();
		}
		while (path.getPath() != null) {
			path = path.getPath();
		}
		ErrorPropagationInstance propagationInstance;
		if (path.getNamedElement() instanceof ErrorPropagation propagation) {
			if (propagation.getFeatureorPPRef() != null
					&& propagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
				throw new InternalFeatureEncounteredException();
			}
			var annex = findEMV2AnnexInstance(currentComponent);
			if (annex == null) {
				// Shouldn't happen if the declarative model is valid.
				return null;
			}
			propagationInstance = findErrorPropagationInstance(annex, propagation);
		} else if (path.getEmv2PropagationKind().equalsIgnoreCase("access")) {
			propagationInstance = findAccessPropagation(currentComponent);
		} else {
			propagationInstance = findBindingPropagation(currentComponent,
					BindingType.get(path.getEmv2PropagationKind().toLowerCase()));
		}
		if (propagationInstance == null) {
			// Shouldn't happen if the declarative model is valid.
			return null;
		}
		if (constraint != null && !constraint.getTypeTokens().isEmpty()
				&& constraint.getTypeTokens().get(0).isNoError()) {
			return createNoErrorPropagationReference(propagationInstance, namePrefix.toString());
		} else {
			return createConditionPropagationReference(propagationInstance, constraint, currentComponent == component,
					namePrefix.toString());
		}
	}

	private ConditionPropagationReference createConditionPropagationReference(ErrorPropagationInstance propagation,
			TypeSet constraint, boolean isInPropagation, String namePrefix) {
		var propagationReference = EMV2InstanceFactory.eINSTANCE.createConditionPropagationReference();
		propagationReference.setPropagation(propagation);
		TypeSet typeSet;
		if (constraint != null) {
			typeSet = constraint;
		} else if (isInPropagation) {
			typeSet = propagation.getInErrorPropagation().getTypeSet();
		} else {
			typeSet = propagation.getOutErrorPropagation().getTypeSet();
		}
		var anonymousTypeSet = createAnonymousTypeSet(typeSet);
		propagationReference.setTypeSet(anonymousTypeSet);
		propagationReference.setName(namePrefix + propagation.getName() + ' ' + anonymousTypeSet.getName());
		return propagationReference;
	}

	private NoErrorPropagationReference createNoErrorPropagationReference(ErrorPropagationInstance propagation,
			String namePrefix) {
		var propagationReference = EMV2InstanceFactory.eINSTANCE.createNoErrorPropagationReference();
		propagationReference.setPropagation(propagation);
		propagationReference.setName(namePrefix + propagation.getName() + " {noerror}");
		return propagationReference;
	}

	private CountExpression createCountExpression(List<? extends ConditionExpression> operands,
			CountExpressionOperation operation, long count, ComponentInstance component, EMV2AnnexInstance annex)
			throws InternalFeatureEncounteredException {
		var countExpression = EMV2InstanceFactory.eINSTANCE.createCountExpression();
		for (var operand : operands) {
			countExpression.getOperands().add(createConditionExpressionInstance(operand, component, annex));
		}
		countExpression.setOperation(operation);
		countExpression.setCount(count);
		var symbol = switch (operation) {
		case EQUALS -> "==";
		case GREATER_EQUAL -> ">=";
		case LESS_EQUAL -> "<=";
		};
		countExpression.setName(countExpression.getOperands()
				.stream()
				.map(NamedElement::getName)
				.collect(Collectors.joining(", ", "count(", ") " + symbol + " " + count)));
		return countExpression;
	}

	private TransitionDestination createTransitionDestination(ErrorBehaviorTransition transition, StateSource source,
			ConditionExpressionInstance condition, EMV2AnnexInstance annex) {
		if (transition.isSteadyState()) {
			return createSameState();
		} else if (!transition.getDestinationBranches().isEmpty()) {
			return createBranches(transition.getDestinationBranches(), source, condition, annex);
		} else {
			return createDestinationStateReference(transition, source, condition, annex);
		}
	}

	private DestinationStateReference createDestinationStateReference(ErrorBehaviorTransition transition,
			StateSource source, ConditionExpressionInstance condition, EMV2AnnexInstance annex) {
		var stateReference = EMV2InstanceFactory.eINSTANCE.createDestinationStateReference();
		stateReference.setState(findStateInstance(annex, transition.getTarget()));
		stateReference.setTypeSet(createDestinationTypeSet(transition.getTargetToken(),
				transition.getTarget().getTypeSet() != null, source, condition));
		var name = stateReference.getState().getName();
		if (stateReference.getTypeSet() != null) {
			name += ' ' + stateReference.getTypeSet().getName();
		}
		stateReference.setName(name);
		return stateReference;
	}

	private AnonymousTypeSet createDestinationTypeSet(TypeSet explicitTypeSet, boolean destinationIsTyped,
			StateSource source, ConditionExpressionInstance condition) {
		if (explicitTypeSet != null) {
			return createAnonymousTypeSet(explicitTypeSet);
		} else if (destinationIsTyped && source instanceof SourceStateReference stateReference
				&& !(condition instanceof CountExpression)) {
			var sourceTypeSet = stateReference.getTypeSet();
			AnonymousTypeSet conditionTypeSet = null;
			if (condition instanceof EventReference eventReference) {
				conditionTypeSet = eventReference.getTypeSet();
			} else if (condition instanceof ConditionPropagationReference propagationReference) {
				conditionTypeSet = propagationReference.getTypeSet();
			}
			if (sourceTypeSet != null && sourceTypeSet.flatten().size() == 1 && conditionTypeSet == null) {
				return EcoreUtil.copy(sourceTypeSet);
			} else if (sourceTypeSet == null && conditionTypeSet != null && conditionTypeSet.flatten().size() == 1) {
				return EcoreUtil.copy(conditionTypeSet);
			}
		}
		return null;
	}

	private SameState createSameState() {
		var sameState = EMV2InstanceFactory.eINSTANCE.createSameState();
		sameState.setName("same state");
		return sameState;
	}

	private Branches createBranches(List<TransitionBranch> branches, StateSource source,
			ConditionExpressionInstance condition, EMV2AnnexInstance annex) {
		var hasOthers = false;
		var hasProperty = false;
		for (var branch : branches) {
			if (branch.getValue().isOthers()) {
				hasOthers = true;
			}
			if (branch.getValue().getSymboliclabel() != null) {
				hasProperty = true;
			}
		}
		BigDecimal remaining = null;
		if (hasOthers && !hasProperty) {
			var sum = new BigDecimal(0);
			for (var branch : branches) {
				if (branch.getValue().getRealvalue() != null) {
					sum = sum.add(new BigDecimal(branch.getValue().getRealvalue().replace("_", "")));
				}
			}
			remaining = new BigDecimal(1).subtract(sum);
		}
		var branchesInstance = EMV2InstanceFactory.eINSTANCE.createBranches();
		for (var branch : branches) {
			branchesInstance.getBranches().add(createBranch(branch, source, condition, remaining, annex));
		}
		branchesInstance.setName(branchesInstance.getBranches()
				.stream()
				.map(NamedElement::getName)
				.collect(Collectors.joining(", ", "(", ")")));
		return branchesInstance;
	}

	private Branch createBranch(TransitionBranch branch, StateSource source, ConditionExpressionInstance condition,
			BigDecimal remaining, EMV2AnnexInstance annex) {
		BigDecimal probability;
		String nameSuffix;
		if (branch.getValue().getRealvalue() != null) {
			probability = new BigDecimal(branch.getValue().getRealvalue().replace("_", ""));
			nameSuffix = probability.toString();
		} else if (branch.getValue().getSymboliclabel() != null) {
			probability = null;
			nameSuffix = branch.getValue().getSymboliclabel().getQualifiedName();
		} else if (remaining != null) {
			probability = remaining;
			nameSuffix = remaining.toString();
		} else {
			probability = null;
			nameSuffix = "others";
		}
		if (branch.isSteadyState()) {
			return createBranchSameState(probability, nameSuffix);
		} else {
			return createBranchStateReference(branch, source, condition, probability, nameSuffix, annex);
		}
	}

	private BranchSameState createBranchSameState(BigDecimal probability, String nameSuffix) {
		var sameState = EMV2InstanceFactory.eINSTANCE.createBranchSameState();
		sameState.setName("same state with " + nameSuffix);
		sameState.setProbability(probability);
		return sameState;
	}

	private BranchStateReference createBranchStateReference(TransitionBranch branch, StateSource source,
			ConditionExpressionInstance condition, BigDecimal probability, String nameSuffix, EMV2AnnexInstance annex) {
		var stateReference = EMV2InstanceFactory.eINSTANCE.createBranchStateReference();
		stateReference.setState(findStateInstance(annex, branch.getTarget()));
		stateReference.setTypeSet(createDestinationTypeSet(branch.getTargetToken(),
				branch.getTarget().getTypeSet() != null, source, condition));
		stateReference.setProbability(probability);
		var name = stateReference.getState().getName();
		if (stateReference.getTypeSet() != null) {
			name += ' ' + stateReference.getTypeSet().getName();
		}
		name += " with " + nameSuffix;
		stateReference.setName(name);
		return stateReference;
	}

	private void instantiateCompositeState(CompositeState composite, ComponentInstance component,
			EMV2AnnexInstance annex) {
		try {
			var compositeInstance = EMV2InstanceFactory.eINSTANCE.createCompositeStateInstance();
			compositeInstance.setComposite(composite);
			compositeInstance.setCondition(createCompositeConditionExpression(composite, component, annex));
			compositeInstance.setDestination(findStateInstance(annex, composite.getState()));
			if (composite.getTypedToken() != null) {
				compositeInstance.setDestinationTypeSet(createAnonymousTypeSet(composite.getTypedToken()));
			}
			if (composite.getName() == null) {
				var conditionName = compositeInstance.getCondition().getName();
				var destinationName = compositeInstance.getDestination().getName();
				var destinationTypeSetName = "";
				if (compositeInstance.getDestinationTypeSet() != null) {
					destinationTypeSetName = ' ' + compositeInstance.getDestinationTypeSet().getName();
				}
				compositeInstance.setName('[' + conditionName + "]-> " + destinationName + destinationTypeSetName);
			} else {
				compositeInstance.setName(composite.getName());
				instantiateProperties(compositeInstance, composite, component);
			}
			annex.getComposites().add(compositeInstance);
		} catch (InternalFeatureEncounteredException e) {
			// Abort instantiation of composite state.
		}
	}

	private CompositeConditionExpression createCompositeConditionExpression(CompositeState composite,
			ComponentInstance component, EMV2AnnexInstance annex) throws InternalFeatureEncounteredException {
		if (composite.isOthers()) {
			return createOthersExpression();
		} else {
			return createConditionExpressionInstance(composite.getCondition(), component, annex);
		}
	}

	private OthersExpression createOthersExpression() {
		var othersExpression = EMV2InstanceFactory.eINSTANCE.createOthersExpression();
		othersExpression.setName("others");
		return othersExpression;
	}

	private void instantiateErrorPropagations(List<ErrorPropagation> eps, ComponentInstance component,
			EMV2AnnexInstance annex) {
		var propagationInstances = new TreeMap<String, ErrorPropagationInstance>(String.CASE_INSENSITIVE_ORDER);
		for (var ep : eps) {
			var epi = propagationInstances.computeIfAbsent(EMV2Util.getPropagationName(ep), name -> {
				try {
					return createErrorPropagationInstance(annex, name, ep, component);
				} catch (InternalFeatureEncounteredException e) {
					return null;
				}
			});
			if (epi == null) {
				// This can happen if the propagation points to an InternalFeature. In that case, simply skip this one.
				break;
			}
			switch (ep.getDirection()) {
			case IN:
				assert epi.getInErrorPropagation() == null && epi.getInTypeSet() == null : "In fields are already set.";
				epi.setInErrorPropagation(ep);
				epi.setInTypeSet(createAnonymousTypeSet(ep.getTypeSet()));
				instantiateProperties(epi.getInTypeSet(), ep, component);
				break;
			case OUT:
				assert epi.getOutErrorPropagation() == null && epi.getOutTypeSet() == null
						: "Out fields are already set.";
				epi.setOutErrorPropagation(ep);
				epi.setOutTypeSet(createAnonymousTypeSet(ep.getTypeSet()));
				instantiateProperties(epi.getOutTypeSet(), ep, component);
				break;
			case IN_OUT:
				throw new RuntimeException(
						"Propagation has an in out direction which is not supported by the grammar: " + ep);
			}
		}
	}

	private ErrorPropagationInstance createErrorPropagationInstance(EMV2AnnexInstance annex, String name,
			ErrorPropagation ep, ComponentInstance component) throws InternalFeatureEncounteredException {
		ErrorPropagationInstance propagation;
		if ("access".equalsIgnoreCase(ep.getKind())) {
			propagation = createAccessPropagation(name);
		} else if (ep.getKind() != null) {
			propagation = createBindingPropagation(name, ep.getKind());
		} else if (ep.getFeatureorPPRef() != null) {
			var featureOrPPRef = ep.getFeatureorPPRef();
			var featureOrPP = featureOrPPRef.getFeatureorPP();
			if (featureOrPP instanceof Feature) {
				propagation = createFeaturePropagation(annex, name, featureOrPPRef);
			} else if (featureOrPP instanceof PropagationPoint point) {
				propagation = createPointPropagation(annex, name, point);
			} else if (featureOrPP instanceof InternalFeature) {
				// Propagation not instantiated since InternalFeatures are not instantiated.
				throw new InternalFeatureEncounteredException();
			} else {
				throw new RuntimeException(
						"featureorPPRef points to something other than a Feature, an InternalFeature, or a PropagationPoint: "
								+ featureOrPP);
			}
		} else {
			throw new RuntimeException("Both kind and featureOrPPRef are null: " + ep);
		}
		instantiateProperties(propagation, ep, component);
		annex.getPropagations().add(propagation);
		return propagation;
	}

	private FeaturePropagation createFeaturePropagation(EMV2AnnexInstance annex, String name,
			FeatureorPPReference featureReference) {
		var propagation = EMV2InstanceFactory.eINSTANCE.createFeaturePropagation();
		propagation.setName(name);
		propagation
				.setFeature(findFeatureInstance(getContainerOfType(annex, ComponentInstance.class), featureReference));
		return propagation;
	}

	private PointPropagation createPointPropagation(EMV2AnnexInstance annex, String name,
			PropagationPoint propagationPoint) {
		var propagation = EMV2InstanceFactory.eINSTANCE.createPointPropagation();
		propagation.setName(name);
		propagation.setPoint(findPropagationPointInstance(annex, propagationPoint));
		return propagation;
	}

	private AccessPropagation createAccessPropagation(String name) {
		var propagation = EMV2InstanceFactory.eINSTANCE.createAccessPropagation();
		propagation.setName(name);
		return propagation;
	}

	private BindingPropagation createBindingPropagation(String name, String kind) {
		var propagation = EMV2InstanceFactory.eINSTANCE.createBindingPropagation();
		propagation.setName(name);
		propagation.setBinding(BindingType.get(kind.toLowerCase()));
		return propagation;
	}

	private AnonymousTypeSet createAnonymousTypeSet(TypeSet set) {
		var anonymousTypeSet = EMV2InstanceFactory.eINSTANCE.createAnonymousTypeSet();
		anonymousTypeSet.getElements().addAll(createTypeSetElements(set.getTypeTokens(), 0));
		anonymousTypeSet.setName(anonymousTypeSet.getElements()
				.stream()
				.map(NamedElement::getName)
				.collect(Collectors.joining(", ", "{", "}")));
		return anonymousTypeSet;
	}

	private List<TypeSetElement> createTypeSetElements(List<TypeToken> tokens, int depth) {
		var results = new ArrayList<TypeSetElement>();
		for (var token : tokens) {
			if (token.getType().size() == 1) {
				var element = token.getType().get(0);
				if (element instanceof ErrorType type) {
					results.add(createTypeInstance(type));
				} else if (element instanceof TypeSet set) {
					results.add(createTypeSetInstance(set, depth));
				} else {
					throw new RuntimeException("element is something other than an ErrorType or a TypeSet: " + element);
				}
			} else {
				results.add(createTypeProductInstance(token));
			}
		}
		return results;
	}

	private TypeInstance createTypeInstance(ErrorType type) {
		var typeInstance = EMV2InstanceFactory.eINSTANCE.createTypeInstance();
		typeInstance.setName(type.getName());
		typeInstance.setType(type);
		return typeInstance;
	}

	private TypeSetInstance createTypeSetInstance(TypeSet set, int depth) {
		var typeSetInstance = EMV2InstanceFactory.eINSTANCE.createTypeSetInstance();
		typeSetInstance.setName(set.getName());
		typeSetInstance.setTypeSet(set);
		if (depth > 50) {
			// TODO Add error marker stating that there is a cycle.
		} else {
			typeSetInstance.getElements()
					.addAll(createTypeSetElements(EMV2Util.resolveAlias(set).getTypeTokens(), depth + 1));
		}
		return typeSetInstance;
	}

	private TypeProductInstance createTypeProductInstance(TypeToken token) {
		var product = EMV2InstanceFactory.eINSTANCE.createTypeProductInstance();
		product.setName(token.getType().stream().map(NamedElement::getName).collect(Collectors.joining(" * ")));
		for (var element : token.getType()) {
			if (element instanceof ErrorType type) {
				product.getTypes().add(createTypeInstance(type));
			}
			// TODO Add error marker to instance model if element is a type set.
		}
		return product;
	}

	private void instantiateErrorSource(ErrorSource source, ComponentInstance component, EMV2AnnexInstance annex) {
		if (source.isAll()) {
			// TODO Instantiate 'all' error sources after we figure out what 'all' means.
			return;
		}
		var propagation = (ErrorPropagation) source.getSourceModelElement();
		if (propagation.getFeatureorPPRef() != null
				&& propagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
			// Error source not instantiated since propagations that refer to internal features are not instantiated.
			return;
		}
		var sourceInstance = EMV2InstanceFactory.eINSTANCE.createErrorSourceInstance();
		sourceInstance.setName(source.getName());
		sourceInstance.setErrorSource(source);
		sourceInstance.setPropagation(findErrorPropagationInstance(annex, propagation));
		var typeSet = source.getTypeTokenConstraint();
		if (typeSet == null) {
			typeSet = propagation.getTypeSet();
		}
		sourceInstance.setTypeSet(createAnonymousTypeSet(typeSet));
		instantiateProperties(sourceInstance, source, component);
		instantiateProperties(sourceInstance.getTypeSet(), source, component);
		annex.getErrorFlows().add(sourceInstance);
	}

	private void instantiateErrorSink(ErrorSink sink, ComponentInstance component, EMV2AnnexInstance annex) {
		if (sink.isAllIncoming()) {
			// TODO Instantiate 'all' error sinks after we figure out what 'all' means.
			return;
		}
		var propagation = sink.getIncoming();
		if (propagation.getFeatureorPPRef() != null
				&& propagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
			// Error sink not instantiated since propagations that refer to internal features are not instantiated.
			return;
		}
		var sinkInstance = EMV2InstanceFactory.eINSTANCE.createErrorSinkInstance();
		sinkInstance.setName(sink.getName());
		sinkInstance.setErrorSink(sink);
		sinkInstance.setPropagation(findErrorPropagationInstance(annex, propagation));
		var typeSet = sink.getTypeTokenConstraint();
		if (typeSet == null) {
			typeSet = propagation.getTypeSet();
		}
		sinkInstance.setTypeSet(createAnonymousTypeSet(typeSet));
		instantiateProperties(sinkInstance, sink, component);
		instantiateProperties(sinkInstance.getTypeSet(), sink, component);
		annex.getErrorFlows().add(sinkInstance);
	}

	private void instantiateErrorPath(ErrorPath path, ComponentInstance component, EMV2AnnexInstance annex) {
		if (path.isAllIncoming() || path.isAllOutgoing()) {
			// TODO Instantiate 'all' error paths after we figure out what 'all' means.
			return;
		}
		if (path.getTargetToken() == null) {
			// TODO Instantiate after we figure out what it means when the target token is omitted.
			return;
		}
		var sourcePropagation = path.getIncoming();
		if (sourcePropagation.getFeatureorPPRef() != null
				&& sourcePropagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
			// Error path not instantiated since propagations that refer to internal features are not instantiated.
			return;
		}
		var destinationPropagation = path.getOutgoing();
		if (destinationPropagation.getFeatureorPPRef() != null
				&& destinationPropagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
			// Error path not instantiated since propagations that refer to internal features are not instantiated.
			return;
		}
		var pathInstance = EMV2InstanceFactory.eINSTANCE.createErrorPathInstance();
		pathInstance.setName(path.getName());
		pathInstance.setErrorPath(path);
		pathInstance.setSourcePropagation(findErrorPropagationInstance(annex, sourcePropagation));
		pathInstance.setDestinationPropagation(findErrorPropagationInstance(annex, destinationPropagation));
		var sourceTypeSet = path.getTypeTokenConstraint();
		if (sourceTypeSet == null) {
			sourceTypeSet = sourcePropagation.getTypeSet();
		}
		pathInstance.setSourceTypeSet(createAnonymousTypeSet(sourceTypeSet));
		pathInstance.setDestinationTypeSet(createAnonymousTypeSet(path.getTargetToken()));
		instantiateProperties(pathInstance, path, component);
		annex.getErrorFlows().add(pathInstance);
	}

	private void instantiateOutgoingPropagationCondition(OutgoingPropagationCondition condition,
			ComponentInstance component, EMV2AnnexInstance annex) {
		try {
			var conditionInstance = EMV2InstanceFactory.eINSTANCE.createOutgoingPropagationConditionInstance();
			conditionInstance.setOutgoingPropagationCondition(condition);
			conditionInstance.setSource(createStateSource(condition, annex));
			if (condition.getCondition() != null) {
				conditionInstance
						.setCondition(createConditionExpressionInstance(condition.getCondition(), component, annex));
			}
			conditionInstance.setDestination(createOutgoingPropagationConditionDestination(condition,
					conditionInstance.getSource(), conditionInstance.getCondition(), annex));
			if (condition.getName() == null) {
				var sourceName = conditionInstance.getSource().getName();
				var conditionExpressionName = "";
				if (conditionInstance.getCondition() != null) {
					conditionExpressionName = conditionInstance.getCondition().getName();
				}
				var destinationName = conditionInstance.getDestination().getName();
				conditionInstance.setName(sourceName + " -[" + conditionExpressionName + "]-> " + destinationName);
			} else {
				conditionInstance.setName(condition.getName());
				instantiateProperties(conditionInstance, condition, component);
			}
			annex.getConditions().add(conditionInstance);
		} catch (InternalFeatureEncounteredException e) {
			// Abort instantiation of outgoing propagation condition.
		}
	}

	private OutgoingPropagationConditionDestination createOutgoingPropagationConditionDestination(
			OutgoingPropagationCondition condition, StateSource source, ConditionExpressionInstance conditionExpression,
			EMV2AnnexInstance annex) throws InternalFeatureEncounteredException {
		if (condition.getTypeToken() != null && !condition.getTypeToken().getTypeTokens().isEmpty()
				&& condition.getTypeToken().getTypeTokens().get(0).isNoError()) {
			if (condition.isAllPropagations()) {
				return createAllPropagationsNoError();
			} else {
				var propagation = condition.getOutgoing();
				if (propagation.getFeatureorPPRef() != null
						&& propagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
					throw new InternalFeatureEncounteredException();
				}
				var propagationInstance = findErrorPropagationInstance(annex, propagation);
				if (propagationInstance == null) {
					// Shouldn't happen if the declarative model is valid.
					return null;
				}
				return createNoErrorPropagationReference(propagationInstance, "");
			}
		} else if (condition.isAllPropagations()) {
			return createAllPropagations(condition, source, conditionExpression);
		} else {
			return createDestinationPropagationReference(condition, source, conditionExpression, annex);
		}
	}

	private DestinationPropagationReference createDestinationPropagationReference(
			OutgoingPropagationCondition condition, StateSource source, ConditionExpressionInstance conditionExpression,
			EMV2AnnexInstance annex) throws InternalFeatureEncounteredException {
		var propagation = condition.getOutgoing();
		if (propagation.getFeatureorPPRef() != null
				&& propagation.getFeatureorPPRef().getFeatureorPP() instanceof InternalFeature) {
			throw new InternalFeatureEncounteredException();
		}
		var propagationReference = EMV2InstanceFactory.eINSTANCE.createDestinationPropagationReference();
		propagationReference.setPropagation(findErrorPropagationInstance(annex, propagation));
		propagationReference
				.setTypeSet(createDestinationTypeSet(condition.getTypeToken(), true, source, conditionExpression));
		var name = propagationReference.getPropagation().getName();
		if (propagationReference.getTypeSet() != null) {
			name += ' ' + propagationReference.getTypeSet().getName();
		}
		propagationReference.setName(name);
		return propagationReference;
	}

	private AllPropagations createAllPropagations(OutgoingPropagationCondition condition, StateSource source,
			ConditionExpressionInstance conditionExpression) {
		var allPropagations = EMV2InstanceFactory.eINSTANCE.createAllPropagations();
		allPropagations
				.setTypeSet(createDestinationTypeSet(condition.getTypeToken(), true, source, conditionExpression));
		var name = "all";
		if (allPropagations.getTypeSet() != null) {
			name += ' ' + allPropagations.getTypeSet().getName();
		}
		allPropagations.setName(name);
		return allPropagations;
	}

	private AllPropagationsNoError createAllPropagationsNoError() {
		var allPropagations = EMV2InstanceFactory.eINSTANCE.createAllPropagationsNoError();
		allPropagations.setName("all {noerror}");
		return allPropagations;
	}

	private void instantiateDetection(ErrorDetection detection, ComponentInstance component, EMV2AnnexInstance annex) {
		if (detection.getDetectionReportingPort().getElement() instanceof InternalFeature) {
			// Detection not instantiated since InternalFeatures are not instantiated.
			return;
		}
		try {
			DetectionInstance detectionInstance = EMV2InstanceFactory.eINSTANCE.createDetectionInstance();
			detectionInstance.setDetection(detection);
			detectionInstance.setSource(createStateSource(detection, annex));
			if (detection.getCondition() != null) {
				detectionInstance
						.setCondition(createConditionExpressionInstance(detection.getCondition(), component, annex));
			}
			detectionInstance.setDestination(findFeatureInstance(detection.getDetectionReportingPort(), component));
			if (detection.getErrorCode() != null) {
				detectionInstance.setErrorCode(createErrorCodeInstance(detection.getErrorCode()));
			}
			if (detection.getName() == null) {
				var sourceName = detectionInstance.getSource().getName();
				var conditionName = "";
				if (detectionInstance.getCondition() != null) {
					conditionName = detectionInstance.getCondition().getName();
				}
				var destinationName = detectionInstance.getDestination().getName();
				var codeName = "";
				if (detectionInstance.getErrorCode() != null) {
					codeName = " (" + detectionInstance.getErrorCode().getName() + ')';
				}
				detectionInstance
						.setName(sourceName + " -[" + conditionName + "]-> " + destinationName + " !" + codeName);
			} else {
				detectionInstance.setName(detection.getName());
				instantiateProperties(detectionInstance, detection, component);
			}
			annex.getDetections().add(detectionInstance);
		} catch (InternalFeatureEncounteredException e) {
			// Abort instantiation of detection.
		}
	}

	private ErrorCodeInstance createErrorCodeInstance(ErrorCodeValue code) {
		if (code.getIntValue() != null) {
			return createIntegerCode(Long.parseLong(code.getIntValue()));
		} else if (code.getEnumLiteral() != null) {
			return createStringCode(code.getEnumLiteral());
		} else {
			return createConstantCode(code.getConstant());
		}
	}

	private IntegerCode createIntegerCode(long code) {
		var integerCode = EMV2InstanceFactory.eINSTANCE.createIntegerCode();
		integerCode.setName(Long.toString(code));
		integerCode.setCode(code);
		return integerCode;
	}

	private StringCode createStringCode(String code) {
		var stringCode = EMV2InstanceFactory.eINSTANCE.createStringCode();
		stringCode.setName('"' + code + '"');
		stringCode.setCode(code);
		return stringCode;
	}

	private ConstantCode createConstantCode(PropertyConstant constant) {
		var constantCode = EMV2InstanceFactory.eINSTANCE.createConstantCode();
		constantCode.setName(constant.getQualifiedName());
		constantCode.setCode(constant);
		return constantCode;
	}

	private void instantiateModeMapping(ErrorStateToModeMapping modeMapping, ComponentInstance component,
			EMV2AnnexInstance annex) {
		var modeMappingInstance = EMV2InstanceFactory.eINSTANCE.createModeMappingInstance();
		modeMappingInstance.setModeMapping(modeMapping);
		modeMappingInstance.setState(findStateInstance(annex, modeMapping.getErrorState()));
		if (modeMapping.getTypeToken() != null) {
			modeMappingInstance.setTypeSet(createAnonymousTypeSet(modeMapping.getTypeToken()));
		} else if (modeMapping.getErrorState().getTypeSet() != null) {
			modeMappingInstance.setTypeSet(createAnonymousTypeSet(modeMapping.getErrorState().getTypeSet()));
		}
		for (var mode : modeMapping.getMappedModes()) {
			modeMappingInstance.getModes().add(component.findModeInstance(mode));
		}
		var name = modeMapping.getErrorState().getName();
		if (modeMappingInstance.getTypeSet() != null) {
			name += ' ' + modeMappingInstance.getTypeSet().getName();
		}
		name += " in modes ("
				+ modeMapping.getMappedModes().stream().map(NamedElement::getName).collect(Collectors.joining(", "))
				+ ')';
		modeMappingInstance.setName(name);
		annex.getModeMappings().add(modeMappingInstance);
	}

	private StateInstance findStateInstance(EMV2AnnexInstance annex, ErrorBehaviorState state) {
		for (var stateInstance : annex.getStates()) {
			if (stateInstance.getState() == state) {
				return stateInstance;
			}
		}
		return null;
	}

	private PropagationPointInstance findPropagationPointInstance(EMV2AnnexInstance annex, PropagationPoint pp) {
		for (PropagationPointInstance ei : annex.getPropagationPoints()) {
			if (ei.getPropagationPoint() == pp) {
				return ei;
			}
		}
		return null;
	}

	private EventInstance findEventInstance(EMV2AnnexInstance annex, ErrorBehaviorEvent ev) {
		for (EventInstance ei : annex.getEvents()) {
			if (ei.getName().equalsIgnoreCase(ev.getName())) {
				return ei;
			}
		}
		return null;
	}

	private EMV2AnnexInstance findEMV2AnnexInstance(ComponentInstance ci) {
		for (AnnexInstance ai : ci.getAnnexInstances()) {
			if (ai instanceof EMV2AnnexInstance emv2AI) {
				return emv2AI;
			}
		}
		return null;
	}

	private FeatureInstance findFeatureInstance(ComponentInstance ci, FeatureorPPReference fppref) {
		if (fppref == null) {
			return null;
		}
		NamedElement fpp = fppref.getFeatureorPP();
		FeatureInstance fi = ci.findFeatureInstance((Feature) fpp);
		FeatureorPPReference curfppref = fppref.getNext();
		while (curfppref != null) {
			fi = fi.findFeatureInstance((Feature) curfppref.getFeatureorPP());
			if (curfppref.getNext() != null) {
				if (fi == null) {
					return null;
				}
				curfppref = curfppref.getNext();
			} else {
				return fi;
			}
		}
		return fi;
	}

	private FeatureInstance findFeatureInstance(ReportingPortReference portReference, ComponentInstance component) {
		if (portReference.getPrevious() == null) {
			return component.findFeatureInstance((Feature) portReference.getElement());
		} else {
			var previousFeatureGroup = findFeatureInstance(portReference.getPrevious(), component);
			return previousFeatureGroup.findFeatureInstance((Feature) portReference.getElement());
		}
	}

	private ErrorPropagationInstance findErrorPropagationInstance(EMV2AnnexInstance annex, ErrorPropagation ep) {
		var declarativeName = EMV2Util.getPropagationName(ep);
		for (ErrorPropagationInstance epi : annex.getPropagations()) {
			if (epi.getName().equalsIgnoreCase(declarativeName)) {
				return epi;
			}
		}
		return null;
	}

	private FeaturePropagation findFeaturePropagation(FeatureInstance feature) {
		var annex = findEMV2AnnexInstance(EcoreUtil2.getContainerOfType(feature, ComponentInstance.class));
		if (annex == null) {
			return null;
		}
		for (var propagation : annex.getPropagations()) {
			if (propagation instanceof FeaturePropagation featurePropagation
					&& featurePropagation.getFeature() == feature) {
				return featurePropagation;
			}
		}
		return null;
	}

	private PointPropagation findPointPropagation(PropagationPointInstance point) {
		var annex = findEMV2AnnexInstance(EcoreUtil2.getContainerOfType(point, ComponentInstance.class));
		if (annex == null) {
			return null;
		}
		for (var propagation : annex.getPropagations()) {
			if (propagation instanceof PointPropagation pointPropagation && pointPropagation.getPoint() == point) {
				return pointPropagation;
			}
		}
		return null;
	}

	private AccessPropagation findAccessPropagation(ComponentInstance component) {
		var annex = findEMV2AnnexInstance(component);
		if (annex == null) {
			return null;
		}
		for (var propagation : annex.getPropagations()) {
			if (propagation instanceof AccessPropagation accessPropagation) {
				return accessPropagation;
			}
		}
		return null;
	}

	private BindingPropagation findBindingPropagation(ComponentInstance component, BindingType binding) {
		var annex = findEMV2AnnexInstance(component);
		if (annex == null) {
			return null;
		}
		for (var propagation : annex.getPropagations()) {
			if (propagation instanceof BindingPropagation bindingPropagation
					&& bindingPropagation.getBinding() == binding) {
				return bindingPropagation;
			}
		}
		return null;
	}

	private void instantiateUserDefinedPath(PropagationPath path, EMV2AnnexInstance annex, ComponentInstance context) {
		// Paths that point to a feature are not instantiated.
		if (getQualifiedPointReference(path.getSource()) instanceof PropagationPoint source
				&& getQualifiedPointReference(path.getTarget()) instanceof PropagationPoint destination) {
			var pathInstance = EMV2InstanceFactory.eINSTANCE.createUserDefinedPath();
			pathInstance.setPath(path);
			var sourcePointInstance = findPropagationPointInstance(path.getSource(), context, source);
			pathInstance.setSourcePoint(sourcePointInstance);
			var destinationPointInstance = findPropagationPointInstance(path.getTarget(), context, destination);
			pathInstance.setDestinationPoint(destinationPointInstance);
			if (path.getName() == null) {
				var substringIndex = context.getInstanceObjectPath().length() + 1;
				var sourcePath = sourcePointInstance.getInstanceObjectPath().substring(substringIndex);
				var destinationPath = destinationPointInstance.getInstanceObjectPath().substring(substringIndex);
				pathInstance.setName(sourcePath + " -> " + destinationPath);
			} else {
				pathInstance.setName(path.getName());
				instantiateProperties(pathInstance, path, context);
			}
			var sourcePropagation = findPointPropagation(sourcePointInstance);
			if (sourcePropagation != null && sourcePropagation.getDirection().outgoing()) {
				pathInstance.setSourcePropagation(sourcePropagation);
			}
			var destinationPropagation = findPointPropagation(destinationPointInstance);
			if (destinationPropagation != null && destinationPropagation.getDirection().incoming()) {
				pathInstance.setDestinationPropagation(destinationPropagation);
			}
			annex.getPropagationPaths().add(pathInstance);
		}
	}

	private static NamedElement getQualifiedPointReference(QualifiedPropagationPoint path) {
		while (path.getNext() != null) {
			path = path.getNext();
		}
		return path.getPropagationPoint();
	}

	private PropagationPointInstance findPropagationPointInstance(QualifiedPropagationPoint path,
			ComponentInstance component, PropagationPoint point) {
		while (path.getSubcomponent() != null) {
			component = component.findSubcomponentInstance(path.getSubcomponent().getSubcomponent());
			path = path.getNext();
		}
		return findPropagationPointInstance(findEMV2AnnexInstance(component), point);
	}

	private void instantiateProperties(EMV2InstanceObject instanceHolder, NamedElement declarativeHolder,
			ComponentInstance component) {
		String name;
		if (declarativeHolder instanceof ErrorPropagation errorPropagation) {
			name = EMV2Util.getPropagationName(errorPropagation);
		} else {
			name = declarativeHolder.getName();
		}
		var associations = new LinkedHashMap<Property, EMV2PropertyAssociation>();
		collectAssociations(associations, declarativeHolder, component, name, instanceHolder);
		for (var association : associations.values()) {
			instantiatePropertyAssociation(association, instanceHolder.getOwnedPropertyAssociations(), associations,
					component);
		}
	}

	private void instantiateProperties(AnonymousTypeSet anonymousTypeSet, NamedElement declarativeHolder,
			ComponentInstance component) {
		String holderName;
		if (declarativeHolder instanceof ErrorPropagation errorPropagation) {
			holderName = EMV2Util.getPropagationName(errorPropagation);
		} else {
			holderName = declarativeHolder.getName();
		}
		for (var token : anonymousTypeSet.flatten()) {
			if (token instanceof TypeInstance type) {
				var associations = new LinkedHashMap<Property, EMV2PropertyAssociation>();

				/*
				 * Find properties which are defined on the EMV2 element (propagation, state, event, etc) and can be
				 * inherited by the error type.
				 */
				collectAssociations(associations, declarativeHolder, component, holderName, type);

				// Find properties which are defined on a containing type set and can be inherited by the error type.
				var sets = new ArrayDeque<TypeSet>();
				for (var typeSetInstance = EcoreUtil2.getContainerOfType(type,
						TypeSetInstance.class); typeSetInstance != null; typeSetInstance = EcoreUtil2
								.getContainerOfType(typeSetInstance.eContainer(), TypeSetInstance.class)) {
					sets.addFirst(typeSetInstance.resolveAlias());
				}
				for (var set : sets) {
					var library = EcoreUtil2.getContainerOfType(set, ErrorModelLibrary.class);
					collectAssociations(associations, library.getProperties(), Collections.emptyList(), set.getName(),
							type);
				}

				var resolvedType = type.resolveAlias();

				// Find properties which are defined on a super type which this error type extends from.
				var types = new ArrayDeque<ErrorType>();
				types.addFirst(resolvedType);
				for (var superType = EMV2Util
						.resolveAlias(resolvedType.getSuperType()); superType != null; superType = EMV2Util
								.resolveAlias(superType.getSuperType())) {
					types.addFirst(superType);
				}
				for (var lookupType : types) {
					var library = EcoreUtil2.getContainerOfType(lookupType, ErrorModelLibrary.class);
					collectAssociations(associations, library.getProperties(), Collections.emptyList(),
							lookupType.getName(), type);
				}

				// Find properties which are defined on the actual error type in a state machine or a subclause.
				var name = holderName + '.' + resolvedType.getName();
				collectAssociations(associations, declarativeHolder, component, name, type);

				for (var association : associations.values()) {
					instantiatePropertyAssociation(association, type.getOwnedPropertyAssociations(), associations,
							component);
				}
			}
		}
	}

	private void instantiateProperties(List<ErrorModelSubclause> subclauses, EMV2AnnexInstance annex,
			ComponentInstance component) {
		var associations = new LinkedHashMap<Property, EMV2PropertyAssociation>();
		for (var subclause : Lists.reverse(subclauses)) {
			for (var association : subclause.getProperties()) {
				if (!association.isModal() && association.getEmv2Path().isEmpty()) {
					associations.put(association.getProperty(), association);
				}
			}
		}
		for (var association : associations.values()) {
			instantiatePropertyAssociation(association, annex.getOwnedPropertyAssociations(), associations, component);
		}
	}

	private void instantiateProperties(ErrorBehaviorStateMachine stateMachine, EMV2AnnexInstance annex,
			ComponentInstance component) {
		var associations = new LinkedHashMap<Property, EMV2PropertyAssociation>();
		for (var association : stateMachine.getProperties()) {
			if (!association.isModal() && association.getEmv2Path().isEmpty()) {
				associations.put(association.getProperty(), association);
			}
		}
		instantiateStateMachineProperties(stateMachine, associations, annex, component);
	}

	private void instantiateStateMachineProperties(ErrorBehaviorStateMachine stateMachine,
			Map<Property, EMV2PropertyAssociation> associations, EMV2AnnexInstance annex, ComponentInstance component) {
		var stateMachineProperties = EMV2InstanceFactory.eINSTANCE.createStateMachineProperties();
		for (var association : associations.values()) {
			instantiatePropertyAssociation(association, stateMachineProperties.getOwnedPropertyAssociations(),
					associations, component);
		}
		if (!stateMachineProperties.getOwnedPropertyAssociations().isEmpty()) {
			stateMachineProperties.setName(stateMachine.getName());
			stateMachineProperties.setStateMachine(stateMachine);
			annex.setStateMachineProperties(stateMachineProperties);
		}
	}

	private void collectAssociations(Map<Property, EMV2PropertyAssociation> associations,
			NamedElement declarativeHolder, ComponentInstance component, String name,
			EMV2InstanceObject instanceHolder) {
		// Find properties which are defined in the containing state machine.
		var stateMachine = EcoreUtil2.getContainerOfType(declarativeHolder, ErrorBehaviorStateMachine.class);
		if (stateMachine != null) {
			collectAssociations(associations, stateMachine.getProperties(), Collections.emptyList(), name,
					instanceHolder);
		}

		/*
		 * This outer loop finds properties which are defined in the local component as well as contained properties.
		 * The loop starts at the local component and moves up the containment hierarchy so that the top level
		 * component, which has the highest precedence for property lookup, is processed last.
		 */
		var expectedContainmentPath = new ArrayDeque<ComponentInstance>();
		for (var currentComponent = component; currentComponent != null; currentComponent = currentComponent
				.getContainingComponentInstance()) {
			/*
			 * This inner loop finds properties which are defined in the local subclause as well as subclauses inherited
			 * via component extension and realization.
			 */
			for (var subclause : Lists.reverse(EMV2Util.getAllContainingClassifierEMV2Subclauses(currentComponent))) {
				collectAssociations(associations, subclause.getProperties(), expectedContainmentPath, name,
						instanceHolder);
			}
			expectedContainmentPath.addFirst(currentComponent);
		}
	}

	private void collectAssociations(Map<Property, EMV2PropertyAssociation> associations,
			List<EMV2PropertyAssociation> declarativeAssociations, Iterable<ComponentInstance> expectedContainmentPath,
			String name, EMV2InstanceObject instanceHolder) {
		for (var association : declarativeAssociations) {
			var property = association.getProperty();
			if (!association.isModal() && instanceHolder.acceptsProperty(property)) {
				for (var path : association.getEmv2Path()) {
					if (matchesContainmentPath(expectedContainmentPath, path)
							&& buildTargetName(path).equalsIgnoreCase(name)) {
						associations.put(property, association);
					}
				}
			}
		}
	}

	private String buildTargetName(EMV2Path path) {
		var targetName = new StringBuilder();
		for (var target = path.getEmv2Target(); target != null; target = target.getPath()) {
			var namedElement = target.getNamedElement();
			if (namedElement instanceof ErrorPropagation errorPropagation) {
				var featureOrPPRef = errorPropagation.getFeatureorPPRef();
				while (featureOrPPRef.getNext() != null) {
					featureOrPPRef = featureOrPPRef.getNext();
				}
				targetName.append(featureOrPPRef.getFeatureorPP().getName());
			} else if (namedElement != null) {
				targetName.append(namedElement.getName());
			} else {
				targetName.append(target.getEmv2PropagationKind());
				if (target.getErrorType() != null) {
					targetName.append('.');
					targetName.append(target.getErrorType().getName());
				}
			}
			if (target.getPath() != null) {
				targetName.append('.');
			}
		}
		return targetName.toString();
	}

	private static boolean matchesContainmentPath(Iterable<ComponentInstance> expectedContainmentPath, EMV2Path path) {
		var expectedIter = expectedContainmentPath.iterator();
		var currentCPE = path.getContainmentPath();
		while (expectedIter.hasNext() && currentCPE != null) {
			var expectedComponent = expectedIter.next();
			if (expectedComponent.getSubcomponent() != currentCPE.getNamedElement()) {
				return false;
			}
			currentCPE = currentCPE.getPath();
		}
		return !expectedIter.hasNext() && currentCPE == null;
	}

	private void instantiatePropertyAssociation(EMV2PropertyAssociation declarativeAssociation,
			List<PropertyAssociation> instanceAssociations,
			Map<Property, EMV2PropertyAssociation> collectedAssociations, ComponentInstance component) {
		var declarativeValue = declarativeAssociation.getOwnedValues().get(0).getOwnedValue();
		instantiatePropertyExpression(declarativeValue, collectedAssociations, component, 0)
				.ifPresent(expressionInstance -> {
			var propertyInstance = InstanceFactory.eINSTANCE.createPropertyAssociationInstance();
			propertyInstance.setPropertyAssociation(declarativeAssociation);
			propertyInstance.setProperty(declarativeAssociation.getProperty());
			propertyInstance.createOwnedValue().setOwnedValue(expressionInstance);
			instanceAssociations.add(propertyInstance);
		});
	}

	private Optional<PropertyExpression> instantiatePropertyExpression(PropertyExpression propertyExpression,
			Map<Property, EMV2PropertyAssociation> collectedAssociations, ComponentInstance component, int depth) {
		if (depth > 50) {
			return Optional.empty();
		}
		if (propertyExpression instanceof NamedValue namedValue) {
			var abstractNamedValue = namedValue.getNamedValue();
			if (abstractNamedValue instanceof Property property) {
				var nextAssociation = collectedAssociations.get(property);
				if (nextAssociation != null) {
					var nextPropertyExpression = nextAssociation.getOwnedValues().get(0).getOwnedValue();
					return instantiatePropertyExpression(nextPropertyExpression, collectedAssociations, component,
							depth + 1);
				} else {
					return Optional.ofNullable(property.getDefaultValue())
							.flatMap(defaultValue -> instantiatePropertyExpression(defaultValue, collectedAssociations,
									component, depth + 1));
				}
			} else if (abstractNamedValue instanceof PropertyConstant constant) {
				return instantiatePropertyExpression(constant.getConstantValue(), collectedAssociations, component,
						depth + 1);
			} else {
				return Optional.of(EcoreUtil.copy(propertyExpression));
			}
		} else if (propertyExpression instanceof ListValue listValue) {
			var listInstance = Aadl2Factory.eINSTANCE.createListValue();
			for (var element : listValue.getOwnedListElements()) {
				var elementInstance = instantiatePropertyExpression(element, collectedAssociations, component,
						depth + 1);
				if (elementInstance.isPresent()) {
					listInstance.getOwnedListElements().add(elementInstance.get());
				} else {
					return Optional.empty();
				}
			}
			return Optional.of(listInstance);
		} else if (propertyExpression instanceof RecordValue recordValue) {
			var recordInstance = Aadl2Factory.eINSTANCE.createRecordValue();
			for (var field : recordValue.getOwnedFieldValues()) {
				var valueInstance = instantiatePropertyExpression(field.getOwnedValue(), collectedAssociations,
						component, depth + 1);
				if (valueInstance.isPresent()) {
					var fieldInstance = recordInstance.createOwnedFieldValue();
					fieldInstance.setProperty(field.getProperty());
					fieldInstance.setOwnedValue(valueInstance.get());
				} else {
					return Optional.empty();
				}
			}
			return Optional.of(recordInstance);
		} else if (propertyExpression instanceof RangeValue rangeValue) {
			var rangeInstance = Aadl2Factory.eINSTANCE.createRangeValue();
			var minimumInstance = instantiatePropertyExpression(rangeValue.getMinimum(), collectedAssociations,
					component, depth + 1);
			var maximumInstance = instantiatePropertyExpression(rangeValue.getMaximum(), collectedAssociations,
					component, depth + 1);
			if (minimumInstance.isPresent() && maximumInstance.isPresent()) {
				rangeInstance.setMinimum(minimumInstance.get());
				rangeInstance.setMaximum(maximumInstance.get());
			} else {
				return Optional.empty();
			}
			if (rangeValue.getDelta() != null) {
				var deltaInstance = instantiatePropertyExpression(rangeValue.getDelta(), collectedAssociations,
						component, depth + 1);
				if (deltaInstance.isPresent()) {
					rangeInstance.setDelta(deltaInstance.get());
				} else {
					return Optional.empty();
				}
			}
			return Optional.of(rangeInstance);
		} else if (propertyExpression instanceof ReferenceValue referenceValue) {
			try {
				return Optional.ofNullable(referenceValue.instantiate(component));
			} catch (InvalidModelException e) {
				return Optional.empty();
			}
		} else {
			return Optional.of(EcoreUtil.copy(propertyExpression));
		}
	}

	/**
	 * This exception is thrown when attempting to instantiate an element, but a reference to an internal feature was
	 * encountered. This could be a direct reference to an internal feature or a reference to a propagation that refers
	 * to an internal feature. When this is thrown, the element (transition, detection, etc) should not be instantiated.
	 *
	 */
	@SuppressWarnings("serial")
	private static class InternalFeatureEncounteredException extends Exception {
	}
}