ClassifierOperationExecutor.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.ge.aadl2.internal.util.classifiers;

import java.util.Objects;

import org.eclipse.core.resources.IProject;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.AadlPackage;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.ComponentType;
import org.osate.aadl2.ComponentTypeRename;
import org.osate.aadl2.FeatureGroupType;
import org.osate.aadl2.GroupExtension;
import org.osate.aadl2.ImplementationExtension;
import org.osate.aadl2.PackageSection;
import org.osate.aadl2.Realization;
import org.osate.aadl2.TypeExtension;
import org.osate.ge.BusinessObjectContext;
import org.osate.ge.aadl2.AadlImportsUtil;
import org.osate.ge.aadl2.internal.util.AadlClassifierUtil;
import org.osate.ge.aadl2.internal.util.AadlNameUtil;
import org.osate.ge.aadl2.ui.AadlModelAccessUtil;
import org.osate.ge.operations.OperationBuilder;
import org.osate.ge.operations.StepResultBuilder;

public class ClassifierOperationExecutor {
	private final ClassifierCreationHelper classifierCreationHelper;
	private final IProject project;

	public ClassifierOperationExecutor(final ResourceSet resourceSet,
			final IProject project) {
		Objects.requireNonNull(resourceSet, "resourceSet must not be null");
		this.project = Objects.requireNonNull(project, "project must not be null");
		this.classifierCreationHelper = new ClassifierCreationHelper(resourceSet);
	}

	public OperationBuilder<Classifier> execute(final OperationBuilder<?> operation,
			final ClassifierOperation classifierOp,
			final BusinessObjectContext primaryPkgBoc) {
		final OperationBuilder<Classifier> baseOp = addStep(operation, classifierOp.getBasePart(), null, null);
		return addStep(baseOp, classifierOp.getPrimaryPart(), classifierOp.getBasePart(), primaryPkgBoc);
	}

	/**
	 *
	 * @param operation
	 * @param part
	 * @param basePart
	 * @param pkgBoc if non-null then the operation result will indicate that the object should be added to the diagram.
	 * @return
	 */
	private OperationBuilder<Classifier> addStep(final OperationBuilder<?> operation,
			final ClassifierOperationPart part,
			final ClassifierOperationPart basePart, final BusinessObjectContext pkgBoc) {
		Objects.requireNonNull(part, "part must not be null");
		Objects.requireNonNull(part.getType(), "operation part type must not be null");

		switch (part.getType()) {
		case EXISTING:
			return operation.supply(() -> StepResultBuilder
					.create(classifierCreationHelper.getResolvedClassifier(part.getSelectedClassifier())).build());

		case NEW_COMPONENT_IMPLEMENTATION:
		case NEW_COMPONENT_TYPE:
		case NEW_FEATURE_GROUP_TYPE:
			final EClass creationEClass = getCreationEClass(part);

			final PackageSection unmodifiableSection = classifierCreationHelper
					.getResolvedPublicSection(part.getSelectedPackage());

			return operation.modifyModel(unmodifiableSection, (tag, prevResult) -> tag, (tag, section, prevResult) -> {
				// Create the new classifier
				final Classifier newClassifier = section.createOwnedClassifier(creationEClass);

				// Set name
				newClassifier.setName(classifierCreationHelper.getName(part, basePart));

				final Classifier baseOperationResult = resolveWithLiveResourceSetOrThrowIfProxy(
						(Classifier) prevResult);

				// Handle component implementations
				if (newClassifier instanceof ComponentImplementation) {
					final ComponentImplementation newImpl = (ComponentImplementation) newClassifier;
					final ComponentType baseComponentType;
					if (baseOperationResult instanceof ComponentType) {
						final Realization realization = newImpl.createOwnedRealization();
						baseComponentType = (ComponentType) baseOperationResult;
						realization.setImplemented(baseComponentType);
					} else if (baseOperationResult instanceof ComponentImplementation) {
						final ComponentImplementation baseImpl = (ComponentImplementation) baseOperationResult;
						final ImplementationExtension extension = newImpl.createOwnedExtension();
						extension.setExtended(baseImpl);

						final Realization realization = newImpl.createOwnedRealization();
						realization.setImplemented(baseImpl.getType());

						baseComponentType = baseImpl.getType();

						// Import the base implementation's package
						final AadlPackage baseImplPkg = (AadlPackage) baseImpl.getNamespace().getOwner();
						AadlImportsUtil.addImportIfNeeded(section, baseImplPkg);
					} else {
						throw new RuntimeException("Invalid base classifier: " + baseOperationResult);
					}

					// Get the base component type
					if (baseComponentType == null) {
						throw new RuntimeException("Unable to determine base component type");
					}

					if (!AadlNameUtil.namesAreEqual(section, baseComponentType.getNamespace())) {
						// Import the package if necessary
						final AadlPackage baseComponentTypePkg = (AadlPackage) baseComponentType.getNamespace()
								.getOwner();
						AadlImportsUtil.addImportIfNeeded(section, baseComponentTypePkg);

						// Create an alias for the component type
						final ClassifierCreationHelper.RenamedTypeDetails aliasDetails = classifierCreationHelper
								.getRenamedType(section, baseComponentTypePkg, baseComponentType.getName());
						if (!aliasDetails.exists) {
							final ComponentTypeRename ctr = section.createOwnedComponentTypeRename();
							ctr.setName(aliasDetails.aliasName);
							ctr.setCategory(baseComponentType.getCategory());
							ctr.setRenamedComponentType(baseComponentType);
						}
					}

				} else if (newClassifier instanceof ComponentType && baseOperationResult instanceof ComponentType) {
					final ComponentType newType = (ComponentType) newClassifier;
					final TypeExtension extension = newType.createOwnedExtension();
					extension.setExtended((ComponentType) baseOperationResult);
				} else if (newClassifier instanceof FeatureGroupType
						&& baseOperationResult instanceof FeatureGroupType) {
					final FeatureGroupType newFgt = (FeatureGroupType) newClassifier;
					final GroupExtension extension = newFgt.createOwnedExtension();
					extension.setExtended((FeatureGroupType) baseOperationResult);
				}

				final StepResultBuilder<Classifier> resultBuilder = StepResultBuilder.create(newClassifier);

				// Hint for adding to diagram
				if (pkgBoc != null && newClassifier != null) {
					resultBuilder.showNewBusinessObject(pkgBoc, newClassifier);
				}

				return resultBuilder.build();
			});

		case NONE:
			return operation.map(prevResult -> StepResultBuilder.create((Classifier) null).build());

		default:
			throw new RuntimeException("Unexpected operation: " + part.getType());
		}

	}

// Resolves the classifier using a new resource set. Using the ClassifierCreationHelper to resolve the base classifier does not
// work if the classifier was just created. Throws if the classifier is a proxy and cannot be resolved
	private Classifier resolveWithLiveResourceSetOrThrowIfProxy(final Classifier c) {
		if (c == null || !c.eIsProxy()) {
			return c;
		}

		// Resolve the base classifier using a new resource set. Using the ClassifierCreationHelper to resolve the base classifier does not
		// work if the classifier was just created.
		final ResourceSet liveResourceSet = AadlModelAccessUtil.getLiveResourceSet(project);

		final Classifier resolvedClassifier = (Classifier) EcoreUtil.resolve(c, liveResourceSet);
		if (resolvedClassifier == null) {
			throw new RuntimeException("Unable to resolve classifier: " + c);
		}

		return resolvedClassifier;
	}

	private static EClass getCreationEClass(final ClassifierOperationPart configuredOp) {
		switch (configuredOp.getType()) {
		case NEW_COMPONENT_TYPE:
			return AadlClassifierUtil.getComponentTypeEClass(configuredOp.getComponentCategory());

		case NEW_COMPONENT_IMPLEMENTATION:
			return AadlClassifierUtil.getComponentImplementationEClass(configuredOp.getComponentCategory());

		case NEW_FEATURE_GROUP_TYPE:
			return Aadl2Package.eINSTANCE.getFeatureGroupType();

		default:
			return null;
		}
	}
}