InstanceObjectImpl.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.instance.impl;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.util.EObjectContainmentEList;
import org.eclipse.emf.ecore.util.InternalEList;
import org.osate.aadl2.ArrayRange;
import org.osate.aadl2.ContainmentPathElement;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.impl.NamedElementImpl;
import org.osate.aadl2.instance.AnnexInstance;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.EndToEndFlowInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.InstancePackage;
import org.osate.aadl2.instance.ModeTransitionInstance;
import org.osate.aadl2.instance.PropertyAssociationInstance;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.aadl2.properties.InvalidModelException;
import org.osate.aadl2.properties.PropertyAcc;

/**
 * <!-- begin-user-doc --> An implementation of the model object '
 * <em><b>Object</b></em>'. <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * </p>
 * <ul>
 *   <li>{@link org.osate.aadl2.instance.impl.InstanceObjectImpl#getAnnexInstances <em>Annex Instance</em>}</li>
 *   <li>{@link org.osate.aadl2.instance.impl.InstanceObjectImpl#getOwnedPropertyAssociations <em>Owned Property Association</em>}</li>
 * </ul>
 *
 * @generated
 */
public abstract class InstanceObjectImpl extends NamedElementImpl implements InstanceObject {
	/**
	 * The cached value of the '{@link #getAnnexInstances() <em>Annex Instance</em>}' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getAnnexInstances()
	 * @generated
	 * @ordered
	 */
	protected EList<AnnexInstance> annexInstances;

	/*
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 *
	 * @generated
	 */
	protected InstanceObjectImpl() {
		super();
	}

	/*
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 *
	 * @generated
	 */
	protected EClass eStaticClass() {
		return InstancePackage.Literals.INSTANCE_OBJECT;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public EList<AnnexInstance> getAnnexInstances() {
		if (annexInstances == null) {
			annexInstances = new EObjectContainmentEList<>(AnnexInstance.class, this,
					InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE);
		}
		return annexInstances;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public EList<PropertyAssociation> getOwnedPropertyAssociations() {
		if (ownedPropertyAssociations == null) {
			ownedPropertyAssociations = new EObjectContainmentEList<>(
					PropertyAssociationInstance.class, this,
					InstancePackage.INSTANCE_OBJECT__OWNED_PROPERTY_ASSOCIATION);
		}
		return ownedPropertyAssociations;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public PropertyAssociation createOwnedPropertyAssociation() {
		PropertyAssociationInstance newOwnedPropertyAssociation = (PropertyAssociationInstance) create(
				InstancePackage.Literals.PROPERTY_ASSOCIATION_INSTANCE);
		getOwnedPropertyAssociations().add(newOwnedPropertyAssociation);
		return newOwnedPropertyAssociation;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public boolean isSetOwnedPropertyAssociations() {
		return ownedPropertyAssociations != null && !ownedPropertyAssociations.isEmpty();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
		case InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE:
			return ((InternalEList<?>) getAnnexInstances()).basicRemove(otherEnd, msgs);
		case InstancePackage.INSTANCE_OBJECT__OWNED_PROPERTY_ASSOCIATION:
			return ((InternalEList<?>) getOwnedPropertyAssociations()).basicRemove(otherEnd, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
		case InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE:
			return getAnnexInstances();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
		case InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE:
			getAnnexInstances().clear();
			getAnnexInstances().addAll((Collection<? extends AnnexInstance>) newValue);
			return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
		case InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE:
			getAnnexInstances().clear();
			return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
		case InstancePackage.INSTANCE_OBJECT__ANNEX_INSTANCE:
			return annexInstances != null && !annexInstances.isEmpty();
		case InstancePackage.INSTANCE_OBJECT__OWNED_PROPERTY_ASSOCIATION:
			return isSetOwnedPropertyAssociations();
		}
		return super.eIsSet(featureID);
	}

	/**
	 * Get the closest component instance that contains this object.
	 * If the object is a component instance, get its enclosing instance
	 * @return Closest containing component instance
	 */
	public ComponentInstance getContainingComponentInstance() {
		EObject eobj = this;

		do {
			eobj = eobj.eContainer();
		} while (eobj != null && !(eobj instanceof ComponentInstance));

		return (ComponentInstance) eobj;
	}

	/**
	 * Find the enclosing system instance.
	 *
	 * @return The system instance object
	 */
	public SystemInstance getSystemInstance() {
		EObject eobj = this;

		while (eobj != null && !(eobj instanceof SystemInstance)) {
			eobj = eobj.eContainer();
		}
		return (SystemInstance) eobj;
	}

	/*
	 * construct a string path from the systeminstance as root
	 *
	 * @return path as string
	 */
	public String getInstanceObjectPath() {
		if (eContainer == null) {
			return getName();
		}
		final String path = ((InstanceObject) eContainer).getInstanceObjectPath();
		final String localname = getFullName();

		return path.length() == 0 ? localname : path + "." + localname;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.osate.aadl2.instance.InstanceObject#getComponentInstancePath()
	 */
	public String getComponentInstancePath() {
		if (eContainer == null) {
			return "";
		}
		final String path = ((InstanceObject) eContainer).getComponentInstancePath();
		final String localname = getFullName();

		return path.length() == 0 ? localname : path + "." + localname;
	}

	public void getPropertyValueInternal(final Property property, final PropertyAcc pas,
			final boolean fromInstanceSlaveCall, final boolean all) throws InvalidModelException {
		/*
		 * First see if the property is defined locally in the instance. Such
		 * local property associations arise from component property
		 * associations in the declarative spec, from explicit programmatic
		 * setting of the property, or as cached results from earlier property
		 * lookups.
		 */
		if (pas.addLocal(this)) {
			if (!all) {
				return;
			}
		}

		/*
		 * If we don't find the property locally we defer to the declarative
		 * specifications.
		 */
		getPropertyValueFromDeclarativeModel(property, pas);

		/*
		 * If we still don't have a value, and the property is "inherit", get it
		 * from the enclosing component instance. Don't short-circuit this step
		 * because the property caching during instantiation doesn't catch
		 * contained property values that may be attached to an ancestor
		 * instance and that might be inherited by this instance.
		 */
		if (property.isInherit()) {
			final NamedElement ph = (NamedElement) eContainer();
			if (ph != null) {
				ph.getPropertyValueInternal(property, pas, false, all);
			}
		}
	}

	/**
	 * Get the property value from the declarative model elements associated
	 * with the instance object. This implementation works for everything but
	 * ConnectionInstances; this method is overridden in ConnectionInstance to
	 * handle the problem of dealing with all the connections that make up the
	 * semantic connection.
	 *
	 * @param property The property to lookup
	 * @param pva The property value accumulator to use to build the property
	 *            value
	 * @throws InvalidModelException Thrown if the property value cannot be
	 *             retrieved because the model is incomplete or otherwise
	 *             invalid.
	 */
	protected void getPropertyValueFromDeclarativeModel(final Property property, final PropertyAcc pas)
			throws InvalidModelException {
		// apv.pushCurrentComponent(getContainingComponentInstanceOrSelf());
		try {
			final List<? extends NamedElement> compDecls = getInstantiatedObjects();
			// Here we assume compDecls is empty or has only one element
			if (!compDecls.isEmpty()) {
				final NamedElement compDecl = compDecls.get(0);
				if (compDecl == null) {
					return;
				}
				compDecl.getPropertyValueInternal(property, pas, true);
			}
		} finally {
			// apv.popCurrentComponent();
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.osate.aadl2.instance.InstanceObject#getComponentInstance()
	 */
	public ComponentInstance getComponentInstance() {
		EObject current = this;
		while (current != null && !(current instanceof ComponentInstance)) {
			current = current.eContainer();
		}
		return (ComponentInstance) current;
	}

	/**
	 * Return the declarative objects that define this instance. Most of the
	 * time this will return a singleton list. But in the case of semantic
	 * connections, it will return a list of connection objects.
	 *
	 * @return A List of NamedElement objects that should be declarative model
	 *         elements. Returns an empty list if no named declarative object
	 *         exists, such as in the case of {@link ModeTransitionInstance}s.
	 */
	public List<? extends NamedElement> getInstantiatedObjects() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * org.osate.aadl2.impl.NamedElementImpl#acceptsProperty(edu.cmu.
	 * sei.aadl.aadl2.Property)
	 */
	public boolean acceptsProperty(Property property) {
		// OsateDebug.osateDebug ("[InstanceObjectImpl] property=" + property);

		for (NamedElement ne : getInstantiatedObjects()) {
			if (!ne.acceptsProperty(property)) {
				return false;
			}
		}
		return true;
	}

	public Iterable<ConnectionInstance> allEnclosingConnectionInstances() {
		final InstanceObject target = this;
		return () -> new Iterator<>() {
			ConnectionInstance next;
			ComponentInstance head = target instanceof ComponentInstance ? (ComponentInstance) target
					: target.getContainingComponentInstance();
			Iterator<ConnectionInstance> iter = head.getConnectionInstances().iterator();

			private boolean advance() {
				next = null;
				if (iter.hasNext()) {
					next = iter.next();
					return true;
				}
				while (head != null) {
					head = head.getContainingComponentInstance();
					if (head == null) {
						return false;
					} else {
						iter = head.getConnectionInstances().iterator();
						if (iter.hasNext()) {
							next = iter.next();
							return true;
						}
					}
				}
				return false;
			}

			public boolean hasNext() {
				return next != null || advance();
			}

			public ConnectionInstance next() {
				if (next == null && !advance()) {
					throw new NoSuchElementException();
				}
				ConnectionInstance result = next;
				next = null;
				return result;
			}

			public void remove() {
				throw new UnsupportedOperationException();
			}
		};
	}

	public EList<ConnectionInstance> getAllEnclosingConnectionInstances() {
		EList<ConnectionInstance> result = new BasicEList<>();

		for (ConnectionInstance conni : allEnclosingConnectionInstances()) {
			result.add(conni);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.osate.aadl2.instance.InstanceObject#findInstanceObjects(org.eclipse.emf.common.util.EList)
	 */
	public List<InstanceObject> findInstanceObjects(EList<ContainmentPathElement> referencePath) {
		List<InstanceObject> result = new LinkedList<>();

		findInstanceObjectsHelper(referencePath.listIterator(), result);
		return result;
	}

	protected boolean findInstanceObjectsHelper(ListIterator<ContainmentPathElement> pathIter,
			List<InstanceObject> ios) {
		boolean result = false;

		if (!pathIter.hasNext()) {
			ios.add(this);
			result = true;
		} else {
			ContainmentPathElement cpe = pathIter.next();
			NamedElement ne = cpe.getNamedElement();

			for (EObject eo : eContents()) {
				if (!(eo instanceof ConnectionInstance) && eo instanceof InstanceObjectImpl io) {
					// one end to end flow can have many instances with generated names
					String name = io instanceof EndToEndFlowInstance ? io.getInstantiatedObjects().get(0).getName()
							: io.getName();
					// need to compare by name because of possible refinement
					if (ne.getName() != null && ne.getName().equalsIgnoreCase(name)) {
						EList<ArrayRange> ranges = cpe.getArrayRanges();

						if (io.matchesIndex(ranges)) {
							io.findInstanceObjectsHelper(pathIter, ios);
						}
						result = true;
					}
				}
			}
			pathIter.previous();
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.osate.aadl2.instance.InstanceObject#matchesIndex(java.util.List)
	 */
	public boolean matchesIndex(List<ArrayRange> ranges) {
		return ranges.isEmpty();
	}
} // InstanceObjectImpl