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;fp=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2Fwizard%2FMigrateWizard.java;h=f0eee752920fad488de5d51018a11b96f2e089e4;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;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 new file mode 100644 index 000000000..f0eee7529 --- /dev/null +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/wizard/MigrateWizard.java @@ -0,0 +1,672 @@ +/******************************************************************************* + * 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 gnu.trove.map.hash.THashMap; +import gnu.trove.set.hash.THashSet; +import gnu.trove.set.hash.TIntHashSet; + +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.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.Display; +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.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; + +/** + * @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 { + + final String report = Simantics.getSession().syncRequest(new WriteResultRequest() { + @Override + public String perform(WriteGraph graph) throws DatabaseException { + graph.markUndoPoint(); + String report = UserComponentMigration.doMigration(graph, result); + UserComponentMigration.doPostMigration(graph, result); + return report; + } + }); + + class InfoMessageDialog extends MessageDialog { + + public InfoMessageDialog(Shell shell) { + super(shell, + "Migration report", null, + "", + MessageDialog.INFORMATION, new String[] { "Continue" }, 0); + } + + @Override + protected boolean isResizable() { + return true; + } + + @Override + protected Point getInitialSize() { + return new Point(800, 500); + } + + @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; + + } + + } + + InfoMessageDialog md = new InfoMessageDialog(Display.getCurrent().getActiveShell()); + md.open(); + + + } catch (DatabaseException e) { + Logger.defaultLogError(e); + } + + return true; + } + + 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); + } + } + +}