]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling/src/org/simantics/modeling/CompilePGraphs.java
Added Simantics/PGraph SCL API for compiling shared ontologies TGs
[simantics/platform.git] / bundles / org.simantics.modeling / src / org / simantics / modeling / CompilePGraphs.java
diff --git a/bundles/org.simantics.modeling/src/org/simantics/modeling/CompilePGraphs.java b/bundles/org.simantics.modeling/src/org/simantics/modeling/CompilePGraphs.java
new file mode 100644 (file)
index 0000000..9b620c2
--- /dev/null
@@ -0,0 +1,314 @@
+package org.simantics.modeling;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.osgi.framework.Bundle;
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.IndexRoot;
+import org.simantics.db.common.request.ObjectsWithType;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.CopyHandler;
+import org.simantics.db.layer0.adapter.SubgraphExtent.ExtentStatus;
+import org.simantics.db.layer0.adapter.impl.DefaultCopyHandler;
+import org.simantics.db.layer0.adapter.impl.DefaultPasteHandler;
+import org.simantics.db.layer0.adapter.impl.DefaultPasteImportAdvisor;
+import org.simantics.db.layer0.util.ClipboardUtils;
+import org.simantics.db.layer0.util.DomainProcessorState;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.util.ModelTransferableGraphSource;
+import org.simantics.db.layer0.util.ModelTransferableGraphSourceRequest;
+import org.simantics.db.layer0.util.SimanticsClipboard.Representation;
+import org.simantics.db.layer0.util.SimanticsClipboardImpl;
+import org.simantics.db.layer0.util.SimanticsKeys;
+import org.simantics.db.layer0.util.TransferableGraphConfiguration2;
+import org.simantics.db.service.SerialisationSupport;
+import org.simantics.graph.compiler.CompilationResult;
+import org.simantics.graph.compiler.ExternalFileLoader;
+import org.simantics.graph.compiler.GraphCompiler;
+import org.simantics.graph.compiler.GraphCompilerPreferences;
+import org.simantics.graph.compiler.ValidationMode;
+import org.simantics.graph.db.TransferableGraphException;
+import org.simantics.graph.db.TransferableGraphSource;
+import org.simantics.graph.db.TransferableGraphs;
+import org.simantics.graph.diff.Diff;
+import org.simantics.graph.diff.TransferableGraphDelta1;
+import org.simantics.graph.representation.Identity;
+import org.simantics.graph.representation.Root;
+import org.simantics.graph.representation.TransferableGraph1;
+import org.simantics.graphfile.ontology.GraphFileResource;
+import org.simantics.layer0.Layer0;
+import org.simantics.ltk.ISource;
+import org.simantics.ltk.Problem;
+import org.simantics.modeling.internal.Activator;
+import org.simantics.utils.FileUtils;
+import org.simantics.utils.datastructures.Pair;
+
+/**
+ * @author Antti Villberg
+ */
+public class CompilePGraphs {
+
+    public static interface UserAgent {
+        void reportProblems(CompilationResult result);
+    }
+
+    /**
+     * <code>Simantics/PGraph#compilePGraphs</code> SCL API.
+     * 
+     * @param r
+     * @throws IOException
+     * @throws DatabaseException
+     */
+    public static void compilePGraphs(Resource r) throws IOException, DatabaseException {
+        compilePGraphs(r, null);
+    }
+
+    public static void compilePGraphs(Resource r, UserAgent userAgent) throws IOException, DatabaseException {
+        final Collection<ISource> sources = new ArrayList<>();
+        Collection<TransferableGraph1> dependencies = new ArrayList<>();
+
+        for (Bundle b : Activator.getContext().getBundles()) {
+            URL tg = b.getEntry("/graph.tg");
+            if (tg == null) continue;
+            File f = url2file(FileLocator.resolve(tg), b.getSymbolicName());
+            try {
+                dependencies.add(GraphCompiler.read(f));
+            } catch (Exception e) {
+                throw new IOException("Failed to read compiled transferable graph as dependency: " + f, e);
+            }
+        }
+
+        final TransferableGraph1 thisOntology = Simantics.sync(new UniqueRead<TransferableGraph1>() {
+
+            @Override
+            public TransferableGraph1 perform(ReadGraph graph) throws DatabaseException {
+                Layer0 L0 = Layer0.getInstance(graph);
+                Resource parent = graph.getSingleObject(r, L0.PartOf);
+
+                CopyHandler ch = new DefaultCopyHandler(r) {
+                    protected TransferableGraphConfiguration2 createConfiguration(ReadGraph graph, boolean cut) throws DatabaseException {
+                        Map<Resource, ExtentStatus> preStatus = new HashMap<>();
+                        preStatus.put(r, ExtentStatus.EXTERNAL);
+                        if (!parent.equals(graph.getRootLibrary()))
+                            preStatus.put(parent, ExtentStatus.EXTERNAL);
+                        return new TransferableGraphConfiguration2(null, Collections.emptyList(), preStatus, true, true);
+                    }
+
+                    protected TransferableGraphSource computeSource(ReadGraph graph, TransferableGraphConfiguration2 conf) throws DatabaseException {
+                        return graph.syncRequest(new ModelTransferableGraphSourceRequest(conf) {
+                            protected ModelTransferableGraphSource getSource(ReadGraph graph, TransferableGraphConfiguration2 configuration, DomainProcessorState state, File otherStatementsFile, File valueFile) throws DatabaseException {
+                                return new ModelTransferableGraphSource(graph, configuration, state, otherStatementsFile, valueFile) {
+                                    @Override
+                                    protected Identity getRootIdentity(DomainProcessorState state, SerialisationSupport support, Resource rootLibrary) throws DatabaseException {
+                                        return new Identity(state.ids.get(support.getTransientId(rootLibrary)), new Root("", ""));
+                                    }
+                                };
+                            }
+                        });
+                    }
+                };
+
+                SimanticsClipboardImpl clipboard = new SimanticsClipboardImpl();
+                ch.copyToClipboard(graph, clipboard);
+                for (Set<Representation> object : clipboard.getContents()) {
+                    TransferableGraph1 tg = ClipboardUtils.accept(graph, object, SimanticsKeys.KEY_TRANSFERABLE_GRAPH);
+                    if (tg != null)
+                        return tg;
+                }
+
+                return null;
+            }
+        });
+
+        if (thisOntology == null)      
+            throw new DatabaseException("Failed to dump the containing ontology of " + r + " into TransferableGraph1");
+
+        dependencies.add(thisOntology);
+
+        Simantics.sync(new ReadRequest() {
+            @Override
+            public void run(ReadGraph graph) throws DatabaseException {
+                Layer0 L0 = Layer0.getInstance(graph);
+                for (Resource file : graph.syncRequest(new ObjectsWithType(r, L0.ConsistsOf, L0.PGraph))) {
+                    String src = graph.getRelatedValue(file, L0.PGraph_definition, Bindings.STRING);
+                    sources.add(new StringSource(src));
+                }
+            }
+        });
+
+        GraphCompilerPreferences prefs = new GraphCompilerPreferences();
+        prefs.validate = true;
+        prefs.validateRelationRestrictions = ValidationMode.ERROR;
+        prefs.validateResourceHasType = ValidationMode.IGNORE;
+        final CompilationResult result = Simantics.sync(new UniqueRead<CompilationResult>() {
+            @Override
+            public CompilationResult perform(ReadGraph graph) throws DatabaseException {
+                final Resource root = graph.syncRequest(new IndexRoot(r));
+                final String baseURI = graph.getURI(root);
+
+                GraphFileResource GF = GraphFileResource.getInstance(graph);
+                ExternalFileLoader fileLoader = fileName -> {
+                    try {
+                        Resource file = graph.getResource(baseURI + "/" + fileName);
+                        return graph.getRelatedValue(file, GF.HasFiledata, Bindings.BYTE_ARRAY);
+                    } catch (DatabaseException e) {
+                        throw new IOException(e);
+                    }
+                };
+
+                return GraphCompiler.compile("1.1", sources, dependencies, fileLoader, prefs);
+            }
+        });
+
+        if (!result.getErrors().isEmpty() || !result.getWarnings().isEmpty()) {
+            if (userAgent != null) {
+                userAgent.reportProblems(result);
+                return;
+            } else {
+                StringBuilder error = new StringBuilder();
+                for (Problem problem : result.getErrors())
+                    error.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
+                for (Problem problem : result.getWarnings())
+                    error.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
+                throw new DatabaseException(error.toString());
+            }
+        }
+
+        final Pair<TransferableGraph1, long[]> existing = Simantics.sync(new UniqueRead<Pair<TransferableGraph1, long[]>>() {
+            @Override
+            public Pair<TransferableGraph1, long[]> perform(ReadGraph graph) throws DatabaseException {
+                Layer0 L0 = Layer0.getInstance(graph);
+                TransferableGraph1 tg = graph.getPossibleRelatedValue(r, L0.SharedOntology_tg, Bindings.getBindingUnchecked( TransferableGraph1.class ));
+                if(tg == null) return null;
+                long[] tgResources = graph.getPossibleRelatedValue(r, L0.SharedOntology_tgResources, Bindings.LONG_ARRAY);
+                if(tgResources == null) return null;
+                return Pair.make(tg,  tgResources);
+            }
+        });
+
+        if (existing != null) {
+            Simantics.sync(new WriteRequest() {
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    TransferableGraphDelta1 delta = new Diff(existing.first, result.getGraph()).diff();
+                    long[] resourceArray = TransferableGraphs.applyDelta(graph, existing.second, delta);
+
+                    Layer0 L0 = Layer0.getInstance(graph);
+                    graph.claimLiteral(r, L0.SharedOntology_tg, result.getGraph(), Bindings.getBindingUnchecked( TransferableGraph1.class ));
+                    graph.claimLiteral(r, L0.SharedOntology_tgResources, L0.ResourceIdArray, resourceArray, Bindings.LONG_ARRAY);
+
+                    Layer0Utils.addCommentMetadata(graph, "Compiled ontology " + graph.getURI(r));
+                }
+            });
+        } else {
+            final DefaultPasteImportAdvisor advisor = new DefaultPasteImportAdvisor(r);
+            try {
+                DefaultPasteHandler.defaultExecute(result.getGraph(), r, advisor);
+            } catch (TransferableGraphException e) {
+                // TODO: defaultExecute never actually throws this exception!
+                throw new DatabaseException(e);
+            }
+
+            Simantics.sync(new WriteRequest() {
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    Layer0 L0 = Layer0.getInstance(graph);
+                    graph.claimLiteral(r, L0.SharedOntology_tg, result.getGraph(), Bindings.getBindingUnchecked( TransferableGraph1.class ));
+                    graph.claimLiteral(r, L0.SharedOntology_tgResources, L0.ResourceIdArray, advisor.getResourceIds(), Bindings.LONG_ARRAY);
+
+                    Layer0Utils.addCommentMetadata(graph, "Compiled ontology " + graph.getURI(r));
+                }
+            });
+        }
+    }
+
+    private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
+        String tmpDirStr = System.getProperty("java.io.tmpdir");
+        if (tmpDirStr == null)
+            throw new NullPointerException("java.io.tmpdir property is null");
+        File tmpDir = new File(tmpDirStr);
+        File libFile = new File(tmpDir, libName);
+        return FileUtils.copyResource(libURL, libFile, false);
+    }
+
+    private static File url2file(URL url, String fileName) {
+        if ("file".equals(url.getProtocol())) {
+            try {
+                File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
+                return path;
+            } catch (UnsupportedEncodingException e) {
+                Logger.defaultLogError(e);
+            }
+        } else if ("jar".equals(url.getProtocol())) {
+            try {
+                File libFile = extractLib(url, fileName);
+                return libFile;
+            } catch (FileNotFoundException e) {
+                Logger.defaultLogError(e);
+            } catch (IOException e) {
+                Logger.defaultLogError(e);
+            }
+        } else {
+            System.err.println("Unsupported URL protocol '" + url + "' for FastLZ native library file '" + fileName);
+        }   
+        return null;
+    }
+
+    private static class StringSource implements ISource {
+        private String src;
+        private ByteArrayInputStream baos;
+
+        public StringSource(String s) {
+            this.src = s;
+            this.baos = new ByteArrayInputStream(src.getBytes());
+        }
+
+        @Override
+        public int startPos() {
+            return 0;
+        }
+
+        @Override
+        public int startLine() {
+            return 0;
+        }
+
+        @Override
+        public InputStream open() throws IOException {
+            return baos;
+        }
+
+        @Override
+        public int length() throws IOException {
+            return src.length();
+        }
+
+        @Override
+        public String getName() {
+            return "Source";
+        }
+    }
+
+}