DiagramToBusinessObjectTreeConverter.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.internal.diagram.runtime.updating;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import org.osate.ge.RelativeBusinessObjectReference;
import org.osate.ge.internal.diagram.runtime.AgeDiagram;
import org.osate.ge.internal.diagram.runtime.DiagramElement;
import org.osate.ge.internal.diagram.runtime.DiagramNode;
import org.osate.ge.internal.model.EmbeddedBusinessObject;

/**
 * Utility class which builds a business object tree from an AgeDiagram
 * The nodes generated by this class will not have their business object fields set. They should be processed by a {@link BusinessObjectTreeUpdater}
 * to set the business object.
 */
public final class DiagramToBusinessObjectTreeConverter {
	/**
	 * Private constructor to prevent instantiation.
	 */
	private DiagramToBusinessObjectTreeConverter() {
	}

	/**
	 * Creates a new business object tree from the specified diagram.
	 * @param diagram the diagram from which to create the business object tree
	 * @return the new business object tree
	 * @see #createBusinessObjectNode(AgeDiagram, Map, Map)
	 */
	public static BusinessObjectNode createBusinessObjectNode(final AgeDiagram diagram) {
		return createBusinessObjectNode(diagram, Collections.emptyMap(), Collections.emptyMap());
	}

	/**
	 * Creates a new business object tree from the specified diagram.
	 * @param diagram the diagram from which to create the business object tree
	 * @param futureElementInfoMap a mapping from parent {@link DiagramNode} objects to a map containing information about elements which should be created.
	 * @param containerToRelativeReferenceToGhostMap a mapping from parent {@link DiagramNode} objects to a map containing ghost diagram elements.
	 * @return the new tree.
	 */
	public static BusinessObjectNode createBusinessObjectNode(final AgeDiagram diagram,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, FutureElementInfo>> futureElementInfoMap,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, DiagramElement>> containerToRelativeReferenceToGhostMap) {
		final BusinessObjectNode rootNode = new BusinessObjectNode(null, UUID.randomUUID(), null, null,
				Completeness.UNKNOWN,
				true);
		createBusinessObjectNodesForElements(rootNode, diagram.getChildren(), futureElementInfoMap,
				containerToRelativeReferenceToGhostMap);
		createBusinessObjectNodesForGhostedElements(rootNode, diagram, futureElementInfoMap,
				containerToRelativeReferenceToGhostMap);
		createBusinessObjectNodesForFutureElements(rootNode, diagram, futureElementInfoMap);

		return rootNode;
	}


	private static void createBusinessObjectNodesForElements(final BusinessObjectNode parent,
			final Collection<DiagramElement> elements,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, FutureElementInfo>> futureElementInfoMap,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, DiagramElement>> containerToRelativeReferenceToGhostMap) {
		for(final DiagramElement e : elements) {
			createBusinessObjectNodesForElements(parent, e, futureElementInfoMap,
					containerToRelativeReferenceToGhostMap);
		}
	}

	private static void createBusinessObjectNodesForElements(final BusinessObjectNode parent,
			final DiagramElement e,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, FutureElementInfo>> futureElementInfoMap,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, DiagramElement>> containerToRelativeReferenceToGhostMap) {
		// For embedded business objects, store the object in the business object node.
		// Typically, the business object node isn't stored in the business object node to prevent retaining references
		// to old business objects.
		final Object bo = e.getBusinessObject() instanceof EmbeddedBusinessObject ? e.getBusinessObject() : null;

		// Don't keep the business object when building the business object tree. This will ensure that tree expander or other user of the tree updates
		// the business object based on the model.
		final BusinessObjectNode childNode = new BusinessObjectNode(parent, e.getId(), e.getRelativeReference(), bo,
				Completeness.UNKNOWN, true);
		createBusinessObjectNodesForElements(childNode, e.getChildren(), futureElementInfoMap,
				containerToRelativeReferenceToGhostMap);
		createBusinessObjectNodesForGhostedElements(childNode, e, futureElementInfoMap,
				containerToRelativeReferenceToGhostMap);
		createBusinessObjectNodesForFutureElements(childNode, e, futureElementInfoMap);
	}

	private static void createBusinessObjectNodesForGhostedElements(final BusinessObjectNode parent,
			final DiagramNode diagramNode,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, FutureElementInfo>> futureElementInfoMap,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, DiagramElement>> containerToRelativeReferenceToGhostMap) {

		final Map<RelativeBusinessObjectReference, DiagramElement> ghostedElements = containerToRelativeReferenceToGhostMap.get(diagramNode);
		if(ghostedElements != null) {
			for(final DiagramElement ghostedElement : ghostedElements.values()) {
				// Don't create a node if a node has already been created for the relative reference.
				if (parent.getChild(ghostedElement.getRelativeReference()) == null) {
					createBusinessObjectNodesForElements(parent, ghostedElement, futureElementInfoMap,
							containerToRelativeReferenceToGhostMap);
				}
			}
		}
	}

	private static void createBusinessObjectNodesForFutureElements(final BusinessObjectNode parent,
			final DiagramNode diagramNode,
			final Map<DiagramNode, Map<RelativeBusinessObjectReference, FutureElementInfo>> futureElementInfoMap) {
		final Map<RelativeBusinessObjectReference, FutureElementInfo> futureElements = futureElementInfoMap
				.get(diagramNode);

		if(futureElements != null) {
			for (final Entry<RelativeBusinessObjectReference, FutureElementInfo> futureElementEntry : futureElements
					.entrySet()) {
				// An incomplete node is created. The tree expander will fill in missing fields.
				final RelativeBusinessObjectReference ref = futureElementEntry.getKey();
				if (parent.getChild(ref) == null) {
					new BusinessObjectNode(parent, UUID.randomUUID(), ref, futureElementEntry.getValue().embeddedBo,
							Completeness.UNKNOWN, false);
				}
			}
		}
	}
}