1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Semantum Oy - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.wizard;
14 import java.lang.reflect.InvocationTargetException;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.Comparator;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.OperationCanceledException;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.core.runtime.SubMonitor;
26 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
27 import org.eclipse.core.runtime.preferences.InstanceScope;
28 import org.eclipse.jface.dialogs.IDialogSettings;
29 import org.eclipse.jface.dialogs.MessageDialog;
30 import org.eclipse.jface.layout.GridDataFactory;
31 import org.eclipse.jface.layout.GridLayoutFactory;
32 import org.eclipse.jface.layout.RowLayoutFactory;
33 import org.eclipse.jface.resource.ImageDescriptor;
34 import org.eclipse.jface.window.Window;
35 import org.eclipse.jface.wizard.Wizard;
36 import org.eclipse.jface.wizard.WizardPage;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.custom.CCombo;
39 import org.eclipse.swt.events.ModifyEvent;
40 import org.eclipse.swt.events.ModifyListener;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.events.SelectionListener;
43 import org.eclipse.swt.graphics.Point;
44 import org.eclipse.swt.layout.GridLayout;
45 import org.eclipse.swt.widgets.Button;
46 import org.eclipse.swt.widgets.Composite;
47 import org.eclipse.swt.widgets.Control;
48 import org.eclipse.swt.widgets.Event;
49 import org.eclipse.swt.widgets.Label;
50 import org.eclipse.swt.widgets.List;
51 import org.eclipse.swt.widgets.Listener;
52 import org.eclipse.swt.widgets.Shell;
53 import org.eclipse.swt.widgets.Text;
54 import org.simantics.Simantics;
55 import org.simantics.databoard.Bindings;
56 import org.simantics.databoard.util.URIStringUtils;
57 import org.simantics.db.ReadGraph;
58 import org.simantics.db.Resource;
59 import org.simantics.db.WriteGraph;
60 import org.simantics.db.common.NamedResource;
61 import org.simantics.db.common.request.ObjectsWithType;
62 import org.simantics.db.common.request.UnaryRead;
63 import org.simantics.db.common.request.UniqueRead;
64 import org.simantics.db.common.request.WriteResultRequest;
65 import org.simantics.db.common.utils.Logger;
66 import org.simantics.db.common.utils.Versions;
67 import org.simantics.db.exception.CancelTransactionException;
68 import org.simantics.db.exception.DatabaseException;
69 import org.simantics.diagram.stubs.DiagramResource;
70 import org.simantics.layer0.Layer0;
71 import org.simantics.modeling.MigrateModel;
72 import org.simantics.modeling.MigrateModel.MigrationOperation;
73 import org.simantics.modeling.ModelingResources;
74 import org.simantics.modeling.UserComponentMigration;
75 import org.simantics.modeling.ui.Activator;
76 import org.simantics.structural.stubs.StructuralResource2;
77 import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3;
78 import org.simantics.utils.datastructures.Pair;
79 import org.simantics.utils.datastructures.Triple;
80 import org.simantics.utils.strings.AlphanumComparator;
82 import gnu.trove.map.hash.THashMap;
83 import gnu.trove.set.hash.THashSet;
84 import gnu.trove.set.hash.TIntHashSet;
87 * @author Antti Villberg
89 public class MigrateWizard extends Wizard {
93 MigratePage migratePage;
95 IEclipsePreferences prefnode;
97 public MigrateWizard(String initial) {
99 this.initial = initial;
101 setWindowTitle("Perform migration");
102 setNeedsProgressMonitor(true);
103 setForcePreviousAndNextButtons(false);
104 setDialogSettings(Activator.getDefault().getDialogSettings());
106 prefnode = InstanceScope.INSTANCE.getNode( "org.simantics.modeling.ui.wizard.MigrateWizard" );
111 public void addPages() {
112 migratePage = new MigratePage(initial);
113 addPage(migratePage);
117 public boolean canFinish() {
118 // if (model.spec.name.isEmpty())
124 public boolean performFinish() {
126 int locationIndex = migratePage.locations.getSelectionIndex();
127 if(locationIndex == -1) return true;
129 if(migratePage.model == null || migratePage.model.instances.isEmpty()) return true;
131 int[] sel = migratePage.instances.getSelectionIndices();
132 TIntHashSet sels = new TIntHashSet();
133 for(int i : sel) sels.add(i);
135 Collection<MigrationOperation> ops = migratePage.model.sortedShownInstances;
137 final ArrayList<MigrationOperation> result = new ArrayList<>();
138 for(MigrationOperation op : ops) {
139 if(sels.contains(index)) result.add(op);
143 if(result.isEmpty()) return true;
146 String[] report = { null };
147 getContainer().run(true, true, monitor -> {
148 SubMonitor mon = SubMonitor.convert(monitor, 1000);
150 report[0] = Simantics.getSession().syncRequest(new WriteResultRequest<String>() {
152 public String perform(WriteGraph graph) throws DatabaseException {
153 graph.markUndoPoint();
154 String report = UserComponentMigration.doMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result);
155 UserComponentMigration.doPostMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result);
156 mon.setTaskName("Committing Changes");
161 } catch (DatabaseException e) {
162 throw new InvocationTargetException(e);
169 ReportDialog md = new ReportDialog(getShell(), report[0], 800, 500);
173 } catch (InvocationTargetException e) {
174 // Don't show user cancellations as errors.
175 Throwable cause = e.getCause();
176 if (!(cause instanceof CancelTransactionException || cause instanceof OperationCanceledException)) {
177 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
178 "Migration failed, see Error Log for details.", e.getCause()));
180 } catch (InterruptedException e) {
181 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
182 "Migration interrupted, see Error Log for details.", e));
187 static class ReportDialog extends MessageDialog {
188 private final String report;
189 private final int initialWidth;
190 private final int initialHeight;
192 public ReportDialog(Shell shell, String report, int width, int height) {
194 "Migration report", null,
196 MessageDialog.INFORMATION, new String[] { "Continue" }, 0);
197 this.report = report;
198 this.initialWidth = width;
199 this.initialHeight = height;
203 protected boolean isResizable() {
208 protected Point getInitialSize() {
209 return new Point(initialWidth, initialHeight);
213 protected Control createCustomArea(Composite composite) {
214 GridLayoutFactory.fillDefaults().applyTo(composite);
215 Text text = new Text(composite, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER);
216 text.setText(report);
217 GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
222 abstract static class SelectionAdapter implements SelectionListener {
224 public void widgetDefaultSelected(SelectionEvent e) {
229 class MigratePage extends WizardPage {
242 Label locationsLabel;
245 Label instancesLabel;
249 * ID of the location that has been previously selected by the user.
250 * Used to prevent the location from being reset every time the user
251 * makes a change in any of the other selections.
253 * See first field of {@link MigrateModel#instances}.
255 String previouslySelectedLocationId = null;
257 public MigratePage(String initial) {
258 super("Perform migration", "Perform migration", null);
259 this.initial = initial;
263 public void createControl(Composite parent) {
264 Composite container = new Composite(parent, SWT.NONE);
266 GridLayout layout = new GridLayout();
267 layout.horizontalSpacing = 20;
268 layout.verticalSpacing = 10;
269 layout.numColumns = 10;
270 container.setLayout(layout);
273 new Label(container, SWT.NONE).setText("&Source:");
274 source = new Text(container, SWT.BORDER);
275 source.setText(initial);
276 source.addModifyListener(new ModifyListener() {
278 public void modifyText(ModifyEvent e) {
283 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(source);
285 browseSource = new Button(container, SWT.NONE);
286 browseSource.setText("&Browse");
287 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseSource);
288 browseSource.addSelectionListener(new SelectionAdapter() {
290 public void widgetSelected(SelectionEvent e_) {
292 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseSourceContentRequest(target.getText()));
293 String uri = queryTargetSelection("Select Source Type", map);
296 } catch (DatabaseException e) {
297 Logger.defaultLogError(e);
302 new Label(container, SWT.NONE).setText("&Target:");
303 target = new Text(container, SWT.BORDER);
304 target.setText(initial);
305 target.addModifyListener(new ModifyListener() {
307 public void modifyText(ModifyEvent e) {
313 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(target);
315 browseTarget = new Button(container, SWT.NONE);
316 browseTarget.setText("B&rowse");
317 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseTarget);
318 browseTarget.addSelectionListener(new SelectionAdapter() {
320 public void widgetSelected(SelectionEvent e_) {
322 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseTargetContentRequest(source.getText()));
323 String uri = queryTargetSelection("Select Target Type", map);
326 } catch (DatabaseException e) {
327 Logger.defaultLogError(e);
332 symbolsLabel = new Label(container, SWT.NONE);
333 symbolsLabel.setText("Target &symbol:");
334 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
336 symbols = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
337 symbols.setVisibleItemCount(10);
338 symbols.addSelectionListener(new SelectionAdapter() {
340 public void widgetSelected(SelectionEvent e) {
345 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
347 instanceLabel = new Label(container, SWT.NONE);
348 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instanceLabel);
349 instanceLabel.setText("");
351 locationsLabel = new Label(container, SWT.NONE);
352 locationsLabel.setText("&Locations:");
353 locations = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
354 locations.setVisibleItemCount(25);
355 locations.addSelectionListener(new SelectionAdapter() {
357 public void widgetSelected(SelectionEvent e) {
358 int index = locations.getSelectionIndex();
359 if (index >= 0 && index < model.instances.size()) {
360 previouslySelectedLocationId = model.instances.get(index).first;
365 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
367 instancesLabel = new Label(container, SWT.NONE);
368 instancesLabel.setText("&Select instances to migrate:");
369 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instancesLabel);
371 buttonBar = new Composite(container, SWT.NONE);
372 RowLayoutFactory.fillDefaults().type(SWT.HORIZONTAL).applyTo(buttonBar);
373 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
374 Button selectAll = new Button(buttonBar, SWT.PUSH);
375 selectAll.setText("Select &All");
376 Button selectNone = new Button(buttonBar, SWT.PUSH);
377 selectNone.setText("Select &None");
378 selectAll.addSelectionListener(new SelectionAdapter() {
380 public void widgetSelected(SelectionEvent e) {
381 instances.selectAll();
384 selectNone.addSelectionListener(new SelectionAdapter() {
386 public void widgetSelected(SelectionEvent e) {
387 instances.deselectAll();
391 instances = new List(container, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
392 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
393 instances.addListener(SWT.Modify, validateListener);
399 setControl(container);
403 String queryTargetSelection(String title, Map<String, Pair<String, ImageDescriptor>> map) {
404 Shell shell = source.getShell();
405 ResourceSelectionDialog3<String> dialog = new ResourceSelectionDialog3<String>(shell, map, title, false) {
407 protected IDialogSettings getBaseDialogSettings() {
408 return Activator.getDefault().getDialogSettings();
411 if (dialog.open() == Window.OK)
412 return (String) dialog.getFirstResult();
416 void refreshSymbols() {
419 final String uri = target.getText();
421 Collection<NamedResource> syms = Simantics.getSession().syncRequest(new UniqueRead<Collection<NamedResource>>() {
424 public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {
426 Layer0 L0 = Layer0.getInstance(graph);
427 DiagramResource DIA = DiagramResource.getInstance(graph);
428 ModelingResources MOD = ModelingResources.getInstance(graph);
430 Set<NamedResource> result = new THashSet<>();
431 Resource componentType = graph.getResource(uri);
433 Set<Resource> potentialSymbols = new THashSet<>();
434 potentialSymbols.addAll(graph.syncRequest(new ObjectsWithType(componentType, L0.ConsistsOf, DIA.ElementClass)));
435 potentialSymbols.addAll(graph.getObjects(componentType, MOD.ComponentTypeToSymbol));
437 for(Resource symbol : potentialSymbols) {
438 if(!graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) continue;
439 String name = graph.getRelatedValue(symbol, L0.HasName, Bindings.STRING);
440 if(graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType))
441 result.add(new NamedResource(name, symbol));
444 return new ArrayList<>(result);
451 symbols.add("<retain symbol name>");
452 for(NamedResource nr : syms) {
453 symbols.add(nr.getName());
455 symbols.setData(syms);
459 symbolsLabel.setVisible(false);
460 symbols.setVisible(false);
461 GridDataFactory.fillDefaults().exclude(true).applyTo(symbolsLabel);
462 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(symbols);
464 symbolsLabel.setVisible(true);
465 symbols.setVisible(true);
466 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
467 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
470 symbols.getParent().layout();
472 } catch (DatabaseException e) {
473 Logger.defaultLogError(e);
477 void refreshModel() {
478 final String sourceText = source.getText();
479 final String targetText = target.getText();
480 int symbolIndex = symbols.getSelectionIndex();
481 Resource symbol = null;
482 if(symbolIndex > 0) {
483 @SuppressWarnings("unchecked")
484 java.util.List<NamedResource> nrs = (java.util.List<NamedResource>)symbols.getData();
485 symbol = nrs.get(symbolIndex-1).getResource();
487 final Resource finalSymbol = symbol;
490 model = Simantics.getSession().syncRequest(new UniqueRead<MigrateModel>() {
492 public MigrateModel perform(ReadGraph graph) throws DatabaseException {
493 Layer0 L0 = Layer0.getInstance(graph);
494 StructuralResource2 STR = StructuralResource2.getInstance(graph);
496 Resource source = graph.getPossibleResource(sourceText);
497 if(source == null) return model;
498 Resource target = graph.getPossibleResource(targetText);
499 if(target == null) return model;
501 if(graph.isInstanceOf(source, STR.ComponentType)) {
502 return UserComponentMigration.getComponentTypeModel(graph, source, target, finalSymbol);
503 } else if(graph.isInstanceOf(source, L0.SharedOntology)) {
504 return UserComponentMigration.getSharedOntologyModel(graph, source, target);
510 } catch (DatabaseException e) {
511 Logger.defaultLogError(e);
514 instances.removeAll();
515 locations.removeAll();
520 int preSelect = -1, i = 0;
521 for (Triple<String,NamedResource,Collection<MigrationOperation>> r : model.instances) {
522 locations.add(r.second.getName() + " (" + r.third.size() + " instances)");
523 if (r.first.equals(previouslySelectedLocationId))
525 if (preSelect < 0 && model.activeModels.contains(r.second.getResource()))
529 if (locations.getItemCount() == 0) {
530 locations.add("<no instances were found>");
533 locations.select(preSelect > -1 ? preSelect : 0);
537 void refreshInstances() {
540 for(Triple<String,NamedResource,Collection<MigrationOperation>> pair : model.instances) {
541 toMigrate += pair.third.size();
544 if(model.instanceCount == 0)
545 instanceLabel.setText("No instances were found.");
546 else if(model.instanceCount == 1) {
548 instanceLabel.setText("1 migratable instance found.");
550 instanceLabel.setText("1 instance found, but it cannot be migrated with current settings.");
553 if(toMigrate < model.instanceCount) {
555 instanceLabel.setText(model.instanceCount + " instances were found. None of them can be migrated with current settings.");
557 instanceLabel.setText(model.instanceCount + " instances were found. " + (model.instanceCount-toMigrate) + " of them cannot be migrated with current settings.");
560 instanceLabel.setText(model.instanceCount + " migratable instances found. ");
564 instances.removeAll();
570 locationsLabel.setVisible(false);
571 locations.setVisible(false);
572 GridDataFactory.fillDefaults().exclude(true).applyTo(locationsLabel);
573 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(locations);
575 locationsLabel.setVisible(true);
576 locations.setVisible(true);
577 GridDataFactory.fillDefaults().applyTo(locationsLabel);
578 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
581 if(!model.instances.isEmpty()) {
583 int locationIndex = locations.getSelectionIndex();
584 if(locationIndex == -1) return;
586 model.sortedShownInstances = new ArrayList<>();
587 for(MigrationOperation o : model.instances.get(locationIndex).third)
588 model.sortedShownInstances.add(o);
589 Collections.sort(model.sortedShownInstances, MIGRATION_OP_COMPARATOR);
590 for(MigrationOperation o : model.sortedShownInstances) {
591 String uri = o.toString();
592 uri = uri.replace("http://Projects/Development%20Project/", "");
593 uri = URIStringUtils.unescape(uri);
599 if(model.sortedShownInstances.isEmpty()) {
600 instancesLabel.setVisible(false);
601 instances.setVisible(false);
602 buttonBar.setVisible(false);
603 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(instancesLabel);
604 GridDataFactory.fillDefaults().exclude(true).grab(true, true).span(10, 1).applyTo(instances);
605 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(10, 1).applyTo(buttonBar);
607 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(instancesLabel);
608 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
609 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
610 instancesLabel.setVisible(true);
611 instances.setVisible(true);
612 buttonBar.setVisible(true);
615 locations.getParent().layout();
619 Listener validateListener = new Listener() {
621 public void handleEvent(Event event) {
622 switch (event.type) {
630 if (event.widget instanceof Text) {
631 Text t = (Text) event.widget;
640 private void validatePage() {
642 setErrorMessage(null);
643 setPageComplete(true);
648 private static final Comparator<MigrationOperation> MIGRATION_OP_COMPARATOR = new Comparator<MigrateModel.MigrationOperation>() {
650 public int compare(MigrationOperation o1, MigrationOperation o2) {
651 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.instanceToMigrate.getName(), o2.instanceToMigrate.getName());
655 private static abstract class BrowseContentRequest extends UnaryRead<String, Map<String, Pair<String, ImageDescriptor>>> {
657 public BrowseContentRequest(String uri) {
662 public Map<String, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
663 Resource root = graph.getPossibleResource(parameter);
667 Map<String, Pair<String, ImageDescriptor>> result = new THashMap<>();
668 Collection<NamedResource> infos = getVersions(graph, root);
669 for (NamedResource info : infos)
670 result.put(graph.getURI(info.getResource()), Pair.<String,ImageDescriptor>make(Versions.getStandardPathNameString(graph, info.getResource()), null));
675 protected abstract Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException;
679 private static class BrowseSourceContentRequest extends BrowseContentRequest {
680 public BrowseSourceContentRequest(String uri) {
684 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
685 return Versions.getOlderVersions(graph, root);
689 private static class BrowseTargetContentRequest extends BrowseContentRequest {
690 public BrowseTargetContentRequest(String uri) {
694 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
695 return Versions.getNewerVersions(graph, root);