RestoreMissingDiagramElementsDialog.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.ui.dialogs;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ComboBoxViewerCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.osate.ge.internal.ui.util.ContextHelpUtil;
import com.google.common.collect.ImmutableMap;
/**
* Dialog intended to allow the user to update ghosted business objects so that they will be restored.
* @param <ElementType> the type of element being modified by the user.
* @param <BusinessObjectType> the type of the business object being selected by the user. Is not required to be a raw business object.
*/
public class RestoreMissingDiagramElementsDialog<ElementType, BusinessObjectType> {
public interface Model<ElementType, BusinessObjectType> {
Collection<ElementType> getElements();
String getParentLabel(ElementType element);
String getMissingReferenceLabel(ElementType element);
Collection<BusinessObjectType> getAvailableBusinessObjects(ElementType element);
String getBusinessObjectLabel(BusinessObjectType bo);
}
/**
* Result only contains a mapping for elements for which the user has specified an updated business object
*/
public static class Result<ElementType, BusinessObjectType> {
private Map<ElementType, BusinessObjectType> objectToNewBoMap;
private Result(final Map<ElementType, BusinessObjectType> objectToNewBoMap) {
this.objectToNewBoMap = objectToNewBoMap;
}
public Map<ElementType, BusinessObjectType> getObjectToNewBoMap() {
return objectToNewBoMap;
}
}
private static class BrokenElement<ElementType, BusinessObjectType> {
private final ElementType element;
private BusinessObjectType newBo;
private BrokenElement(final ElementType element, final BusinessObjectType newBo) {
this.element = element;
this.newBo = newBo;
}
private void setNewBusinessObject(final BusinessObjectType newBo) {
this.newBo = newBo;
}
}
private class InnerDialog extends Dialog {
private TableViewer tableViewer;
final Collection<BrokenElement<ElementType, BusinessObjectType>> updatedElements = model.getElements().stream()
.map(ob -> new BrokenElement<ElementType, BusinessObjectType>(ob, null)).collect(Collectors.toList());
protected InnerDialog(final Shell parentShell) {
super(parentShell);
setShellStyle(getShellStyle() | SWT.RESIZE);
}
@Override
protected void configureShell(final Shell newShell) {
super.configureShell(newShell);
newShell.setText("Restore Missing Diagram Elements");
newShell.setMinimumSize(250, 200);
newShell.setSize(600, 400);
ContextHelpUtil.setHelp(newShell, ContextHelpUtil.RESTORE_MISSING_DES);
}
@Override
protected Control createDialogArea(final Composite parent) {
final Composite area = (Composite) super.createDialogArea(parent);
final Composite container = new Composite(area, SWT.NONE);
container.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
tableViewer = new TableViewer(container, SWT.FULL_SELECTION | SWT.BORDER);
tableViewer.setUseHashlookup(true);
// Parent Column
final TableViewerColumn parentColumn = new TableViewerColumn(tableViewer, SWT.NONE);
parentColumn.getColumn().setText("Container");
parentColumn.setLabelProvider(new CellLabelProvider() {
@Override
public void update(final ViewerCell cell) {
@SuppressWarnings("unchecked")
final BrokenElement<ElementType, BusinessObjectType> updatedElement = (BrokenElement<ElementType, BusinessObjectType>) cell
.getElement();
cell.setText(model.getParentLabel(updatedElement.element));
}
});
final BrokenElementColumnComparator comp = new BrokenElementColumnComparator();
parentColumn.getViewer().setComparator(comp);
final Table table = tableViewer.getTable();
parentColumn.getColumn().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
comp.setColumn(0);
int dir = comp.getDirection();
table.setSortDirection(dir);
table.setSortColumn(parentColumn.getColumn());
tableViewer.refresh();
}
});
// Missing Reference
final TableViewerColumn missingReferenceColumn = new TableViewerColumn(tableViewer, SWT.NONE);
missingReferenceColumn.getColumn().setText("Missing Reference");
missingReferenceColumn.getViewer().setComparator(comp);
missingReferenceColumn.getColumn().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
comp.setColumn(1);
int dir = comp.getDirection();
table.setSortDirection(dir);
table.setSortColumn(missingReferenceColumn.getColumn());
tableViewer.refresh();
}
});
missingReferenceColumn.setLabelProvider(new CellLabelProvider() {
@Override
public void update(final ViewerCell cell) {
@SuppressWarnings("unchecked")
final BrokenElement<ElementType, BusinessObjectType> updatedElement = (BrokenElement<ElementType, BusinessObjectType>) cell
.getElement();
cell.setText(model.getMissingReferenceLabel(updatedElement.element));
}
});
// Dropdown Column for New Business Object
final TableViewerColumn newBusinessObjectColumn = new TableViewerColumn(tableViewer, SWT.READ_ONLY);
final TableColumnLayout tcl = new TableColumnLayout();
container.setLayout(tcl);
tcl.setColumnData(parentColumn.getColumn(), new ColumnWeightData(15, true));
tcl.setColumnData(missingReferenceColumn.getColumn(), new ColumnWeightData(10, true));
tcl.setColumnData(newBusinessObjectColumn.getColumn(), new ColumnWeightData(10, true));
newBusinessObjectColumn.getColumn().setText("New Reference");
newBusinessObjectColumn.getColumn().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
comp.setColumn(2);
int dir = comp.getDirection();
table.setSortDirection(dir);
table.setSortColumn(newBusinessObjectColumn.getColumn());
tableViewer.refresh();
}
});
newBusinessObjectColumn.setLabelProvider(new CellLabelProvider() {
@Override
public void update(final ViewerCell cell) {
@SuppressWarnings("unchecked")
final BrokenElement<ElementType, BusinessObjectType> updatedElement = (BrokenElement<ElementType, BusinessObjectType>) cell
.getElement();
cell.setText(getBusinessObjectLabel(updatedElement.newBo));
}
});
newBusinessObjectColumn.setEditingSupport(new NewBusinessObjectEditingSupport(tableViewer));
table.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).span(2, 1).minSize(SWT.DEFAULT, 100)
.hint(200, SWT.DEFAULT).create());
table.setHeaderVisible(true);
table.setLinesVisible(true);
table.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
tableViewer.setInput(updatedElements);
return area;
}
private class BrokenElementColumnComparator extends ViewerComparator {
private int propertyIndex;
private static final int DESCENDING = 1;
private int direction = DESCENDING;
private BrokenElementColumnComparator() {
this.propertyIndex = 0;
direction = 1 - DESCENDING;
}
private int getDirection() {
return direction == 1 ? SWT.DOWN : SWT.UP;
}
private void setColumn(int column) {
if (column == this.propertyIndex) {
// Same column as last sort; toggle the direction
direction = 1 - direction;
} else {
// New column; do an ascending sort
this.propertyIndex = column;
direction = DESCENDING;
}
}
@SuppressWarnings("unchecked")
@Override
public int compare(final Viewer viewer, final Object e1, final Object e2) {
final BrokenElement<ElementType, BusinessObjectType> ue1 = (BrokenElement<ElementType, BusinessObjectType>) e1;
final BrokenElement<ElementType, BusinessObjectType> ue2 = (BrokenElement<ElementType, BusinessObjectType>) e2;
int rc = 0;
switch (propertyIndex) {
case 0:
final String ue1Label = model.getParentLabel(ue1.element);
final String ue2Label = model.getParentLabel(ue2.element);
rc = ue1Label.compareToIgnoreCase(ue2Label);
break;
case 1:
final String curContextLabel1 = model.getMissingReferenceLabel(ue1.element);
final String curContextLabel2 = model.getMissingReferenceLabel(ue2.element);
rc = curContextLabel1.compareToIgnoreCase(curContextLabel2);
break;
case 2:
final String newContextLabel1 = getBusinessObjectLabel(ue1.newBo);
final String newContextLabel2 = getBusinessObjectLabel(ue2.newBo);
rc = newContextLabel1.compareToIgnoreCase(newContextLabel2);
break;
default:
rc = 0;
}
// If descending order, flip the direction
if (direction == DESCENDING) {
rc = -rc;
}
return rc;
}
}
/**
* Returns a blank string if bo is null, otherwise delegates to the model.
* @param bo
* @return
*/
@SuppressWarnings("unchecked")
private String getBusinessObjectLabel(final Object bo) {
return bo == null ? "" : model.getBusinessObjectLabel((BusinessObjectType) bo);
}
private class NewBusinessObjectEditingSupport extends EditingSupport {
private final ComboBoxViewerCellEditor editor;
public NewBusinessObjectEditingSupport(final TableViewer viewer) {
super(viewer);
editor = new ComboBoxViewerCellEditor(viewer.getTable());
editor.getViewer().setComparator(new ViewerComparator());
editor.setContentProvider(ArrayContentProvider.getInstance());
editor.setLabelProvider(new LabelProvider() {
@Override
public String getText(final Object element) {
return getBusinessObjectLabel(element);
}
});
}
@SuppressWarnings("unchecked")
@Override
protected CellEditor getCellEditor(final Object element) {
editor.setInput(model.getAvailableBusinessObjects(
((BrokenElement<ElementType, BusinessObjectType>) element).element));
return editor;
}
@Override
protected boolean canEdit(final Object element) {
return true;
}
@SuppressWarnings("unchecked")
@Override
protected Object getValue(final Object element) {
return ((BrokenElement<ElementType, BusinessObjectType>) element).newBo;
}
@SuppressWarnings("unchecked")
@Override
protected void setValue(final Object element, final Object value) {
final BrokenElement<ElementType, BusinessObjectType> updatedElement = (BrokenElement<ElementType, BusinessObjectType>) element;
updatedElement.setNewBusinessObject((BusinessObjectType) value);
tableViewer.update(element, null);
}
}
}
private final InnerDialog dlg;
private final Model<ElementType, BusinessObjectType> model;
private RestoreMissingDiagramElementsDialog(final Shell parentShell,
final Model<ElementType, BusinessObjectType> model) {
this.model = model;
this.dlg = new InnerDialog(parentShell);
}
/**
* Returns null if there were no changes or if the user did not select OK.
* @return
*/
private Result<ElementType, BusinessObjectType> open() {
if (dlg.open() == Window.OK) {
final ImmutableMap<ElementType, BusinessObjectType> elementToNewBo = dlg.updatedElements.stream()
.filter(ue -> ue.newBo != null)
.collect(ImmutableMap.toImmutableMap(ue -> ue.element, ue -> ue.newBo));
return elementToNewBo.isEmpty() ? null : new Result<>(elementToNewBo);
} else {
return null;
}
}
/**
*
* @param ElementType
* @param ElementType
* @return see open()
*/
public static <ElementType, BusinessObjectType> Result<ElementType, BusinessObjectType> show(
final Shell parentShell, final Model<ElementType, BusinessObjectType> ElementType) {
final RestoreMissingDiagramElementsDialog<ElementType, BusinessObjectType> dlg = new RestoreMissingDiagramElementsDialog<>(
parentShell, ElementType);
return dlg.open();
}
public static void main(final String[] args) {
final Model<Integer, Integer> model = new Model<Integer, Integer>() {
@Override
public Collection<Integer> getElements() {
return IntStream.range(0, 100).boxed().collect(Collectors.toList());
}
@Override
public String getParentLabel(Integer element) {
return "Parent" + element;
}
@Override
public String getMissingReferenceLabel(Integer element) {
return "MissingRef" + element;
}
@Override
public Collection<Integer> getAvailableBusinessObjects(Integer element) {
return IntStream.range(0, 100).boxed().collect(Collectors.toList());
}
@Override
public String getBusinessObjectLabel(Integer bo) {
return "NewBo" + bo;
}
};
final Result<Integer, Integer> result = RestoreMissingDiagramElementsDialog.show(new Shell(), model);
if (result != null) {
result.objectToNewBoMap.forEach((e, newBo) -> System.out
.println(model.getParentLabel(e) + " : " + model.getBusinessObjectLabel(newBo)));
}
}
}