ShowConnectedElementsHandler.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.ui.internal.handlers;
import java.util.AbstractMap;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.handlers.HandlerUtil;
import org.osate.aadl2.ComponentClassifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.ConnectedElement;
import org.osate.aadl2.Connection;
import org.osate.aadl2.ConnectionEnd;
import org.osate.aadl2.Context;
import org.osate.aadl2.Element;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.ConnectionInstanceEnd;
import org.osate.aadl2.instance.ConnectionReference;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.ge.BusinessObjectContext;
import org.osate.ge.RelativeBusinessObjectReference;
import org.osate.ge.aadl2.internal.util.AadlClassifierUtil;
import org.osate.ge.internal.diagram.runtime.AgeDiagram;
import org.osate.ge.internal.diagram.runtime.layout.DiagramElementLayoutUtil;
import org.osate.ge.internal.diagram.runtime.layout.LayoutInfoProvider;
import org.osate.ge.internal.diagram.runtime.updating.BusinessObjectNode;
import org.osate.ge.internal.diagram.runtime.updating.BusinessObjectTreeUpdater;
import org.osate.ge.internal.diagram.runtime.updating.Completeness;
import org.osate.ge.internal.diagram.runtime.updating.DiagramToBusinessObjectTreeConverter;
import org.osate.ge.internal.diagram.runtime.updating.DiagramUpdater;
import org.osate.ge.internal.services.ActionExecutor.ExecutionMode;
import org.osate.ge.internal.services.ProjectReferenceService;
import org.osate.ge.internal.ui.editor.InternalDiagramEditor;
import org.osate.ge.internal.ui.handlers.AgeHandlerUtil;
public class ShowConnectedElementsHandler extends AbstractHandler {
private ProjectReferenceService referenceService;
@Override
public void setEnabled(final Object evaluationContext) {
setBaseEnabled(AgeHandlerUtil.getSelectedBusinessObjectContexts().stream()
.filter(boc -> isSubcomponentOrConnectionEnd(boc)).findAny().isPresent());
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final InternalDiagramEditor editor = getDiagramEditor(event);
final List<BusinessObjectContext> selectedElements = AgeHandlerUtil.getSelectedBusinessObjectContexts().stream()
.filter(boc -> isSubcomponentOrConnectionEnd(boc)).collect(Collectors.toList());
referenceService = Objects.requireNonNull(Adapters.adapt(editor, ProjectReferenceService.class),
"Unable to retrieve reference service");
final BusinessObjectTreeUpdater treeUpdater = editor.getBoTreeUpdater();
final BusinessObjectNode boTree = getBoTree(editor, treeUpdater);
for (final BusinessObjectContext selectedElement : selectedElements) {
final BusinessObjectNode selectedNode = getSelectedNode(boTree, selectedElement);
final Object selectedBo = selectedNode.getBusinessObject();
if (selectedBo instanceof ConnectionEnd) {
final SimpleEntry<Optional<ComponentClassifier>, BusinessObjectNode> classifierToConnectionEnd = getClassifierToConnectionEnd(
selectedNode);
final Optional<ComponentClassifier> compImplOpt = classifierToConnectionEnd.getKey();
final BusinessObjectNode conEndNode = classifierToConnectionEnd.getValue();
// Determine component implementation of selected node
final BusinessObjectNode compNode = selectedBo instanceof Subcomponent
&& conEndNode == selectedNode
? selectedNode
: conEndNode.getParent();
// Internal Connections to Connection End
compImplOpt.ifPresent(ci -> {
if (ci instanceof ComponentImplementation) {
enableInternalConnections((ComponentImplementation) ci, conEndNode.getBusinessObject(),
selectedBo, compNode);
}
});
// Parent Connections to Connection End
AadlClassifierUtil.getComponentImplementation(compNode.getParent()).ifPresent(ci -> {
enableParentConnections(ci, conEndNode.getBusinessObject(), selectedBo, compNode);
});
}
if (selectedBo instanceof Subcomponent) {
final BusinessObjectNode parent = selectedNode.getParent();
// Parent Connections to Subcomponent
AadlClassifierUtil.getComponentImplementation(parent).ifPresent(
compImpl -> enableComponentImplementationConnections(compImpl, parent, selectedNode));
// Internal Connections to Subcomponent
AadlClassifierUtil.getComponentImplementation(selectedBo)
.ifPresent(compImpl -> enableSelectedElementConnections(compImpl, selectedNode));
} else if (selectedBo instanceof InstanceObject) {
if (selectedBo instanceof ComponentInstance) {
final ComponentInstance compInstance = (ComponentInstance) selectedBo;
enableComponentInstanceConnections(compInstance, boTree);
}
if (selectedBo instanceof ConnectionInstanceEnd) {
final InstanceObject selectedConnectionEnd = (InstanceObject) selectedBo;
enableInstanceEndConnections(selectedConnectionEnd, boTree);
}
}
}
final AgeDiagram diagram = editor.getDiagram();
final DiagramUpdater diagramUpdater = editor.getDiagramUpdater();
final LayoutInfoProvider layoutInfoProvider = Objects.requireNonNull(
Adapters.adapt(editor, LayoutInfoProvider.class), "Unable to retrieve layout info provider");
// Update the diagram
editor.getActionExecutor().execute("Show Connected Elements", ExecutionMode.NORMAL, () -> {
// Update the diagram
diagramUpdater.updateDiagram(diagram, boTree);
// Update layout
diagram.modify("Layout Incrementally",
m -> DiagramElementLayoutUtil.layoutIncrementally(diagram, m, layoutInfoProvider));
return null;
});
return null;
}
private void enableInternalConnections(final ComponentImplementation ci, final Object conEndContextBo,
final Object selectedBo, final BusinessObjectNode compImplNode) {
ci.getAllConnections().stream().map(Connection::getRootConnection).forEach(con -> {
final ConnectedElement dest = con.getDestination();
final ConnectedElement src = con.getSource();
if (isConnectedConnection(dest, selectedBo, conEndContextBo, compImplNode, con.getAllSourceContext(), src)
|| isConnectedConnection(src, selectedBo, conEndContextBo, compImplNode,
con.getAllDestinationContext(), dest)) {
enableConnection(con, compImplNode);
}
});
}
private void enableParentConnections(final ComponentImplementation ci, final Object conEndBo,
final Object selectedBo, final BusinessObjectNode compNode) {
ci.getAllConnections().stream().map(Connection::getRootConnection).forEach(con -> {
final ConnectedElement dest = con.getDestination();
final ConnectedElement src = con.getSource();
if (isConnectedToConnectionEnd(dest, src, con.getAllSourceContext(), selectedBo, conEndBo, compNode)
|| isConnectedToConnectionEnd(src, dest, con.getAllDestinationContext(), selectedBo, conEndBo,
compNode)) {
enableConnection(con, compNode.getParent());
}
});
}
private void enableInstanceEndConnections(final InstanceObject selectedConnectionEnd,
final BusinessObjectNode boTree) {
getConnectionReferences(selectedConnectionEnd).forEach(connectionReference -> {
if (connectionEndFound(connectionReference.getSource(), selectedConnectionEnd)
|| connectionEndFound(connectionReference.getDestination(), selectedConnectionEnd)) {
enableConnectionReferenceNodes(boTree, connectionReference);
}
});
}
private void enableComponentInstanceConnections(final ComponentInstance compInstance,
final BusinessObjectNode boTree) {
if (compInstance.getOwner() instanceof ComponentInstance) {
getConnectionReferences((InstanceObject) compInstance.getOwner()).forEach(connectionReference -> {
if (connectionReference.getSource().getComponentInstance() == compInstance
|| connectionReference.getDestination().getComponentInstance() == compInstance) {
enableConnectionReferenceNodes(boTree, connectionReference);
}
});
}
}
private static Stream<ConnectionReference> getConnectionReferences(final InstanceObject io) {
return io.getAllEnclosingConnectionInstances().stream()
.flatMap(connectionInstance -> connectionInstance.getConnectionReferences().stream());
}
private boolean isConnectedConnection(ConnectedElement destination, Object selectedBo, Object connectionEndBo,
BusinessObjectNode ciNode, Context allSourceContext, ConnectedElement source) {
// Destination Connection Ends
final List<ConnectionEnd> destConnectionEnds = getConnectionEnds(destination);
if (destConnectionEnds.contains(selectedBo) && destination.getConnectionEnd() == connectionEndBo) {
// Enable Selected Element
enableConnectedElements(ciNode, destination);
// Enable Source Context
BusinessObjectNode context = ciNode;
if (allSourceContext != null) {
final RelativeBusinessObjectReference contextRef = getRelativeBusinessObjectReference(allSourceContext);
context = ciNode.getChild(contextRef);
if (context == null) {
context = createNode(ciNode, contextRef, allSourceContext);
}
}
// Enable Source
enableConnectedElements(context, source);
return true;
}
return false;
}
private boolean isConnectedToConnectionEnd(final ConnectedElement ce1, final ConnectedElement ce2,
final Context ctx, final Object selectedBo, final Object connectionEndBo, final BusinessObjectNode ciNode) {
final List<ConnectionEnd> connectionEnds = getConnectionEnds(ce1);
if (connectionEnds.contains(selectedBo) && ce1
.getConnectionEnd() == connectionEndBo
&& (ciNode.getBusinessObject() == ce1.getContext()
|| ce1.getConnectionEnd() == ciNode.getBusinessObject())) {
enableConnectedElements(ciNode, ce1);
final BusinessObjectNode parentCiNode = ciNode.getParent();
BusinessObjectNode context = parentCiNode;
if (ctx != null) {
final RelativeBusinessObjectReference contextRef = getRelativeBusinessObjectReference(ctx);
context = parentCiNode.getChild(contextRef);
if (context == null) {
context = createNode(parentCiNode, contextRef, ctx);
}
}
enableConnectedElements(context, ce2);
return true;
}
return false;
}
private void enableConnectionReferenceNodes(final BusinessObjectNode boTree, final ConnectionReference cr) {
Element tmpElement = cr;
// Ancestors to ensure are enabled on the diagram
final Queue<Element> ancestors = Collections.asLifoQueue(new LinkedList<Element>());
while (tmpElement.getOwner() != null) {
ancestors.add(tmpElement);
tmpElement = tmpElement.getOwner();
// First owner of connection reference is connection instance
if (tmpElement instanceof ConnectionInstance) {
tmpElement = tmpElement.getOwner();
}
}
// Connection reference
final Map<Object, BusinessObjectNode> nodes = getEnableAncestorNodes(boTree, ancestors, tmpElement);
// Enable source and destination nodes
enableAncestorNodes(nodes, cr.getSource());
enableAncestorNodes(nodes, cr.getDestination());
}
// Find ancestors and create if necessary
private void enableAncestorNodes(final Map<Object, BusinessObjectNode> nodes, final Element ancestor) {
final Queue<Element> ancestors = Collections.asLifoQueue(new LinkedList<Element>());
populateAncestorsQueue(nodes, ancestors, ancestor);
enableAncestorNodes(nodes, ancestors, ancestors.poll());
}
// Gets the first element ancestor that is enabled
private void populateAncestorsQueue(final Map<Object, BusinessObjectNode> nodes, final Queue<Element> ancestors,
Element ancestor) {
while (!nodes.containsKey(ancestor)) {
ancestors.add(ancestor);
ancestor = ancestor.getOwner();
}
ancestors.add(ancestor);
}
// Create ancestor nodes
private void enableAncestorNodes(final Map<Object, BusinessObjectNode> ancestorToNode,
final Queue<Element> ancestors, final Element ancestor) {
BusinessObjectNode ancestorNode = (BusinessObjectNode) ancestorToNode.get(ancestor);
for (final Element ancestorToEnable : ancestors) {
final RelativeBusinessObjectReference ancestorRef = getRelativeBusinessObjectReference(ancestorToEnable);
final BusinessObjectNode tmpNode = ancestorNode.getChild(ancestorRef);
ancestorNode = tmpNode == null ? createNode(ancestorNode, ancestorRef, ancestorToEnable) : tmpNode;
}
}
// Create ancestor nodes
private Map<Object, BusinessObjectNode> getEnableAncestorNodes(final BusinessObjectNode boTree,
final Queue<Element> ancestors, final Element ancestor) {
final Map<Object, BusinessObjectNode> boToAncestorNodes = new HashMap<Object, BusinessObjectNode>();
BusinessObjectNode ancestorNode = boTree.getChild(getRelativeBusinessObjectReference(ancestor));
boToAncestorNodes.put(ancestorNode.getBusinessObject(), ancestorNode);
for (final Element ancestorToEnable : ancestors) {
final RelativeBusinessObjectReference ancestorRef = getRelativeBusinessObjectReference(ancestorToEnable);
final BusinessObjectNode tmpNode = ancestorNode.getChild(ancestorRef);
ancestorNode = tmpNode == null ? createNode(ancestorNode, ancestorRef, ancestorToEnable) : tmpNode;
boToAncestorNodes.put(ancestorNode.getBusinessObject(), ancestorNode);
}
return boToAncestorNodes;
}
private List<ConnectionEnd> getConnectionEnds(ConnectedElement ce) {
final List<ConnectionEnd> connectionEnds = new ArrayList<>();
connectionEnds.add(ce.getConnectionEnd());
while (ce.getNext() != null) {
ce = ce.getNext();
connectionEnds.add(ce.getConnectionEnd());
}
return connectionEnds;
}
private SimpleEntry<Optional<ComponentClassifier>, BusinessObjectNode> getClassifierToConnectionEnd(
BusinessObjectNode node) {
BusinessObjectNode connectionEnd = node;
for (; node != null; node = node.getParent()) {
final Optional<ComponentClassifier> classifierOpt = AadlClassifierUtil.getComponentClassifier(node);
if (classifierOpt.isPresent()) {
return new AbstractMap.SimpleEntry<Optional<ComponentClassifier>, BusinessObjectNode>(classifierOpt,
connectionEnd);
}
connectionEnd = node;
}
return new AbstractMap.SimpleEntry<Optional<ComponentClassifier>, BusinessObjectNode>(Optional.empty(),
connectionEnd);
}
private void enableSelectedElementConnections(final ComponentImplementation ci,
final BusinessObjectNode selectedNode) {
ci.getAllConnections().stream().map(Connection::getRootConnection).forEach(con -> {
if (enabledConnectionEnds(
selectedNode, con.getAllDestinationContext(), con.getAllSourceContext(), con.getSource(),
con.getDestination())
|| enabledConnectionEnds(selectedNode, con.getAllSourceContext(), con.getAllDestinationContext(),
con.getDestination(), con.getSource())) {
enableConnection(con, selectedNode);
}
});
}
private void enableConnection(final Connection con, final BusinessObjectNode parent) {
final RelativeBusinessObjectReference conRef = getRelativeBusinessObjectReference(con);
final BusinessObjectNode conNode = parent.getChild(conRef);
if (conNode == null) {
createNode(parent, conRef, con);
}
}
private boolean enabledConnectionEnds(final BusinessObjectNode selectedNode, final Context context1,
final Context context2, final ConnectedElement connectedElement1,
final ConnectedElement connectedElement2) {
if (context1 == null || !(context1 instanceof Subcomponent)) {
if (context2 != null) {
final RelativeBusinessObjectReference contextRef = getRelativeBusinessObjectReference(context2);
BusinessObjectNode contextNode = selectedNode.getChild(contextRef);
if (contextNode == null) {
contextNode = createNode(selectedNode, contextRef, context2);
}
enableConnectedElements(contextNode, connectedElement1);
}
enableConnectedElements(selectedNode, connectedElement2);
return true;
}
return false;
}
private void enableComponentImplementationConnections(final ComponentImplementation compImpl,
final BusinessObjectNode parent, final BusinessObjectNode selectedNode) {
compImpl.getAllConnections().stream().map(Connection::getRootConnection).forEach(con -> {
if (isConnectedToSubcomponent(con.getAllDestinationContext(), selectedNode, con.getDestination(), parent,
con.getAllSourceContext(), con.getSource())
|| isConnectedToSubcomponent(con.getAllSourceContext(), selectedNode, con.getSource(), parent,
con.getAllDestinationContext(), con.getDestination())) {
enableConnection(con, parent);
}
});
}
private boolean isConnectedToSubcomponent(final Context context1, final BusinessObjectNode selectedNode,
final ConnectedElement connectedElement1, final BusinessObjectNode parent, final Context context2,
final ConnectedElement connectedElement2) {
if (context1 == selectedNode.getBusinessObject()) {
enableConnectedElements(selectedNode, connectedElement1);
BusinessObjectNode context = parent;
if (context2 != null) {
// Enable context node
final RelativeBusinessObjectReference srcContextRef = getRelativeBusinessObjectReference(context2);
context = parent.getChild(srcContextRef);
if (context == null) {
context = createNode(parent, srcContextRef, context2);
}
}
enableConnectedElements(context, connectedElement2);
return true;
}
return false;
}
private void enableConnectedElements(BusinessObjectNode parent, ConnectedElement ce) {
while (ce != null) {
final ConnectionEnd connectionEnd = ce.getConnectionEnd();
final RelativeBusinessObjectReference ceRef = getRelativeBusinessObjectReference(connectionEnd);
final BusinessObjectNode ceNode = parent.getChild(ceRef);
parent = ceNode == null ? createNode(parent, ceRef, connectionEnd) : ceNode;
ce = ce.getNext();
}
}
/**
* Get and create selected node and ancestors
*/
private BusinessObjectNode getSelectedNode(BusinessObjectNode ancestorNode, BusinessObjectContext selectedBoc) {
final Queue<Object> ancestors = Collections.asLifoQueue(new LinkedList<Object>());
while (selectedBoc.getBusinessObject() != null) {
ancestors.add(selectedBoc.getBusinessObject());
selectedBoc = selectedBoc.getParent();
}
// Find ancestors to parent
for (final Object ancestor : ancestors) {
ancestorNode = getAncestor(ancestor, ancestorNode);
}
return ancestorNode;
}
private BusinessObjectNode getAncestor(final Object ancestor, final BusinessObjectNode parent) {
final RelativeBusinessObjectReference ref = getRelativeBusinessObjectReference(ancestor);
final BusinessObjectNode node = parent.getChild(ref);
// Enable ancestor node if necessary
return node == null ? createNode(parent, ref, ancestor) : node;
}
// Check if connection end matches selected element
private static boolean connectionEndFound(Element connectionEnd, final InstanceObject selectedConnectionEnd) {
while (!(connectionEnd instanceof ComponentInstance)) {
if (connectionEnd == selectedConnectionEnd) {
return true;
}
connectionEnd = connectionEnd.getOwner();
}
return false;
}
private static BusinessObjectNode createNode(final BusinessObjectNode container,
final RelativeBusinessObjectReference ref, final Object bo) {
return new BusinessObjectNode(container, UUID.randomUUID(), ref, bo, Completeness.UNKNOWN, false);
}
// Eligible Subcomponents and ConnectionEnds
private static boolean isSubcomponentOrConnectionEnd(final BusinessObjectContext boc) {
final Object bo = boc.getBusinessObject();
return (Subcomponent.class.isInstance(bo)/* && ((Subcomponent) bo).getComponentImplementation() != null */)
|| (ComponentInstance.class.isInstance(bo) && ((ComponentInstance) bo).getSubcomponent() != null)
|| ConnectionEnd.class.isInstance(bo) || ConnectionInstanceEnd.class.isInstance(bo);
}
private static InternalDiagramEditor getDiagramEditor(final ExecutionEvent event) {
final IEditorPart activeEditor = HandlerUtil.getActiveEditor(event);
if (!(activeEditor instanceof InternalDiagramEditor)) {
throw new RuntimeException("Unexpected editor: " + activeEditor);
}
return (InternalDiagramEditor) activeEditor;
}
private static BusinessObjectNode getBoTree(final InternalDiagramEditor editor, final BusinessObjectTreeUpdater boTreeUpdater) {
final BusinessObjectNode boTree = DiagramToBusinessObjectTreeConverter
.createBusinessObjectNode(editor.getDiagram());
return boTreeUpdater.updateTree(editor.getDiagram().getConfiguration(), boTree);
}
private RelativeBusinessObjectReference getRelativeBusinessObjectReference(final Object bo) {
return referenceService.getRelativeReference(bo);
}
}