]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/ModelTransferableGraphSource.java
Allow external customization fo TG Root type URIs.
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / util / ModelTransferableGraphSource.java
index 79f0937480dff59eeea2de8935576aea8e8809cf..f0a0893eb447d40455835d39fdd41cc40d6e3c5f 100644 (file)
-package org.simantics.db.layer0.util;\r
-\r
-import java.io.Closeable;\r
-import java.io.DataInput;\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.nio.file.Files;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.TreeMap;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Databoard;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.container.DataContainer;\r
-import org.simantics.databoard.serialization.Serializer;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.util.binary.ByteBufferReadable;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.RuntimeDatabaseException;\r
-import org.simantics.db.exception.ValidationException;\r
-import org.simantics.db.layer0.adapter.SubgraphExtent.ExtentStatus;\r
-import org.simantics.db.layer0.util.TransferableGraphConfiguration2.RootSpec;\r
-import org.simantics.db.service.SerialisationSupport;\r
-import org.simantics.graph.db.TransferableGraphSource;\r
-import org.simantics.graph.representation.External;\r
-import org.simantics.graph.representation.Identity;\r
-import org.simantics.graph.representation.Root;\r
-import org.simantics.graph.representation.Value;\r
-import org.simantics.layer0.Layer0;\r
-\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.procedure.TIntIntProcedure;\r
-\r
-public class ModelTransferableGraphSource implements TransferableGraphSource {\r
-\r
-       final private TransferableGraphConfiguration2 configuration;\r
-       final private DomainProcessorState state;\r
-       final private int externalBase;\r
-       final private int resourceCount;\r
-       final private File[] files;\r
-       final private TGValueModifier valueModifier;\r
-\r
-       private volatile boolean closed = false;\r
-\r
-       TIntArrayList externalParents = new TIntArrayList();\r
-       ArrayList<String> externalNames = new ArrayList<String>();\r
-\r
-       public ModelTransferableGraphSource(final ReadGraph graph, TransferableGraphConfiguration2 configuration, final DomainProcessorState state, File ... fs) throws DatabaseException {\r
-\r
-               this.configuration = configuration;\r
-               this.state = state;\r
-               this.files = fs;\r
-               this.valueModifier = state.valueModifier;\r
-\r
-               SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-\r
-               // At this point ids contains all internal resources. Now add roots and externals.\r
-               \r
-               // Root Library\r
-               state.ids.put(ss.getTransientId(graph.getRootLibrary()), state.id++);\r
-               \r
-               // External roots - internal roots were already processed as internal resources by domain processor\r
-               for(RootSpec rs : configuration.roots) {\r
-                       if(!rs.internal) {\r
-                               int resourceId = ss.getTransientId(rs.resource);\r
-                               state.ids.put(resourceId, state.id++);\r
-                               // The fixed roots have been seen as externals by domain processor. Now remove them from external set.\r
-                               state.externals.remove(resourceId);\r
-                       }\r
-               }\r
-               \r
-               this.externalBase = state.id;\r
-\r
-               final Collection<String> errors = new HashSet<String>();\r
-\r
-               // All resource considered as not internal by domain processor. Can also contain roots.\r
-               int[] externals = state.externals.toArray();\r
-               \r
-               // Build up the state.externals, externalNames and externalParents\r
-               for(int i=0;i<externals.length;i++) {\r
-                       getId(graph, externals[i], errors);\r
-               }\r
-               \r
-               state.inverses.forEachEntry(new TIntIntProcedure() {\r
-                       \r
-                       @Override\r
-                       public boolean execute(int predicate, int inverse) {\r
-                               try {\r
-                                       getId(graph, predicate, errors);\r
-                                       if(inverse != 0) getId(graph, inverse, errors);\r
-                               } catch (DatabaseException e) {\r
-                                       throw new RuntimeDatabaseException(e);\r
-                               }\r
-                               return true;\r
-                       }\r
-                       \r
-               });\r
-               \r
-               if(!errors.isEmpty()) {\r
-                       ArrayList<String> sorted = new ArrayList<String>(errors);\r
-                       Collections.sort(sorted);\r
-                       StringBuilder message = new StringBuilder();\r
-                       message.append("Errors in exported model:\n");\r
-                       for(String error : sorted) {\r
-                               message.append(error);\r
-                               message.append("\n");\r
-                       }\r
-                       throw new DatabaseException(message.toString());\r
-               }\r
-               \r
-               this.resourceCount = state.id;\r
-               \r
-       }\r
-\r
-       int indent = 0;\r
-       \r
-       public boolean validateExternal(Resource ext) {\r
-               if(configuration.validate) {\r
-                       ExtentStatus status = configuration.preStatus.get(ext);\r
-                       if(status != null) {\r
-                               if(ExtentStatus.INTERNAL.equals(status)) return false;\r
-                               else if(ExtentStatus.EXCLUDED.equals(status)) return false;\r
-                       }\r
-               }\r
-               return true;\r
-       }\r
-       \r
-       private Resource getResource(ReadGraph graph, int r) throws DatabaseException {\r
-               SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-               return ss.getResource(r);\r
-       }\r
-       \r
-       final public int getExistingId(ReadGraph graph, int r) throws DatabaseException {\r
-               \r
-               int ret = state.ids.get(r);\r
-               if(ret != -1) {\r
-                       return ret;\r
-               } else {\r
-                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-                       throw new DatabaseException("Id has not been created for " + NameUtils.getSafeName(graph, ss.getResource(r)));\r
-               }\r
-\r
-       }\r
-       \r
-       /*\r
-        * \r
-        * @return -2 if r is not really external and the statement should be excluded\r
-        * \r
-        */\r
-       public int getId(ReadGraph graph, int r, Collection<String> errors) throws DatabaseException {\r
-\r
-//             // First external is root library\r
-//             if(r == rootId) return internalCount;\r
-               \r
-               SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-               \r
-               if(state.ids.containsKey(r)) {\r
-                   int ret = state.ids.get(r);\r
-                   if(ret == -1) {\r
-                       for(int i=0;i<=indent;++i)\r
-                           System.out.print("  ");\r
-                       System.out.println("Cycle!!!"); // with " + GraphUtils.getReadableName(g, r));\r
-                   }\r
-                       return ret;\r
-               }\r
-               else {\r
-                       Resource res = getResource(graph, r);\r
-            if(!validateExternal(res)) {\r
-               errors.add("Illegal reference to " + graph.getPossibleURI(getResource(graph, r)));\r
-               return -2;\r
-            }\r
-                       Collection<Resource> parents = graph.getObjects(res, L0.PartOf);                        \r
-                       if(parents.size() != 1) {\r
-                               throw new ValidationException("Reference to external resource " \r
-                                               + NameUtils.getSafeName(graph, getResource(graph, r), true) + " without unique uri (" + parents.size() + " parents).");\r
-                       }\r
-                       int pid = 0;\r
-                       for(Resource p : parents) {\r
-                           ++indent;\r
-                           pid = getId(graph, ss.getTransientId(p), errors);\r
-                           if(pid == -2) {\r
-                       errors.add("Illegal reference to " + graph.getPossibleURI(getResource(graph, r)));\r
-                               return -2;\r
-                           }\r
-                       }\r
-                       --indent;\r
-                       String name = graph.getRelatedValue(res, L0.HasName);\r
-                       // Record the external entry\r
-                       externalParents.add(pid);\r
-            externalNames.add(name);\r
-                       state.ids.put(r, state.id);\r
-                       // Ensure that this resource is included into the set of externals to maintain the total number of externals \r
-                       state.externals.add(r);\r
-                       return state.id++;\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public DataContainer getHeader() throws Exception {\r
-           return null;\r
-       }\r
-       \r
-       @Override\r
-       public int getResourceCount() {\r
-               return resourceCount;\r
-       }\r
-       \r
-       @Override\r
-       public int getIdentityCount() {\r
-               return configuration.roots.size() + state.externals.size() + 1;\r
-       }\r
-       \r
-       @Override\r
-       public int getStatementCount() {\r
-               return state.statementCount;\r
-       }\r
-       \r
-       @Override\r
-       public int getValueCount() {\r
-               return state.valueCount;\r
-       }\r
-\r
-       @Override\r
-       public void forStatements(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {\r
-               \r
-               int[] value = new int[4];\r
-               long length = state.otherStatementsInput.length();\r
-               state.otherStatementsInput.position(0);\r
-               \r
-               while(state.otherStatementsInput.position() < length && !state.monitor.isCanceled()) {\r
-                       \r
-                       int s = state.otherStatementsInput.readInt();\r
-                       int subjectId = state.ids.get(s);\r
-                       \r
-                       boolean exclude = subjectId == -1;\r
-                       \r
-                       int size = state.otherStatementsInput.readInt();\r
-                       for(int i=0;i<size;i++) {\r
-                               int p = state.otherStatementsInput.readInt();\r
-                               int o = state.otherStatementsInput.readInt();\r
-                               if(!exclude) {\r
-                                       if(state.pending.contains(o)) {\r
-                                               System.err.println("excluding garbage statement " + s + " " + p + " " + o + ", object resource is garbage");\r
-                                       } else if(state.excludedShared.contains(o)) {\r
-                                               System.err.println("excluding shared " + s + " " + p + " " + o);\r
-                                       } else {\r
-                                               \r
-                                               int objectId = getExistingId(graph, o);\r
-                                               // The statement can be denied still\r
-                                               if(objectId != -2) {\r
-                                                       value[0] = subjectId;\r
-                                                       value[1] = getExistingId(graph, p);\r
-                                                       int inverse = state.inverses.get(p);\r
-                                                       if(inverse != 0) {\r
-                                                               value[2] = getExistingId(graph, inverse);\r
-                                                       } else {\r
-                                                               value[2] = -1;\r
-                                                       }\r
-                                                       value[3] = objectId;\r
-\r
-                                                       procedure.execute(value);\r
-                                                       \r
-                                               } else {\r
-                                                       System.err.println("Denied (" + NameUtils.getSafeName(graph, getResource(graph, s)) + ", " + NameUtils.getSafeName(graph, getResource(graph, p)) + "," + NameUtils.getSafeName(graph, getResource(graph, o)) + ")");\r
-                                               }                                               \r
-                                               \r
-                                       }\r
-                               } else {\r
-                                       System.err.println("excluding shared " + s);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public void forValues(ReadGraph graph, TransferableGraphSourceProcedure<Value> procedure) throws Exception {\r
-\r
-               Serializer variantSerializer = graph.getService(Databoard.class).getSerializerUnchecked(Bindings.VARIANT);\r
-               List<Object> idContext = new ArrayList<>();\r
-               long length = state.valueInput.length();\r
-               state.valueInput.position(0);\r
-\r
-               while(state.valueInput.position() < length && !state.monitor.isCanceled()) {\r
-\r
-                       // Ignore value type tag\r
-                       int s = state.valueInput.readInt();\r
-                       byte valueType = state.valueInput.readByte();\r
-                       switch (valueType) {\r
-                       case TAG_RAW_COPY_VARIANT_VALUE:\r
-                               state.valueInput.readInt();\r
-                               // Intentional fallthrough.\r
-\r
-                       case TAG_POTENTIALLY_MODIFIED_VARIANT_VALUE: {\r
-                               idContext.clear();\r
-                               Variant variant = (Variant) variantSerializer.deserialize((DataInput)state.valueInput, idContext);\r
-                               if (valueModifier.mayNeedModification(variant.type())) {\r
-                                       Object currentObject = variant.getValue();\r
-                                       Object newObject = valueModifier.modify(state, variant.getBinding(), currentObject);\r
-                                       if (newObject != currentObject)\r
-                                               variant = new Variant(variant.getBinding(), newObject);\r
-                               }\r
-                               procedure.execute(new Value(state.ids.get(s), variant));\r
-                               break;\r
-                       }\r
-\r
-                       default:\r
-                               throw new IllegalArgumentException("Unrecognized variant value type encountered: " + valueType);\r
-                       }\r
-               }\r
-       }\r
-\r
-    @Override\r
-    public void forValues2(ReadGraph graph, TransferableGraphSourceValueProcedure procedure) throws Exception {\r
-\r
-        Serializer datatypeSerializer = graph.getService(Databoard.class).getSerializerUnchecked(Bindings.getBindingUnchecked(Datatype.class));\r
-        List<Object> idContext = new ArrayList<>();\r
-        long length = state.valueInput.length();\r
-        state.valueInput.position(0);\r
-\r
-        while(state.valueInput.position() < length && !state.monitor.isCanceled()) {\r
-\r
-            int s = state.valueInput.readInt();\r
-            byte valueType = state.valueInput.readByte();\r
-\r
-            switch (valueType) {\r
-            case TAG_RAW_COPY_VARIANT_VALUE: {\r
-                // Variant data could be copied raw, no need for modifications\r
-                // Note that the variant data is a concatenation of the\r
-                // variant datatype serialization and the value serialization.\r
-                // variantLength contains both datatype and value data.\r
-                int variantLength = state.valueInput.readInt();\r
-                procedure.rawCopy(state.ids.get(s), variantLength, state.valueInput);\r
-                break;\r
-            }\r
-\r
-            case TAG_POTENTIALLY_MODIFIED_VARIANT_VALUE: {\r
-                // Variant data may need to be modified.\r
-                // Cannot optimize this case with raw copying.\r
-                idContext.clear();\r
-                Datatype type = (Datatype)datatypeSerializer.deserialize((DataInput)state.valueInput, idContext);\r
-\r
-                if (valueModifier.mayNeedModification(type)) {\r
-                    Binding binding = Bindings.getBinding(type);\r
-                    Serializer serializer = Bindings.getSerializerUnchecked(binding);\r
-                    Object value = serializer.deserialize((DataInput)state.valueInput);\r
-                    value = valueModifier.modify(state, binding, value);\r
-                    byte[] bytes = serializer.serialize(value);\r
-                    procedure.execute(state.ids.get(s), type, new ByteBufferReadable(bytes));\r
-                } else {\r
-                    procedure.execute(state.ids.get(s), type, state.valueInput);\r
-                }\r
-                break;\r
-            }\r
-\r
-            default:\r
-                throw new IllegalArgumentException("Unrecognized variant value type encountered: " + valueType);\r
-            }\r
-        }\r
-    }\r
-\r
-       protected Identity getRootIdentity(DomainProcessorState state, SerialisationSupport support, Resource rootLibrary) throws DatabaseException {\r
-               return new Identity(state.ids.get(support.getTransientId(rootLibrary)), new External(-1, ""));\r
-       }\r
-\r
-       @Override\r
-       public void forIdentities(ReadGraph graph, TransferableGraphSourceProcedure<Identity> procedure) throws Exception {\r
-               \r
-               SerialisationSupport support = graph.getService(SerialisationSupport.class);\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-\r
-               // TODO: this should be Root with name ""\r
-               procedure.execute(getRootIdentity(state, support, graph.getRootLibrary()));\r
-\r
-               // Declare internal and external roots\r
-               for(RootSpec r : configuration.roots) {\r
-                       Resource type = graph.getPossibleType(r.resource, L0.Entity);\r
-                       if(type == null) type = L0.Entity;\r
-                       procedure.execute(new Identity(\r
-                                       state.ids.get(support.getTransientId(r.resource)),\r
-                                       new Root(r.name, graph.getURI(type))));\r
-               }\r
-\r
-               for(int i = 0; i < state.externals.size() ; i++) {\r
-                       int parent = externalParents.get(i);\r
-                       String name = externalNames.get(i);\r
-                       procedure.execute(new Identity(externalBase + i, new External(parent,name)));\r
-       }\r
-       \r
-       }\r
-\r
-       @Override\r
-       public TreeMap<String, Variant> getExtensions() {\r
-               return state.extensions;\r
-       }\r
-\r
-       public File[] getFiles() {\r
-           return files;\r
-       }\r
-\r
-       private static <T> T tryClose(T c) throws IOException {\r
-               if (c != null && c instanceof Closeable)\r
-                       ((Closeable) c).close();\r
-               return null;\r
-       }\r
-\r
-       public void closeStreams() throws IOException {\r
-               state.valueInput = tryClose(state.valueInput);\r
-               state.otherStatementsInput = tryClose(state.otherStatementsInput);\r
-               state.statementsOutput = tryClose(state.statementsOutput);\r
-               state.valueOutput = tryClose(state.valueOutput);\r
-       }\r
-\r
-       @Override\r
-       public void reset() throws Exception {\r
-           throw new UnsupportedOperationException();\r
-       }\r
-       \r
-       public long[] getResourceArray(ReadGraph graph) throws DatabaseException {\r
-               final SerialisationSupport ss = graph.getService(SerialisationSupport.class);\r
-               final long[] result = new long[state.ids.size()];\r
-               state.ids.forEachEntry(new TIntIntProcedure() {\r
-\r
-                       @Override\r
-                       public boolean execute(int a, int b) {\r
-                               \r
-                               try {\r
-                                       Resource r = ss.getResource(a);\r
-                                       result[b] = r.getResourceId();\r
-                               } catch (DatabaseException e) {\r
-                                       e.printStackTrace();\r
-                               }\r
-                               \r
-                               return true;\r
-                               \r
-                       }\r
-               });\r
-               return result;\r
-       }\r
-       \r
-       public DomainProcessorState getState() {\r
-               return state;\r
-       }\r
-       \r
-       public void forResourceStatements(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {\r
-               \r
-               int[] value = new int[4];\r
-               long length = state.otherStatementsInput.length();\r
-               state.otherStatementsInput.position(0);\r
-               \r
-               while(state.otherStatementsInput.position() < length) {\r
-                       \r
-                       int s = state.otherStatementsInput.readInt();\r
-                       int subjectId = state.ids.get(s);\r
-                       \r
-                       boolean exclude = subjectId == -1;\r
-                       \r
-                       int size = state.otherStatementsInput.readInt();\r
-                       for(int i=0;i<size;i++) {\r
-                               int p = state.otherStatementsInput.readInt();\r
-                               int o = state.otherStatementsInput.readInt();\r
-                               if(!exclude) {\r
-                                       if(state.excludedShared.contains(o)) {\r
-                                               System.err.println("excluding shared " + s + " " + p + " " + o);\r
-                                       } else {\r
-                                               \r
-                                               int objectId = getExistingId(graph, o);\r
-                                               // The statement can be denied still\r
-                                               if(objectId != -2) {\r
-                                                       value[0] = s;\r
-                                                       value[1] = p;\r
-                                                       int inverse = state.inverses.get(p);\r
-                                                       if(inverse != 0) {\r
-                                                               value[2] = inverse;\r
-                                                       } else {\r
-                                                               value[2] = -1;\r
-                                                       }\r
-                                                       value[3] = o;\r
-\r
-                                                       procedure.execute(value);\r
-                                                       \r
-                                               } else {\r
-                                                       System.err.println("Denied (" + NameUtils.getSafeName(graph, getResource(graph, s)) + ", " + NameUtils.getSafeName(graph, getResource(graph, p)) + "," + NameUtils.getSafeName(graph, getResource(graph, o)) + ")");\r
-                                               }\r
-                                               \r
-                                       }\r
-                               } else {\r
-                                       System.err.println("excluding shared " + s);\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       public void forValueResources(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {\r
-               int[] value = { 0 };\r
-               long length = state.valueInput.length();\r
-               while (state.valueInput.position() < length) {\r
-                       value[0] = state.valueInput.readInt();\r
-                       procedure.execute(value);\r
-               }\r
-       }\r
-\r
-       public TransferableGraphConfiguration2 getConfiguration() {\r
-               return configuration;\r
-       }\r
-\r
-       @Override\r
-       public void close() throws IOException {\r
-               synchronized (this) {\r
-                       if (closed)\r
-                               return;\r
-                       closed = true;\r
-               }\r
-               closeStreams();\r
-               if (files != null) {\r
-                       for (File f : files) {\r
-                               Files.deleteIfExists(f.toPath());\r
-                       }\r
-               }\r
-       }\r
-\r
+package org.simantics.db.layer0.util;
+
+import java.io.Closeable;
+import java.io.DataInput;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.TreeMap;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Databoard;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.container.DataContainer;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.binary.ByteBufferReadable;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.RuntimeDatabaseException;
+import org.simantics.db.exception.ValidationException;
+import org.simantics.db.layer0.adapter.SubgraphExtent.ExtentStatus;
+import org.simantics.db.layer0.util.ConsistsOfProcess.InternalEntry;
+import org.simantics.db.layer0.util.TransferableGraphConfiguration2.RootSpec;
+import org.simantics.db.service.SerialisationSupport;
+import org.simantics.graph.db.TransferableGraphSource;
+import org.simantics.graph.representation.External;
+import org.simantics.graph.representation.Identity;
+import org.simantics.graph.representation.Internal;
+import org.simantics.graph.representation.Root;
+import org.simantics.graph.representation.Value;
+import org.simantics.layer0.Layer0;
+
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.map.TIntObjectMap;
+import gnu.trove.map.hash.TIntObjectHashMap;
+import gnu.trove.procedure.TIntIntProcedure;
+
+public class ModelTransferableGraphSource implements TransferableGraphSource {
+
+       final private TransferableGraphConfiguration2 configuration;
+       final private DomainProcessorState state;
+       final private int externalBase;
+       final private int resourceCount;
+       final private File[] files;
+       final private TGValueModifier valueModifier;
+
+       private volatile boolean closed = false;
+
+       TIntArrayList externalParents = new TIntArrayList();
+       ArrayList<String> externalNames = new ArrayList<>();
+       TreeMap<String,String> downloads = new TreeMap<String,String>();
+
+       public ModelTransferableGraphSource(final ReadGraph graph, TransferableGraphConfiguration2 configuration, final DomainProcessorState state, File ... fs) throws DatabaseException {
+
+               this.configuration = configuration;
+               this.state = state;
+               this.files = fs;
+               this.valueModifier = state.valueModifier;
+
+               SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+
+               // At this point ids contains all internal resources. Now add roots and externals.
+               
+               // Root Library
+               state.ids.put(ss.getTransientId(graph.getRootLibrary()), state.id++);
+               
+               // External roots - internal roots were already processed as internal resources by domain processor
+               for(RootSpec rs : configuration.roots) {
+                       if(!rs.internal) {
+                               int resourceId = ss.getTransientId(rs.resource);
+                               state.ids.put(resourceId, state.id++);
+                               // The fixed roots have been seen as externals by domain processor. Now remove them from external set.
+                               state.externals.remove(resourceId);
+                       }
+               }
+               
+               this.externalBase = state.id;
+
+               final Collection<String> errors = new HashSet<>();
+
+               // All resource considered as not internal by domain processor. Can also contain roots.
+               int[] externals = state.externals.toArray();
+               
+               // Build up the state.externals, externalNames and externalParents
+               for(int i=0;i<externals.length;i++) {
+                       getId(graph, externals[i], errors);
+               }
+               
+               state.inverses.forEachEntry(new TIntIntProcedure() {
+                       
+                       @Override
+                       public boolean execute(int predicate, int inverse) {
+                               try {
+                                       getId(graph, predicate, errors);
+                                       if(inverse != 0) getId(graph, inverse, errors);
+                               } catch (DatabaseException e) {
+                                       throw new RuntimeDatabaseException(e);
+                               }
+                               return true;
+                       }
+                       
+               });
+               
+               if(!errors.isEmpty()) {
+                       ArrayList<String> sorted = new ArrayList<>(errors);
+                       Collections.sort(sorted);
+                       StringBuilder message = new StringBuilder();
+                       message.append("Errors in exported model:\n");
+                       for(String error : sorted) {
+                               message.append(error);
+                               message.append("\n");
+                       }
+                       throw new DatabaseException(message.toString());
+               }
+               
+               this.resourceCount = state.id;
+               
+               state.extensions.put(ExternalDownloadBean.EXTENSION_KEY, new Variant(ExternalDownloadBean.BINDING, new ExternalDownloadBean(downloads)));
+               
+       }
+
+       int indent = 0;
+       
+       public boolean validateExternal(Resource ext) {
+               if(configuration.validate) {
+                       ExtentStatus status = configuration.preStatus.get(ext);
+                       if(status != null) {
+                               if(ExtentStatus.INTERNAL.equals(status)) return false;
+                               else if(ExtentStatus.EXCLUDED.equals(status)) return false;
+                       }
+               }
+               return true;
+       }
+       
+       private Resource getResource(ReadGraph graph, int r) throws DatabaseException {
+               SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+               return ss.getResource(r);
+       }
+       
+       final public int getExistingId(ReadGraph graph, int r) throws DatabaseException {
+               
+               int ret = state.ids.get(r);
+               if(ret != -1) {
+                       return ret;
+               } else {
+                       SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+                       throw new DatabaseException("Id has not been created for " + NameUtils.getSafeName(graph, ss.getResource(r)));
+               }
+
+       }
+       
+       /*
+        * 
+        * @return -2 if r is not really external and the statement should be excluded
+        * 
+        */
+       public int getId(ReadGraph graph, int r, Collection<String> errors) throws DatabaseException {
+
+//             // First external is root library
+//             if(r == rootId) return internalCount;
+               
+               SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+               Layer0 L0 = Layer0.getInstance(graph);
+               
+               if(state.ids.containsKey(r)) {
+                   int ret = state.ids.get(r);
+                   if(ret == -1) {
+                       for(int i=0;i<=indent;++i)
+                           System.out.print("  ");
+                       System.out.println("Cycle!!!"); // with " + GraphUtils.getReadableName(g, r));
+                   }
+                       return ret;
+               }
+               else {
+                       Resource res = getResource(graph, r);
+            if(!validateExternal(res)) {
+               errors.add("Illegal reference to " + graph.getPossibleURI(getResource(graph, r)));
+               return -2;
+            }
+                       Collection<Resource> parents = graph.getObjects(res, L0.PartOf);                        
+                       if(parents.size() != 1) {
+                               throw new ValidationException("Reference to external resource " 
+                                               + NameUtils.getSafeName(graph, getResource(graph, r), true) + " without unique uri (" + parents.size() + " parents).");
+                       }
+                       int pid = 0;
+                       for(Resource p : parents) {
+                           ++indent;
+                           pid = getId(graph, ss.getTransientId(p), errors);
+                           if(pid == -2) {
+                       errors.add("Illegal reference to " + graph.getPossibleURI(getResource(graph, r)));
+                               return -2;
+                           }
+                       }
+                       --indent;
+                       String name = graph.getRelatedValue(res, L0.HasName);
+                       // Record the external entry
+                       externalParents.add(pid);
+            externalNames.add(name);
+                       state.ids.put(r, state.id);
+                       // Ensure that this resource is included into the set of externals to maintain the total number of externals 
+                       state.externals.add(r);
+                       String download = graph.getPossibleRelatedValue(res, L0.Ontology_download, Bindings.STRING);
+                       if(download != null) {
+                               String uri = graph.getURI(res);
+                               downloads.put(uri, download);
+                       }
+                       return state.id++;
+               }
+       }
+       
+       @Override
+       public DataContainer getHeader() throws Exception {
+           return null;
+       }
+       
+       @Override
+       public int getResourceCount() {
+               return resourceCount;
+       }
+       
+       @Override
+       public int getIdentityCount() {
+               return configuration.roots.size() + state.externals.size() + state.internalEntries.size() + 1;
+       }
+       
+       @Override
+       public int getStatementCount() {
+               return state.statementCount;
+       }
+       
+       @Override
+       public int getValueCount() {
+               return state.valueCount;
+       }
+
+       @Override
+       public void forStatements(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {
+               
+               int[] value = new int[4];
+               long length = state.otherStatementsInput.length();
+               state.otherStatementsInput.position(0);
+               
+               while(state.otherStatementsInput.position() < length && !state.monitor.isCanceled()) {
+                       
+                       int s = state.otherStatementsInput.readInt();
+                       int subjectId = state.ids.get(s);
+                       
+                       boolean exclude = subjectId == -1;
+                       
+                       int size = state.otherStatementsInput.readInt();
+                       for(int i=0;i<size;i++) {
+                               int p = state.otherStatementsInput.readInt();
+                               int o = state.otherStatementsInput.readInt();
+                               if(!exclude) {
+                                       if(state.pending.contains(o)) {
+                                               System.err.println("excluding garbage statement " + s + " " + p + " " + o + ", object resource is garbage");
+                                       } else if(state.excludedShared.contains(o)) {
+                                               System.err.println("excluding shared " + s + " " + p + " " + o);
+                                       } else {
+                                               
+                                               int objectId = getExistingId(graph, o);
+                                               // The statement can be denied still
+                                               if(objectId != -2) {
+                                                       value[0] = subjectId;
+                                                       value[1] = getExistingId(graph, p);
+                                                       int inverse = state.inverses.get(p);
+                                                       if(inverse != 0) {
+                                                               value[2] = getExistingId(graph, inverse);
+                                                       } else {
+                                                               value[2] = -1;
+                                                       }
+                                                       value[3] = objectId;
+
+                                                       procedure.execute(value);
+                                                       
+                                               } else {
+                                                       System.err.println("Denied (" + NameUtils.getSafeName(graph, getResource(graph, s)) + ", " + NameUtils.getSafeName(graph, getResource(graph, p)) + "," + NameUtils.getSafeName(graph, getResource(graph, o)) + ")");
+                                               }                                               
+                                               
+                                       }
+                               } else {
+                                       System.err.println("excluding shared " + s);
+                               }
+                       }
+               }
+       }
+
+       @Override
+       public void forValues(ReadGraph graph, TransferableGraphSourceProcedure<Value> procedure) throws Exception {
+
+               Serializer variantSerializer = graph.getService(Databoard.class).getSerializerUnchecked(Bindings.VARIANT);
+               List<Object> idContext = new ArrayList<>();
+               long length = state.valueInput.length();
+               state.valueInput.position(0);
+
+               while(state.valueInput.position() < length && !state.monitor.isCanceled()) {
+
+                       // Ignore value type tag
+                       int s = state.valueInput.readInt();
+                       byte valueType = state.valueInput.readByte();
+                       switch (valueType) {
+                       case TAG_RAW_COPY_VARIANT_VALUE:
+                               state.valueInput.readInt();
+                               // Intentional fallthrough.
+
+                       case TAG_POTENTIALLY_MODIFIED_VARIANT_VALUE: {
+                               idContext.clear();
+                               Variant variant = (Variant) variantSerializer.deserialize((DataInput)state.valueInput, idContext);
+                               if (valueModifier.mayNeedModification(variant.type())) {
+                                       Object currentObject = variant.getValue();
+                                       Object newObject = valueModifier.modify(state, variant.getBinding(), currentObject);
+                                       if (newObject != currentObject)
+                                               variant = new Variant(variant.getBinding(), newObject);
+                               }
+                               procedure.execute(new Value(state.ids.get(s), variant));
+                               break;
+                       }
+
+                       default:
+                               throw new IllegalArgumentException("Unrecognized variant value type encountered: " + valueType);
+                       }
+               }
+       }
+
+    @Override
+    public void forValues2(ReadGraph graph, TransferableGraphSourceValueProcedure procedure) throws Exception {
+
+        Serializer datatypeSerializer = graph.getService(Databoard.class).getSerializerUnchecked(Bindings.getBindingUnchecked(Datatype.class));
+        List<Object> idContext = new ArrayList<>();
+        long length = state.valueInput.length();
+        state.valueInput.position(0);
+
+        while(state.valueInput.position() < length && !state.monitor.isCanceled()) {
+
+            int s = state.valueInput.readInt();
+            byte valueType = state.valueInput.readByte();
+
+            switch (valueType) {
+            case TAG_RAW_COPY_VARIANT_VALUE: {
+                // Variant data could be copied raw, no need for modifications
+                // Note that the variant data is a concatenation of the
+                // variant datatype serialization and the value serialization.
+                // variantLength contains both datatype and value data.
+                int variantLength = state.valueInput.readInt();
+                procedure.rawCopy(state.ids.get(s), variantLength, state.valueInput);
+                break;
+            }
+
+            case TAG_POTENTIALLY_MODIFIED_VARIANT_VALUE: {
+                // Variant data may need to be modified.
+                // Cannot optimize this case with raw copying.
+                idContext.clear();
+                Datatype type = (Datatype)datatypeSerializer.deserialize((DataInput)state.valueInput, idContext);
+
+                if (valueModifier.mayNeedModification(type)) {
+                    Binding binding = Bindings.getBinding(type);
+                    Serializer serializer = Bindings.getSerializerUnchecked(binding);
+                    Object value = serializer.deserialize((DataInput)state.valueInput);
+                    value = valueModifier.modify(state, binding, value);
+                    byte[] bytes = serializer.serialize(value);
+                    procedure.execute(state.ids.get(s), type, new ByteBufferReadable(bytes));
+                } else {
+                    procedure.execute(state.ids.get(s), type, state.valueInput);
+                }
+                break;
+            }
+
+            default:
+                throw new IllegalArgumentException("Unrecognized variant value type encountered: " + valueType);
+            }
+        }
+    }
+
+       protected Identity getRootIdentity(DomainProcessorState state, SerialisationSupport support, Resource rootLibrary) throws DatabaseException {
+               return new Identity(state.ids.get(support.getTransientId(rootLibrary)), new External(-1, ""));
+       }
+
+       @Override
+       public void forIdentities(ReadGraph graph, TransferableGraphSourceProcedure<Identity> procedure) throws Exception {
+               
+               SerialisationSupport support = graph.getService(SerialisationSupport.class);
+               Layer0 L0 = Layer0.getInstance(graph);
+
+               // TODO: this should be Root with name ""
+               procedure.execute(getRootIdentity(state, support, graph.getRootLibrary()));
+
+               TIntObjectMap<Identity> internalMap = new TIntObjectHashMap<>(100, 0.5f, Integer.MIN_VALUE);
+
+               // Declare internal and external roots
+               for(RootSpec r : configuration.roots) {
+                       String typeId = r.type;
+                       if (typeId == null) {
+                               Resource type = graph.getPossibleType(r.resource, L0.Entity);
+                               typeId = type != null ? graph.getURI(type) : Layer0.URIs.Entity;
+                       }
+                       int id = state.ids.get(support.getTransientId(r.resource));
+                       Root root = new Root(r.name, typeId);
+                       Identity rootId = new Identity(id,root);
+                       internalMap.put(id, rootId);
+                       procedure.execute(rootId);
+               }
+
+               for(int i = 0; i < state.externals.size() ; i++) {
+                       int parent = externalParents.get(i);
+                       String name = externalNames.get(i);
+                       procedure.execute(new Identity(externalBase + i, new External(parent,name)));
+       }
+               
+               if(state.internalEntries != null) {
+                       for(InternalEntry ie : state.internalEntries) {
+                               if(ie.parent != null && ie.name != null) {
+                                       procedure.execute(resolveInternal(graph, support, ie, internalMap));
+                               } else {
+                                       throw new DatabaseException("Invalid internal entry " + ie);
+                               }
+                       }
+               }
+       
+       }
+       
+       private Identity resolveInternal(ReadGraph graph, SerialisationSupport ss, InternalEntry entry, TIntObjectMap<Identity> internalMap) throws DatabaseException {
+               int id = state.ids.get(ss.getTransientId(entry.resource));
+               Identity existing = internalMap.get(id);
+               if(existing != null) return existing;
+               Identity parent = resolveInternal(graph, ss, entry.parent, internalMap);
+               Identity result = new Identity(id,
+                               new Internal(parent.resource, entry.name));
+               internalMap.put(id, result);
+               return result;
+       }
+
+       @Override
+       public TreeMap<String, Variant> getExtensions() {
+               return state.extensions;
+       }
+
+       public File[] getFiles() {
+           return files;
+       }
+
+       private static <T> T tryClose(T c) throws IOException {
+               if (c != null && c instanceof Closeable)
+                       ((Closeable) c).close();
+               return null;
+       }
+
+       public void closeStreams() throws IOException {
+               state.valueInput = tryClose(state.valueInput);
+               state.otherStatementsInput = tryClose(state.otherStatementsInput);
+               state.statementsOutput = tryClose(state.statementsOutput);
+               state.valueOutput = tryClose(state.valueOutput);
+       }
+
+       @Override
+       public void reset() throws Exception {
+           throw new UnsupportedOperationException();
+       }
+       
+       public long[] getResourceArray(ReadGraph graph) throws DatabaseException {
+               final SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+               final long[] result = new long[state.ids.size()];
+               state.ids.forEachEntry(new TIntIntProcedure() {
+
+                       @Override
+                       public boolean execute(int a, int b) {
+                               
+                               try {
+                                       Resource r = ss.getResource(a);
+                                       result[b] = r.getResourceId();
+                               } catch (DatabaseException e) {
+                                       e.printStackTrace();
+                               }
+                               
+                               return true;
+                               
+                       }
+               });
+               return result;
+       }
+       
+       public DomainProcessorState getState() {
+               return state;
+       }
+       
+       public void forResourceStatements(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {
+               
+               int[] value = new int[4];
+               long length = state.otherStatementsInput.length();
+               state.otherStatementsInput.position(0);
+               
+               while(state.otherStatementsInput.position() < length) {
+                       
+                       int s = state.otherStatementsInput.readInt();
+                       int subjectId = state.ids.get(s);
+                       
+                       boolean exclude = subjectId == -1;
+                       
+                       int size = state.otherStatementsInput.readInt();
+                       for(int i=0;i<size;i++) {
+                               int p = state.otherStatementsInput.readInt();
+                               int o = state.otherStatementsInput.readInt();
+                               if(!exclude) {
+                                       if(state.excludedShared.contains(o)) {
+                                               System.err.println("excluding shared " + s + " " + p + " " + o);
+                                       } else {
+                                               
+                                               int objectId = getExistingId(graph, o);
+                                               // The statement can be denied still
+                                               if(objectId != -2) {
+                                                       value[0] = s;
+                                                       value[1] = p;
+                                                       int inverse = state.inverses.get(p);
+                                                       if(inverse != 0) {
+                                                               value[2] = inverse;
+                                                       } else {
+                                                               value[2] = -1;
+                                                       }
+                                                       value[3] = o;
+
+                                                       procedure.execute(value);
+                                                       
+                                               } else {
+                                                       System.err.println("Denied (" + NameUtils.getSafeName(graph, getResource(graph, s)) + ", " + NameUtils.getSafeName(graph, getResource(graph, p)) + "," + NameUtils.getSafeName(graph, getResource(graph, o)) + ")");
+                                               }
+                                               
+                                       }
+                               } else {
+                                       System.err.println("excluding shared " + s);
+                               }
+                       }
+               }
+       }
+
+       public void forValueResources(ReadGraph graph, TransferableGraphSourceProcedure<int[]> procedure) throws Exception {
+               int[] value = { 0 };
+               long length = state.valueInput.length();
+               while (state.valueInput.position() < length) {
+                       value[0] = state.valueInput.readInt();
+                       procedure.execute(value);
+               }
+       }
+
+       public TransferableGraphConfiguration2 getConfiguration() {
+               return configuration;
+       }
+
+       @Override
+       public void close() throws IOException {
+               synchronized (this) {
+                       if (closed)
+                               return;
+                       closed = true;
+               }
+               closeStreams();
+               if (files != null) {
+                       for (File f : files) {
+                               Files.deleteIfExists(f.toPath());
+                       }
+               }
+       }
+
 }
\ No newline at end of file