--- /dev/null
+package org.simantics.db.layer0.migration;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;
+import org.simantics.db.common.request.DelayedWriteRequest;
+import org.simantics.db.common.utils.ListUtils;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.impl.EntityInstances.QueryIndex;
+import org.simantics.db.layer0.request.PossibleResource;
+import org.simantics.layer0.Layer0;
+
+/**
+ * Changes L0.InstanceOf relations according to descriptions in the migrated
+ * index root.
+ *
+ * @author Tuukka Lehtonen
+ * @since 1.33.0
+ */
+public class InstanceOfMigrationStep implements MigrationStep {
+
+ private static class Migration {
+ public final String fromUri;
+ public final String toUri;
+
+ public Migration(String from, String to) {
+ this.fromUri = from;
+ this.toUri = to;
+ }
+
+ @Override
+ public String toString() {
+ return fromUri + " -> " + toUri;
+ }
+ }
+
+ private static class RMigration {
+ public final Migration m;
+ public final Resource from;
+ public final Resource to;
+
+ public RMigration(RequestProcessor processor, Migration m) throws DatabaseException {
+ this.m = m;
+ this.from = processor.syncRequest(new PossibleResource(m.fromUri));
+ this.to = processor.syncRequest(new PossibleResource(m.toUri));
+ }
+
+ public boolean isValid() {
+ return from != null && to != null;
+ }
+ }
+
+ private final Migration[] migrations;
+
+ public InstanceOfMigrationStep(String fromUri, String toUri) {
+ this.migrations = new Migration[] { new Migration(fromUri, toUri) };
+ }
+
+ public InstanceOfMigrationStep(ReadGraph graph, Resource step) throws DatabaseException {
+ List<Migration> ms = new ArrayList<>();
+ List<Resource> uris = ListUtils.toList(graph, step);
+ int size = uris.size() & ~1;
+ for (int i = 0; i < size; i += 2) {
+ String from = graph.getPossibleValue(uris.get(i), Bindings.STRING);
+ String to = graph.getPossibleValue(uris.get(i+1), Bindings.STRING);
+ if (from != null && to != null)
+ ms.add(new Migration(from, to));
+ }
+ migrations = ms.toArray(new Migration[ms.size()]);
+ }
+
+ @Override
+ public void applyTo(final IProgressMonitor monitor, Session session, MigrationState state) throws DatabaseException {
+ final Collection<Resource> roots = state.getProperty(MigrationStateKeys.CURRENT_ROOT_RESOURCES);
+ if (roots.isEmpty())
+ return;
+ final PrintWriter log = MigrationUtils.getProperty(state, MigrationStateKeys.MESSAGE_LOG_WRITER, NullWriter.PRINT_INSTANCE);
+
+ session.sync(new DelayedWriteRequest() {
+ @Override
+ public void perform(WriteGraph graph) throws DatabaseException {
+ migrateInstances(monitor, graph, roots, log);
+ }
+ });
+ }
+
+ private void migrateInstances(IProgressMonitor monitor, WriteGraph graph, Collection<Resource> roots, PrintWriter log) throws DatabaseException {
+ for (Migration m : migrations)
+ migrateInstances(monitor, graph, new RMigration(graph, m), roots, log);
+ }
+
+ private void migrateInstances(IProgressMonitor monitor, WriteGraph graph, RMigration rm, Collection<Resource> roots, PrintWriter log) throws DatabaseException {
+ log.println("## InstanceOf Migration ##");
+ log.println("* From: `" + rm.m.fromUri + "`");
+ log.println("* To: `" + rm.m.toUri + "`");
+ if (rm.isValid()) {
+ for (Resource root : roots)
+ migrateInstances(monitor, graph, root, rm, log);
+ } else {
+ log.println("\nSkipping migration as invalid.\n");
+ }
+ }
+
+ private static void migrateInstances(IProgressMonitor monitor, WriteGraph graph, Resource root, RMigration migration, PrintWriter log) throws DatabaseException {
+ log.println("### Migrating root `" + NameUtils.getSafeName(graph, root) + "` ###");
+ String rootUri = NameUtils.getURIOrSafeNameInternal(graph, root);
+ Layer0 L0 = Layer0.getInstance(graph);
+
+ for (Resource instance : instancesOf(graph, root, migration.from)) {
+ if (graph.hasStatement(instance, L0.InstanceOf, migration.from)) {
+ graph.deny(instance, L0.InstanceOf, null, migration.from);
+ graph.claim(instance, L0.InstanceOf, null, migration.to);
+ log.println("* `" + relativeUri(graph, rootUri, instance) + "`");
+ }
+ }
+ }
+
+ private static List<Resource> instancesOf(ReadGraph graph, Resource root, Resource type) throws DatabaseException {
+ return graph.syncRequest(
+ new QueryIndex(root, type, ""),
+ TransientCacheListener.<List<Resource>>instance());
+ }
+
+ private static String relativeUri(ReadGraph graph, String rootUri, Resource r) throws DatabaseException {
+ String uri = graph.getPossibleURI(r);
+ return uri != null
+ ? uri.substring(rootUri.length())
+ : NameUtils.getURIOrSafeNameInternal(graph, r);
+ }
+
+}
\ No newline at end of file