1 /*******************************************************************************
\r
2 * Copyright (c) 2014, 2015 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * Semantum Oy - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.modeling.ui.wizard;
\r
14 import gnu.trove.map.hash.THashMap;
\r
15 import gnu.trove.set.hash.THashSet;
\r
16 import gnu.trove.set.hash.TIntHashSet;
\r
18 import java.util.ArrayList;
\r
19 import java.util.Collection;
\r
20 import java.util.Collections;
\r
21 import java.util.Comparator;
\r
22 import java.util.Map;
\r
23 import java.util.Set;
\r
25 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
\r
26 import org.eclipse.core.runtime.preferences.InstanceScope;
\r
27 import org.eclipse.jface.dialogs.IDialogSettings;
\r
28 import org.eclipse.jface.dialogs.MessageDialog;
\r
29 import org.eclipse.jface.layout.GridDataFactory;
\r
30 import org.eclipse.jface.layout.GridLayoutFactory;
\r
31 import org.eclipse.jface.layout.RowLayoutFactory;
\r
32 import org.eclipse.jface.resource.ImageDescriptor;
\r
33 import org.eclipse.jface.window.Window;
\r
34 import org.eclipse.jface.wizard.Wizard;
\r
35 import org.eclipse.jface.wizard.WizardPage;
\r
36 import org.eclipse.swt.SWT;
\r
37 import org.eclipse.swt.custom.CCombo;
\r
38 import org.eclipse.swt.events.ModifyEvent;
\r
39 import org.eclipse.swt.events.ModifyListener;
\r
40 import org.eclipse.swt.events.SelectionEvent;
\r
41 import org.eclipse.swt.events.SelectionListener;
\r
42 import org.eclipse.swt.graphics.Point;
\r
43 import org.eclipse.swt.layout.GridLayout;
\r
44 import org.eclipse.swt.widgets.Button;
\r
45 import org.eclipse.swt.widgets.Composite;
\r
46 import org.eclipse.swt.widgets.Control;
\r
47 import org.eclipse.swt.widgets.Display;
\r
48 import org.eclipse.swt.widgets.Event;
\r
49 import org.eclipse.swt.widgets.Label;
\r
50 import org.eclipse.swt.widgets.List;
\r
51 import org.eclipse.swt.widgets.Listener;
\r
52 import org.eclipse.swt.widgets.Shell;
\r
53 import org.eclipse.swt.widgets.Text;
\r
54 import org.simantics.Simantics;
\r
55 import org.simantics.databoard.Bindings;
\r
56 import org.simantics.databoard.util.URIStringUtils;
\r
57 import org.simantics.db.ReadGraph;
\r
58 import org.simantics.db.Resource;
\r
59 import org.simantics.db.WriteGraph;
\r
60 import org.simantics.db.common.NamedResource;
\r
61 import org.simantics.db.common.request.ObjectsWithType;
\r
62 import org.simantics.db.common.request.UnaryRead;
\r
63 import org.simantics.db.common.request.UniqueRead;
\r
64 import org.simantics.db.common.request.WriteResultRequest;
\r
65 import org.simantics.db.common.utils.Logger;
\r
66 import org.simantics.db.common.utils.Versions;
\r
67 import org.simantics.db.exception.DatabaseException;
\r
68 import org.simantics.diagram.stubs.DiagramResource;
\r
69 import org.simantics.layer0.Layer0;
\r
70 import org.simantics.modeling.MigrateModel;
\r
71 import org.simantics.modeling.MigrateModel.MigrationOperation;
\r
72 import org.simantics.modeling.ModelingResources;
\r
73 import org.simantics.modeling.UserComponentMigration;
\r
74 import org.simantics.modeling.ui.Activator;
\r
75 import org.simantics.structural.stubs.StructuralResource2;
\r
76 import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3;
\r
77 import org.simantics.utils.datastructures.Pair;
\r
78 import org.simantics.utils.datastructures.Triple;
\r
79 import org.simantics.utils.strings.AlphanumComparator;
\r
82 * @author Antti Villberg
\r
84 public class MigrateWizard extends Wizard {
\r
86 final String initial;
\r
88 MigratePage migratePage;
\r
90 IEclipsePreferences prefnode;
\r
92 public MigrateWizard(String initial) {
\r
94 this.initial = initial;
\r
96 setWindowTitle("Perform migration");
\r
97 setNeedsProgressMonitor(true);
\r
98 setForcePreviousAndNextButtons(false);
\r
99 setDialogSettings(Activator.getDefault().getDialogSettings());
\r
101 prefnode = InstanceScope.INSTANCE.getNode( "org.simantics.modeling.ui.wizard.MigrateWizard" );
\r
106 public void addPages() {
\r
107 migratePage = new MigratePage(initial);
\r
108 addPage(migratePage);
\r
112 public boolean canFinish() {
\r
113 // if (model.spec.name.isEmpty())
\r
119 public boolean performFinish() {
\r
121 int locationIndex = migratePage.locations.getSelectionIndex();
\r
122 if(locationIndex == -1) return true;
\r
124 if(migratePage.model == null || migratePage.model.instances.isEmpty()) return true;
\r
126 int[] sel = migratePage.instances.getSelectionIndices();
\r
127 TIntHashSet sels = new TIntHashSet();
\r
128 for(int i : sel) sels.add(i);
\r
130 Collection<MigrationOperation> ops = migratePage.model.sortedShownInstances;
\r
132 final ArrayList<MigrationOperation> result = new ArrayList<MigrationOperation>();
\r
133 for(MigrationOperation op : ops) {
\r
134 if(sels.contains(index)) result.add(op);
\r
138 if(result.isEmpty()) return true;
\r
142 final String report = Simantics.getSession().syncRequest(new WriteResultRequest<String>() {
\r
144 public String perform(WriteGraph graph) throws DatabaseException {
\r
145 graph.markUndoPoint();
\r
146 String report = UserComponentMigration.doMigration(graph, result);
\r
147 UserComponentMigration.doPostMigration(graph, result);
\r
152 class InfoMessageDialog extends MessageDialog {
\r
154 public InfoMessageDialog(Shell shell) {
\r
156 "Migration report", null,
\r
158 MessageDialog.INFORMATION, new String[] { "Continue" }, 0);
\r
162 protected boolean isResizable() {
\r
167 protected Point getInitialSize() {
\r
168 return new Point(800, 500);
\r
172 protected Control createCustomArea(Composite composite) {
\r
174 GridLayoutFactory.fillDefaults().applyTo(composite);
\r
175 Text text = new Text(composite, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER);
\r
176 text.setText(report);
\r
177 GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
\r
184 InfoMessageDialog md = new InfoMessageDialog(Display.getCurrent().getActiveShell());
\r
188 } catch (DatabaseException e) {
\r
189 Logger.defaultLogError(e);
\r
195 abstract static class SelectionAdapter implements SelectionListener {
\r
197 public void widgetDefaultSelected(SelectionEvent e) {
\r
202 class MigratePage extends WizardPage {
\r
206 MigrateModel model;
\r
209 Button browseSource;
\r
211 Button browseTarget;
\r
212 Label symbolsLabel;
\r
214 Label instanceLabel;
\r
215 Label locationsLabel;
\r
217 Composite buttonBar;
\r
218 Label instancesLabel;
\r
222 * ID of the location that has been previously selected by the user.
\r
223 * Used to prevent the location from being reset every time the user
\r
224 * makes a change in any of the other selections.
\r
226 * See first field of {@link MigrateModel#instances}.
\r
228 String previouslySelectedLocationId = null;
\r
230 public MigratePage(String initial) {
\r
231 super("Perform migration", "Perform migration", null);
\r
232 this.initial = initial;
\r
236 public void createControl(Composite parent) {
\r
237 Composite container = new Composite(parent, SWT.NONE);
\r
239 GridLayout layout = new GridLayout();
\r
240 layout.horizontalSpacing = 20;
\r
241 layout.verticalSpacing = 10;
\r
242 layout.numColumns = 10;
\r
243 container.setLayout(layout);
\r
246 new Label(container, SWT.NONE).setText("&Source:");
\r
247 source = new Text(container, SWT.BORDER);
\r
248 source.setText(initial);
\r
249 source.addModifyListener(new ModifyListener() {
\r
251 public void modifyText(ModifyEvent e) {
\r
253 refreshInstances();
\r
256 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(source);
\r
258 browseSource = new Button(container, SWT.NONE);
\r
259 browseSource.setText("&Browse");
\r
260 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseSource);
\r
261 browseSource.addSelectionListener(new SelectionAdapter() {
\r
263 public void widgetSelected(SelectionEvent e_) {
\r
265 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseSourceContentRequest(target.getText()));
\r
266 String uri = queryTargetSelection("Select Source Type", map);
\r
268 source.setText(uri);
\r
269 } catch (DatabaseException e) {
\r
270 Logger.defaultLogError(e);
\r
275 new Label(container, SWT.NONE).setText("&Target:");
\r
276 target = new Text(container, SWT.BORDER);
\r
277 target.setText(initial);
\r
278 target.addModifyListener(new ModifyListener() {
\r
280 public void modifyText(ModifyEvent e) {
\r
283 refreshInstances();
\r
286 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(target);
\r
288 browseTarget = new Button(container, SWT.NONE);
\r
289 browseTarget.setText("B&rowse");
\r
290 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseTarget);
\r
291 browseTarget.addSelectionListener(new SelectionAdapter() {
\r
293 public void widgetSelected(SelectionEvent e_) {
\r
295 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseTargetContentRequest(source.getText()));
\r
296 String uri = queryTargetSelection("Select Target Type", map);
\r
298 target.setText(uri);
\r
299 } catch (DatabaseException e) {
\r
300 Logger.defaultLogError(e);
\r
305 symbolsLabel = new Label(container, SWT.NONE);
\r
306 symbolsLabel.setText("Target &symbol:");
\r
307 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
\r
309 symbols = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
\r
310 symbols.setVisibleItemCount(10);
\r
311 symbols.addSelectionListener(new SelectionAdapter() {
\r
313 public void widgetSelected(SelectionEvent e) {
\r
315 refreshInstances();
\r
318 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
\r
320 instanceLabel = new Label(container, SWT.NONE);
\r
321 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instanceLabel);
\r
322 instanceLabel.setText("");
\r
324 locationsLabel = new Label(container, SWT.NONE);
\r
325 locationsLabel.setText("&Locations:");
\r
326 locations = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
\r
327 locations.setVisibleItemCount(25);
\r
328 locations.addSelectionListener(new SelectionAdapter() {
\r
330 public void widgetSelected(SelectionEvent e) {
\r
331 int index = locations.getSelectionIndex();
\r
332 if (index >= 0 && index < model.instances.size()) {
\r
333 previouslySelectedLocationId = model.instances.get(index).first;
\r
335 refreshInstances();
\r
338 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
\r
340 instancesLabel = new Label(container, SWT.NONE);
\r
341 instancesLabel.setText("&Select instances to migrate:");
\r
342 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instancesLabel);
\r
344 buttonBar = new Composite(container, SWT.NONE);
\r
345 RowLayoutFactory.fillDefaults().type(SWT.HORIZONTAL).applyTo(buttonBar);
\r
346 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
\r
347 Button selectAll = new Button(buttonBar, SWT.PUSH);
\r
348 selectAll.setText("Select &All");
\r
349 Button selectNone = new Button(buttonBar, SWT.PUSH);
\r
350 selectNone.setText("Select &None");
\r
351 selectAll.addSelectionListener(new SelectionAdapter() {
\r
353 public void widgetSelected(SelectionEvent e) {
\r
354 instances.selectAll();
\r
357 selectNone.addSelectionListener(new SelectionAdapter() {
\r
359 public void widgetSelected(SelectionEvent e) {
\r
360 instances.deselectAll();
\r
364 instances = new List(container, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
\r
365 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
\r
366 instances.addListener(SWT.Modify, validateListener);
\r
370 refreshInstances();
\r
372 setControl(container);
\r
376 String queryTargetSelection(String title, Map<String, Pair<String, ImageDescriptor>> map) {
\r
377 Shell shell = source.getShell();
\r
378 ResourceSelectionDialog3<String> dialog = new ResourceSelectionDialog3<String>(shell, map, title, false) {
\r
380 protected IDialogSettings getBaseDialogSettings() {
\r
381 return Activator.getDefault().getDialogSettings();
\r
384 if (dialog.open() == Window.OK)
\r
385 return (String) dialog.getFirstResult();
\r
389 void refreshSymbols() {
\r
392 final String uri = target.getText();
\r
394 Collection<NamedResource> syms = Simantics.getSession().syncRequest(new UniqueRead<Collection<NamedResource>>() {
\r
397 public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {
\r
399 Layer0 L0 = Layer0.getInstance(graph);
\r
400 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
401 ModelingResources MOD = ModelingResources.getInstance(graph);
\r
403 Set<NamedResource> result = new THashSet<>();
\r
404 Resource componentType = graph.getResource(uri);
\r
406 Set<Resource> potentialSymbols = new THashSet<>();
\r
407 potentialSymbols.addAll(graph.syncRequest(new ObjectsWithType(componentType, L0.ConsistsOf, DIA.ElementClass)));
\r
408 potentialSymbols.addAll(graph.getObjects(componentType, MOD.ComponentTypeToSymbol));
\r
410 for(Resource symbol : potentialSymbols) {
\r
411 if(!graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) continue;
\r
412 String name = graph.getRelatedValue(symbol, L0.HasName, Bindings.STRING);
\r
413 if(graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType))
\r
414 result.add(new NamedResource(name, symbol));
\r
417 return new ArrayList<NamedResource>(result);
\r
423 symbols.removeAll();
\r
424 symbols.add("<retain symbol name>");
\r
425 for(NamedResource nr : syms) {
\r
426 symbols.add(nr.getName());
\r
428 symbols.setData(syms);
\r
431 if(syms.isEmpty()) {
\r
432 symbolsLabel.setVisible(false);
\r
433 symbols.setVisible(false);
\r
434 GridDataFactory.fillDefaults().exclude(true).applyTo(symbolsLabel);
\r
435 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(symbols);
\r
437 symbolsLabel.setVisible(true);
\r
438 symbols.setVisible(true);
\r
439 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
\r
440 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
\r
443 symbols.getParent().layout();
\r
445 } catch (DatabaseException e) {
\r
446 Logger.defaultLogError(e);
\r
450 void refreshModel() {
\r
451 final String sourceText = source.getText();
\r
452 final String targetText = target.getText();
\r
453 int symbolIndex = symbols.getSelectionIndex();
\r
454 Resource symbol = null;
\r
455 if(symbolIndex > 0) {
\r
456 @SuppressWarnings("unchecked")
\r
457 java.util.List<NamedResource> nrs = (java.util.List<NamedResource>)symbols.getData();
\r
458 symbol = nrs.get(symbolIndex-1).getResource();
\r
460 final Resource finalSymbol = symbol;
\r
463 model = Simantics.getSession().syncRequest(new UniqueRead<MigrateModel>() {
\r
465 public MigrateModel perform(ReadGraph graph) throws DatabaseException {
\r
466 Layer0 L0 = Layer0.getInstance(graph);
\r
467 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
469 Resource source = graph.getPossibleResource(sourceText);
\r
470 if(source == null) return model;
\r
471 Resource target = graph.getPossibleResource(targetText);
\r
472 if(target == null) return model;
\r
474 if(graph.isInstanceOf(source, STR.ComponentType)) {
\r
475 return UserComponentMigration.getComponentTypeModel(graph, source, target, finalSymbol);
\r
476 } else if(graph.isInstanceOf(source, L0.SharedOntology)) {
\r
477 return UserComponentMigration.getSharedOntologyModel(graph, source, target);
\r
483 } catch (DatabaseException e) {
\r
484 Logger.defaultLogError(e);
\r
487 instances.removeAll();
\r
488 locations.removeAll();
\r
493 int preSelect = -1, i = 0;
\r
494 for (Triple<String,NamedResource,Collection<MigrationOperation>> r : model.instances) {
\r
495 locations.add(r.second.getName() + " (" + r.third.size() + " instances)");
\r
496 if (r.first.equals(previouslySelectedLocationId))
\r
498 if (preSelect < 0 && model.activeModels.contains(r.second.getResource()))
\r
502 if (locations.getItemCount() == 0) {
\r
503 locations.add("<no instances were found>");
\r
504 locations.select(0);
\r
506 locations.select(preSelect > -1 ? preSelect : 0);
\r
510 void refreshInstances() {
\r
513 for(Triple<String,NamedResource,Collection<MigrationOperation>> pair : model.instances) {
\r
514 toMigrate += pair.third.size();
\r
517 if(model.instanceCount == 0)
\r
518 instanceLabel.setText("No instances were found.");
\r
519 else if(model.instanceCount == 1) {
\r
520 if(toMigrate == 1) {
\r
521 instanceLabel.setText("1 migratable instance found.");
\r
523 instanceLabel.setText("1 instance found, but it cannot be migrated with current settings.");
\r
526 if(toMigrate < model.instanceCount) {
\r
527 if(toMigrate == 0) {
\r
528 instanceLabel.setText(model.instanceCount + " instances were found. None of them can be migrated with current settings.");
\r
530 instanceLabel.setText(model.instanceCount + " instances were found. " + (model.instanceCount-toMigrate) + " of them cannot be migrated with current settings.");
\r
533 instanceLabel.setText(model.instanceCount + " migratable instances found. ");
\r
537 instances.removeAll();
\r
542 if(toMigrate == 0) {
\r
543 locationsLabel.setVisible(false);
\r
544 locations.setVisible(false);
\r
545 GridDataFactory.fillDefaults().exclude(true).applyTo(locationsLabel);
\r
546 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(locations);
\r
548 locationsLabel.setVisible(true);
\r
549 locations.setVisible(true);
\r
550 GridDataFactory.fillDefaults().applyTo(locationsLabel);
\r
551 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
\r
554 if(!model.instances.isEmpty()) {
\r
556 int locationIndex = locations.getSelectionIndex();
\r
557 if(locationIndex == -1) return;
\r
559 model.sortedShownInstances = new ArrayList<MigrationOperation>();
\r
560 for(MigrationOperation o : model.instances.get(locationIndex).third)
\r
561 model.sortedShownInstances.add(o);
\r
562 Collections.sort(model.sortedShownInstances, MIGRATION_OP_COMPARATOR);
\r
563 for(MigrationOperation o : model.sortedShownInstances) {
\r
564 String uri = o.toString();
\r
565 uri = uri.replace("http://Projects/Development%20Project/", "");
\r
566 uri = URIStringUtils.unescape(uri);
\r
567 instances.add(uri);
\r
572 if(model.sortedShownInstances.isEmpty()) {
\r
573 instancesLabel.setVisible(false);
\r
574 instances.setVisible(false);
\r
575 buttonBar.setVisible(false);
\r
576 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(instancesLabel);
\r
577 GridDataFactory.fillDefaults().exclude(true).grab(true, true).span(10, 1).applyTo(instances);
\r
578 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(10, 1).applyTo(buttonBar);
\r
580 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(instancesLabel);
\r
581 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
\r
582 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
\r
583 instancesLabel.setVisible(true);
\r
584 instances.setVisible(true);
\r
585 buttonBar.setVisible(true);
\r
588 locations.getParent().layout();
\r
592 Listener validateListener = new Listener() {
\r
594 public void handleEvent(Event event) {
\r
595 switch (event.type) {
\r
598 case SWT.Selection:
\r
603 if (event.widget instanceof Text) {
\r
604 Text t = (Text) event.widget;
\r
613 private void validatePage() {
\r
615 setErrorMessage(null);
\r
616 setPageComplete(true);
\r
621 private static final Comparator<MigrationOperation> MIGRATION_OP_COMPARATOR = new Comparator<MigrateModel.MigrationOperation>() {
\r
623 public int compare(MigrationOperation o1, MigrationOperation o2) {
\r
624 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.instanceToMigrate.getName(), o2.instanceToMigrate.getName());
\r
628 private static abstract class BrowseContentRequest extends UnaryRead<String, Map<String, Pair<String, ImageDescriptor>>> {
\r
630 public BrowseContentRequest(String uri) {
\r
635 public Map<String, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
\r
636 Resource root = graph.getPossibleResource(parameter);
\r
640 Map<String, Pair<String, ImageDescriptor>> result = new THashMap<>();
\r
641 Collection<NamedResource> infos = getVersions(graph, root);
\r
642 for (NamedResource info : infos)
\r
643 result.put(graph.getURI(info.getResource()), Pair.<String,ImageDescriptor>make(Versions.getStandardPathNameString(graph, info.getResource()), null));
\r
648 protected abstract Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException;
\r
652 private static class BrowseSourceContentRequest extends BrowseContentRequest {
\r
653 public BrowseSourceContentRequest(String uri) {
\r
657 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
\r
658 return Versions.getOlderVersions(graph, root);
\r
662 private static class BrowseTargetContentRequest extends BrowseContentRequest {
\r
663 public BrowseTargetContentRequest(String uri) {
\r
667 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
\r
668 return Versions.getNewerVersions(graph, root);
\r