X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2Fwizard%2FMigrateWizard.java;h=59c478e0d55f70408237bc23227ae1ee677c1dbc;hb=refs%2Fchanges%2F38%2F238%2F2;hp=533a4303483689340b26e1878fb885377c1d8743;hpb=602614f4502aae85ecf3967abb7152d2d62903e3;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/wizard/MigrateWizard.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/wizard/MigrateWizard.java index 533a43034..59c478e0d 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/wizard/MigrateWizard.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/wizard/MigrateWizard.java @@ -1,699 +1,699 @@ -/******************************************************************************* - * Copyright (c) 2014, 2015 Association for Decentralized Information Management - * in Industry THTH ry. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Semantum Oy - initial API and implementation - *******************************************************************************/ -package org.simantics.modeling.ui.wizard; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.Map; -import java.util.Set; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.core.runtime.preferences.IEclipsePreferences; -import org.eclipse.core.runtime.preferences.InstanceScope; -import org.eclipse.jface.dialogs.IDialogSettings; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.layout.GridLayoutFactory; -import org.eclipse.jface.layout.RowLayoutFactory; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.window.Window; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.CCombo; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.List; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; -import org.simantics.Simantics; -import org.simantics.databoard.Bindings; -import org.simantics.databoard.util.URIStringUtils; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.WriteGraph; -import org.simantics.db.common.NamedResource; -import org.simantics.db.common.request.ObjectsWithType; -import org.simantics.db.common.request.UnaryRead; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.common.request.WriteResultRequest; -import org.simantics.db.common.utils.Logger; -import org.simantics.db.common.utils.Versions; -import org.simantics.db.exception.CancelTransactionException; -import org.simantics.db.exception.DatabaseException; -import org.simantics.diagram.stubs.DiagramResource; -import org.simantics.layer0.Layer0; -import org.simantics.modeling.MigrateModel; -import org.simantics.modeling.MigrateModel.MigrationOperation; -import org.simantics.modeling.ModelingResources; -import org.simantics.modeling.UserComponentMigration; -import org.simantics.modeling.ui.Activator; -import org.simantics.structural.stubs.StructuralResource2; -import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3; -import org.simantics.utils.datastructures.Pair; -import org.simantics.utils.datastructures.Triple; -import org.simantics.utils.strings.AlphanumComparator; - -import gnu.trove.map.hash.THashMap; -import gnu.trove.set.hash.THashSet; -import gnu.trove.set.hash.TIntHashSet; - -/** - * @author Antti Villberg - */ -public class MigrateWizard extends Wizard { - - final String initial; - - MigratePage migratePage; - - IEclipsePreferences prefnode; - - public MigrateWizard(String initial) { - - this.initial = initial; - - setWindowTitle("Perform migration"); - setNeedsProgressMonitor(true); - setForcePreviousAndNextButtons(false); - setDialogSettings(Activator.getDefault().getDialogSettings()); - - prefnode = InstanceScope.INSTANCE.getNode( "org.simantics.modeling.ui.wizard.MigrateWizard" ); - - } - - @Override - public void addPages() { - migratePage = new MigratePage(initial); - addPage(migratePage); - } - - @Override - public boolean canFinish() { -// if (model.spec.name.isEmpty()) -// return false; - return true; - } - - @Override - public boolean performFinish() { - - int locationIndex = migratePage.locations.getSelectionIndex(); - if(locationIndex == -1) return true; - - if(migratePage.model == null || migratePage.model.instances.isEmpty()) return true; - - int[] sel = migratePage.instances.getSelectionIndices(); - TIntHashSet sels = new TIntHashSet(); - for(int i : sel) sels.add(i); - - Collection ops = migratePage.model.sortedShownInstances; - int index = 0; - final ArrayList result = new ArrayList<>(); - for(MigrationOperation op : ops) { - if(sels.contains(index)) result.add(op); - index++; - } - - if(result.isEmpty()) return true; - - try { - String[] report = { null }; - getContainer().run(true, true, monitor -> { - SubMonitor mon = SubMonitor.convert(monitor, 1000); - try { - report[0] = Simantics.getSession().syncRequest(new WriteResultRequest() { - @Override - public String perform(WriteGraph graph) throws DatabaseException { - graph.markUndoPoint(); - String report = UserComponentMigration.doMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result); - UserComponentMigration.doPostMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result); - mon.setTaskName("Committing Changes"); - mon.subTask(""); - return report; - } - }); - } catch (DatabaseException e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); - } - }); - - - ReportDialog md = new ReportDialog(getShell(), report[0], 800, 500); - md.open(); - - return true; - } catch (InvocationTargetException e) { - // Don't show user cancellations as errors. - Throwable cause = e.getCause(); - if (!(cause instanceof CancelTransactionException || cause instanceof OperationCanceledException)) { - Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - "Migration failed, see Error Log for details.", e.getCause())); - } - } catch (InterruptedException e) { - Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, - "Migration interrupted, see Error Log for details.", e)); - } - return false; - } - - static class ReportDialog extends MessageDialog { - private final String report; - private final int initialWidth; - private final int initialHeight; - - public ReportDialog(Shell shell, String report, int width, int height) { - super(shell, - "Migration report", null, - "", - MessageDialog.INFORMATION, new String[] { "Continue" }, 0); - this.report = report; - this.initialWidth = width; - this.initialHeight = height; - } - - @Override - protected boolean isResizable() { - return true; - } - - @Override - protected Point getInitialSize() { - return new Point(initialWidth, initialHeight); - } - - @Override - protected Control createCustomArea(Composite composite) { - GridLayoutFactory.fillDefaults().applyTo(composite); - Text text = new Text(composite, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER); - text.setText(report); - GridDataFactory.fillDefaults().grab(true, true).applyTo(text); - return composite; - } - } - - abstract static class SelectionAdapter implements SelectionListener { - @Override - public void widgetDefaultSelected(SelectionEvent e) { - widgetSelected(e); - } - } - - class MigratePage extends WizardPage { - - String initial; - - MigrateModel model; - - Text source; - Button browseSource; - Text target; - Button browseTarget; - Label symbolsLabel; - CCombo symbols; - Label instanceLabel; - Label locationsLabel; - CCombo locations; - Composite buttonBar; - Label instancesLabel; - List instances; - - /** - * ID of the location that has been previously selected by the user. - * Used to prevent the location from being reset every time the user - * makes a change in any of the other selections. - * - * See first field of {@link MigrateModel#instances}. - */ - String previouslySelectedLocationId = null; - - public MigratePage(String initial) { - super("Perform migration", "Perform migration", null); - this.initial = initial; - } - - @Override - public void createControl(Composite parent) { - Composite container = new Composite(parent, SWT.NONE); - { - GridLayout layout = new GridLayout(); - layout.horizontalSpacing = 20; - layout.verticalSpacing = 10; - layout.numColumns = 10; - container.setLayout(layout); - } - - new Label(container, SWT.NONE).setText("&Source:"); - source = new Text(container, SWT.BORDER); - source.setText(initial); - source.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - refreshModel(); - refreshInstances(); - } - }); - GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(source); - - browseSource = new Button(container, SWT.NONE); - browseSource.setText("&Browse"); - GridDataFactory.fillDefaults().grab(false, false).applyTo(browseSource); - browseSource.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e_) { - try { - Map> map = Simantics.getSession().syncRequest(new BrowseSourceContentRequest(target.getText())); - String uri = queryTargetSelection("Select Source Type", map); - if (uri != null) - source.setText(uri); - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - } - }); - - new Label(container, SWT.NONE).setText("&Target:"); - target = new Text(container, SWT.BORDER); - target.setText(initial); - target.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - refreshSymbols(); - refreshModel(); - refreshInstances(); - } - }); - GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(target); - - browseTarget = new Button(container, SWT.NONE); - browseTarget.setText("B&rowse"); - GridDataFactory.fillDefaults().grab(false, false).applyTo(browseTarget); - browseTarget.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e_) { - try { - Map> map = Simantics.getSession().syncRequest(new BrowseTargetContentRequest(source.getText())); - String uri = queryTargetSelection("Select Target Type", map); - if (uri != null) - target.setText(uri); - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - } - }); - - symbolsLabel = new Label(container, SWT.NONE); - symbolsLabel.setText("Target &symbol:"); - GridDataFactory.fillDefaults().applyTo(symbolsLabel); - - symbols = new CCombo(container, SWT.BORDER | SWT.READ_ONLY); - symbols.setVisibleItemCount(10); - symbols.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - refreshModel(); - refreshInstances(); - } - }); - GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols); - - instanceLabel = new Label(container, SWT.NONE); - GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instanceLabel); - instanceLabel.setText(""); - - locationsLabel = new Label(container, SWT.NONE); - locationsLabel.setText("&Locations:"); - locations = new CCombo(container, SWT.BORDER | SWT.READ_ONLY); - locations.setVisibleItemCount(25); - locations.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - int index = locations.getSelectionIndex(); - if (index >= 0 && index < model.instances.size()) { - previouslySelectedLocationId = model.instances.get(index).first; - } - refreshInstances(); - } - }); - GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations); - - instancesLabel = new Label(container, SWT.NONE); - instancesLabel.setText("&Select instances to migrate:"); - GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instancesLabel); - - buttonBar = new Composite(container, SWT.NONE); - RowLayoutFactory.fillDefaults().type(SWT.HORIZONTAL).applyTo(buttonBar); - GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar); - Button selectAll = new Button(buttonBar, SWT.PUSH); - selectAll.setText("Select &All"); - Button selectNone = new Button(buttonBar, SWT.PUSH); - selectNone.setText("Select &None"); - selectAll.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - instances.selectAll(); - } - }); - selectNone.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - instances.deselectAll(); - } - }); - - instances = new List(container, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); - GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances); - instances.addListener(SWT.Modify, validateListener); - - refreshSymbols(); - refreshModel(); - refreshInstances(); - - setControl(container); - validatePage(); - } - - String queryTargetSelection(String title, Map> map) { - Shell shell = source.getShell(); - ResourceSelectionDialog3 dialog = new ResourceSelectionDialog3(shell, map, title, false) { - @Override - protected IDialogSettings getBaseDialogSettings() { - return Activator.getDefault().getDialogSettings(); - } - }; - if (dialog.open() == Window.OK) - return (String) dialog.getFirstResult(); - return null; - } - - void refreshSymbols() { - try { - - final String uri = target.getText(); - - Collection syms = Simantics.getSession().syncRequest(new UniqueRead>() { - - @Override - public Collection perform(ReadGraph graph) throws DatabaseException { - - Layer0 L0 = Layer0.getInstance(graph); - DiagramResource DIA = DiagramResource.getInstance(graph); - ModelingResources MOD = ModelingResources.getInstance(graph); - - Set result = new THashSet<>(); - Resource componentType = graph.getResource(uri); - - Set potentialSymbols = new THashSet<>(); - potentialSymbols.addAll(graph.syncRequest(new ObjectsWithType(componentType, L0.ConsistsOf, DIA.ElementClass))); - potentialSymbols.addAll(graph.getObjects(componentType, MOD.ComponentTypeToSymbol)); - - for(Resource symbol : potentialSymbols) { - if(!graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) continue; - String name = graph.getRelatedValue(symbol, L0.HasName, Bindings.STRING); - if(graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) - result.add(new NamedResource(name, symbol)); - } - - return new ArrayList<>(result); - - } - - }); - - symbols.removeAll(); - symbols.add(""); - for(NamedResource nr : syms) { - symbols.add(nr.getName()); - } - symbols.setData(syms); - symbols.select(0); - - if(syms.isEmpty()) { - symbolsLabel.setVisible(false); - symbols.setVisible(false); - GridDataFactory.fillDefaults().exclude(true).applyTo(symbolsLabel); - GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(symbols); - } else { - symbolsLabel.setVisible(true); - symbols.setVisible(true); - GridDataFactory.fillDefaults().applyTo(symbolsLabel); - GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols); - } - - symbols.getParent().layout(); - - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - } - - void refreshModel() { - final String sourceText = source.getText(); - final String targetText = target.getText(); - int symbolIndex = symbols.getSelectionIndex(); - Resource symbol = null; - if(symbolIndex > 0) { - @SuppressWarnings("unchecked") - java.util.List nrs = (java.util.List)symbols.getData(); - symbol = nrs.get(symbolIndex-1).getResource(); - } - final Resource finalSymbol = symbol; - - try { - model = Simantics.getSession().syncRequest(new UniqueRead() { - @Override - public MigrateModel perform(ReadGraph graph) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - StructuralResource2 STR = StructuralResource2.getInstance(graph); - - Resource source = graph.getPossibleResource(sourceText); - if(source == null) return model; - Resource target = graph.getPossibleResource(targetText); - if(target == null) return model; - - if(graph.isInstanceOf(source, STR.ComponentType)) { - return UserComponentMigration.getComponentTypeModel(graph, source, target, finalSymbol); - } else if(graph.isInstanceOf(source, L0.SharedOntology)) { - return UserComponentMigration.getSharedOntologyModel(graph, source, target); - } else { - return null; - } - } - }); - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - - instances.removeAll(); - locations.removeAll(); - - if (model == null) - return; - - int preSelect = -1, i = 0; - for (Triple> r : model.instances) { - locations.add(r.second.getName() + " (" + r.third.size() + " instances)"); - if (r.first.equals(previouslySelectedLocationId)) - preSelect = i; - if (preSelect < 0 && model.activeModels.contains(r.second.getResource())) - preSelect = i; - ++i; - } - if (locations.getItemCount() == 0) { - locations.add(""); - locations.select(0); - } else { - locations.select(preSelect > -1 ? preSelect : 0); - } - } - - void refreshInstances() { - - int toMigrate = 0; - for(Triple> pair : model.instances) { - toMigrate += pair.third.size(); - } - - if(model.instanceCount == 0) - instanceLabel.setText("No instances were found."); - else if(model.instanceCount == 1) { - if(toMigrate == 1) { - instanceLabel.setText("1 migratable instance found."); - } else { - instanceLabel.setText("1 instance found, but it cannot be migrated with current settings."); - } - } else { - if(toMigrate < model.instanceCount) { - if(toMigrate == 0) { - instanceLabel.setText(model.instanceCount + " instances were found. None of them can be migrated with current settings."); - } else { - instanceLabel.setText(model.instanceCount + " instances were found. " + (model.instanceCount-toMigrate) + " of them cannot be migrated with current settings."); - } - } else { - instanceLabel.setText(model.instanceCount + " migratable instances found. "); - } - } - - instances.removeAll(); - - if (model == null) - return; - - if(toMigrate == 0) { - locationsLabel.setVisible(false); - locations.setVisible(false); - GridDataFactory.fillDefaults().exclude(true).applyTo(locationsLabel); - GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(locations); - } else { - locationsLabel.setVisible(true); - locations.setVisible(true); - GridDataFactory.fillDefaults().applyTo(locationsLabel); - GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations); - } - - if(!model.instances.isEmpty()) { - - int locationIndex = locations.getSelectionIndex(); - if(locationIndex == -1) return; - - model.sortedShownInstances = new ArrayList<>(); - for(MigrationOperation o : model.instances.get(locationIndex).third) - model.sortedShownInstances.add(o); - Collections.sort(model.sortedShownInstances, MIGRATION_OP_COMPARATOR); - for(MigrationOperation o : model.sortedShownInstances) { - String uri = o.toString(); - uri = uri.replace("http://Projects/Development%20Project/", ""); - uri = URIStringUtils.unescape(uri); - instances.add(uri); - } - - } - - if(model.sortedShownInstances.isEmpty()) { - instancesLabel.setVisible(false); - instances.setVisible(false); - buttonBar.setVisible(false); - GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(instancesLabel); - GridDataFactory.fillDefaults().exclude(true).grab(true, true).span(10, 1).applyTo(instances); - GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(10, 1).applyTo(buttonBar); - } else { - GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(instancesLabel); - GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances); - GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar); - instancesLabel.setVisible(true); - instances.setVisible(true); - buttonBar.setVisible(true); - } - - locations.getParent().layout(); - - } - - Listener validateListener = new Listener() { - @Override - public void handleEvent(Event event) { - switch (event.type) { - case SWT.Modify: - validatePage(); - case SWT.Selection: - validatePage(); - break; - case SWT.FocusIn: - { - if (event.widget instanceof Text) { - Text t = (Text) event.widget; - t.selectAll(); - } - break; - } - } - } - }; - - private void validatePage() { - setMessage(null); - setErrorMessage(null); - setPageComplete(true); - } - - } - - private static final Comparator MIGRATION_OP_COMPARATOR = new Comparator() { - @Override - public int compare(MigrationOperation o1, MigrationOperation o2) { - return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.instanceToMigrate.getName(), o2.instanceToMigrate.getName()); - } - }; - - private static abstract class BrowseContentRequest extends UnaryRead>> { - - public BrowseContentRequest(String uri) { - super(uri); - } - - @Override - public Map> perform(ReadGraph graph) throws DatabaseException { - Resource root = graph.getPossibleResource(parameter); - if (root == null) - return null; - - Map> result = new THashMap<>(); - Collection infos = getVersions(graph, root); - for (NamedResource info : infos) - result.put(graph.getURI(info.getResource()), Pair.make(Versions.getStandardPathNameString(graph, info.getResource()), null)); - - return result; - } - - protected abstract Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException; - - } - - private static class BrowseSourceContentRequest extends BrowseContentRequest { - public BrowseSourceContentRequest(String uri) { - super(uri); - } - @Override - protected Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException { - return Versions.getOlderVersions(graph, root); - } - } - - private static class BrowseTargetContentRequest extends BrowseContentRequest { - public BrowseTargetContentRequest(String uri) { - super(uri); - } - @Override - protected Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException { - return Versions.getNewerVersions(graph, root); - } - } - -} +/******************************************************************************* + * Copyright (c) 2014, 2015 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Semantum Oy - initial API and implementation + *******************************************************************************/ +package org.simantics.modeling.ui.wizard; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.layout.RowLayoutFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.window.Window; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.simantics.Simantics; +import org.simantics.databoard.Bindings; +import org.simantics.databoard.util.URIStringUtils; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.NamedResource; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.UnaryRead; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.request.WriteResultRequest; +import org.simantics.db.common.utils.Logger; +import org.simantics.db.common.utils.Versions; +import org.simantics.db.exception.CancelTransactionException; +import org.simantics.db.exception.DatabaseException; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.layer0.Layer0; +import org.simantics.modeling.MigrateModel; +import org.simantics.modeling.MigrateModel.MigrationOperation; +import org.simantics.modeling.ModelingResources; +import org.simantics.modeling.UserComponentMigration; +import org.simantics.modeling.ui.Activator; +import org.simantics.structural.stubs.StructuralResource2; +import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3; +import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.datastructures.Triple; +import org.simantics.utils.strings.AlphanumComparator; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; +import gnu.trove.set.hash.TIntHashSet; + +/** + * @author Antti Villberg + */ +public class MigrateWizard extends Wizard { + + final String initial; + + MigratePage migratePage; + + IEclipsePreferences prefnode; + + public MigrateWizard(String initial) { + + this.initial = initial; + + setWindowTitle("Perform migration"); + setNeedsProgressMonitor(true); + setForcePreviousAndNextButtons(false); + setDialogSettings(Activator.getDefault().getDialogSettings()); + + prefnode = InstanceScope.INSTANCE.getNode( "org.simantics.modeling.ui.wizard.MigrateWizard" ); + + } + + @Override + public void addPages() { + migratePage = new MigratePage(initial); + addPage(migratePage); + } + + @Override + public boolean canFinish() { +// if (model.spec.name.isEmpty()) +// return false; + return true; + } + + @Override + public boolean performFinish() { + + int locationIndex = migratePage.locations.getSelectionIndex(); + if(locationIndex == -1) return true; + + if(migratePage.model == null || migratePage.model.instances.isEmpty()) return true; + + int[] sel = migratePage.instances.getSelectionIndices(); + TIntHashSet sels = new TIntHashSet(); + for(int i : sel) sels.add(i); + + Collection ops = migratePage.model.sortedShownInstances; + int index = 0; + final ArrayList result = new ArrayList<>(); + for(MigrationOperation op : ops) { + if(sels.contains(index)) result.add(op); + index++; + } + + if(result.isEmpty()) return true; + + try { + String[] report = { null }; + getContainer().run(true, true, monitor -> { + SubMonitor mon = SubMonitor.convert(monitor, 1000); + try { + report[0] = Simantics.getSession().syncRequest(new WriteResultRequest() { + @Override + public String perform(WriteGraph graph) throws DatabaseException { + graph.markUndoPoint(); + String report = UserComponentMigration.doMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result); + UserComponentMigration.doPostMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result); + mon.setTaskName("Committing Changes"); + mon.subTask(""); + return report; + } + }); + } catch (DatabaseException e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + }); + + + ReportDialog md = new ReportDialog(getShell(), report[0], 800, 500); + md.open(); + + return true; + } catch (InvocationTargetException e) { + // Don't show user cancellations as errors. + Throwable cause = e.getCause(); + if (!(cause instanceof CancelTransactionException || cause instanceof OperationCanceledException)) { + Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Migration failed, see Error Log for details.", e.getCause())); + } + } catch (InterruptedException e) { + Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, + "Migration interrupted, see Error Log for details.", e)); + } + return false; + } + + static class ReportDialog extends MessageDialog { + private final String report; + private final int initialWidth; + private final int initialHeight; + + public ReportDialog(Shell shell, String report, int width, int height) { + super(shell, + "Migration report", null, + "", + MessageDialog.INFORMATION, new String[] { "Continue" }, 0); + this.report = report; + this.initialWidth = width; + this.initialHeight = height; + } + + @Override + protected boolean isResizable() { + return true; + } + + @Override + protected Point getInitialSize() { + return new Point(initialWidth, initialHeight); + } + + @Override + protected Control createCustomArea(Composite composite) { + GridLayoutFactory.fillDefaults().applyTo(composite); + Text text = new Text(composite, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER); + text.setText(report); + GridDataFactory.fillDefaults().grab(true, true).applyTo(text); + return composite; + } + } + + abstract static class SelectionAdapter implements SelectionListener { + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + } + } + + class MigratePage extends WizardPage { + + String initial; + + MigrateModel model; + + Text source; + Button browseSource; + Text target; + Button browseTarget; + Label symbolsLabel; + CCombo symbols; + Label instanceLabel; + Label locationsLabel; + CCombo locations; + Composite buttonBar; + Label instancesLabel; + List instances; + + /** + * ID of the location that has been previously selected by the user. + * Used to prevent the location from being reset every time the user + * makes a change in any of the other selections. + * + * See first field of {@link MigrateModel#instances}. + */ + String previouslySelectedLocationId = null; + + public MigratePage(String initial) { + super("Perform migration", "Perform migration", null); + this.initial = initial; + } + + @Override + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NONE); + { + GridLayout layout = new GridLayout(); + layout.horizontalSpacing = 20; + layout.verticalSpacing = 10; + layout.numColumns = 10; + container.setLayout(layout); + } + + new Label(container, SWT.NONE).setText("&Source:"); + source = new Text(container, SWT.BORDER); + source.setText(initial); + source.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + refreshModel(); + refreshInstances(); + } + }); + GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(source); + + browseSource = new Button(container, SWT.NONE); + browseSource.setText("&Browse"); + GridDataFactory.fillDefaults().grab(false, false).applyTo(browseSource); + browseSource.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e_) { + try { + Map> map = Simantics.getSession().syncRequest(new BrowseSourceContentRequest(target.getText())); + String uri = queryTargetSelection("Select Source Type", map); + if (uri != null) + source.setText(uri); + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + } + }); + + new Label(container, SWT.NONE).setText("&Target:"); + target = new Text(container, SWT.BORDER); + target.setText(initial); + target.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + refreshSymbols(); + refreshModel(); + refreshInstances(); + } + }); + GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(target); + + browseTarget = new Button(container, SWT.NONE); + browseTarget.setText("B&rowse"); + GridDataFactory.fillDefaults().grab(false, false).applyTo(browseTarget); + browseTarget.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e_) { + try { + Map> map = Simantics.getSession().syncRequest(new BrowseTargetContentRequest(source.getText())); + String uri = queryTargetSelection("Select Target Type", map); + if (uri != null) + target.setText(uri); + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + } + }); + + symbolsLabel = new Label(container, SWT.NONE); + symbolsLabel.setText("Target &symbol:"); + GridDataFactory.fillDefaults().applyTo(symbolsLabel); + + symbols = new CCombo(container, SWT.BORDER | SWT.READ_ONLY); + symbols.setVisibleItemCount(10); + symbols.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + refreshModel(); + refreshInstances(); + } + }); + GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols); + + instanceLabel = new Label(container, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instanceLabel); + instanceLabel.setText(""); + + locationsLabel = new Label(container, SWT.NONE); + locationsLabel.setText("&Locations:"); + locations = new CCombo(container, SWT.BORDER | SWT.READ_ONLY); + locations.setVisibleItemCount(25); + locations.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + int index = locations.getSelectionIndex(); + if (index >= 0 && index < model.instances.size()) { + previouslySelectedLocationId = model.instances.get(index).first; + } + refreshInstances(); + } + }); + GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations); + + instancesLabel = new Label(container, SWT.NONE); + instancesLabel.setText("&Select instances to migrate:"); + GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instancesLabel); + + buttonBar = new Composite(container, SWT.NONE); + RowLayoutFactory.fillDefaults().type(SWT.HORIZONTAL).applyTo(buttonBar); + GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar); + Button selectAll = new Button(buttonBar, SWT.PUSH); + selectAll.setText("Select &All"); + Button selectNone = new Button(buttonBar, SWT.PUSH); + selectNone.setText("Select &None"); + selectAll.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + instances.selectAll(); + } + }); + selectNone.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + instances.deselectAll(); + } + }); + + instances = new List(container, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); + GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances); + instances.addListener(SWT.Modify, validateListener); + + refreshSymbols(); + refreshModel(); + refreshInstances(); + + setControl(container); + validatePage(); + } + + String queryTargetSelection(String title, Map> map) { + Shell shell = source.getShell(); + ResourceSelectionDialog3 dialog = new ResourceSelectionDialog3(shell, map, title, false) { + @Override + protected IDialogSettings getBaseDialogSettings() { + return Activator.getDefault().getDialogSettings(); + } + }; + if (dialog.open() == Window.OK) + return (String) dialog.getFirstResult(); + return null; + } + + void refreshSymbols() { + try { + + final String uri = target.getText(); + + Collection syms = Simantics.getSession().syncRequest(new UniqueRead>() { + + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + + Layer0 L0 = Layer0.getInstance(graph); + DiagramResource DIA = DiagramResource.getInstance(graph); + ModelingResources MOD = ModelingResources.getInstance(graph); + + Set result = new THashSet<>(); + Resource componentType = graph.getResource(uri); + + Set potentialSymbols = new THashSet<>(); + potentialSymbols.addAll(graph.syncRequest(new ObjectsWithType(componentType, L0.ConsistsOf, DIA.ElementClass))); + potentialSymbols.addAll(graph.getObjects(componentType, MOD.ComponentTypeToSymbol)); + + for(Resource symbol : potentialSymbols) { + if(!graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) continue; + String name = graph.getRelatedValue(symbol, L0.HasName, Bindings.STRING); + if(graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) + result.add(new NamedResource(name, symbol)); + } + + return new ArrayList<>(result); + + } + + }); + + symbols.removeAll(); + symbols.add(""); + for(NamedResource nr : syms) { + symbols.add(nr.getName()); + } + symbols.setData(syms); + symbols.select(0); + + if(syms.isEmpty()) { + symbolsLabel.setVisible(false); + symbols.setVisible(false); + GridDataFactory.fillDefaults().exclude(true).applyTo(symbolsLabel); + GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(symbols); + } else { + symbolsLabel.setVisible(true); + symbols.setVisible(true); + GridDataFactory.fillDefaults().applyTo(symbolsLabel); + GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols); + } + + symbols.getParent().layout(); + + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + } + + void refreshModel() { + final String sourceText = source.getText(); + final String targetText = target.getText(); + int symbolIndex = symbols.getSelectionIndex(); + Resource symbol = null; + if(symbolIndex > 0) { + @SuppressWarnings("unchecked") + java.util.List nrs = (java.util.List)symbols.getData(); + symbol = nrs.get(symbolIndex-1).getResource(); + } + final Resource finalSymbol = symbol; + + try { + model = Simantics.getSession().syncRequest(new UniqueRead() { + @Override + public MigrateModel perform(ReadGraph graph) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + StructuralResource2 STR = StructuralResource2.getInstance(graph); + + Resource source = graph.getPossibleResource(sourceText); + if(source == null) return model; + Resource target = graph.getPossibleResource(targetText); + if(target == null) return model; + + if(graph.isInstanceOf(source, STR.ComponentType)) { + return UserComponentMigration.getComponentTypeModel(graph, source, target, finalSymbol); + } else if(graph.isInstanceOf(source, L0.SharedOntology)) { + return UserComponentMigration.getSharedOntologyModel(graph, source, target); + } else { + return null; + } + } + }); + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + + instances.removeAll(); + locations.removeAll(); + + if (model == null) + return; + + int preSelect = -1, i = 0; + for (Triple> r : model.instances) { + locations.add(r.second.getName() + " (" + r.third.size() + " instances)"); + if (r.first.equals(previouslySelectedLocationId)) + preSelect = i; + if (preSelect < 0 && model.activeModels.contains(r.second.getResource())) + preSelect = i; + ++i; + } + if (locations.getItemCount() == 0) { + locations.add(""); + locations.select(0); + } else { + locations.select(preSelect > -1 ? preSelect : 0); + } + } + + void refreshInstances() { + + int toMigrate = 0; + for(Triple> pair : model.instances) { + toMigrate += pair.third.size(); + } + + if(model.instanceCount == 0) + instanceLabel.setText("No instances were found."); + else if(model.instanceCount == 1) { + if(toMigrate == 1) { + instanceLabel.setText("1 migratable instance found."); + } else { + instanceLabel.setText("1 instance found, but it cannot be migrated with current settings."); + } + } else { + if(toMigrate < model.instanceCount) { + if(toMigrate == 0) { + instanceLabel.setText(model.instanceCount + " instances were found. None of them can be migrated with current settings."); + } else { + instanceLabel.setText(model.instanceCount + " instances were found. " + (model.instanceCount-toMigrate) + " of them cannot be migrated with current settings."); + } + } else { + instanceLabel.setText(model.instanceCount + " migratable instances found. "); + } + } + + instances.removeAll(); + + if (model == null) + return; + + if(toMigrate == 0) { + locationsLabel.setVisible(false); + locations.setVisible(false); + GridDataFactory.fillDefaults().exclude(true).applyTo(locationsLabel); + GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(locations); + } else { + locationsLabel.setVisible(true); + locations.setVisible(true); + GridDataFactory.fillDefaults().applyTo(locationsLabel); + GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations); + } + + if(!model.instances.isEmpty()) { + + int locationIndex = locations.getSelectionIndex(); + if(locationIndex == -1) return; + + model.sortedShownInstances = new ArrayList<>(); + for(MigrationOperation o : model.instances.get(locationIndex).third) + model.sortedShownInstances.add(o); + Collections.sort(model.sortedShownInstances, MIGRATION_OP_COMPARATOR); + for(MigrationOperation o : model.sortedShownInstances) { + String uri = o.toString(); + uri = uri.replace("http://Projects/Development%20Project/", ""); + uri = URIStringUtils.unescape(uri); + instances.add(uri); + } + + } + + if(model.sortedShownInstances.isEmpty()) { + instancesLabel.setVisible(false); + instances.setVisible(false); + buttonBar.setVisible(false); + GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(instancesLabel); + GridDataFactory.fillDefaults().exclude(true).grab(true, true).span(10, 1).applyTo(instances); + GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(10, 1).applyTo(buttonBar); + } else { + GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(instancesLabel); + GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances); + GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar); + instancesLabel.setVisible(true); + instances.setVisible(true); + buttonBar.setVisible(true); + } + + locations.getParent().layout(); + + } + + Listener validateListener = new Listener() { + @Override + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Modify: + validatePage(); + case SWT.Selection: + validatePage(); + break; + case SWT.FocusIn: + { + if (event.widget instanceof Text) { + Text t = (Text) event.widget; + t.selectAll(); + } + break; + } + } + } + }; + + private void validatePage() { + setMessage(null); + setErrorMessage(null); + setPageComplete(true); + } + + } + + private static final Comparator MIGRATION_OP_COMPARATOR = new Comparator() { + @Override + public int compare(MigrationOperation o1, MigrationOperation o2) { + return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.instanceToMigrate.getName(), o2.instanceToMigrate.getName()); + } + }; + + private static abstract class BrowseContentRequest extends UnaryRead>> { + + public BrowseContentRequest(String uri) { + super(uri); + } + + @Override + public Map> perform(ReadGraph graph) throws DatabaseException { + Resource root = graph.getPossibleResource(parameter); + if (root == null) + return null; + + Map> result = new THashMap<>(); + Collection infos = getVersions(graph, root); + for (NamedResource info : infos) + result.put(graph.getURI(info.getResource()), Pair.make(Versions.getStandardPathNameString(graph, info.getResource()), null)); + + return result; + } + + protected abstract Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException; + + } + + private static class BrowseSourceContentRequest extends BrowseContentRequest { + public BrowseSourceContentRequest(String uri) { + super(uri); + } + @Override + protected Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException { + return Versions.getOlderVersions(graph, root); + } + } + + private static class BrowseTargetContentRequest extends BrowseContentRequest { + public BrowseTargetContentRequest(String uri) { + super(uri); + } + @Override + protected Collection getVersions(ReadGraph graph, Resource root) throws DatabaseException { + return Versions.getNewerVersions(graph, root); + } + } + +}