blob: 120b40affd6df46edd9290dc6e50322d6b69b0c8 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2015-2021 Robert Bosch GmbH and others.
*
* 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
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.amalthea.converters.ui.dialog;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.app4mc.amalthea.converters.common.MigrationHelper;
import org.eclipse.app4mc.amalthea.converters.common.MigrationInputFile;
import org.eclipse.app4mc.amalthea.converters.common.MigrationProcessor;
import org.eclipse.app4mc.amalthea.converters.common.MigrationSettings;
import org.eclipse.app4mc.amalthea.converters.common.utils.ModelVersion;
import org.eclipse.app4mc.amalthea.converters.ui.jobs.IMigrationJobConstants;
import org.eclipse.app4mc.amalthea.converters.ui.jobs.ModelMigrationJob;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
public class ModelMigrationDialog extends Dialog {
private static final String MODEL_MIGRATION = "AMALTHEA Model Migration";
private MigrationProcessor migrationProcessor;
private IProject iProject;
private IResource selectedContainer;
private Label messageLabel;
private Combo migModelVersionCombo;
private CheckboxTableViewer tableViewer;
private MigrationSettings currentSettings;
private MigrationSettings migrationSettings;
private MigrationSettings recursiveMigrationSettings;
private ArrayList<MigrationInputFile> selectedFiles = new ArrayList<>();
private ArrayList<String> warningFolders = new ArrayList<>();
public ModelMigrationDialog(Shell parentShell, MigrationProcessor migrationProcessor, MigrationSettings settings, IProject iProject) {
super(parentShell);
setShellStyle(SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM | SWT.RESIZE);
if (migrationProcessor == null) {
throw new IllegalStateException("MigrationProcessor cannot be null");
}
this.migrationProcessor = migrationProcessor;
if (settings == null) {
this.migrationSettings = new MigrationSettings();
} else {
this.migrationSettings = settings;
}
this.currentSettings = this.migrationSettings;
this.iProject = iProject;
}
public void setSelectedContainer(IContainer container) {
this.selectedContainer = container;
}
@Override
protected void configureShell(final Shell newShell) {
super.configureShell(newShell);
newShell.setText(MODEL_MIGRATION);
}
@Override
protected Point getInitialSize() {
return new Point(700, 600);
}
@Override
protected Control createDialogArea(final Composite parent) {
parent.setToolTipText(MODEL_MIGRATION);
Composite parentComposite = (Composite) super.createDialogArea(parent);
GridLayout gridLayout = (GridLayout) parentComposite.getLayout();
gridLayout.numColumns = 1;
Group grpInitialModel = new Group(parentComposite, SWT.NONE);
grpInitialModel.setText("AMALTHEA Models");
grpInitialModel.setLayout(new GridLayout());
GridDataFactory
.fillDefaults()
.grab(true, true)
.applyTo(grpInitialModel);
Composite tableComposite = createFileTableViewer(grpInitialModel);
GridDataFactory
.fillDefaults()
.grab(true, true)
.applyTo(tableComposite);
this.messageLabel = new Label(grpInitialModel, SWT.WRAP);
GridDataFactory
.fillDefaults()
.grab(true, false)
.applyTo(this.messageLabel);
updateWarningMessage();
final Group grpMigrationModels = new Group(parentComposite, SWT.NONE);
grpMigrationModels.setText("Migration details");
grpMigrationModels.setLayout(new GridLayout(3, false));
GridDataFactory
.fillDefaults()
.grab(true, false)
.applyTo(grpMigrationModels);
Label migModelVersionText = new Label(grpMigrationModels, SWT.NONE);
migModelVersionText.setText("Model Version");
GridDataFactory
.fillDefaults()
.grab(false, false)
.applyTo(migModelVersionText);
this.migModelVersionCombo = new Combo(grpMigrationModels, SWT.READ_ONLY);
GridDataFactory
.swtDefaults()
.grab(false, false)
.span(2, 1)
.applyTo(migModelVersionCombo);
populateMigrationVersionCombo();
// Added SWT.Modify listener to Checkbox (for selection of output model version) - this is work around for JFace databinding issue reported on MAC
migModelVersionCombo.addListener(SWT.Modify, event -> {
String selectedOutputModelVersion = migModelVersionCombo.getText();
if (selectedOutputModelVersion != null && selectedOutputModelVersion.length() > 0) {
currentSettings.setMigrationModelVersion(selectedOutputModelVersion);
}
});
Button backup = new Button(grpMigrationModels, SWT.CHECK);
backup.setText("Create backup file");
backup.setToolTipText("Copies the file to migrate next to the original file as a backup.");
backup.setSelection(this.currentSettings.isCreateBackupFile());
GridDataFactory
.swtDefaults()
.grab(false, false)
.span(3, 1)
.applyTo(backup);
backup.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
currentSettings.setCreateBackupFile(backup.getSelection());
}
});
if (this.selectedContainer != null) {
Button recursive = new Button(grpMigrationModels, SWT.CHECK);
recursive.setText("Recursive");
recursive.setToolTipText("Migrate all model files in all sub-folders");
GridDataFactory
.swtDefaults()
.grab(false, false)
.span(3, 1)
.applyTo(recursive);
recursive.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Button button = (Button) e.getSource();
if (button.getSelection()) {
if (recursiveMigrationSettings == null) {
recursiveMigrationSettings = new MigrationSettings();
recursiveMigrationSettings.setProject(migrationSettings.getProject());
recursiveMigrationSettings.setMigrationModelVersion(migrationSettings.getMigrationModelVersion());
Path modelFilePath = Paths.get(selectedContainer.getLocationURI());
try (Stream<Path> directoryStream = Files.walk(modelFilePath, Integer.MAX_VALUE)) {
List<File> modelFiles = directoryStream
.filter(Files::isRegularFile)
.filter(file -> file.toString().toLowerCase().endsWith(".amxmi"))
.map(Path::toFile)
.collect(Collectors.toList());
recursiveMigrationSettings.getMigModelFiles().addAll(MigrationHelper.populateModels(modelFiles, recursiveMigrationSettings));
} catch (Exception ex) {
MessageDialog.openError(getParentShell(), "Failed to load model files", "Failed to load model files: " + ex.getLocalizedMessage());
return;
}
}
currentSettings = recursiveMigrationSettings;
} else {
currentSettings = migrationSettings;
}
selectedFiles = new ArrayList<>(currentSettings.getMigModelFiles());
tableViewer.setInput(currentSettings.getMigModelFiles());
tableViewer.setAllChecked(true);
updateWarningMessage();
populateMigrationVersionCombo();
}
});
}
return parentComposite;
}
private void updateWarningMessage() {
Map<String, Map<String, List<MigrationInputFile>>> collect = this.selectedFiles.stream()
.collect(Collectors.groupingBy(
mig -> mig.getOriginalFile().getParent(),
Collectors.groupingBy(MigrationInputFile::getModelVersion)));
this.warningFolders.clear();
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Map<String, List<MigrationInputFile>>> folderMapping : collect.entrySet()) {
if (folderMapping.getValue().size() > 1) {
String ident = this.currentSettings.getProject().toURI().relativize(new File(folderMapping.getKey()).toURI()).getPath();
builder.append("The folder ").append(ident).append(" contains multiple files with different model versions!\n");
this.warningFolders.add(ident);
}
}
this.messageLabel.setText(builder.toString());
this.messageLabel.setVisible(this.messageLabel.getText() != null && !this.messageLabel.getText().isEmpty());
this.messageLabel.getParent().layout();
this.tableViewer.refresh(true);
}
private void populateMigrationVersionCombo() {
int selectedIndex = 0;
List<String> comboInput = null;
List<String> allSupportedVersions = ModelVersion.getAllSupportedVersions();
int totalVersionSize = allSupportedVersions.size();
String version = this.currentSettings.getInputModelVersion();
if (version != null) {
int startIndex = allSupportedVersions.indexOf(version);
if (startIndex != -1) {
comboInput = allSupportedVersions.subList(startIndex + 1, totalVersionSize);
}
} else {
String smallestInputVersion = null;
for (MigrationInputFile file : this.currentSettings.getMigModelFiles()) {
if (smallestInputVersion == null) {
smallestInputVersion = file.getModelVersion();
} else if (!smallestInputVersion.equals(file.getModelVersion())) {
int indexOfCurrent = allSupportedVersions.indexOf(smallestInputVersion);
int indexOfFound = allSupportedVersions.indexOf(file.getModelVersion());
if (indexOfFound < indexOfCurrent) {
smallestInputVersion = file.getModelVersion();
}
}
}
int startIndex = allSupportedVersions.indexOf(smallestInputVersion);
if (startIndex != -1) {
comboInput = allSupportedVersions.subList(startIndex + 1, totalVersionSize);
} else {
comboInput = new ArrayList<>(allSupportedVersions);
}
}
if (comboInput != null) {
Collections.reverse(comboInput);
migModelVersionCombo.setItems(comboInput.toArray(new String[] {}));
selectedIndex = comboInput.indexOf(this.currentSettings.getMigrationModelVersion());
migModelVersionCombo.select(selectedIndex);
}
}
private Composite createFileTableViewer(Composite parent) {
Composite tableComposite = new Composite(parent, SWT.NONE);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
this.tableViewer = CheckboxTableViewer.newCheckList(tableComposite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.READ_ONLY | SWT.MULTI);
Table table = this.tableViewer.getTable();
table.setLinesVisible(true);
table.setHeaderVisible(true);
table.getHorizontalBar().setEnabled(true);
TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
TableColumn column = viewerColumn.getColumn();
column.setText("Relative file paths");
viewerColumn.setLabelProvider(new StyledCellLabelProvider() {
@Override
public void update(ViewerCell cell) {
Object element = cell.getElement();
if (element instanceof MigrationInputFile) {
MigrationInputFile migModelFile = (MigrationInputFile) element;
File file = migModelFile.getOriginalFile();
if (file != null) {
try {
// building relative path from the project location
String path = migModelFile.getProjectRelativePath();
cell.setText(path);
if (migModelFile.isSelectedFile()) {
StyleRange styledRange =
new StyleRange(0, path.length(),
Display.getCurrent().getSystemColor(SWT.COLOR_BLUE), null);
StyleRange[] range = { styledRange };
cell.setStyleRanges(range);
}
if (warningFolders.stream().anyMatch(path::startsWith)) {
StyleRange styledRange =
new StyleRange(0, path.length(),
Display.getCurrent().getSystemColor(SWT.COLOR_RED), null);
StyleRange[] range = { styledRange };
cell.setStyleRanges(range);
} else {
cell.setStyleRanges(null);
}
}
catch (final Exception e) {
Platform.getLog(getClass()).warn("unable to build the relative path for file : " + file.getAbsolutePath(), e);
// Displaying the absolute path of the file, as it is not possible to construct relative path
cell.setText(file.getAbsolutePath());
}
}
}
super.update(cell);
}
});
tableColumnLayout.setColumnData(viewerColumn.getColumn(), new ColumnWeightData(70, true));
TableViewerColumn modelVersionColumn = new TableViewerColumn(this.tableViewer, SWT.NONE);
modelVersionColumn.setLabelProvider(new StyledCellLabelProvider() {
@Override
public void update(ViewerCell cell) {
Object element = cell.getElement();
if (element instanceof MigrationInputFile) {
MigrationInputFile migModelFile = (MigrationInputFile) element;
cell.setText(migModelFile.getModelVersion());
if (migModelFile.isSelectedFile()) {
StyleRange styledRange =
new StyleRange(0, migModelFile.getModelVersion().length(),
Display.getCurrent().getSystemColor(SWT.COLOR_BLUE), null);
StyleRange[] range = { styledRange };
cell.setStyleRanges(range);
}
String path = migModelFile.getProjectRelativePath();
if (warningFolders.stream().anyMatch(path::startsWith)) {
StyleRange styledRange =
new StyleRange(0, path.length(),
Display.getCurrent().getSystemColor(SWT.COLOR_RED), null);
StyleRange[] range = { styledRange };
cell.setStyleRanges(range);
} else {
cell.setStyleRanges(null);
}
}
super.update(cell);
}
});
column = modelVersionColumn.getColumn();
column.setText("Model Version");
tableColumnLayout.setColumnData(column, new ColumnWeightData(20, true));
this.tableViewer.setContentProvider(ArrayContentProvider.getInstance());
this.currentSettings.getMigModelFiles().sort((o1, o2) -> o1.getProjectRelativePath().compareTo(o2.getProjectRelativePath()));
this.tableViewer.addCheckStateListener(event -> {
if (event.getChecked()) {
selectedFiles.add((MigrationInputFile) event.getElement());
} else {
selectedFiles.remove(event.getElement());
}
updateWarningMessage();
});
this.selectedFiles = new ArrayList<>(this.currentSettings.getMigModelFiles());
this.tableViewer.setInput(this.currentSettings.getMigModelFiles());
this.tableViewer.setAllChecked(true);
return tableComposite;
}
/**
* Create contents of the button bar.
*
* @param parent
*/
@Override
protected void createButtonsForButtonBar(final Composite parent) {
Button migrateModelsButton = createButton(parent, IDialogConstants.OK_ID, "Migrate Models", true);
migrateModelsButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
currentSettings.getMigModelFiles().clear();
currentSettings.getMigModelFiles().addAll(selectedFiles);
//perform model migration
ModelMigrationJob migrationJob = new ModelMigrationJob(
MODEL_MIGRATION,
migrationProcessor,
currentSettings,
iProject);
migrationJob.setUser(true);
migrationJob.schedule();
}
});
Button cancelMigrationButton = createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
cancelMigrationButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
super.widgetSelected(e);
Job.getJobManager().cancel(IMigrationJobConstants.FAMILY);
}
});
}
}