--- /dev/null
+package org.simantics.datatypes.utils;\r
+\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.DataInput;\r
+import java.io.DataOutput;\r
+import java.io.IOException;\r
+import java.util.Collection;\r
+import java.util.HashMap;\r
+import java.util.IdentityHashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingConstructionException;\r
+import org.simantics.databoard.binding.impl.BindingPrintContext;\r
+import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.util.IdentityPair;\r
+import org.simantics.datatypes.DatatypeResource;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.primitiverequest.RelatedValue;\r
+import org.simantics.db.common.procedure.adapter.TransientCacheListener;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.service.Bytes;\r
+import org.simantics.db.service.SerialisationSupport;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.utils.datastructures.Pair;\r
+\r
+interface LogContentManager {\r
+ \r
+ LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException;\r
+ void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException;\r
+ \r
+}\r
+\r
+class LogContentBinding extends Binding {\r
+\r
+ private final SerialisationSupport ss;\r
+ \r
+ public LogContentBinding(SerialisationSupport ss) {\r
+ this.ss = ss;\r
+ }\r
+ \r
+ private final Serializer serializer = new Serializer() {\r
+\r
+ @Override\r
+ public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void serialize(DataOutput out, Object obj) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public Object deserialize(DataInput in, List<Object> identities)\r
+ throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public Object deserialize(DataInput in) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void deserializeTo(DataInput in, List<Object> identities,\r
+ Object dst) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void deserializeTo(DataInput in, Object dst) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void skip(DataInput in, List<Object> identities)\r
+ throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void skip(DataInput in) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public Integer getConstantSize() {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int getSize(Object obj, TObjectIntHashMap<Object> identities)\r
+ throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int getSize(Object obj) throws IOException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int getMinSize() {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ \r
+ public byte[] serialize(Object obj) throws IOException {\r
+ LogContentBean bean = (LogContentBean)obj;\r
+ int bytes = 1 + 4 + 4 + 8 * bean.stamps.length + 8 * bean.resources.length;\r
+ byte[] result = new byte[bytes];\r
+ Bytes.write(result, 0, bean.leaf ? (byte)1 : (byte)0);\r
+ int byteIndex = 1;\r
+ Bytes.writeLE(result, byteIndex, bean.n);\r
+ byteIndex += 4;\r
+ Bytes.writeLE(result, byteIndex, bean.stamps.length);\r
+ byteIndex += 4;\r
+ for(long l : bean.stamps) {\r
+ Bytes.writeLE(result, byteIndex, l);\r
+ byteIndex += 8;\r
+ }\r
+ for(PossibleResource pr : bean.resources) {\r
+ Bytes.writeLE(result, byteIndex, pr.longValue());\r
+ byteIndex += 8;\r
+ }\r
+ return result;\r
+ }\r
+ \r
+ public Object deserialize(byte[] data) throws IOException {\r
+ \r
+ LogContentBean result = new LogContentBean();\r
+\r
+ try {\r
+ \r
+ result.leaf = Bytes.read(data, 0) == 1 ? true : false;\r
+ int byteIndex = 1;\r
+ result.n = Bytes.readLE4(data, byteIndex);\r
+ byteIndex += 4;\r
+ int t = Bytes.readLE4(data, byteIndex);\r
+ byteIndex += 4;\r
+ \r
+ result.stamps = new long[t];\r
+ result.resources = new PossibleResource[t];\r
+ \r
+ for(int i=0;i<t;i++) {\r
+ result.stamps[i] = Bytes.readLE8(data, byteIndex);\r
+ byteIndex += 8;\r
+ }\r
+ \r
+ for(int i=0;i<t;i++) {\r
+ result.resources[i] = PossibleResource.read(ss, Bytes.readLE8(data, byteIndex));\r
+ byteIndex += 8;\r
+ }\r
+ \r
+ } catch (DatabaseException e) {\r
+ \r
+ e.printStackTrace();\r
+ \r
+ }\r
+ \r
+ return result;\r
+ \r
+ }\r
+ \r
+ };\r
+ \r
+ @Override\r
+ public void accept(Visitor1 v, Object obj) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public <T> T accept(Visitor<T> v) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public boolean isInstance(Object obj) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void readFrom(Binding srcBinding, Object src, Object dst)\r
+ throws BindingException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public void assertInstaceIsValid(Object obj, Set<Object> validInstances)\r
+ throws BindingException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int deepHashValue(Object value,\r
+ IdentityHashMap<Object, Object> hashedObjects)\r
+ throws BindingException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int deepCompare(Object o1, Object o2,\r
+ Set<IdentityPair<Object, Object>> compareHistory)\r
+ throws BindingException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ protected void toString(Object value, BindingPrintContext ctx)\r
+ throws BindingException {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public int getComponentCount() {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public Binding getComponentBinding(int index) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ @Override\r
+ public Binding getComponentBinding(ChildReference path) {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ \r
+ @Override\r
+ public Serializer serializer() throws RuntimeSerializerConstructionException {\r
+ return serializer;\r
+ }\r
+ \r
+}\r
+\r
+final public class LogUtils implements LogContentManager {\r
+\r
+ final public static boolean DEBUG = false;\r
+ \r
+ final public Binding CONTENT_BEAN_BINDING;\r
+ final public DatatypeResource DATA;\r
+ \r
+ public LogUtils(ReadGraph graph) throws DatabaseException {\r
+ try {\r
+ CONTENT_BEAN_BINDING = new LogContentBinding(graph.getService(SerialisationSupport.class));\r
+ DATA = DatatypeResource.getInstance(graph);\r
+ } catch (RuntimeBindingConstructionException e) {\r
+ Logger.defaultLogError(e);\r
+ throw new DatabaseException(e);\r
+ }\r
+ }\r
+ \r
+ public Resource create(WriteGraph graph, int t, int stamp) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Resource tree = graph.newResource();\r
+ graph.claim(tree, L0.InstanceOf, null, DATA.Log);\r
+ Resource index = createIndexNode(graph, this, stamp, t); \r
+ graph.claim(tree, DATA.Log_root, DATA.Log_root_Inverse, index);\r
+ graph.claimLiteral(tree, DATA.Log_t, t, Bindings.INTEGER);\r
+ Resource leaf = createLeafNode(graph, this, stamp, t);\r
+ graph.claim(index, DATA.BTreeNode_Content, null, leaf);\r
+ LogContentBean rContent = getContentBean(graph, index);\r
+ rContent.n = 1;\r
+ rContent.stamps[0] = stamp;\r
+ rContent.resources[0].r = leaf;\r
+ setContentBean(graph, index, rContent);\r
+ return tree;\r
+ }\r
+\r
+ public void insert(WriteGraph graph, Resource T, int stamp, Resource v) throws DatabaseException {\r
+\r
+ Resource r = getRoot(graph, T);\r
+ int t = getDegree(graph, T);\r
+ insertImpl(graph, this, T, r, t, stamp, v);\r
+ \r
+ }\r
+\r
+ static class BatchContentManager implements LogContentManager {\r
+\r
+ final private LogUtils bu;\r
+ \r
+ final Map<Resource, LogContentBean> beans = new HashMap<Resource, LogContentBean>();\r
+ \r
+ public BatchContentManager(LogUtils bu) {\r
+ this.bu = bu;\r
+ }\r
+ \r
+ @Override\r
+ public LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException {\r
+ LogContentBean bean = beans.get(node);\r
+ if(bean != null) return bean;\r
+ return bu.getContentBean(graph, node);\r
+ }\r
+\r
+ @Override\r
+ public void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException {\r
+ beans.put(node, bean);\r
+ }\r
+ \r
+ public void apply(WriteGraph graph) throws DatabaseException {\r
+ for(Map.Entry<Resource, LogContentBean> entry : beans.entrySet()) {\r
+ bu.setContentBean(graph, entry.getKey(), entry.getValue());\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+ public void insertAll(WriteGraph graph, Resource T, Collection<Pair<Integer, Resource>> values) throws DatabaseException {\r
+\r
+ Resource r = getRoot(graph, T);\r
+ int t = getDegree(graph, T);\r
+ BatchContentManager cm = new BatchContentManager(this);\r
+ for(Pair<Integer, Resource> entry : values) {\r
+ insertImpl(graph, cm, T, r, t, entry.first, entry.second);\r
+ }\r
+ cm.apply(graph);\r
+ \r
+ }\r
+ \r
+ // Implementation\r
+ private void insertImpl(WriteGraph graph, LogContentManager manager, Resource T, Resource r, int t, int k, Resource v) throws DatabaseException {\r
+ \r
+ int code = insertImpl2(graph, manager, 0, T, r, t, k, v); \r
+ \r
+ if(code > 0) {\r
+ \r
+ LogContentBean rContent = manager.getContentBean(graph, r);\r
+ \r
+ if(DEBUG) System.err.println("[insert index code=" + code + "]");\r
+\r
+ Resource newRoot = createIndexNode(graph, manager, k, t);\r
+ graph.claim(newRoot, DATA.Log_Node_Contains, null, r);\r
+ setRoot(graph, T, newRoot);\r
+ \r
+ LogContentBean nContent = manager.getContentBean(graph, newRoot);\r
+ nContent.stamps[0] = rContent.stamps[0];\r
+ nContent.resources[0].r = r;\r
+ nContent.n = 1;\r
+ manager.setContentBean(graph, newRoot, nContent);\r
+\r
+ Resource leaf = createLeaf(graph, manager, newRoot, code, k, t);\r
+ \r
+ LogContentBean lContent = manager.getContentBean(graph, leaf);\r
+ lContent.n = 1;\r
+ lContent.stamps[0] = k;\r
+ lContent.resources[0].r = v;\r
+\r
+ if(DEBUG) System.err.println("[insert " + k + "]: started a new branch");\r
+\r
+ manager.setContentBean(graph, leaf, lContent);\r
+\r
+ }\r
+ \r
+ }\r
+\r
+ private int insertImpl2(WriteGraph graph, LogContentManager manager, int level, Resource T, Resource r, int t, int k, Resource v) throws DatabaseException {\r
+\r
+ LogContentBean rContent = manager.getContentBean(graph, r);\r
+\r
+ // Index\r
+ if(!rContent.leaf) {\r
+ \r
+ Resource child = rContent.resources[rContent.n-1].r;\r
+ int code = insertImpl2(graph, manager, level+1, T, child, t, k, v); \r
+ \r
+ if(code == 0) {\r
+ \r
+ // Value was inserted successfully\r
+ return 0;\r
+ \r
+ } else {\r
+\r
+ // The child was full\r
+ if(rContent.n < t) {\r
+ \r
+ // We can create a new child\r
+ Resource leaf = createLeaf(graph, manager, r, code-level-1, k, t);\r
+ LogContentBean lContent = manager.getContentBean(graph, leaf);\r
+ lContent.stamps[0] = k;\r
+ lContent.resources[0].r = v;\r
+ lContent.n = 1;\r
+ manager.setContentBean(graph, leaf, lContent);\r
+ \r
+ if(DEBUG) System.err.println("[insert " + k + "]: created a fresh leaf");\r
+ \r
+ return 0;\r
+ \r
+ } else {\r
+\r
+ // We are full, let the parent handle this\r
+ return code;\r
+ \r
+ }\r
+ \r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+ // Leaf\r
+ else {\r
+\r
+ if(rContent.n < t) {\r
+ \r
+ if(DEBUG) System.err.println("[insert " + k + "]: fit into leaf at level " + level);\r
+ \r
+ // Append\r
+ rContent.stamps[rContent.n] = k;\r
+ rContent.resources[rContent.n].r = v;\r
+ rContent.n++;\r
+ manager.setContentBean(graph, r, rContent);\r
+ return 0;\r
+ \r
+ } else {\r
+\r
+ // This leaf is full\r
+ return level;\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+ }\r
+ \r
+ private Resource createLeaf(WriteGraph graph, LogContentManager manager, Resource r, int code, int stamp, int t) throws DatabaseException {\r
+ LogContentBean rContent = manager.getContentBean(graph, r);\r
+ if(code == 0) {\r
+ if(DEBUG) System.err.println("[insert leaf code=" + code + "]");\r
+ Resource result = createLeafNode(graph, manager, stamp, t);\r
+ graph.claim(r, DATA.Log_Node_Contains, null, result);\r
+ rContent.stamps[rContent.n] = stamp;\r
+ rContent.resources[rContent.n].r = result;\r
+ rContent.n++;\r
+ manager.setContentBean(graph, r, rContent);\r
+ return result;\r
+ } else {\r
+ if(DEBUG) System.err.println("[insert index code=" + code + "]");\r
+ Resource index = createIndexNode(graph, manager, stamp, t);\r
+ graph.claim(r, DATA.Log_Node_Contains, null, index);\r
+ rContent.stamps[rContent.n] = stamp;\r
+ rContent.resources[rContent.n].r = index;\r
+ rContent.n++;\r
+ manager.setContentBean(graph, r, rContent);\r
+ return createLeaf(graph, manager, index, code-1, stamp, t);\r
+ }\r
+ }\r
+ \r
+ private Resource createIndexNode(WriteGraph graph, LogContentManager manager, int stamp, int t) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Resource result = graph.newResource();\r
+ graph.claim(result, L0.InstanceOf, null, DATA.Log_IndexNode);\r
+ manager.setContentBean(graph, result, LogContentBean.create(t, stamp, false));\r
+ return result;\r
+ }\r
+\r
+ private Resource createLeafNode(WriteGraph graph, LogContentManager manager, int stamp, int t) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Resource result = graph.newResource();\r
+ graph.claim(result, L0.InstanceOf, null, DATA.Log_LeafNode);\r
+ manager.setContentBean(graph, result, LogContentBean.create(t, stamp, true));\r
+ return result;\r
+ }\r
+ \r
+ private Resource getRoot(ReadGraph graph, Resource T) throws DatabaseException {\r
+ return graph.getPossibleObject(T, DATA.Log_root);\r
+ }\r
+ \r
+ private void setRoot(WriteGraph graph, Resource T, Resource r) throws DatabaseException {\r
+ graph.deny(T, DATA.Log_root);\r
+ graph.claim(T, DATA.Log_root, r);\r
+ }\r
+ \r
+ public void setContentBean(WriteGraph graph, Resource node, LogContentBean bean) throws DatabaseException {\r
+ graph.claimLiteral(node, DATA.Log_Node_content, DATA.Log_Content, bean, CONTENT_BEAN_BINDING );\r
+ }\r
+\r
+ private int getDegree(ReadGraph graph, Resource tree) throws DatabaseException {\r
+ return graph.syncRequest(new RelatedValue<Integer>(tree, DATA.Log_t, Bindings.INTEGER), TransientCacheListener.<Integer>instance());\r
+ }\r
+ \r
+ public LogContentBean getContentBean(ReadGraph graph, Resource node) throws DatabaseException {\r
+ return graph.syncRequest(new RelatedValue<LogContentBean>(node, DATA.Log_Node_content, CONTENT_BEAN_BINDING), TransientCacheListener.<LogContentBean>instance());\r
+ }\r
+ \r
+}\r