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;
19 import java.util.HashSet;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.OperationCanceledException;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.core.runtime.SubMonitor;
27 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
28 import org.eclipse.core.runtime.preferences.InstanceScope;
29 import org.eclipse.jface.dialogs.IDialogSettings;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.layout.GridDataFactory;
32 import org.eclipse.jface.layout.GridLayoutFactory;
33 import org.eclipse.jface.layout.RowLayoutFactory;
34 import org.eclipse.jface.resource.ImageDescriptor;
35 import org.eclipse.jface.window.Window;
36 import org.eclipse.jface.wizard.Wizard;
37 import org.eclipse.jface.wizard.WizardPage;
38 import org.eclipse.osgi.util.NLS;
39 import org.eclipse.swt.SWT;
40 import org.eclipse.swt.custom.CCombo;
41 import org.eclipse.swt.events.ModifyEvent;
42 import org.eclipse.swt.events.ModifyListener;
43 import org.eclipse.swt.events.SelectionEvent;
44 import org.eclipse.swt.events.SelectionListener;
45 import org.eclipse.swt.graphics.Point;
46 import org.eclipse.swt.layout.GridLayout;
47 import org.eclipse.swt.widgets.Button;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Control;
50 import org.eclipse.swt.widgets.Event;
51 import org.eclipse.swt.widgets.Label;
52 import org.eclipse.swt.widgets.List;
53 import org.eclipse.swt.widgets.Listener;
54 import org.eclipse.swt.widgets.Shell;
55 import org.eclipse.swt.widgets.Text;
56 import org.simantics.Simantics;
57 import org.simantics.databoard.Bindings;
58 import org.simantics.databoard.util.URIStringUtils;
59 import org.simantics.db.ReadGraph;
60 import org.simantics.db.Resource;
61 import org.simantics.db.WriteGraph;
62 import org.simantics.db.common.NamedResource;
63 import org.simantics.db.common.request.ObjectsWithType;
64 import org.simantics.db.common.request.UnaryRead;
65 import org.simantics.db.common.request.UniqueRead;
66 import org.simantics.db.common.request.WriteResultRequest;
67 import org.simantics.db.common.utils.Logger;
68 import org.simantics.db.common.utils.Versions;
69 import org.simantics.db.exception.CancelTransactionException;
70 import org.simantics.db.exception.DatabaseException;
71 import org.simantics.db.request.Read;
72 import org.simantics.diagram.stubs.DiagramResource;
73 import org.simantics.layer0.Layer0;
74 import org.simantics.modeling.MigrateModel;
75 import org.simantics.modeling.MigrateModel.MigrationOperation;
76 import org.simantics.modeling.ModelingResources;
77 import org.simantics.modeling.UserComponentMigration;
78 import org.simantics.modeling.ui.Activator;
79 import org.simantics.modeling.ui.diagramEditor.DiagramEditor;
80 import org.simantics.structural.stubs.StructuralResource2;
81 import org.simantics.ui.workbench.dialogs.ResourceSelectionDialog3;
82 import org.simantics.utils.datastructures.Pair;
83 import org.simantics.utils.datastructures.Triple;
84 import org.simantics.utils.strings.AlphanumComparator;
85 import org.simantics.utils.ui.SWTUtils;
87 import gnu.trove.map.hash.THashMap;
88 import gnu.trove.set.hash.THashSet;
89 import gnu.trove.set.hash.TIntHashSet;
92 * @author Antti Villberg
94 public class MigrateWizard extends Wizard {
98 MigratePage migratePage;
100 IEclipsePreferences prefnode;
102 public MigrateWizard(String initial) {
104 this.initial = initial;
106 setWindowTitle(Messages.MigrateWizard_PerformMigration);
107 setNeedsProgressMonitor(true);
108 setForcePreviousAndNextButtons(false);
109 setDialogSettings(Activator.getDefault().getDialogSettings());
111 prefnode = InstanceScope.INSTANCE.getNode( "org.simantics.modeling.ui.wizard.MigrateWizard" ); //$NON-NLS-1$
116 public void addPages() {
117 migratePage = new MigratePage(initial);
118 addPage(migratePage);
122 public boolean canFinish() {
123 // if (model.spec.name.isEmpty())
129 public boolean performFinish() {
131 int locationIndex = migratePage.locations.getSelectionIndex();
132 if(locationIndex == -1) return true;
134 if(migratePage.model == null || migratePage.model.instances.isEmpty()) return true;
136 int[] sel = migratePage.instances.getSelectionIndices();
137 TIntHashSet sels = new TIntHashSet();
138 for(int i : sel) sels.add(i);
140 Collection<MigrationOperation> ops = migratePage.model.sortedShownInstances;
142 final ArrayList<MigrationOperation> result = new ArrayList<>();
143 for(MigrationOperation op : ops) {
144 if(sels.contains(index)) result.add(op);
148 if(result.isEmpty()) return true;
151 String[] report = { null };
152 getContainer().run(true, true, monitor -> {
153 SubMonitor mon = SubMonitor.convert(monitor, 1000);
155 report[0] = Simantics.getSession().syncRequest(new WriteResultRequest<String>() {
157 public String perform(WriteGraph graph) throws DatabaseException {
158 graph.markUndoPoint();
159 String report = UserComponentMigration.doMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result);
160 UserComponentMigration.doPostMigration(mon.newChild(500, SubMonitor.SUPPRESS_NONE), graph, result);
161 mon.setTaskName(Messages.MigrateWizard_MonitorCommitingChanges);
162 mon.subTask(""); //$NON-NLS-1$
167 // Schedule diagram reinitialization to see actual migration changes
168 // This needs to be done because the diagram editor can't currently
169 // refresh itself after type (InstanceOf) changes in diagram elements.
170 Set<Resource> diagramsToReopen = Simantics.getSession().syncRequest(
171 (Read<Set<Resource>>) graph -> componentsToDiagrams(graph, ops));
172 SWTUtils.asyncExec(MigrateWizard.this.getContainer().getShell().getDisplay(),
173 () -> DiagramEditor.reinitializeDiagram(diagramsToReopen));
174 } catch (DatabaseException e) {
175 throw new InvocationTargetException(e);
181 ReportDialog md = new ReportDialog(getShell(), report[0], 800, 500);
185 } catch (InvocationTargetException e) {
186 // Don't show user cancellations as errors.
187 Throwable cause = e.getCause();
188 if (!(cause instanceof CancelTransactionException || cause instanceof OperationCanceledException)) {
189 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
190 Messages.MigrateWizard_ActivatorMigrationFailed, e.getCause()));
192 } catch (InterruptedException e) {
193 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
194 Messages.MigrateWizard_ActivatorMigrationInterrupted, e));
199 private static final Set<Resource> componentsToDiagrams(ReadGraph graph, Collection<MigrationOperation> ops) throws DatabaseException {
200 Layer0 L0 = Layer0.getInstance(graph);
201 ModelingResources MOD = ModelingResources.getInstance(graph);
202 Set<Resource> diagrams = new HashSet<>();
203 for (MigrationOperation op : ops) {
204 Resource composite = graph.getPossibleObject(op.instanceToMigrate.getResource(), L0.PartOf);
205 if (composite != null) {
206 Resource diagram = graph.getPossibleObject(composite, MOD.CompositeToDiagram);
208 diagrams.add(diagram);
214 static class ReportDialog extends MessageDialog {
215 private final String report;
216 private final int initialWidth;
217 private final int initialHeight;
219 public ReportDialog(Shell shell, String report, int width, int height) {
221 Messages.MigrateWizard_MigrationReport, null,
223 MessageDialog.INFORMATION, new String[] { Messages.MigrateWizard_Continue }, 0);
224 this.report = report;
225 this.initialWidth = width;
226 this.initialHeight = height;
230 protected boolean isResizable() {
235 protected Point getInitialSize() {
236 return new Point(initialWidth, initialHeight);
240 protected Control createCustomArea(Composite composite) {
241 GridLayoutFactory.fillDefaults().applyTo(composite);
242 Text text = new Text(composite, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER);
243 text.setText(report);
244 GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
249 abstract static class SelectionAdapter implements SelectionListener {
251 public void widgetDefaultSelected(SelectionEvent e) {
256 class MigratePage extends WizardPage {
269 Label locationsLabel;
272 Label instancesLabel;
276 * ID of the location that has been previously selected by the user.
277 * Used to prevent the location from being reset every time the user
278 * makes a change in any of the other selections.
280 * See first field of {@link MigrateModel#instances}.
282 String previouslySelectedLocationId = null;
284 public MigratePage(String initial) {
285 super(Messages.MigrateWizard_PerformMigration, Messages.MigrateWizard_PerformMigration, null);
286 this.initial = initial;
290 public void createControl(Composite parent) {
291 Composite container = new Composite(parent, SWT.NONE);
293 GridLayout layout = new GridLayout();
294 layout.horizontalSpacing = 20;
295 layout.verticalSpacing = 10;
296 layout.numColumns = 10;
297 container.setLayout(layout);
300 new Label(container, SWT.NONE).setText(Messages.MigrateWizard_SourceAnd);
301 source = new Text(container, SWT.BORDER);
302 source.setText(initial);
303 source.addModifyListener(new ModifyListener() {
305 public void modifyText(ModifyEvent e) {
310 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(source);
312 browseSource = new Button(container, SWT.NONE);
313 browseSource.setText(Messages.MigrateWizard_BrowseAnd);
314 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseSource);
315 browseSource.addSelectionListener(new SelectionAdapter() {
317 public void widgetSelected(SelectionEvent e_) {
319 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseSourceContentRequest(target.getText()));
320 String uri = queryTargetSelection(Messages.MigrateWizard_SelectSourceType, map);
323 } catch (DatabaseException e) {
324 Logger.defaultLogError(e);
329 new Label(container, SWT.NONE).setText(Messages.MigrateWizard_TargetAnd);
330 target = new Text(container, SWT.BORDER);
331 target.setText(initial);
332 target.addModifyListener(new ModifyListener() {
334 public void modifyText(ModifyEvent e) {
340 GridDataFactory.fillDefaults().grab(true, false).span(8, 1).applyTo(target);
342 browseTarget = new Button(container, SWT.NONE);
343 browseTarget.setText(Messages.MigrateWizard_BrowseAnd1);
344 GridDataFactory.fillDefaults().grab(false, false).applyTo(browseTarget);
345 browseTarget.addSelectionListener(new SelectionAdapter() {
347 public void widgetSelected(SelectionEvent e_) {
349 Map<String, Pair<String, ImageDescriptor>> map = Simantics.getSession().syncRequest(new BrowseTargetContentRequest(source.getText()));
350 String uri = queryTargetSelection(Messages.MigrateWizard_SelectTargetType, map);
353 } catch (DatabaseException e) {
354 Logger.defaultLogError(e);
359 symbolsLabel = new Label(container, SWT.NONE);
360 symbolsLabel.setText(Messages.MigrateWizard_TargetAndSymbol);
361 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
363 symbols = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
364 symbols.setVisibleItemCount(10);
365 symbols.addSelectionListener(new SelectionAdapter() {
367 public void widgetSelected(SelectionEvent e) {
372 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
374 instanceLabel = new Label(container, SWT.NONE);
375 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instanceLabel);
376 instanceLabel.setText(""); //$NON-NLS-1$
378 locationsLabel = new Label(container, SWT.NONE);
379 locationsLabel.setText(Messages.MigrateWizard_LocationAnd);
380 locations = new CCombo(container, SWT.BORDER | SWT.READ_ONLY);
381 locations.setVisibleItemCount(25);
382 locations.addSelectionListener(new SelectionAdapter() {
384 public void widgetSelected(SelectionEvent e) {
385 int index = locations.getSelectionIndex();
386 if (index >= 0 && index < model.instances.size()) {
387 previouslySelectedLocationId = model.instances.get(index).first;
392 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
394 instancesLabel = new Label(container, SWT.NONE);
395 instancesLabel.setText(Messages.MigrateWizard_SelectInstancesToMigrate);
396 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(instancesLabel);
398 buttonBar = new Composite(container, SWT.NONE);
399 RowLayoutFactory.fillDefaults().type(SWT.HORIZONTAL).applyTo(buttonBar);
400 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
401 Button selectAll = new Button(buttonBar, SWT.PUSH);
402 selectAll.setText(Messages.MigrateWizard_SelectAllAnd);
403 Button selectNone = new Button(buttonBar, SWT.PUSH);
404 selectNone.setText(Messages.MigrateWizard_SelectNoneAnd);
405 selectAll.addSelectionListener(new SelectionAdapter() {
407 public void widgetSelected(SelectionEvent e) {
408 instances.selectAll();
411 selectNone.addSelectionListener(new SelectionAdapter() {
413 public void widgetSelected(SelectionEvent e) {
414 instances.deselectAll();
418 instances = new List(container, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
419 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
420 instances.addListener(SWT.Modify, validateListener);
426 setControl(container);
430 String queryTargetSelection(String title, Map<String, Pair<String, ImageDescriptor>> map) {
431 Shell shell = source.getShell();
432 ResourceSelectionDialog3<String> dialog = new ResourceSelectionDialog3<String>(shell, map, title, false) {
434 protected IDialogSettings getBaseDialogSettings() {
435 return Activator.getDefault().getDialogSettings();
438 if (dialog.open() == Window.OK)
439 return (String) dialog.getFirstResult();
443 void refreshSymbols() {
446 final String uri = target.getText();
448 Collection<NamedResource> syms = Simantics.getSession().syncRequest(new UniqueRead<Collection<NamedResource>>() {
451 public Collection<NamedResource> perform(ReadGraph graph) throws DatabaseException {
453 Layer0 L0 = Layer0.getInstance(graph);
454 DiagramResource DIA = DiagramResource.getInstance(graph);
455 ModelingResources MOD = ModelingResources.getInstance(graph);
457 Set<NamedResource> result = new THashSet<>();
458 Resource componentType = graph.getResource(uri);
460 Set<Resource> potentialSymbols = new THashSet<>();
461 potentialSymbols.addAll(graph.syncRequest(new ObjectsWithType(componentType, L0.ConsistsOf, DIA.ElementClass)));
462 potentialSymbols.addAll(graph.getObjects(componentType, MOD.ComponentTypeToSymbol));
464 for(Resource symbol : potentialSymbols) {
465 if(!graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType)) continue;
466 String name = graph.getRelatedValue(symbol, L0.HasName, Bindings.STRING);
467 if(graph.hasStatement(symbol, MOD.SymbolToComponentType, componentType))
468 result.add(new NamedResource(name, symbol));
471 return new ArrayList<>(result);
478 symbols.add(Messages.MigrateWizard_RetainSymbolName);
479 for(NamedResource nr : syms) {
480 symbols.add(nr.getName());
482 symbols.setData(syms);
486 symbolsLabel.setVisible(false);
487 symbols.setVisible(false);
488 GridDataFactory.fillDefaults().exclude(true).applyTo(symbolsLabel);
489 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(symbols);
491 symbolsLabel.setVisible(true);
492 symbols.setVisible(true);
493 GridDataFactory.fillDefaults().applyTo(symbolsLabel);
494 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(symbols);
497 symbols.getParent().layout();
499 } catch (DatabaseException e) {
500 Logger.defaultLogError(e);
504 void refreshModel() {
505 final String sourceText = source.getText();
506 final String targetText = target.getText();
507 int symbolIndex = symbols.getSelectionIndex();
508 Resource symbol = null;
509 if(symbolIndex > 0) {
510 @SuppressWarnings("unchecked")
511 java.util.List<NamedResource> nrs = (java.util.List<NamedResource>)symbols.getData();
512 symbol = nrs.get(symbolIndex-1).getResource();
514 final Resource finalSymbol = symbol;
517 model = Simantics.getSession().syncRequest(new UniqueRead<MigrateModel>() {
519 public MigrateModel perform(ReadGraph graph) throws DatabaseException {
520 Layer0 L0 = Layer0.getInstance(graph);
521 StructuralResource2 STR = StructuralResource2.getInstance(graph);
523 Resource source = graph.getPossibleResource(sourceText);
524 if(source == null) return model;
525 Resource target = graph.getPossibleResource(targetText);
526 if(target == null) return model;
528 if(graph.isInstanceOf(source, STR.ComponentType)) {
529 return UserComponentMigration.getComponentTypeModel(graph, source, target, finalSymbol);
530 } else if(graph.isInstanceOf(source, L0.SharedOntology)) {
531 return UserComponentMigration.getSharedOntologyModel(graph, source, target);
537 } catch (DatabaseException e) {
538 Logger.defaultLogError(e);
541 instances.removeAll();
542 locations.removeAll();
547 int preSelect = -1, i = 0;
548 for (Triple<String,NamedResource,Collection<MigrationOperation>> r : model.instances) {
549 locations.add(NLS.bind(Messages.MigrateWizard_LocationInstance, r.second.getName(), r.third.size()));
550 if (r.first.equals(previouslySelectedLocationId))
552 if (preSelect < 0 && model.activeModels.contains(r.second.getResource()))
556 if (locations.getItemCount() == 0) {
557 locations.add(Messages.MigrateWizard_NoInstancesFound);
560 locations.select(preSelect > -1 ? preSelect : 0);
564 void refreshInstances() {
567 for (Triple<String, NamedResource, Collection<MigrationOperation>> pair : model.instances) {
568 toMigrate += pair.third.size();
571 if (model.instanceCount == 0)
572 instanceLabel.setText(Messages.MigrateWizard_NoInstancesWereFound);
573 else if (model.instanceCount == 1) {
574 if (toMigrate == 1) {
575 instanceLabel.setText(Messages.MigrateWizard_OneMigratableInstanceFound);
577 instanceLabel.setText(Messages.MigrateWizard_InstanceFoundCannotBeMigrated);
580 if (toMigrate < model.instanceCount) {
581 if (toMigrate == 0) {
583 .setText(NLS.bind(Messages.MigrateWizard_NumberOfInstancesFound, model.instanceCount));
585 instanceLabel.setText(NLS.bind(Messages.MigrateWizard_InstancesWereFound, model.instanceCount,
586 (model.instanceCount - toMigrate)));
590 .setText(NLS.bind(Messages.MigrateWizard_MigratableInstancesFound, model.instanceCount));
594 instances.removeAll();
599 if (toMigrate == 0) {
600 locationsLabel.setVisible(false);
601 locations.setVisible(false);
602 GridDataFactory.fillDefaults().exclude(true).applyTo(locationsLabel);
603 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(locations);
605 locationsLabel.setVisible(true);
606 locations.setVisible(true);
607 GridDataFactory.fillDefaults().applyTo(locationsLabel);
608 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(locations);
611 if (!model.instances.isEmpty()) {
613 int locationIndex = locations.getSelectionIndex();
614 if (locationIndex == -1)
617 model.sortedShownInstances = new ArrayList<>();
618 for (MigrationOperation o : model.instances.get(locationIndex).third)
619 model.sortedShownInstances.add(o);
620 Collections.sort(model.sortedShownInstances, MIGRATION_OP_COMPARATOR);
621 for (MigrationOperation o : model.sortedShownInstances) {
622 String uri = o.toString();
623 uri = uri.replace("http://Projects/Development%20Project/", ""); //$NON-NLS-1$ //$NON-NLS-2$
624 uri = URIStringUtils.unescape(uri);
630 if (model.sortedShownInstances.isEmpty()) {
631 instancesLabel.setVisible(false);
632 instances.setVisible(false);
633 buttonBar.setVisible(false);
634 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(9, 1).applyTo(instancesLabel);
635 GridDataFactory.fillDefaults().exclude(true).grab(true, true).span(10, 1).applyTo(instances);
636 GridDataFactory.fillDefaults().exclude(true).grab(true, false).span(10, 1).applyTo(buttonBar);
638 GridDataFactory.fillDefaults().grab(true, false).span(9, 1).applyTo(instancesLabel);
639 GridDataFactory.fillDefaults().grab(true, true).span(10, 1).applyTo(instances);
640 GridDataFactory.fillDefaults().grab(true, false).span(10, 1).applyTo(buttonBar);
641 instancesLabel.setVisible(true);
642 instances.setVisible(true);
643 buttonBar.setVisible(true);
646 locations.getParent().layout();
650 Listener validateListener = new Listener() {
652 public void handleEvent(Event event) {
653 switch (event.type) {
661 if (event.widget instanceof Text) {
662 Text t = (Text) event.widget;
671 private void validatePage() {
673 setErrorMessage(null);
674 setPageComplete(true);
679 private static final Comparator<MigrationOperation> MIGRATION_OP_COMPARATOR = new Comparator<MigrateModel.MigrationOperation>() {
681 public int compare(MigrationOperation o1, MigrationOperation o2) {
682 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.instanceToMigrate.getName(), o2.instanceToMigrate.getName());
686 private static abstract class BrowseContentRequest extends UnaryRead<String, Map<String, Pair<String, ImageDescriptor>>> {
688 public BrowseContentRequest(String uri) {
693 public Map<String, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
694 Resource root = graph.getPossibleResource(parameter);
698 Map<String, Pair<String, ImageDescriptor>> result = new THashMap<>();
699 Collection<NamedResource> infos = getVersions(graph, root);
700 for (NamedResource info : infos)
701 result.put(graph.getURI(info.getResource()), Pair.<String,ImageDescriptor>make(Versions.getStandardPathNameString(graph, info.getResource()), null));
706 protected abstract Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException;
710 private static class BrowseSourceContentRequest extends BrowseContentRequest {
711 public BrowseSourceContentRequest(String uri) {
715 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
716 return Versions.getOlderVersions(graph, root);
720 private static class BrowseTargetContentRequest extends BrowseContentRequest {
721 public BrowseTargetContentRequest(String uri) {
725 protected Collection<NamedResource> getVersions(ReadGraph graph, Resource root) throws DatabaseException {
726 return Versions.getNewerVersions(graph, root);