CreateDiagramComposite.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.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
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.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.osate.ge.internal.util.DiagramUtil;
import org.osate.ge.swt.SwtUtil;
import com.google.common.collect.ImmutableCollection;
/**
* Composite for prompting the user for a name and type for a diagram. Results in a filename and diagram type.
*/
public class CreateDiagramComposite<DiagramType> extends Composite {
public static final String WIDGET_ID_NAME = "org.osate.ge.createDiagram.name";
public static final String WIDGET_ID_TYPE = "org.osate.ge.createDiagram.type";
public static interface Model<DiagramType> {
ImmutableCollection<DiagramType> getDiagramTypes();
String getDiagramTypeName(final DiagramType diagramType);
IProject getProject();
String getDefaultName();
DiagramType getDefaultDiagramType();
}
public static class Value<DiagramType> {
private final IFile file;
private final DiagramType diagramType;
Value(final IFile file, final DiagramType diagramType) {
this.file = file;
this.diagramType = diagramType;
}
public final IFile getFile() {
return file;
}
public final DiagramType getDiagramType() {
return diagramType;
}
public boolean isValid() {
return file != null && diagramType != null;
}
}
private final CopyOnWriteArrayList<SelectionListener> selectionListeners = new CopyOnWriteArrayList<>();
private Model<DiagramType> model;
private final Text nameField;
private IFile file;
private DiagramType diagramType;
private String errorMessage;
private final ComboViewer typeField;
@SuppressWarnings("unchecked")
public CreateDiagramComposite(final Composite parent, final Model<DiagramType> model) {
super(parent, SWT.NONE);
setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
setLayout(GridLayoutFactory.swtDefaults().numColumns(2).create());
final Label nameLabel = new Label(this, SWT.NONE);
nameLabel.setText("Name");
nameField = new Text(this, SWT.SINGLE | SWT.BORDER);
SwtUtil.setTestingId(nameField, WIDGET_ID_NAME);
nameField.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
nameField.addModifyListener(e -> {
if (CreateDiagramComposite.this.model != null) {
updateFile();
}
});
// Determine the initial name. A suffix will be added to the default name if the file already exists
final IProject project = model.getProject();
final String baseName = model.getDefaultName();
if (baseName != null) {
IFile tmpFile;
int nameCount = 1;
String name;
do {
final String suffix = nameCount == 1 ? "" : "(" + nameCount + ")";
name = baseName + suffix;
tmpFile = createFile(project, name);
nameCount++;
} while (tmpFile != null && tmpFile.exists());
nameField.setText(name);
}
//
// Create type field
//
final Label typeLabel = new Label(this, SWT.NONE);
typeLabel.setText("Type:");
typeField = new ComboViewer(this);
SwtUtil.setTestingId(typeField.getCombo(), WIDGET_ID_TYPE);
typeField.getCombo().setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
typeField.setComparator(new ViewerComparator());
typeField.setContentProvider(new ArrayContentProvider());
typeField.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
return model.getDiagramTypeName((DiagramType) element);
}
});
setModel(model);
}
@SuppressWarnings("unchecked")
public void setModel(final Model<DiagramType> model) {
this.model = Objects.requireNonNull(model, "model must not be null");
final Collection<DiagramType> diagramTypes = model.getDiagramTypes();
typeField.setInput(diagramTypes);
typeField.addSelectionChangedListener(event -> {
diagramType = (DiagramType) ((StructuredSelection) typeField.getSelection()).getFirstElement();
validate();
notifySelectionListeners();
});
// Reset the selected diagram type
diagramType = model.getDefaultDiagramType();
typeField.setSelection(diagramType == null ? StructuredSelection.EMPTY : new StructuredSelection(diagramType));
// Update the initial value for the file and validate initial values
updateFile();
}
private void updateFile() {
file = createFile(model.getProject(), nameField.getText());
validate();
notifySelectionListeners();
}
/**
*
* @param project
* @param name
* @return null if project is null.
*/
private IFile createFile(final IProject project, final String name) {
if (project == null) {
return null;
}
final IFolder diagramFolder = project.getFolder("diagrams/");
return diagramFolder.getFile(name + DiagramUtil.EXTENSION);
}
/**
* Selection listeners are called when the value of the widget changes.
* @param listener
*/
public void addSelectionListener(final SelectionListener listener) {
selectionListeners.add(listener);
}
private void notifySelectionListeners() {
final Event e = new Event();
e.widget = this;
final SelectionEvent selectionEvent = new SelectionEvent(e);
for (final SelectionListener l : selectionListeners) {
l.widgetSelected(selectionEvent);
}
}
private void validate() {
// Check values and set error message
if (model.getProject() == null) {
setErrorMessage("Invalid project.");
} else if (file == null) {
setErrorMessage("Enter a name.");
} else if (file.exists()) {
setErrorMessage("File '" + file.getProjectRelativePath().makeAbsolute().toString() + "' already exists.");
} else if (!nameField.getText().matches("\\S[\\w\\d \\(\\)-]*")) {
setErrorMessage("Name is not valid.");
} else if (diagramType == null) {
setErrorMessage("Select a diagram type.");
} else {
setErrorMessage(null);
}
}
private void setErrorMessage(final String value) {
this.errorMessage = value;
}
public String getErrorMessage() {
return this.errorMessage;
}
/**
* May return partial value. Use getErrorMessage() to determine if the value is complete.
* @return
*/
public Value<DiagramType> getValue() {
return new Value<DiagramType>(file, diagramType);
}
}