| /** |
| ******************************************************************************** |
| * 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); |
| } |
| }); |
| } |
| } |