InstanceModelUtil.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.properties.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.osate.aadl2.AbstractSubcomponent;
import org.osate.aadl2.BusAccess;
import org.osate.aadl2.BusSubcomponent;
import org.osate.aadl2.ComponentCategory;
import org.osate.aadl2.DeviceSubcomponent;
import org.osate.aadl2.Element;
import org.osate.aadl2.MemorySubcomponent;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.ProcessSubcomponent;
import org.osate.aadl2.ProcessorSubcomponent;
import org.osate.aadl2.SystemSubcomponent;
import org.osate.aadl2.ThreadSubcomponent;
import org.osate.aadl2.VirtualBusSubcomponent;
import org.osate.aadl2.VirtualProcessorSubcomponent;
import org.osate.aadl2.contrib.aadlproject.SupportedDispatchProtocols;
import org.osate.aadl2.contrib.communication.CommunicationProperties;
import org.osate.aadl2.contrib.communication.Timing;
import org.osate.aadl2.contrib.deployment.DeploymentProperties;
import org.osate.aadl2.contrib.thread.ThreadProperties;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.ConnectionInstanceEnd;
import org.osate.aadl2.instance.ConnectionKind;
import org.osate.aadl2.instance.FeatureCategory;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.aadl2.modelsupport.modeltraversal.ForAllElement;
import org.osate.aadl2.modelsupport.util.AadlUtil;

public class InstanceModelUtil {

	/* XXX: Where to put this? */
	private static final <V> boolean propertyEquals(final Function<NamedElement, Optional<V>> f, final NamedElement ne,
			final V value) {
		return f.apply(ne).map(x -> x == value).orElse(false);
	}

	/* XXX: WHere to put this? */
	private static final List<ComponentInstance> getListAsComponentInstance(
			final Function<NamedElement, Optional<List<InstanceObject>>> f, final NamedElement ne) {
		return f.apply(ne).map(v -> {
			final List<ComponentInstance> list = new ArrayList<>(v.size());
			v.forEach(x -> list.add((ComponentInstance) x));
			return list;
		}).orElse(Collections.emptyList());
	}

	/**
	 * true if connection is delayed connection (Timing property on connection)
	 * @param conn
	 * @return
	 */
	public static boolean isDelayedPortConnection(final ConnectionInstance conn) {
		if ((conn == null) ? false : conn.getKind() == ConnectionKind.PORT_CONNECTION) {
			return propertyEquals(CommunicationProperties::getTiming, conn, Timing.DELAYED);
		}
		return false;
	}

	/**
	 * true if a sampled connection (Timing property set no no Timing value (default Sampled)
	 * @param conn
	 */
	public static boolean isSampledPortConnection(final ConnectionInstance conn) {
		if ((conn == null) ? false : conn.getKind() == ConnectionKind.PORT_CONNECTION) {
			return propertyEquals(CommunicationProperties::getTiming, conn, Timing.SAMPLED);
		}
		return false;
	}

	/**
	 * true if connection is immediate connection (Timing property on connection)
	 * @param conn
	 */
	public static boolean isImmediatePortConnection(final ConnectionInstance conn) {
		if ((conn == null) ? false : conn.getKind() == ConnectionKind.PORT_CONNECTION) {
			return propertyEquals(CommunicationProperties::getTiming, conn, Timing.IMMEDIATE);
		}
		return false;
	}

	/**
	 * true if the connection is a port connection
	 * @param conn
	 * @return
	 */
	public static boolean isPortConnection(final ConnectionInstance conn) {
		return (conn == null) ? false : conn.getKind() == ConnectionKind.PORT_CONNECTION;
	}

	/**
	 * true is event data port connection. Determined by destination feature
	 * @param conn
	 * @return
	 */
	public static boolean isEventDataConnection(final ConnectionInstance conn) {
		if (conn == null) {
			return false;
		}
		ConnectionInstanceEnd cie = conn.getDestination();
		if (cie instanceof FeatureInstance) {
			return ((FeatureInstance) cie).getCategory() == FeatureCategory.EVENT_DATA_PORT;
		}
		return false;
	}

	/**
	 * true is event port connection. Determined by destination feature
	 * @param conn
	 * @return
	 */
	public static boolean isEventConnection(final ConnectionInstance conn) {
		if (conn == null) {
			return false;
		}
		ConnectionInstanceEnd cie = conn.getDestination();
		if (cie instanceof FeatureInstance) {
			return ((FeatureInstance) cie).getCategory() == FeatureCategory.EVENT_PORT;
		}
		return false;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category thread or a ThreadSubcomponent
	 * @param thread
	 * @return
	 */
	public static boolean isThread(final NamedElement thread) {
		return ((thread instanceof ComponentInstance)
				&& (((ComponentInstance) thread).getCategory() == ComponentCategory.THREAD))
				|| thread instanceof ThreadSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category device or a DeviceSubcomponent
	 * @param device
	 * @return
	 */
	public static boolean isDevice(final NamedElement device) {
		return ((device instanceof ComponentInstance)
				&& (((ComponentInstance) device).getCategory() == ComponentCategory.DEVICE))
				|| device instanceof DeviceSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category bus or a BusSubcomponent
	 * @param bus
	 * @return
	 */
	public static boolean isBus(final NamedElement bus) {
		return ((bus instanceof ComponentInstance)
				&& (((ComponentInstance) bus).getCategory() == ComponentCategory.BUS))
				|| bus instanceof BusSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category bus or a BusSubcomponent
	 * @param bus
	 * @return
	 */
	public static boolean isBusAccessConnection(final ConnectionInstance conni) {
		return ((conni.getSource() instanceof FeatureInstance
				&& ((FeatureInstance) conni.getSource()).getFeature() instanceof BusAccess)
				|| (conni.getDestination() instanceof FeatureInstance
						&& ((FeatureInstance) conni.getDestination()).getFeature() instanceof BusAccess));

	}

	/**
	 * true of NamedElement is a ComponentInstance of category virtual bus or a VirtualBusSubcomponent
	 * @param vbus
	 * @return
	 */
	public static boolean isVirtualBus(final NamedElement vbus) {
		return ((vbus instanceof ComponentInstance)
				&& (((ComponentInstance) vbus).getCategory() == ComponentCategory.VIRTUAL_BUS))
				|| vbus instanceof VirtualBusSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category processor or a ProcessorSubcomponent
	 * @param vprocessor
	 * @return
	 */
	public static boolean isVirtualProcessor(final NamedElement vprocessor) {
		return ((vprocessor instanceof ComponentInstance)
				&& (((ComponentInstance) vprocessor).getCategory() == ComponentCategory.VIRTUAL_PROCESSOR))
				|| vprocessor instanceof VirtualProcessorSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category virtual processor or a VirtualProcessorSubcomponent
	 * @param processor
	 * @return
	 */
	public static boolean isProcessor(final NamedElement processor) {
		return ((processor instanceof ComponentInstance)
				&& (((ComponentInstance) processor).getCategory() == ComponentCategory.PROCESSOR))
				|| processor instanceof ProcessorSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category memory or a MemorySubcomponent
	 * @param memory
	 * @return
	 */
	public static boolean isMemory(final NamedElement memory) {
		return ((memory instanceof ComponentInstance)
				&& (((ComponentInstance) memory).getCategory() == ComponentCategory.MEMORY))
				|| memory instanceof MemorySubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category system or a SystemSubcomponent
	 * @param system
	 * @return
	 */
	public static boolean isSystem(final NamedElement system) {
		return ((system instanceof ComponentInstance)
				&& (((ComponentInstance) system).getCategory() == ComponentCategory.SYSTEM))
				|| system instanceof SystemSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category system or a SystemSubcomponent
	 * @param system
	 * @return
	 */
	public static boolean isAbstract(final NamedElement system) {
		return ((system instanceof ComponentInstance)
				&& (((ComponentInstance) system).getCategory() == ComponentCategory.ABSTRACT))
				|| system instanceof AbstractSubcomponent;
	}

	/**
	 * true of NamedElement is a ComponentInstance of category process or a ProcessSubcomponent
	 * @param process
	 * @return
	 */
	public static boolean isProcess(final NamedElement process) {
		return ((process instanceof ComponentInstance)
				&& (((ComponentInstance) process).getCategory() == ComponentCategory.PROCESS))
				|| process instanceof ProcessSubcomponent;
	}

	/**
	 * true if component (thread or device) is periodic
	 * @param subcomponent
	 * @return
	 */
	public static boolean isPeriodicComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent, SupportedDispatchProtocols.PERIODIC);
	}

	/**
	 * true if component (thread or device) is aperiodic
	 * @param subcomponent
	 * @return
	 */
	public static boolean isAperiodicComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent,
				SupportedDispatchProtocols.APERIODIC);
	}

	/**
	 * true if component (thread or device) is sporadic
	 * @param subcomponent
	 * @return
	 */
	public static boolean isSporadicComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent, SupportedDispatchProtocols.SPORADIC);
	}

	/**
	 * true if component (thread or device) is timed
	 * @param subcomponent
	 * @return
	 */
	public static boolean isTimedComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent, SupportedDispatchProtocols.TIMED);
	}

	/**
	 * true if component (thread or device) is hybrid
	 * @param subcomponent
	 * @return
	 */
	public static boolean isHybridComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent, SupportedDispatchProtocols.HYBRID);
	}

	/**
	 * true if component (thread or device) is background
	 * @param subcomponent
	 * @return
	 */
	public static boolean isBackgroundComponent(final NamedElement subcomponent) {
		return propertyEquals(ThreadProperties::getDispatchProtocol, subcomponent,
				SupportedDispatchProtocols.BACKGROUND);
	}

	/**
	 * true thread is periodic
	 * @param subcomponent
	 * @return
	 */
	public static boolean isPeriodicThread(final NamedElement thread) {
		return isThread(thread) && isPeriodicComponent(thread);
	}

	/**
	 * true if device is periodic
	 * @param subcomponent
	 * @return
	 */
	public static boolean isPeriodicDevice(final NamedElement device) {
		return (device instanceof ComponentInstance
				&& ((ComponentInstance) device).getCategory().equals(ComponentCategory.DEVICE))
				&& isPeriodicComponent(device);
	}

	/**
	 * true if component instance is directly or indirectly bound to the processor
	 * It could be bound to a virtual processor which in turn is bound to a processor
	 * the component instance can be a thread, process, or a virtual processor instance
	 * @param componentInstance component or VP
	 * @param processor or VP
	 * @return
	 */
	public static boolean isBoundToProcessor(ComponentInstance componentInstance, ComponentInstance processor) {
		/**
		 * We consider that a virtual processor is bound to a processor
		 * if the virtual processor is contained as a subcomponent.
		 */
		if ((componentInstance.getCategory() == ComponentCategory.VIRTUAL_PROCESSOR)
				&& (getEnclosingProcessor(componentInstance) == processor)) {
			return true;
		}

		List<InstanceObject> bindinglist = canHaveActualProcessorBinding(componentInstance)
				? getProcessorBindings(componentInstance)
				: Collections.emptyList();
		for (final InstanceObject boundCompInstance : bindinglist) {
			if (boundCompInstance == processor) {
				return true;
			} else if (isVirtualProcessor(boundCompInstance)) {
				// it is bound to or contained in
				if (isBoundToProcessor((ComponentInstance) boundCompInstance, processor)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * return the list of system that the functional component is directly bound to.
	 *
	 * <p>This method copies a list internally for the sole purpose of changing the type
	 * from {@code List<InstanceObject>} to {@code List<ComponentInstance>}.  Please use
	 * {@link #getFunctionBindings} and change the call site to handle {@code InstanceObject}
	 * to avoid this.
	 *
	 * @param io
	 * @return list of system component instances
	 * @deprecated To be removed in 2.10.0.  Please use {@link #getFunctionBindings}.
	 */
	@Deprecated
	public static List<ComponentInstance> getFunctionBinding(final ComponentInstance io) {
		return getListAsComponentInstance(DeploymentProperties::getActualFunctionBinding, io);
	}

	/**
	 * return the list of system that the functional component is directly bound to.
	 * @param io
	 * @return list of system component instances
	 * @since 3.1
	 */
	public static List<InstanceObject> getFunctionBindings(final ComponentInstance io) {
		return DeploymentProperties.getActualFunctionBinding(io).orElse(Collections.emptyList());
	}

	/**
	 * return the processor or virtual processor that the component is directly bound to
	 *
	 * <p>This method recopies a list internally for the sole purpose of changing the type
	 * from {@code List<InstanceObject>} to {@code List<ComponentInstance>}.  Please use
	 * {@link #getFunctionBindings} and change the call site to handle {@code InstanceObject}
	 * to avoid this.
	 *
	 * @param io
	 * @return
	 * @deprecated To be removed in 2.10.0.  Please use {@link #getMemoryBindings}.
	 */
	@Deprecated
	public static List<ComponentInstance> getMemoryBinding(final ComponentInstance io) {
		return getListAsComponentInstance(DeploymentProperties::getActualMemoryBinding, io);
	}

	/**
	 * return the processor or virtual processor that the component is directly bound to
	 *
	 * @param io
	 * @return
	 * @since 3.1
	 */
	public static List<InstanceObject> getMemoryBindings(final ComponentInstance io) {
		return DeploymentProperties.getActualMemoryBinding(io).orElse(Collections.emptyList());
	}

	/**
	 * return the processor or virtual processor that the component is directly bound to
	 *
	 * <p>This method recopies a list internally for the sole purpose of changing the type
	 * from {@code List<InstanceObject>} to {@code List<ComponentInstance>}.  Please use
	 * {@link #getFunctionBindings} and change the call site to handle {@code InstanceObject}
	 * to avoid this.
	 *
	 * @param io
	 * @return
	 * @deprecated To be removed in 2.10.0.  Please use {@link #getProcessorBindings}.
	 */
	@Deprecated
	public static List<ComponentInstance> getProcessorBinding(final ComponentInstance io) {
		return getListAsComponentInstance(DeploymentProperties::getActualProcessorBinding, io);
	}

	/**
	 * return the processor or virtual processor that the component is directly bound to
	 *
	 * @param io
	 * @return
	 * @since 3.1
	 */
	public static List<InstanceObject> getProcessorBindings(final ComponentInstance io) {
		return DeploymentProperties.getActualProcessorBinding(io).orElse(Collections.emptyList());
	}

	/**
	 * return the hardware component of the connection instance end.
	 * If it is a hardware component or device return it.
	 * If it is a software component, return the processor it is bound to.
	 * If it is a component instance (BUS), return the bus
	 * If it is a DATA, SUBPROGRAM, or SUBPROGRAM GROUP component instance, then return the memory it is bound to.
	 * @param cie
	 * @return hw component instance
	 */
	public static ComponentInstance getHardwareComponent(ConnectionInstanceEnd cie) {
		ComponentInstance swci = null;
		if (cie instanceof FeatureInstance) {
			FeatureInstance fi = (FeatureInstance) cie;
			swci = fi.getContainingComponentInstance();
		} else if (cie instanceof ComponentInstance) {
			swci = (ComponentInstance) cie;
		}
		if (isDevice(swci) || isProcessor(swci) || isBus(swci) || isMemory(swci)) {
			return swci;
		}
		return getBoundPhysicalProcessor(swci);
	}

	private static boolean canHaveActualProcessorBinding(final ComponentInstance ci) {
		switch (ci.getCategory()) {
		case ABSTRACT:
		case THREAD:
		case THREAD_GROUP:
		case PROCESS:
		case SYSTEM:
		case VIRTUAL_PROCESSOR:
		case DEVICE:
			return true;
		default:
			return false;

		}
	}

	/**
	 * processor instance is directly or indirectly bound to the processor
	 * It could be bound to a virtual processor which in turn is bound to a processor
	 * the component instance can be a thread, process, or a virtual processor instance
	 * @param componentInstance
	 * @return processor instance
	 */
	public static ComponentInstance getBoundPhysicalProcessor(ComponentInstance componentInstance) {
		final List<InstanceObject> cil = canHaveActualProcessorBinding(componentInstance)
				? getProcessorBindings(componentInstance)
				: Collections.emptyList();
		if (cil.isEmpty()) {
			return null;
		}
		for (final InstanceObject ci : cil) {
			if (isProcessor(ci) || isSystem(ci) || isAbstract(ci)) {
				return (ComponentInstance) ci;
			}
		}
		for (final InstanceObject io : cil) {
			final ComponentInstance boundCompInstance = (ComponentInstance) io;
			if (isVirtualProcessor(boundCompInstance)) {
				// it is bound to or contained in
				ComponentInstance res = getBoundPhysicalProcessor(boundCompInstance);
				if (res == null) {
					// check whether VP is a subcomponent of a processor
					res = getEnclosingProcessor(boundCompInstance);
				}
				return res;
			}
		}
		return null;
	}

	public static ComponentInstance getEnclosingProcessor(ComponentInstance vpi) {
		ComponentInstance ci = vpi.getContainingComponentInstance();
		while (ci != null) {
			if (ci.getCategory().equals(ComponentCategory.PROCESSOR)) {
				return ci;
			}
			ci = ci.getContainingComponentInstance();
		}
		return null;
	}

	/**
	 * processor instance is directly or indirectly bound to the processor
	 * It could be bound to a virtual processor which in turn is bound to a processor
	 * the component instance can be a thread, process, or a virtual processor instance
	 * @param componentInstance
	 * @return processor instance
	 */
	public static Collection<ComponentInstance> getBoundPhysicalProcessors(ComponentInstance componentInstance) {
		final Collection<ComponentInstance> actualProcs = new HashSet<ComponentInstance>();
		if (canHaveActualProcessorBinding(componentInstance)) {
			addBoundProcessors(componentInstance, actualProcs);
		}
		return actualProcs;
	}

	protected static void addBoundProcessors(ComponentInstance componentInstance,
			Collection<ComponentInstance> result) {
		final List<InstanceObject> bindinglist = getProcessorBindings(componentInstance);
		for (InstanceObject io : bindinglist) {
			final ComponentInstance boundCompInstance = (ComponentInstance) io;
			if (isVirtualProcessor(boundCompInstance)) {
				// it is bound to or contained in
				addBoundProcessors(boundCompInstance, result);
			} else if (isProcessor(boundCompInstance) || isSystem(boundCompInstance) || isAbstract(boundCompInstance)) {
				result.add(boundCompInstance);
			}
			// we should not have another else
		}
		if (isVirtualProcessor(componentInstance)) {
			// check whether VP is a subcomponent of a processor
			ComponentInstance res = getEnclosingProcessor(componentInstance);
			if (res != null) {
				result.add(res);
			}

		}
	}

	/**
	 * virtual processor instances directly bound to by component
	 * @param componentInstance
	 * @return virtual processor instance
	 */
	public static Collection<ComponentInstance> getBoundVirtualProcessors(ComponentInstance componentInstance) {
		final Collection<ComponentInstance> actualProcs = new HashSet<ComponentInstance>();
		if (canHaveActualProcessorBinding(componentInstance)) {
			addBoundVirtualProcessors(componentInstance, actualProcs, false);
		}
		return actualProcs;
	}

	/**
	 * virtual processor instances directly or indirectly bound to by component
	 * @param componentInstance
	 * @return virtual processor instance
	 */
	public static Collection<ComponentInstance> getAllBoundVirtualProcessors(ComponentInstance componentInstance) {
		final Collection<ComponentInstance> actualProcs = new ArrayList<ComponentInstance>();
		if (canHaveActualProcessorBinding(componentInstance)) {
			addBoundVirtualProcessors(componentInstance, actualProcs, true);
		}
		return actualProcs;
	}

	protected static void addBoundVirtualProcessors(ComponentInstance componentInstance,
			Collection<ComponentInstance> result, boolean doAll) {
		final Collection<InstanceObject> bindinglist = getProcessorBindings(componentInstance);
		for (final InstanceObject io : bindinglist) {
			final ComponentInstance boundCompInstance = (ComponentInstance) io;
			if (isVirtualProcessor(boundCompInstance)) {
				result.add(boundCompInstance);
				if (doAll) {
					addBoundVirtualProcessors(boundCompInstance, result, doAll);
				}
			}
		}
	}

	/**
	 * @deprecated
	 */
	@Deprecated(since = "3.2.1")
	public static void clearCache() {
		// cache has been removed
	}

	/**
	 * get all top level SW components bound to the given processor or VP component
	 * The list contains only the top component if a component and its children are bound
	 * to the same processor.
	 * @param procorVP
	 * @return
	 */
	public static EList<ComponentInstance> getBoundSWComponents(final ComponentInstance associatedObject) {
		EList<Element> boundComponents = null;
		SystemInstance root = associatedObject.getSystemInstance();

		if ((associatedObject.getCategory() == ComponentCategory.PROCESSOR)
				|| (associatedObject.getCategory() == ComponentCategory.VIRTUAL_PROCESSOR)) {
			boundComponents = new ForAllElement() {
				@Override
				protected boolean suchThat(Element obj) {
					ComponentInstance ci = (ComponentInstance) obj;
					ComponentCategory cat = ci.getCategory();
					return ((cat == ComponentCategory.THREAD || cat == ComponentCategory.THREAD_GROUP
							|| cat == ComponentCategory.PROCESS || cat == ComponentCategory.SYSTEM)
							&& InstanceModelUtil.isBoundToProcessor((ComponentInstance) obj, associatedObject));
				}
			}.processPreOrderComponentInstance(root);
		}

		if (associatedObject.getCategory() == ComponentCategory.MEMORY) {
			boundComponents = new ForAllElement() {
				@Override
				protected boolean suchThat(Element obj) {
					return DeploymentProperties.getActualMemoryBinding((ComponentInstance) obj)
							.map(boundMemoryList -> !boundMemoryList.isEmpty()
									&& boundMemoryList.get(0) == associatedObject)
							.orElse(false);
				}
				// process bottom up so we can check whether children had budgets
			}.processPostOrderComponentInstance(root);
		}

		EList<ComponentInstance> topobjects = new BasicEList<ComponentInstance>();
		for (Object componentInstance : boundComponents) {
			addAsRoot(topobjects, (ComponentInstance) componentInstance);
		}
		return topobjects;
	}

	/**
	 * get all SW components bound to the given processor or VP component
	 * This includes the children of a component that is bound as the binding property is inherited.
	 * @param procorVP
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static EList<ComponentInstance> getAllBoundSWComponents(final ComponentInstance procorVP) {
		SystemInstance root = procorVP.getSystemInstance();
		EList boundComponents = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				ComponentInstance ci = (ComponentInstance) obj;
				ComponentCategory cat = ci.getCategory();
				return ((cat == ComponentCategory.THREAD || cat == ComponentCategory.THREAD_GROUP
						|| cat == ComponentCategory.PROCESS || cat == ComponentCategory.SYSTEM)
						&& InstanceModelUtil.isBoundToProcessor((ComponentInstance) obj, procorVP));
			}
		}.processPreOrderComponentInstance(root);
		return boundComponents;
	}

	/**
	 * get all threads bound to the given component
	 * @param procorVP
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static EList<ComponentInstance> getBoundThreads(final ComponentInstance procorVP) {
		SystemInstance root = procorVP.getSystemInstance();
		EList boundComponents = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				ComponentInstance ci = (ComponentInstance) obj;
				ComponentCategory cat = ci.getCategory();
				return ((cat == ComponentCategory.THREAD)
						&& InstanceModelUtil.isBoundToProcessor((ComponentInstance) obj, procorVP));
			}
		}.processPreOrderComponentInstance(root);
		return boundComponents;
	}

	/**
	 * get all processes bound to the given component
	 * @param procorVP
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static EList<ComponentInstance> getBoundProcesses(final ComponentInstance procorVP) {
		SystemInstance root = procorVP.getSystemInstance();
		EList boundComponents = new ForAllElement() {
			@Override
			protected boolean suchThat(Element obj) {
				ComponentInstance ci = (ComponentInstance) obj;
				ComponentCategory cat = ci.getCategory();
				return ((cat == ComponentCategory.PROCESS)
						&& InstanceModelUtil.isBoundToProcessor((ComponentInstance) obj, procorVP));
			}
		}.processPreOrderComponentInstance(root);
		return boundComponents;
	}

	public static void addAsRoot(EList<ComponentInstance> blist, ComponentInstance ci) {
		BasicEList<ComponentInstance> removeme = new BasicEList<ComponentInstance>();
		for (ComponentInstance bi : blist) {
			if (AadlUtil.containedIn(ci, bi)) {
				return;
			}
			if (AadlUtil.containedIn(bi, ci)) {
				removeme.add(bi);
			}
		}
		if (!removeme.isEmpty()) {
			blist.removeAll(removeme);
		}
		blist.add(ci);
	}

	/**
	 * true if connection or virtual bus instance is directly or indirectly bound to the bus
	 * It could be bound to a virtual bus which in turn is bound to a bus
	 * the connectionInstance can be a connection or a virtual bus instance
	 * @param boundObject
	 * @param bus
	 * @return
	 */
	public static boolean isBoundToBus(InstanceObject boundObject, ComponentInstance bus) {
		List<InstanceObject> bindinglist = getConnectionBindings(boundObject);
		for (InstanceObject boundCompInstance : bindinglist) {
			if (isVirtualProcessor(boundCompInstance)) {
				// it is bound to or contained in
				if (isBoundToBus(boundCompInstance, bus)) {
					return true;
				}
			} else if (boundCompInstance == bus) {
				return true;
			}
		}
		return false;
	}

	/**
	 * true if connection or virtual bus instance has connection binding
	 * @param boundObject
	 * @return
	 */
	public static boolean hasBusBinding(InstanceObject boundObject) {
		return !getConnectionBindings(boundObject).isEmpty();
	}

	/**
	 * return set of components the specified instance object (connection or virtual bus) is bound to.
	 * Takes into account virtual buses contained in buses or virtual buses
	 *
	 * <p>This method copies a list internally for the sole purpose of changing the type
	 * from {@code List<InstanceObject>} to {@code List<ComponentInstance>}.  Please use
	 * {@link #getConnectionBindings} and change the call site to handle {@code InstanceObject}
	 * to avoid this.
	 *
	 * @param io
	 * @return
	 *
	 * @deprecated To be removed in 2.10.0.  Use {@link #getConnectionBindings(InstanceObject)
	 */
	@Deprecated
	public static List<ComponentInstance> getConnectionBinding(final InstanceObject io) {
		final List<ComponentInstance> bindinglist = getListAsComponentInstance(
				DeploymentProperties::getActualConnectionBinding, io);
		/**
		 * If we have a virtual bus, we consider that it is bound to
		 * its containing bus. Semantically, we thus consider
		 * that all contained virtual bus are bound to the enclosing
		 * physical bus or VB. Then, we add it in the list.
		 */
		if (bindinglist.isEmpty() && io instanceof ComponentInstance
				&& ((ComponentInstance) io).getCategory() == ComponentCategory.VIRTUAL_BUS) {
			ComponentInstance parent = io.getContainingComponentInstance();
			if (parent.getCategory() == ComponentCategory.BUS
					|| parent.getCategory() == ComponentCategory.VIRTUAL_BUS) {
				return Collections.singletonList(parent);
			}
		}
		return bindinglist;
	}

	/**
	 * return set of components the specified instance object (connection or virtual bus) is bound to.
	 * Takes into account virtual buses contained in buses or virtual buses
	 * @param io
	 * @return
	 * @since 3.1
	 */
	public static List<InstanceObject> getConnectionBindings(final InstanceObject io) {
		final List<InstanceObject> bindinglist = DeploymentProperties.getActualConnectionBinding(io)
				.orElse(Collections.emptyList());
		/**
		 * If we have a virtual bus, we consider that it is bound to
		 * its containing bus. Semantically, we thus consider
		 * that all contained virtual bus are bound to the enclosing
		 * physical bus or VB. Then, we add it in the list.
		 */
		if (bindinglist.isEmpty() && io instanceof ComponentInstance
				&& ((ComponentInstance) io).getCategory() == ComponentCategory.VIRTUAL_BUS) {
			ComponentInstance parent = io.getContainingComponentInstance();
			if (parent.getCategory() == ComponentCategory.BUS
					|| parent.getCategory() == ComponentCategory.VIRTUAL_BUS) {
				return Collections.singletonList(parent);
			}
		}
		return bindinglist;
	}

	/**
	 * HW instances that connection instance is directly or indirectly bound to
	 * It could be bound to a virtual bus which in turn is bound to a bus
	 * or a device, processor, memory
	 * @param connectionInstance
	 * @return list of hardware components involved in connection binding
	 */
	public static List<ComponentInstance> getPhysicalConnectionBinding(ConnectionInstance connectionInstance) {
		final List<ComponentInstance> actualHW = new ArrayList<ComponentInstance>();
		addPhysicalConnectionBinding(connectionInstance, actualHW);
		return actualHW;
	}

	protected static void addPhysicalConnectionBinding(InstanceObject VBorConni, Collection<ComponentInstance> result) {
		List<InstanceObject> bindinglist = getConnectionBindings(VBorConni);
		for (InstanceObject boundCompInstance : bindinglist) {
			if (isVirtualBus(boundCompInstance)) {
				// it is bound to or contained in
				addPhysicalConnectionBinding(boundCompInstance, result);
			} else {
				result.add((ComponentInstance) boundCompInstance);
			}
		}
	}

	/**
	 * get all connections bound to the given bus or virtual bus
	 * @param busorVB
	 * @return
	 */
	public static EList<ConnectionInstance> getBoundConnections(final ComponentInstance busorVB) {
		EList<ConnectionInstance> result;
		EList<ConnectionInstance> connections;
		SystemInstance root;

		result = new BasicEList<ConnectionInstance>();
		root = busorVB.getSystemInstance();
		connections = root.getAllConnectionInstances();
		for (ConnectionInstance connectionInstance : connections) {

			if (InstanceModelUtil.isBoundToBus(connectionInstance, busorVB) ||
			// we derived a bus connection from the connection end bindings
					(!InstanceModelUtil.hasBusBinding(connectionInstance)
							&& InstanceModelUtil.connectedByBus(connectionInstance, busorVB))) {
				result.add(connectionInstance);
			}

		}
		return result;
	}

	/**
	 * get all virtual buses bound to the given bus or virtual bus
	 * @param busorVB
	 * @return
	 * @since 1.2
	 */
	public static EList<ComponentInstance> getBoundVirtualBuses(final ComponentInstance busorVB) {
		final SystemInstance root = busorVB.getSystemInstance();
		final EList<ComponentInstance> virtualBuses = root.getAllComponentInstances(ComponentCategory.VIRTUAL_BUS);
		final EList<ComponentInstance> result = new BasicEList<ComponentInstance>();
		for (ComponentInstance ci : virtualBuses) {
			if (InstanceModelUtil.isBoundToBus(ci, busorVB)) {
				result.add(ci);
			}
		}
		return result;
	}

	/**
	 * Get the component that is connected at the source side of the connection.
	 * @param connectionInstance - the connection to process
	 * @return - the component that is the source
	 */
	public static ComponentInstance getRelatedComponentSource(ConnectionInstance connectionInstance) {
		ConnectionInstanceEnd sourceEnd;
		ComponentInstance source;

		source = null;
		sourceEnd = connectionInstance.getSource();

		if (!(sourceEnd instanceof ComponentInstance)) {
			source = sourceEnd.getContainingComponentInstance();
		} else {
			source = (ComponentInstance) sourceEnd;
		}
		return source;
	}

	/**
	 * Get the component that is connected at the destination side of the connection.
	 * @param connectionInstance - the connection to be processed
	 * @return - the component that is the connection destination (and not its feature)
	 */
	public static ComponentInstance getRelatedComponentDestination(ConnectionInstance connectionInstance) {
		ConnectionInstanceEnd destinationEnd;
		ComponentInstance destination = null;
		destinationEnd = connectionInstance.getDestination();

		if (!(destinationEnd instanceof ComponentInstance)) {
			destination = destinationEnd.getContainingComponentInstance();
		} else {
			destination = (ComponentInstance) destinationEnd;
		}
		return destination;
	}

	/**
	 * true if the processor of the port connection source is connected to the
	 * specified bus
	 *
	 * @param pci
	 * @param curBus
	 * @return
	 */
	public static boolean connectedByBus(ConnectionInstance pci, ComponentInstance curBus) {
		ComponentInstance srcHW = getHardwareComponent(pci.getSource());
		ComponentInstance dstHW = getHardwareComponent(pci.getDestination());
		if (srcHW == null || dstHW == null || srcHW == dstHW) {
			return false;
		}

		return connectedToBus(srcHW, curBus) && connectedToBus(dstHW, curBus);
	}

	/**
	 * Get the bus for a connection. If the bus is not found, we try
	 * to get the bus by inspecting the component bound (processor)
	 * to each part of the connection (thread/process/device).
	 *
	 * If the connection is directly bound to a bus through the property
	 * mechanism, we automatically return it.
	 *
	 * @param connectionInstance - the connection instance
	 * @return the bus bound to the connection.
	 */
	public static List<ComponentInstance> deriveBoundBuses(ConnectionInstance connectionInstance) {
		ComponentInstance srcHW = getHardwareComponent(connectionInstance.getSource());
		ComponentInstance dstHW = getHardwareComponent(connectionInstance.getDestination());
		if (isBusAccessConnection(connectionInstance)) {
			return Collections.emptyList();
		}
		return connectedByBus(srcHW, dstHW);
	}

	/**
	 * returns list of buses connecting to HW components. Can be empty list (if
	 * same component), or null (no connection).
	 *
	 * @param source HW component
	 * @param destination HW component
	 * @return list of buses involved in the physical connection
	 */
	public static List<ComponentInstance> connectedByBus(ComponentInstance srcHW, ComponentInstance dstHW) {
		List<ComponentInstance> visitedBuses = new ArrayList<ComponentInstance>();
		return doConnectedByBus(srcHW, dstHW, visitedBuses);
	}

	/**
	 * returns list of buses connecting to HW components. Can be empty list (if
	 * same component), or null (no connection).
	 *
	 * @param source HW component
	 * @param destination HW component
	 * @return list of buses involved in the physical connection
	 */
	protected static List<ComponentInstance> doConnectedByBus(ComponentInstance srcHW, ComponentInstance dstHW,
			List<ComponentInstance> visitedBuses) {
		if (srcHW == null || dstHW == null || srcHW == dstHW) {
			return visitedBuses;
		}
		for (FeatureInstance fi : srcHW.getFeatureInstances()) {
			if (fi.getCategory() == FeatureCategory.BUS_ACCESS) {
				for (ConnectionInstance aci : fi.getDstConnectionInstances()) {
					ConnectionInstanceEnd src = aci.getSource();
					ComponentInstance curBus = src instanceof ComponentInstance ? (ComponentInstance) src
							: ((FeatureInstance) src).getComponentInstance();
					if (!visitedBuses.contains(curBus)) {
						if (connectedToBus(dstHW, curBus)) {
							List<ComponentInstance> res = new ArrayList<ComponentInstance>();
							res.add(curBus);
							return res;
						} else {
							// first check if there is a bus this bus is connected to
							visitedBuses.add(curBus);
							List<ComponentInstance> res = doConnectedByBus(curBus, dstHW, visitedBuses);
							if (res != null) {
								res.add(0, curBus);
								return res;
							} else {
								// check for buses that are connected to this bus
								for (ConnectionInstance srcaci : curBus.getSrcConnectionInstances()) {
									ComponentInstance bi = srcaci.getDestination().getContainingComponentInstance();
									if (bi.getCategory() == ComponentCategory.BUS) {
										if (connectedToBus(dstHW, bi)) {
											res = new BasicEList<ComponentInstance>();
											res.add(bi);
											return res;
										} else {
											visitedBuses.add(bi);
											res = doConnectedByBus(bi, dstHW, visitedBuses);
											if (res != null) {
												res.add(0, curBus);
												return res;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return visitedBuses;
	}

	/**
	 * list of buses the hardware component is directly connected to
	 *
	 * @param HWcomp ComponentInstance hardware component
	 * @return list of buses
	 */
	public static EList<ComponentInstance> getConnectedBuses(ComponentInstance HWcomp) {
		EList<ComponentInstance> result = new BasicEList<ComponentInstance>();
		EList<ConnectionInstance> acl = HWcomp.getSrcConnectionInstances();
		for (ConnectionInstance srcaci : acl) {
			ComponentInstance res = srcaci.getDestination().getComponentInstance();
			if (res.getCategory() == ComponentCategory.BUS) {
				result.add(res);
			}
		}
		// we have to check the connection the other way around. The bus be the source or destination
		acl = HWcomp.getDstConnectionInstances();
		for (ConnectionInstance dstaci : acl) {
			ComponentInstance res = dstaci.getSource().getComponentInstance();
			if (res.getCategory() == ComponentCategory.BUS) {
				result.add(res);
			}
		}
		return result;
	}

	/**
	* is hardware component connected (directly) to the given bus
	*
	* @param HWcomp ComponentInstance hardware component
	* @param bus ComponentInstance bus component
	* @return true if they are connected by bus access connection
	*/
	public static boolean connectedToBus(ComponentInstance HWcomp, ComponentInstance bus) {
		EList<ConnectionInstance> acl = bus.getSrcConnectionInstances();
		for (Iterator<ConnectionInstance> it = acl.iterator(); it.hasNext();) {
			ConnectionInstance srcaci = it.next();
			if (srcaci.getDestination().getContainingComponentInstance() == HWcomp) {
				return true;
			}
		}
		// we have to check the connection the other way around. The bus be the source or destination
		acl = bus.getDstConnectionInstances();
		for (Iterator<ConnectionInstance> it = acl.iterator(); it.hasNext();) {
			ConnectionInstance dstaci = it.next();
			if (dstaci.getSource().getContainingComponentInstance() == HWcomp) {
				return true;
			}
		}
		return false;
	}

	/**
	 * is hardware component connected (directly) to the given bus, incl. bus to
	 * bus
	 *
	 * @param HWcomp ComponentInstance hardware component
	 * @param bus ComponentInstance bus component
	 * @return access connection instance if they are connected by bus access
	 *         connection
	 */
	public static ConnectionInstance getBusAccessConnection(ComponentInstance HWcomp, ComponentInstance bus) {
		for (ConnectionInstance srcaci : bus.getSrcConnectionInstances()) {
			if (srcaci.getDestination().getContainingComponentInstance() == HWcomp) {
				return srcaci;
			}
		}
		// check the other way
		for (ConnectionInstance srcaci : HWcomp.getSrcConnectionInstances()) {
			if (srcaci.getDestination().getContainingComponentInstance() == bus) {
				return srcaci;
			}
		}
		return null;
	}

}