CreateEndToEndFlowSpecificationTool.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.tools;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.core.runtime.Adapters;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.Connection;
import org.osate.aadl2.Context;
import org.osate.aadl2.DataSubcomponent;
import org.osate.aadl2.Element;
import org.osate.aadl2.EndToEndFlow;
import org.osate.aadl2.EndToEndFlowElement;
import org.osate.aadl2.EndToEndFlowSegment;
import org.osate.aadl2.FlowKind;
import org.osate.aadl2.FlowSpecification;
import org.osate.aadl2.ModeFeature;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Namespace;
import org.osate.aadl2.Subcomponent;
import org.osate.ge.BusinessObjectContext;
import org.osate.ge.aadl2.internal.AadlNamingUtil;
import org.osate.ge.aadl2.internal.util.AgeAadlUtil;
import org.osate.ge.aadl2.ui.internal.tools.FlowDialogUtil.SegmentData;
import org.osate.ge.graphics.Color;
import org.osate.ge.internal.diagram.runtime.DiagramElement;
import org.osate.ge.internal.diagram.runtime.updating.BusinessObjectNode;
import org.osate.ge.internal.diagram.runtime.updating.Completeness;
import org.osate.ge.internal.services.AadlModificationService;
import org.osate.ge.internal.services.ColoringService;
import org.osate.ge.internal.services.ReferenceService;
import org.osate.ge.internal.services.UiService;
import org.osate.ge.internal.ui.editor.InternalDiagramEditor;
import org.osate.ge.internal.ui.handlers.AgeHandlerUtil;
import org.osate.ge.internal.ui.tools.ActivatedEvent;
import org.osate.ge.internal.ui.tools.DeactivatedEvent;
import org.osate.ge.internal.ui.tools.SelectionChangedEvent;
import org.osate.ge.internal.ui.tools.Tool;
import org.osate.ge.internal.ui.tools.ToolUtil;
import org.osate.ge.internal.ui.util.ContextHelpUtil;
import org.osate.ge.internal.ui.util.UiUtil;

public class CreateEndToEndFlowSpecificationTool implements Tool {
	private ColoringService.Coloring coloring = null;
	private CreateFlowsToolsDialog createFlowDialog;
	private final InternalDiagramEditor editor;

	// Flow segment selections
	private final List<SegmentData> segmentSelections = new ArrayList<>();
	// In mode feature selections
	private final ArrayList<BusinessObjectContext> modeFeatureSelections = new ArrayList<>();

	public CreateEndToEndFlowSpecificationTool(final InternalDiagramEditor editor, final DiagramElement container,
			final EndToEndFlow endToEndFlow) {
		this.editor = editor;
		final ReferenceService referenceService = Objects.requireNonNull(Adapters.adapt(editor, ReferenceService.class),
				"unable to retrieve reference service");

		final Display display = Display.getCurrent();
		final UiService uiService = Adapters.adapt(editor, UiService.class);
		createFlowDialog = new CreateFlowsToolsDialog(display.getActiveShell(), container, endToEndFlow, uiService);
		createFlowDialog.setEndToEndFlowName(endToEndFlow.getName());

		// Find segments in order
		endToEndFlow.getAllFlowSegments()
		.stream()
		.map(flowSegment -> FlowToolUtil.createSegmentData(referenceService, container, flowSegment))
		.forEachOrdered(segmentSelections::add);

		endToEndFlow.getInModeOrTransitions()
		.stream()
		.map(modeFeature -> FlowToolUtil.findOrCreateBusinessObjectContext(referenceService, container,
				modeFeature))
		.forEachOrdered(modeFeatureSelections::add);
	}

	public CreateEndToEndFlowSpecificationTool(final InternalDiagramEditor editor) {
		this.editor = editor;

		final Display display = Display.getCurrent();
		final UiService uiService = Adapters.adapt(editor, UiService.class);
		createFlowDialog = new CreateFlowsToolsDialog(display.getActiveShell(), null, null, uiService);
	}

	@Override
	public void activated(final ActivatedEvent ctx) {
		final UiService uiService = ctx.getUiService();
		try {
			ctx.getSelectedBoc().ifPresent(selectedBoc -> {
				final AadlModificationService aadlModService = ctx.getAadlModificatonService();
				final ColoringService coloringService = ctx.getColoringService();

				// Check for existing errors and warnings
				final Set<Diagnostic> diagnostics = ToolUtil.getAllReferencedPackageDiagnostics(selectedBoc);
				// Do not allow tool activation if there are errors in the models
				final Set<Diagnostic> errors = FlowDialogUtil.getErrors(diagnostics);
				if (!errors.isEmpty()) {
					Display.getDefault()
					.asyncExec(() -> new FlowDialogUtil.ErrorDialog("The Create End-To-End", errors).open());
				} else {
					coloring = coloringService.adjustColors(); // Create a coloring object that will allow adjustment of pictogram
					// Create and update based on current selection
					createFlowDialog.create();
					if (segmentSelections.isEmpty() && modeFeatureSelections.isEmpty()) {
						update(Collections.singletonList(selectedBoc));
					} else {
						final Iterator<SegmentData> segmentIt = segmentSelections.iterator();
						while (segmentIt.hasNext()) {
							final SegmentData segmentData = segmentIt.next();
							setColor(segmentData, Color.MAGENTA.darker());
						}

						for (Iterator<BusinessObjectContext> modeFeatureIt = modeFeatureSelections
								.iterator(); modeFeatureIt
								.hasNext(); setColor(modeFeatureIt.next(), Color.MAGENTA.brighter())) {
						}

						update();
					}

					if (createFlowDialog.open() == Window.OK && createFlowDialog != null) {
						createFlowDialog.getFlow().ifPresent(endToEndFlow -> {
							if (createFlowDialog.eteFlowToEdit != null) {
								// Editing end to end flow
								final EndToEndFlow endToEndFlowToEdit = (EndToEndFlow) createFlowDialog.eteFlowToEdit;
								aadlModService.modify(endToEndFlowToEdit, eTEFlowToEdit -> {
									eTEFlowToEdit.getAllFlowSegments().clear();
									eTEFlowToEdit.getAllFlowSegments().addAll(endToEndFlow.getAllFlowSegments());
									eTEFlowToEdit.setName(endToEndFlow.getName());
									eTEFlowToEdit.getInModeOrTransitions().clear();
									eTEFlowToEdit.getInModeOrTransitions()
									.addAll(endToEndFlow.getInModeOrTransitions());
								});
							} else {
								// Creating end to end flow
								createFlowDialog.getOwnerComponentImplementation().ifPresent(ownerCi -> {
									aadlModService.modify(ownerCi, ci -> {
										ci.getOwnedEndToEndFlows().add(endToEndFlow);
										ci.setNoFlows(false);
									});
								});
							}
						});
					}
				}
			});
		} finally {
			uiService.deactivateActiveTool();
		}
	}

	@Override
	public void deactivated(final DeactivatedEvent ctx) {
		// Dispose of the coloring object
		if (coloring != null) {
			coloring.dispose();
			coloring = null;
		}

		if (createFlowDialog != null) {
			if (createFlowDialog.elementSelectionDlg != null) {
				createFlowDialog.elementSelectionDlg.close();
				createFlowDialog.elementSelectionDlg = null;
			}

			createFlowDialog.close();
			createFlowDialog = null;
		}

		segmentSelections.clear();
		modeFeatureSelections.clear();
	}

	@Override
	public void selectionChanged(SelectionChangedEvent ctx) {
		update(ctx.getSelectedBocs());
	}

	/**
	 * Update the diagram and tool dialog
	 */
	public void update() {
		if (createFlowDialog != null) {
			if (createFlowDialog.getShell() != null && !createFlowDialog.getShell().isDisposed()
					&& createFlowDialog.flowComposite != null && !createFlowDialog.flowComposite.isDisposed()) {
				// Set the name field to the flow being edited
				if (createFlowDialog.newETEFlowName.getText().isEmpty()) {
					createFlowDialog.setFlowNameText(AgeAadlUtil.getRootName(createFlowDialog.eteFlowToEdit));
				}

				final EndToEndFlow endToEndFlow = createEndToEndFlow();
				final boolean isValid = createFlowDialog.isEndToEndFlowValid(endToEndFlow);

				createFlowDialog.updateSegments();
				createFlowDialog.updateWidgets(isValid);
				createFlowDialog.setErrorMessage(null);
				createFlowDialog.setMessage(getDialogMessage());
			}
		}
	}

	/**
	 * Create an EndToEndFlow based on user selections
	 */
	private EndToEndFlow createEndToEndFlow() {
		final EndToEndFlow endToEndFlow = (EndToEndFlow) createFlowDialog.pkg.getEFactoryInstance()
				.create(createFlowDialog.pkg.getEndToEndFlow());
		endToEndFlow.setName(createFlowDialog.endToEndFlowName);
		segmentSelections.forEach(entry -> {
			final BusinessObjectContext boc = entry.getBoc();
			final EndToEndFlowSegment eTEFlowSegment = endToEndFlow.createOwnedEndToEndFlowSegment();
			final EndToEndFlowElement eTEFlowElement = (EndToEndFlowElement) boc.getBusinessObject();
			if (!(boc.getBusinessObject() instanceof Connection)
					&& !(boc.getBusinessObject() instanceof DataSubcomponent)
					&& !(boc.getBusinessObject() instanceof EndToEndFlow)) {
				final Context context = ToolUtil.findContext(boc);
				eTEFlowSegment.setContext(context);
			}
			eTEFlowSegment.setFlowElement(eTEFlowElement);
			endToEndFlow.getOwnedEndToEndFlowSegments().add(eTEFlowSegment);
		});

		modeFeatureSelections.forEach(modeFeatureBoc -> endToEndFlow.getInModeOrTransitions()
				.add((ModeFeature) modeFeatureBoc.getBusinessObject()));

		return endToEndFlow;
	}

	/**
	 * Update the diagram and tool dialog
	 * @param selectedBocs - the selected bocs
	 */
	private void update(final List<BusinessObjectContext> selectedBocs) {
		if (createFlowDialog != null) {
			if (createFlowDialog.getShell() != null && !createFlowDialog.getShell().isDisposed()
					&& createFlowDialog.elementSelectionDlg == null) {
				// If the selection is qualified, add it
				if (selectedBocs.size() > 1) {
					createFlowDialog.setErrorMessage(
							"Multiple elements selected. Select a single element. " + " " + getDialogMessage());
				} else if (selectedBocs.size() == 1) {
					// Get the selected boc
					final BusinessObjectContext selectedBoc = (BusinessObjectContext) selectedBocs.get(0);
					if (!modeFeatureSelections.contains(selectedBoc)
							&& createFlowDialog.addSelectedElement(selectedBoc)) {
						// Insert flow segments before first mode feature
						final Color color;
						if (selectedBoc.getBusinessObject() instanceof ModeFeature) {
							modeFeatureSelections.add(selectedBoc);
							color = Color.MAGENTA.brighter();
						} else {
							segmentSelections.add(new SegmentData(selectedBoc, new ArrayList<>()));
							if (segmentSelections.size() == 1) {
								// Set default name on first selection if one does not exist
								createFlowDialog.getOwnerComponentImplementation().ifPresent(ci -> {
									createFlowDialog.setTitle("Creating End To End Flow in: " + ci.getQualifiedName());
									if (createFlowDialog.endToEndFlowName.isEmpty()) {
										createFlowDialog.setEndToEndFlowName(ci);
									}
								});

								color = Color.ORANGE.darker();
							} else {
								color = Color.MAGENTA.darker();
							}
						}

						setColor(selectedBoc, color);
					}

					createFlowDialog.updateSegments();
					final boolean isValid = createFlowDialog.isEndToEndFlowValid(createEndToEndFlow());
					createFlowDialog.updateWidgets(isValid);
					createFlowDialog.setErrorMessage(null);
					createFlowDialog.setMessage(getDialogMessage());
				} else {
					createFlowDialog.updateSegments();
				}
			} else if (createFlowDialog.elementSelectionDlg != null
					&& createFlowDialog.elementSelectionDlg.getShell() != null
					&& !createFlowDialog.elementSelectionDlg.getShell().isDisposed()
					&& createFlowDialog.elementSelectionDlg.getShell().isVisible()) {
				final CreateFlowsToolsDialog.ElementSelectionDialog elementSelectionDlg = createFlowDialog.elementSelectionDlg;
				// Selecting an element for editing end to end flows
				if (selectedBocs.size() > 1) {
					elementSelectionDlg
					.setErrorMessage("Multiple elements are selected.\n " + elementSelectionDlg.getMessage());
					elementSelectionDlg.setSelection(null);
				} else if (selectedBocs.size() == 1) {
					elementSelectionDlg.setErrorMessage(null);
					final BusinessObjectContext selectedBoc = selectedBocs.get(0);
					elementSelectionDlg.setSelection(selectedBoc);
				}
			}
		}
	}

	private void setColor(final Object o, final Color color) {
		if (o instanceof DiagramElement) {
			final DiagramElement de = (DiagramElement) o;
			coloring.setForeground(de, color);
		} else if (o instanceof SegmentData) {
			final SegmentData segmentData = (SegmentData) o;
			final BusinessObjectContext boc = segmentData.getBoc();
			// Do not remove color from duplicate segments
			if (color == null) {
				for (final SegmentData sd : segmentSelections) {
					if (sd.getBoc() == boc) {
						return;
					}
				}
			}

			setColor(boc, color);
			segmentData.getOwnedSegments().forEach(de -> setColor(de, color));
		}
	}

	// Determine message based on currently selected element
	private String getDialogMessage() {
		String msg = "";
		if (!segmentSelections.isEmpty()) {
			msg = "Select a flow segment.";
		} else {
			createFlowDialog.setDefaultTitle();
			msg = "Select a starting flow specification.";
		}
		msg += "\nOptionally, select a mode or mode transition.";

		return msg;
	}

	private class CreateFlowsToolsDialog extends TitleAreaDialog {
		private final UiService uiService;
		private final Aadl2Package pkg = Aadl2Package.eINSTANCE;
		private DiagramElement eteFlowToEditContainer;
		private final EndToEndFlow eteFlowToEdit;
		private String endToEndFlowName = "";
		private EndToEndFlow flow;
		private Composite flowComposite;
		private TableViewer errorTableViewer;
		private Text newETEFlowName;

		private ElementSelectionDialog elementSelectionDlg;

		public CreateFlowsToolsDialog(final Shell parentShell, final DiagramElement eteFlowToEditContainer,
				final EndToEndFlow eteFlowToEdit, final UiService uiService) {
			super(parentShell);
			setHelpAvailable(true);
			this.eteFlowToEditContainer = eteFlowToEditContainer;
			this.eteFlowToEdit = eteFlowToEdit;
			this.uiService = Objects.requireNonNull(uiService, "ui service must not be null");
			setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE | SWT.RESIZE);
		}

		// Creates a name based on namespace, sets the end to end flow name and text field
		public void setEndToEndFlowName(final Namespace namespace) {
			final String eteName = AadlNamingUtil.buildUniqueIdentifier(namespace, "new_ete_flow");
			setEndToEndFlowName(eteName);
			setFlowNameText(eteName);
		}

		// Set end to end flow name
		public void setEndToEndFlowName(final String name) {
			endToEndFlowName = name;
		}

		// Set end to end flow name text field
		public void setFlowNameText(final String name) {
			newETEFlowName.setText(name);
		}

		private Optional<EndToEndFlow> getFlow() {
			return Optional.ofNullable(flow);
		}

		/**
		 * Determines if the object selected is valid
		 * @param selectedBoc - current selected element
		 * @return - true or false depending if the object selected is valid
		 */
		private boolean addSelectedElement(final BusinessObjectContext selectedBoc) {
			final Object selectedBo = selectedBoc.getBusinessObject();
			if (selectedBo instanceof NamedElement) {
				final Context context = ToolUtil.findContext(selectedBoc);
				final NamedElement selectedEle = (NamedElement) selectedBo;
				return isValid(AgeAadlUtil.getRootRefinedElement(selectedEle),
						(Context) AgeAadlUtil.getRootRefinedElement(context));
			}

			return false;
		}

		// Returns the component implementation that will own the end to end flow
		private Optional<ComponentImplementation> getOwnerComponentImplementation() {
			if (eteFlowToEditContainer != null) {
				// Get owner from end to end flow being edited
				return findOwnerComponentImplementation(eteFlowToEditContainer);
			}

			if (segmentSelections.size() == 0) {
				return Optional.empty();
			}

			final BusinessObjectContext firstSelection = segmentSelections.iterator().next().getBoc();
			final Object bo = firstSelection.getBusinessObject();
			if (bo instanceof EndToEndFlow) {
				final EndToEndFlow eTEFlow = (EndToEndFlow) bo;
				return Optional.of(eTEFlow.getContainingComponentImpl());
			}

			final BusinessObjectContext parent = firstSelection.getParent();
			// If parent of selection is subcomponent, get component impl from parent
			if (parent.getBusinessObject() instanceof Subcomponent) {
				return findOwnerComponentImplementation(parent.getParent());
			}

			// Return first component implementation found
			return findOwnerComponentImplementation(parent);
		}

		private Optional<ComponentImplementation> findOwnerComponentImplementation(final BusinessObjectContext boc) {
			BusinessObjectContext tmp = boc;
			while (tmp != null) {
				if (tmp.getBusinessObject() instanceof ComponentImplementation) {
					return Optional.of((ComponentImplementation) tmp.getBusinessObject());
				} else if (tmp.getBusinessObject() instanceof Subcomponent) {
					return Optional.ofNullable(((Subcomponent) tmp.getBusinessObject()).getComponentImplementation());
				} else if (!(tmp.getBusinessObject() instanceof NamedElement)) {
					return Optional.empty();
				}

				tmp = tmp.getParent();
			}

			return Optional.empty();
		}

		private boolean isEndToEndFlowValid(final EndToEndFlow endToEndFlow) {
			final Set<Diagnostic> diagnostics;
			final Optional<ComponentImplementation> optCi = getOwnerComponentImplementation();
			if (!optCi.isPresent()) {
				if (!segmentSelections.isEmpty()) {
					return false;
				} else {
					diagnostics = Collections.emptySet();
				}
			} else {
				final ComponentImplementation ci = optCi.get();
				diagnostics = ToolUtil.getModificationDiagnostics(ci, modifyObject(endToEndFlow, ci));
			}

			// Errors to show to user
			final Set<Diagnostic> dialogDiagnostics = diagnostics.stream().filter(diagnostic -> {
				final String errorMsg = diagnostic.getMessage();
				return errorMsg == null || !errorMsg.contains("Serialization Error");
			}).collect(Collectors.toSet());

			FlowDialogUtil.setInput(errorTableViewer, dialogDiagnostics);

			return !diagnostics.stream()
					.filter(diagnostic -> diagnostic.getSeverity() == Diagnostic.ERROR)
					.findAny()
					.isPresent();
		}

		private Function<ResourceSet, EObject> modifyObject(final EndToEndFlow endToEndFlow,
				final ComponentImplementation ci) {
			return resourceSet -> {
				// Make modification with test resource
				ComponentImplementation objectToModify = (ComponentImplementation) resourceSet
						.getEObject(EcoreUtil.getURI(ci), true);
				if (eteFlowToEdit != null) {
					final EndToEndFlow endToEndFlowToEdit = getFlowToEdit(objectToModify);
					objectToModify = (ComponentImplementation) resourceSet
							.getEObject(EcoreUtil.getURI(eteFlowToEdit.getContainingComponentImpl()), true);

					endToEndFlowToEdit.getAllFlowSegments().clear();
					endToEndFlowToEdit.getAllFlowSegments().addAll(endToEndFlow.getAllFlowSegments());
					endToEndFlowToEdit.setName(endToEndFlow.getName());
					endToEndFlowToEdit.getInModeOrTransitions().clear();
					endToEndFlow.getInModeOrTransitions()
					.forEach(mf -> endToEndFlowToEdit.getInModeOrTransitions()
							.add((ModeFeature) resourceSet.getEObject(EcoreUtil.getURI(mf), true)));
				} else {
					objectToModify.getOwnedEndToEndFlows().add(endToEndFlow);
					objectToModify.setNoFlows(false);
				}
				return objectToModify;
			};
		}

		// Get flow to edit from resource set
		private EndToEndFlow getFlowToEdit(final ComponentImplementation objectToModify) {
			final String name = AgeAadlUtil.getRootName(eteFlowToEdit);
			for (final EndToEndFlow flow : objectToModify.getAllEndToEndFlows()) {
				if (AgeAadlUtil.getRootName(flow).equalsIgnoreCase(name)) {
					return flow;
				}
			}

			throw new RuntimeException("Cannot find end to end flow: " + name);
		}

		private void updateWidgets(final boolean isValid) {
			createFlowDialog.setMessage(getDialogMessage());
			setNavigationButtonsEnabled(isValid && createFlowDialog.endToEndFlowName.length() != 0);
		}

		private void updateSegments() {
			// Clear existing controls
			for (final Control child : flowComposite.getChildren()) {
				child.dispose();
			}

			if (segmentSelections.isEmpty()) {
				// Button to start with ETEFlow segment
				final Button emptySegmentsButton = new Button(flowComposite, SWT.PUSH);
				emptySegmentsButton.setText("Insert End to End Flow");
				emptySegmentsButton.addSelectionListener(new SelectionAdapter() {
					@Override
					public void widgetSelected(final SelectionEvent e) {
						elementSelectionDlg = new SelectEndToEndFlowDialog(createFlowDialog.getShell(),
								EndToEndFlow.class, "Select End to End Flow");
						createFlowDialog.addSegment();
					}
				});
			} else {
				final Iterator<SegmentData> segmentsIt = segmentSelections.iterator();
				while (segmentsIt.hasNext()) {
					final SegmentData flowSegment = segmentsIt.next();
					final BusinessObjectContext boc = flowSegment.getBoc();
					final StringBuilder segmentNameBuilder = new StringBuilder();
					final Context context = ToolUtil.findContext(boc);
					if (context != null) {
						segmentNameBuilder.append(AgeAadlUtil.getRootName(context));
						segmentNameBuilder.append(".");
					}

					segmentNameBuilder.append(AgeAadlUtil.getRootName((NamedElement) boc.getBusinessObject()));

					createSegmentButton(flowComposite, segmentNameBuilder.toString(), flowSegment);

					// If segment is not last, add an arrow
					if (segmentsIt.hasNext()) {
						FlowDialogUtil.createArrowText(flowComposite);
					}
				}
			}

			if (!modeFeatureSelections.isEmpty()) {
				FlowDialogUtil.createInModeText(flowComposite);

				final Iterator<BusinessObjectContext> it = modeFeatureSelections.iterator();
				final BusinessObjectContext boc = it.next();
				createSegmentButton(flowComposite,
						"(" + ((NamedElement) boc.getBusinessObject()).getName() + (it.hasNext() ? "," : ")"), boc);

				while (it.hasNext()) {
					final BusinessObjectContext mode = it.next();
					createSegmentButton(flowComposite,
							((NamedElement) mode.getBusinessObject()).getName() + (it.hasNext() ? "," : ")"), mode);
				}
			}

			flowComposite.layout(true, true);
		}

		private void setNavigationButtonsEnabled(final boolean enabled) {
			final Button okBtn = getButton(IDialogConstants.OK_ID);
			if (okBtn != null) {
				getButton(IDialogConstants.OK_ID).setEnabled(enabled);
			}
		}

		private Button createSegmentButton(final Composite parent, final String text, final SegmentData flowSegment) {
			final Button segmentButton = new Button(parent, SWT.FLAT);
			segmentButton.setText(text);
			segmentButton.setEnabled(true);
			segmentButton.setLayoutData(new RowData());

			// Create cascading popup menu for editing flow
			final Menu editMenu = new Menu(segmentButton);
			MenuItem cascadeItem = new MenuItem(editMenu, SWT.CASCADE);
			cascadeItem.setText("Replace With");
			Menu subMenu = new Menu(editMenu);
			cascadeItem.setMenu(subMenu);

			Runnable replace = () -> createFlowDialog.addSegment(Optional.of(() -> {
				segmentSelections.remove(flowSegment);
				setColor(flowSegment, null);
			}), flowSegment, false);

			createElementButton(subMenu, "Element", EndToEndFlowElement.class, replace);
			createETEFlowButton(subMenu, "End To End Flow", replace);

			cascadeItem = new MenuItem(editMenu, SWT.CASCADE);
			cascadeItem.setText("Insert");
			subMenu = new Menu(editMenu);
			cascadeItem.setMenu(subMenu);

			MenuItem insertCascadeItem = new MenuItem(subMenu, SWT.CASCADE);
			insertCascadeItem.setText("Element");
			Menu insertSubmenu = new Menu(editMenu);
			insertCascadeItem.setMenu(insertSubmenu);

			final Runnable insertBefore = () -> createFlowDialog.addSegment(flowSegment, false);
			final Runnable insertAfter = () -> createFlowDialog.addSegment(flowSegment, true);

			createElementButton(insertSubmenu, "Before", EndToEndFlowElement.class, insertBefore);
			createElementButton(insertSubmenu, "After", EndToEndFlowElement.class, insertAfter);

			insertCascadeItem = new MenuItem(subMenu, SWT.CASCADE);
			insertCascadeItem.setText("End to End Flow");
			insertSubmenu = new Menu(editMenu);
			insertCascadeItem.setMenu(insertSubmenu);

			createETEFlowButton(insertSubmenu, "Before", insertBefore);
			createETEFlowButton(insertSubmenu, "After", insertAfter);

			createRemoveButton(editMenu, () -> {
				segmentSelections.remove(flowSegment);
				setColor(flowSegment, null);
				if (!segmentSelections.isEmpty()) {
					// Update color for first segment
					final SegmentData newStartSegment = segmentSelections.get(0);
					setColor(newStartSegment, Color.ORANGE.darker());
				}

				uiService.clearSelection();
			});

			segmentButton.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					// Show menu at bottom of button
					final Point location = segmentButton.getLocation();
					editMenu.setLocation(Display.getCurrent()
							.map(createFlowDialog.flowComposite, null,
									new Point(location.x, location.y + segmentButton.getSize().y)));
					editMenu.setVisible(true);
				};
			});

			return segmentButton;
		}

		private void createETEFlowButton(final Menu subMenu, final String text, final Runnable runnable) {
			final MenuItem replaceWithETEFlow = new MenuItem(subMenu, SWT.PUSH);
			replaceWithETEFlow.setText(text);
			replaceWithETEFlow.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					elementSelectionDlg = new SelectEndToEndFlowDialog(createFlowDialog.getShell(), EndToEndFlow.class,
							"Select Replacement End to End Flow");
					runnable.run();
				}
			});
		}

		private void createElementButton(final Menu menu, final String text, final Class<?> clazz,
				final Runnable runnable) {
			final MenuItem insertAfter = new MenuItem(menu, SWT.PUSH);
			insertAfter.setText(text);
			insertAfter.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					elementSelectionDlg = new SelectSegmentOrModeFeatureDialog(createFlowDialog.getShell(), clazz,
							"Select Replacement Element");
					runnable.run();
				}
			});
		}

		private void createRemoveButton(final Menu menu, final Runnable runnable) {
			final MenuItem remove = new MenuItem(menu, SWT.PUSH);
			remove.setText("Remove");
			remove.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					runnable.run();

					final EndToEndFlow endToEndFlow = createEndToEndFlow();
					final boolean isValid = createFlowDialog.isEndToEndFlowValid(endToEndFlow);

					createFlowDialog.updateSegments();

					createFlowDialog.setErrorMessage(null);
					createFlowDialog.setMessage(getDialogMessage());

					createFlowDialog.updateWidgets(isValid);
				}
			});
		}

		private Button createSegmentButton(final Composite parent, final String text, final BusinessObjectContext boc) {
			final Button segmentButton = new Button(parent, SWT.FLAT);
			segmentButton.setText(text);
			segmentButton.setEnabled(true);
			segmentButton.setLayoutData(new RowData());

			final Menu menu = new Menu(getShell(), SWT.POP_UP);
			createElementButton(menu, "Replace", ModeFeature.class, () -> {
				createFlowDialog.addInModeFeature(Optional.of(() -> {
					modeFeatureSelections.remove(boc);
					setColor(boc, null);
				}), boc, false);
			});

			final MenuItem cascadeItem = new MenuItem(menu, SWT.CASCADE);
			cascadeItem.setText("Insert");
			final Menu subMenu = new Menu(menu);
			cascadeItem.setMenu(subMenu);

			createElementButton(subMenu, "Before", ModeFeature.class, () -> {
				createFlowDialog.addInModeFeature(boc, false);
			});

			createElementButton(subMenu, "After", ModeFeature.class, () -> {
				createFlowDialog.addInModeFeature(boc, true);
			});

			createRemoveButton(menu, () -> {
				modeFeatureSelections.remove(boc);
				setColor(boc, null);
			});

			segmentButton.setMenu(menu);
			segmentButton.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					final Point location = segmentButton.getLocation();
					menu.setLocation(Display.getCurrent()
							.map(createFlowDialog.flowComposite, null,
									new Point(location.x, location.y + segmentButton.getSize().y)));
					menu.setVisible(true);
				};
			});

			return segmentButton;
		}

		/**
		 * Determines if the selected element is valid to be added to End to End Flow
		 * @param selectedEle - selected element
		 * @param context - context of element
		 */
		private boolean isValid(final Element selectedEle, final Context context) {
			if (selectedEle instanceof ModeFeature) {
				return true;
			}

			// First selection qualifications
			if (segmentSelections.isEmpty()) {
				if (selectedEle instanceof FlowSpecification || selectedEle instanceof Subcomponent) {
					return isValidFirstElement(selectedEle, context);
				}

				return false;
			}

			return selectedEle instanceof EndToEndFlowElement;
		}

		/**
		 * @param selectedEle - current element
		 * @param context - current context
		 * @return - true or false depending on if the selected element is a valid start element
		 */
		private boolean isValidFirstElement(final Element selectedEle, final Context context) {
			return (selectedEle instanceof FlowSpecification
					&& ((FlowSpecification) selectedEle).getKind() == FlowKind.SOURCE
					&& context instanceof Subcomponent) || selectedEle instanceof Subcomponent;
		}

		@Override
		protected void configureShell(final Shell newShell) {
			super.configureShell(newShell);
			newShell.setText("End To End Flow Specification Tool");
			newShell.setLocation(
					UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50));
			newShell.setSize(800, 400);
			newShell.setMinimumSize(460, 215);
		}

		@Override
		public void create() {
			super.create();
			setDefaultTitle();
			setMessage(CreateEndToEndFlowSpecificationTool.this.getDialogMessage());
			ContextHelpUtil.setHelp(getShell(), ContextHelpUtil.END_TO_END_TOOL);
		}

		private void setDefaultTitle() {
			setTitle("Creating End To End Flow Specification");
		}

		@Override
		protected Control createDialogArea(final Composite parent) {
			final Composite area = FlowDialogUtil.createFlowArea(parent, SWT.NONE);
			final Composite parentComposite = FlowDialogUtil.createFlowArea(area, SWT.BORDER);
			flowComposite = FlowDialogUtil.createFlowComposite(parentComposite);

			createButtonComposite(parentComposite);
			errorTableViewer = FlowDialogUtil.createErrorTableViewer(new Composite(area, SWT.NONE));
			return flowComposite;
		}

		private void createButtonComposite(final Composite parent) {
			final Composite buttonComposite = new Composite(parent, SWT.NONE);
			final RowLayout rowLayout = new RowLayout();
			rowLayout.center = true;
			rowLayout.spacing = 5;
			rowLayout.fill = false;
			rowLayout.marginLeft = 10;
			buttonComposite.setLayout(rowLayout);
		}

		private void addSegment() {
			try {
				createFlowDialog.getShell().setVisible(false);
				if (elementSelectionDlg.open() == Window.OK && elementSelectionDlg != null) {
					final BusinessObjectContext selectedBoc = getReplacementBoc()
							.orElseThrow(() -> new RuntimeException("Starting segment not selected."));
					final List<DiagramElement> highlightableSegments = new ArrayList<>();
					final BusinessObjectContext selectedCi = AgeHandlerUtil.getSelectedBusinessObjectContexts().get(0);
					// Find ancestors of the end to end flow container
					final Queue<Element> ancestors = FlowToolUtil.getAncestors(selectedCi, Optional.empty());
					// Find the diagram element of the container
					final BusinessObjectContext ancestor = FlowToolUtil
							.getAncestorDiagramElement(editor.getDiagram(), ancestors)
							.orElseThrow(() -> new RuntimeException(
									"Cannot find container: " + selectedCi.getBusinessObject()));
					final EndToEndFlow selectedETEFlow = (EndToEndFlow) AgeAadlUtil
							.getRootRefinedElement((NamedElement) selectedBoc.getBusinessObject());
					if (createFlowDialog.newETEFlowName.getText().isEmpty()) {
						createFlowDialog.setEndToEndFlowName((Namespace) selectedCi.getBusinessObject());
					}
					FlowToolUtil.findSegmentDiagramElements(selectedETEFlow, ancestor, highlightableSegments);

					segmentSelections.add(new SegmentData(selectedBoc, highlightableSegments));
					highlightableSegments.forEach(de -> setColor(de, Color.ORANGE.darker()));

					updateSegments();

					final EndToEndFlow endToEndFlow = createEndToEndFlow();
					final boolean isValid = isEndToEndFlowValid(endToEndFlow);
					setErrorMessage(null);
					setMessage(getDialogMessage());
					updateWidgets(isValid);
				}
			} finally {
				elementSelectionDlg = null;
				createFlowDialog.getShell().setVisible(true);
			}
		}

		private void addSegment(final SegmentData segmentData, final boolean isInsertAfter) {
			addSegment(Optional.empty(), segmentData, isInsertAfter);
		}

		private void addInModeFeature(final BusinessObjectContext boc, final boolean isInsertAfter) {
			addInModeFeature(Optional.empty(), boc, isInsertAfter);
		}

		private void addSegment(final Optional<Runnable> opRunnable, final SegmentData segmentData,
				final boolean isInsertAfter) {
			createFlowDialog.getShell().setVisible(false);
			if (elementSelectionDlg.open() == Window.OK && elementSelectionDlg != null) {
				final Optional<BusinessObjectContext> optBoc = getReplacementBoc();
				if (optBoc.isPresent()) {
					final BusinessObjectContext selectedBoc = optBoc.get();
					final List<DiagramElement> highlightableSegments = new ArrayList<>();
					if (selectedBoc.getBusinessObject() instanceof EndToEndFlow) {
						final BusinessObjectContext selectedCi = AgeHandlerUtil.getSelectedBusinessObjectContexts()
								.get(0);
						// Find ancestors of the end to end flow container
						final Queue<Element> ancestors = FlowToolUtil.getAncestors(selectedCi, Optional.empty());
						// Find the diagram element of the container
						final BusinessObjectContext ancestor = FlowToolUtil
								.getAncestorDiagramElement(editor.getDiagram(), ancestors)
								.orElseThrow(() -> new RuntimeException(
										"Cannot find container: " + selectedCi.getBusinessObject()));
						FlowToolUtil.findSegmentDiagramElements((EndToEndFlow) selectedBoc.getBusinessObject(),
								ancestor, highlightableSegments);
					}

					int insertIndex = segmentSelections.indexOf(segmentData);
					if (isInsertAfter) {
						insertIndex++;
					}

					final SegmentData segmentDataToAdd = new SegmentData(selectedBoc, highlightableSegments);
					segmentSelections.add(insertIndex, segmentDataToAdd);

					opRunnable.ifPresent(runnable -> runnable.run());

					// Update new and existing segment colors based on index
					final Color newSegmentColor = segmentSelections.indexOf(segmentDataToAdd) == 0
							? Color.ORANGE.darker()
									: Color.MAGENTA.darker();
					setColor(segmentDataToAdd, newSegmentColor);

					final Color updateSegmentColor = segmentSelections.indexOf(segmentData) == 0 ? Color.ORANGE.darker()
							: Color.MAGENTA.darker();
					setColor(segmentDataToAdd, updateSegmentColor);
				}

				updateSegments();

				final EndToEndFlow endToEndFlow = createEndToEndFlow();
				final boolean isValid = isEndToEndFlowValid(endToEndFlow);
				setErrorMessage(null);
				setMessage(getDialogMessage());
				updateWidgets(isValid);
			}

			elementSelectionDlg = null;
			createFlowDialog.getShell().setVisible(true);
		}

		private void addInModeFeature(final Optional<Runnable> opRunnable, final BusinessObjectContext boc,
				final boolean isInsertAfter) {
			createFlowDialog.getShell().setVisible(false);
			if (elementSelectionDlg.open() == Window.OK && elementSelectionDlg != null) {
				final Optional<BusinessObjectContext> optBoc = getReplacementBoc();
				if (optBoc.isPresent()) {
					final BusinessObjectContext selectedBoc = optBoc.get();
					int insertIndex = modeFeatureSelections.indexOf(boc);
					if (isInsertAfter) {
						insertIndex++;
					}
					modeFeatureSelections.add(insertIndex, selectedBoc);
					setColor(selectedBoc, Color.MAGENTA.brighter());

					opRunnable.ifPresent(runnable -> runnable.run());
				}

				updateSegments();

				final EndToEndFlow endToEndFlow = createEndToEndFlow();
				final boolean isValid = isEndToEndFlowValid(endToEndFlow);
				setErrorMessage(null);
				setMessage(getDialogMessage());
				updateWidgets(isValid);
			}

			elementSelectionDlg = null;
			createFlowDialog.getShell().setVisible(true);
		}

		private Optional<BusinessObjectContext> getReplacementBoc() {
			if (elementSelectionDlg.getSelection() instanceof BusinessObjectContext) {
				return Optional.of((BusinessObjectContext) elementSelectionDlg.getSelection());
			} else if (elementSelectionDlg.getSelection() instanceof EndToEndFlow) {
				return Optional.of(new BusinessObjectNode(null, UUID.randomUUID(), null,
						elementSelectionDlg.getSelection(), Completeness.UNKNOWN, false));
			}

			return Optional.empty();
		}

		@Override
		protected Control createButtonBar(final Composite parent) {
			final Composite buttonBar = new Composite(parent, SWT.NONE);
			final GridLayout buttonBarLayout = new GridLayout();
			buttonBarLayout.horizontalSpacing = 0;
			buttonBarLayout.numColumns = 4;
			buttonBar.setLayout(buttonBarLayout);
			final GridData buttonBarData = new GridData(SWT.FILL, SWT.BOTTOM, true, false);
			buttonBar.setLayoutData(buttonBarData);
			buttonBar.setFont(parent.getFont());

			final Label nameLabel = new Label(buttonBar, SWT.NONE);
			nameLabel.setText("Name: ");
			final GridData nameLabelData = new GridData(SWT.LEFT, SWT.CENTER, false, true);
			nameLabelData.horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
			nameLabel.setLayoutData(nameLabelData);

			newETEFlowName = new Text(buttonBar, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
			newETEFlowName.setEnabled(true);
			final GridData nameTextData = new GridData(SWT.FILL, SWT.CENTER, true, false);
			newETEFlowName.setLayoutData(nameTextData);
			newETEFlowName.setEditable(true);
			newETEFlowName.setOrientation(SWT.LEFT_TO_RIGHT);

			newETEFlowName.addKeyListener(new KeyAdapter() {
				@Override
				public void keyReleased(final KeyEvent e) {
					endToEndFlowName = newETEFlowName.getText();
					final EndToEndFlow endToEndFlow = createEndToEndFlow();
					final boolean isValid = isEndToEndFlowValid(endToEndFlow);
					setErrorMessage(null);
					setMessage(getDialogMessage());
					updateWidgets(isValid);
				}
			});

			super.createButtonBar(buttonBar);
			final Button okBtn = getButton(IDialogConstants.OK_ID);
			okBtn.setEnabled(false);
			okBtn.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(final SelectionEvent e) {
					flow = createEndToEndFlow();
				}
			});

			return buttonBar;
		}

		private abstract class ElementSelectionDialog extends TitleAreaDialog {
			private final String title;
			protected Object selection;
			protected final Class<?> qualifiedType;
			protected final String dialogMessage;

			public ElementSelectionDialog(final Shell parentShell, final String title, final Class<?> type,
					final String dialogMessage) {
				super(parentShell);
				this.title = title;
				this.dialogMessage = dialogMessage;
				this.qualifiedType = type;
				setShellStyle(SWT.CLOSE | SWT.PRIMARY_MODAL | SWT.BORDER | SWT.TITLE | SWT.RESIZE);
			}

			@Override
			public void create() {
				super.create();
				setTitle(title);
			}

			public Object getSelection() {
				return selection;
			}

			public void setSelection(final BusinessObjectContext selection) {
				this.selection = selection;
			}
		}

		/**
		 * Dialog for selecting end to end flow segments from diagram or outline
		 */
		private class SelectSegmentOrModeFeatureDialog extends ElementSelectionDialog {
			private Label selectionLabel;

			public SelectSegmentOrModeFeatureDialog(final Shell parentShell, final Class<?> qualifiedType,
					final String title) {
				super(parentShell, title, qualifiedType, "Select element from diagram or outline view.");
				setShellStyle(SWT.CLOSE | SWT.PRIMARY_MODAL | SWT.BORDER | SWT.TITLE | SWT.RESIZE);
			}

			@Override
			public void create() {
				super.create();
				setMessage(dialogMessage);
				final Button okBtn = getButton(IDialogConstants.OK_ID);

				final List<BusinessObjectContext> bocs = AgeHandlerUtil.getSelectedBusinessObjectContexts();
				if (bocs.size() == 1 && bocs.get(0).getBusinessObject() instanceof NamedElement) {
					final NamedElement selectedElement = (NamedElement) bocs.get(0).getBusinessObject();
					okBtn.setEnabled(qualifiedType.isInstance(selectedElement));
					selectionLabel.setText(AgeAadlUtil.getRootName(selectedElement));
					setErrorMessage(null);
				} else {
					okBtn.setEnabled(false);
					setErrorMessage("Invalid selection.\n " + dialogMessage);
					selectionLabel.setText("<Invalid>");
				}
			}

			@Override
			protected void configureShell(final Shell newShell) {
				super.configureShell(newShell);
				newShell.setText("Element Selection");
				newShell.setLocation(
						UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50));
				newShell.setSize(400, 200);
				newShell.setMinimumSize(400, 200);
			}

			@Override
			public void setSelection(final BusinessObjectContext selection) {
				super.setSelection(selection);
				updateDialog(selection);
				getShell().layout(true, true);
			}

			private void updateDialog(final BusinessObjectContext boc) {
				final Object bo = boc.getBusinessObject();
				final Button okBtn = getButton(IDialogConstants.OK_ID);
				if (okBtn != null) {
					final boolean isEnabled = !modeFeatureSelections.contains(boc) && qualifiedType.isInstance(bo);
					okBtn.setEnabled(isEnabled);
				}

				if (bo instanceof NamedElement) {
					selectionLabel.setText(AgeAadlUtil.getRootName((NamedElement) bo));
					setErrorMessage(null);
				} else {
					setErrorMessage("Invalid selection.\n " + dialogMessage);
					selectionLabel.setText("<Invalid>");
				}
			}

			@Override
			protected Control createDialogArea(final Composite parent) {
				final Composite composite = new Composite(parent, SWT.NONE);
				final GridLayout layout = new GridLayout();
				layout.marginHeight = 0;
				layout.marginWidth = 0;
				layout.verticalSpacing = 0;
				layout.horizontalSpacing = 5;
				layout.numColumns = 2;
				layout.marginLeft = 10;
				layout.marginTop = 8;
				composite.setLayout(layout);

				composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
				composite.setFont(parent.getFont());

				final Label label = new Label(composite, SWT.NONE);
				label.setText("Selected Element: ");
				label.setLayoutData(
						GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create());

				selectionLabel = new Label(composite, SWT.NONE);
				final List<BusinessObjectContext> bocs = AgeHandlerUtil.getSelectedBusinessObjectContexts();
				if (bocs.size() == 1 && bocs.get(0).getBusinessObject() instanceof NamedElement) {
					final String text = AgeAadlUtil
							.getRootRefinedElement((NamedElement) bocs.get(0).getBusinessObject())
							.getName();
					selectionLabel.setText(text);
				} else {
					selectionLabel.setText("<Invalid>");
				}

				selectionLabel.setLayoutData(
						GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create());

				return composite;
			}
		}

		/**
		 * Dialog for selecting end to end flows as segments
		 */
		private class SelectEndToEndFlowDialog extends ElementSelectionDialog {
			private ComboViewer comboViewer;
			private final String none = "<None>";

			public SelectEndToEndFlowDialog(final Shell parentShell, final Class<?> type, final String title) {
				super(parentShell, title, type, "Select an end to end flow container to populate the drop-down.");
				setShellStyle(SWT.CLOSE | SWT.PRIMARY_MODAL | SWT.BORDER | SWT.TITLE | SWT.RESIZE);
			}

			@Override
			public void create() {
				super.create();
				setMessage(dialogMessage);
				getButton(IDialogConstants.OK_ID).setEnabled(false);
			}

			@Override
			protected void configureShell(final Shell newShell) {
				super.configureShell(newShell);
				newShell.setText("Element Selection");
				newShell.setLocation(
						UiUtil.getOffsetRectangleLocation(Display.getCurrent().getActiveShell().getBounds(), 50, 50));
				newShell.setSize(400, 200);
				newShell.setMinimumSize(400, 200);
			}

			@Override
			public void setSelection(final BusinessObjectContext selection) {
				comboViewer.setInput(getOwnedEndToEndFlows(getComponentImplementation(selection)));
				comboViewer.setSelection(new StructuredSelection(none));
				updateDialog(selection.getBusinessObject());
				this.selection = selection;
				getShell().layout(true, true);
			}

			private void updateDialog(final Object bo) {
				final boolean isInstance = qualifiedType.isInstance(bo) || bo instanceof EndToEndFlow;
				final Button okBtn = getButton(IDialogConstants.OK_ID);
				if (okBtn != null) {
					okBtn.setEnabled(isInstance);
				}

				setMessage(dialogMessage
						+ (isInstance ? "\nSelected: " + AgeAadlUtil.getRootName((NamedElement) bo) : ""));
			}

			// Get component implementation from selection
			private Optional<ComponentImplementation> getComponentImplementation(final Object bo) {
				if (bo instanceof BusinessObjectContext) {
					return getComponentImplementation(((BusinessObjectContext) bo).getBusinessObject());
				} else if (bo instanceof ComponentImplementation) {
					return Optional.of((ComponentImplementation) bo);
				} else if (bo instanceof Subcomponent) {
					return Optional.ofNullable(((Subcomponent) bo).getComponentImplementation());
				}

				return Optional.empty();
			}

			@Override
			protected Control createDialogArea(final Composite parent) {
				final Composite composite = new Composite(parent, SWT.NONE);
				final GridLayout layout = new GridLayout();
				layout.marginHeight = 0;
				layout.marginWidth = 0;
				layout.verticalSpacing = 0;
				layout.horizontalSpacing = 5;
				layout.numColumns = 2;
				layout.marginLeft = 10;
				layout.marginTop = 8;
				composite.setLayout(layout);

				composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
				composite.setFont(parent.getFont());

				final Label label = new Label(composite, SWT.NONE);
				label.setText("End to End Flow: ");
				label.setLayoutData(
						GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create());

				comboViewer = new ComboViewer(composite, SWT.READ_ONLY);
				comboViewer.getCombo()
				.setLayoutData(
						GridDataFactory.fillDefaults().grab(true, true).align(SWT.CENTER, SWT.CENTER).create());

				comboViewer.setContentProvider(ArrayContentProvider.getInstance());
				comboViewer.setLabelProvider(new LabelProvider() {
					@Override
					public String getText(final Object element) {
						if (element instanceof String) {
							return (String) element;
						}

						return AgeAadlUtil.getRootName((NamedElement) element);
					}
				});

				comboViewer.addSelectionChangedListener(event -> {
					final Object selectedElement = event.getStructuredSelection().getFirstElement();
					if (selection != selectedElement) {
						updateDialog(selectedElement);
						selection = selectedElement;
					}
				});

				final List<BusinessObjectContext> selectedBocs = AgeHandlerUtil.getSelectedBusinessObjectContexts();
				if (selectedBocs.size() == 1) {
					comboViewer.setInput(getOwnedEndToEndFlows(getComponentImplementation(selectedBocs.get(0))));
				}

				comboViewer.setSelection(new StructuredSelection(none));

				return composite;
			}

			private List<Object> getOwnedEndToEndFlows(final Optional<ComponentImplementation> optCi) {
				final List<Object> input = new ArrayList<>();
				input.add("<None>");
				final List<EndToEndFlow> eTEFlows = optCi
						.map(ci -> ci.getAllEndToEndFlows().stream().collect(Collectors.toList()))
						.orElse(new ArrayList<>());
				input.addAll(eTEFlows);
				return input;
			}
		}
	}
}