InstanceCrossReferenceSerializer.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.textual.serializer;

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

import java.util.stream.Collectors;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic.Acceptor;
import org.eclipse.xtext.serializer.tokens.CrossReferenceSerializer;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentClassifier;
import org.osate.aadl2.EnumerationLiteral;
import org.osate.aadl2.ModeTransition;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyConstant;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.InstancePackage;
import org.osate.aadl2.instance.SystemInstance;

public class InstanceCrossReferenceSerializer extends CrossReferenceSerializer {
	private static final InstancePackage INSTANCE_PACKAGE = InstancePackage.eINSTANCE;
	private static final Aadl2Package AADL2_PACKAGE = Aadl2Package.eINSTANCE;

	@Override
	public String serializeCrossRef(EObject semanticObject, CrossReference crossref, EObject target, INode node,
			Acceptor errors) {
		var reference = GrammarUtil.getReference(crossref, semanticObject.eClass());
		if (INSTANCE_PACKAGE.getSystemInstance_ComponentImplementation().equals(reference)
				|| INSTANCE_PACKAGE.getComponentInstance_Classifier().equals(reference)
				|| AADL2_PACKAGE.getPropertyAssociation_Property().equals(reference)
				|| AADL2_PACKAGE.getClassifierValue_Classifier().equals(reference)) {
			return ((NamedElement) target).getQualifiedName();
		} else if (INSTANCE_PACKAGE.getFeatureInstance_Feature().equals(reference)
				|| INSTANCE_PACKAGE.getComponentInstance_Subcomponent().equals(reference)
				|| INSTANCE_PACKAGE.getConnectionReference_Connection().equals(reference)
				|| INSTANCE_PACKAGE.getFlowSpecificationInstance_FlowSpecification().equals(reference)
				|| INSTANCE_PACKAGE.getEndToEndFlowInstance_EndToEndFlow().equals(reference)
				|| INSTANCE_PACKAGE.getModeInstance_Mode().equals(reference)
				|| AADL2_PACKAGE.getContainmentPathElement_NamedElement().equals(reference)) {
			return getContainerOfType(target, Classifier.class).getQualifiedName() + ':'
					+ ((NamedElement) target).getName();
		} else if (INSTANCE_PACKAGE.getComponentInstance_InMode().equals(reference)
				|| INSTANCE_PACKAGE.getConnectionInstance_InSystemOperationMode().equals(reference)
				|| INSTANCE_PACKAGE.getFlowSpecificationInstance_InMode().equals(reference)
				|| INSTANCE_PACKAGE.getEndToEndFlowInstance_InSystemOperationMode().equals(reference)
				|| INSTANCE_PACKAGE.getModeInstance_Parent().equals(reference)
				|| INSTANCE_PACKAGE.getModeTransitionInstance_Source().equals(reference)
				|| INSTANCE_PACKAGE.getModeTransitionInstance_Destination().equals(reference)
				|| AADL2_PACKAGE.getNumberValue_Unit().equals(reference)
				|| AADL2_PACKAGE.getBasicPropertyAssociation_Property().equals(reference)
				|| AADL2_PACKAGE.getModalElement_InMode().equals(reference)) {
			return ((NamedElement) target).getName();
		} else if (INSTANCE_PACKAGE.getConnectionInstance_Source().equals(reference)
				|| INSTANCE_PACKAGE.getConnectionInstance_Destination().equals(reference)
				|| INSTANCE_PACKAGE.getConnectionReference_Source().equals(reference)
				|| INSTANCE_PACKAGE.getConnectionReference_Destination().equals(reference)
				|| INSTANCE_PACKAGE.getFlowSpecificationInstance_Source().equals(reference)
				|| INSTANCE_PACKAGE.getFlowSpecificationInstance_Destination().equals(reference)
				|| INSTANCE_PACKAGE.getEndToEndFlowInstance_FlowElement().equals(reference)
				|| INSTANCE_PACKAGE.getSystemOperationMode_CurrentMode().equals(reference)
				|| INSTANCE_PACKAGE.getModeTransitionInstance_Trigger().equals(reference)) {
			return serializeChainedInstanceReference(target,
					getContainerOfType(semanticObject, ComponentInstance.class));
		} else if (INSTANCE_PACKAGE.getConnectionInstance_InModeTransition().equals(reference)
				|| INSTANCE_PACKAGE.getFlowSpecificationInstance_InModeTransition().equals(reference)) {
			return "transition#"
					+ getContainerOfType(target, ComponentInstance.class).getModeTransitionInstances().indexOf(target);
		} else if (INSTANCE_PACKAGE.getConnectionReference_Context().equals(reference)) {
			var rootComponent = getContainerOfType(semanticObject, ComponentInstance.class);
			if (rootComponent.equals(target)) {
				return "parent";
			} else {
				return serializeChainedInstanceReference(target, rootComponent);
			}
		} else if (INSTANCE_PACKAGE.getModeTransitionInstance_ModeTransition().equals(reference)) {
			var classifier = getContainerOfType(target, ComponentClassifier.class);
			var transition = (ModeTransition) target;
			var transitionName = transition.getName();
			if (transitionName == null) {
				transitionName = "transition#" + classifier.getOwnedModeTransitions().indexOf(transition);
			}
			return classifier.getQualifiedName() + ':' + transitionName;
		} else if (INSTANCE_PACKAGE.getPropertyAssociationInstance_PropertyAssociation().equals(reference)) {
			var classifier = getContainerOfType(target, Classifier.class);
			var builder = new StringBuilder(classifier.getQualifiedName());
			builder.append(':');
			if (target.eContainer().equals(classifier)) {
				builder.append("property#");
				builder.append(classifier.getOwnedPropertyAssociations().indexOf(target));
			} else {
				var parent = (NamedElement) target.eContainer();
				if (parent instanceof ModeTransition && parent.getName() == null) {
					builder.append("transition#");
					builder.append(((ComponentClassifier) classifier).getOwnedModeTransitions().indexOf(parent));
				} else {
					builder.append(parent.getName());
				}
				builder.append(":property#");
				builder.append(parent.getOwnedPropertyAssociations().indexOf(target));
			}
			return builder.toString();
		} else if (INSTANCE_PACKAGE.getInstanceReferenceValue_ReferencedInstanceObject().equals(reference)) {
			return serializeChainedInstanceReference(target, getContainerOfType(semanticObject, SystemInstance.class));
		} else if (AADL2_PACKAGE.getNamedValue_NamedValue().equals(reference)) {
			if (target instanceof EnumerationLiteral enumLiteral) {
				return enumLiteral.getName();
			} else if (target instanceof Property property) {
				return property.getQualifiedName();
			} else if (target instanceof PropertyConstant constant) {
				return constant.getQualifiedName();
			} else {
				throw new RuntimeException("Unexpected target: " + target);
			}
		} else {
			return super.serializeCrossRef(semanticObject, crossref, target, node, errors);
		}
	}

	private static String serializeChainedInstanceReference(EObject target, ComponentInstance rootComponent) {
		var result = new StringBuilder(serializeChainSegment(target));
		var currentContainer = target.eContainer();
		while (currentContainer != rootComponent) {
			result.insert(0, ".");
			result.insert(0, serializeChainSegment(currentContainer));
			currentContainer = currentContainer.eContainer();
		}
		return result.toString();
	}

	private static String serializeChainSegment(EObject segment) {
		if (segment instanceof ComponentInstance component) {
			return component.getName()
					+ component.getIndices().stream().map(index -> "[" + index + ']').collect(Collectors.joining());
		} else if (segment instanceof FeatureInstance feature) {
			if (feature.getIndex() == 0) {
				return feature.getName();
			} else {
				return feature.getName() + '[' + feature.getIndex() + ']';
			}
		} else if (segment instanceof ConnectionInstance connection) {
			return "connection#" + getContainerOfType(connection, ComponentInstance.class).getConnectionInstances()
					.indexOf(connection);
		} else if (segment instanceof InstanceObject instanceObject) {
			return instanceObject.getName();
		} else {
			throw new RuntimeException("Unexpected segment: " + segment);
		}
	}
}