]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / graph / WriteGraphImpl.java
diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java
new file mode 100644 (file)
index 0000000..f560008
--- /dev/null
@@ -0,0 +1,1152 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.db.impl.graph;\r
+\r
+import java.io.IOException;\r
+import java.io.PrintWriter;\r
+import java.io.StringWriter;\r
+import java.util.TreeMap;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.primitives.MutableBoolean;\r
+import org.simantics.databoard.primitives.MutableByte;\r
+import org.simantics.databoard.primitives.MutableDouble;\r
+import org.simantics.databoard.primitives.MutableFloat;\r
+import org.simantics.databoard.primitives.MutableInteger;\r
+import org.simantics.databoard.primitives.MutableLong;\r
+import org.simantics.databoard.primitives.MutableString;\r
+import org.simantics.databoard.serialization.SerializationException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.util.binary.RandomAccessBinary;\r
+import org.simantics.db.DevelopmentKeys;\r
+import org.simantics.db.ExternalValueSupport;\r
+import org.simantics.db.Metadata;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.VirtualGraph;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.WriteOnlyGraph;\r
+import org.simantics.db.common.request.WriteOnlyRequest;\r
+import org.simantics.db.common.utils.Literals;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.db.exception.ArgumentException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.InternalException;\r
+import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;\r
+import org.simantics.db.exception.ServiceException;\r
+import org.simantics.db.impl.DatabaseUtils;\r
+import org.simantics.db.impl.MemWatch;\r
+import org.simantics.db.impl.ResourceImpl;\r
+import org.simantics.db.impl.internal.RandomAccessValueSupport;\r
+import org.simantics.db.impl.internal.ResourceData;\r
+import org.simantics.db.impl.query.CacheEntry;\r
+import org.simantics.db.impl.query.QueryProcessor;\r
+import org.simantics.db.impl.support.WriteRequestScheduleSupport;\r
+import org.simantics.db.procedure.Procedure;\r
+import org.simantics.db.request.DelayedWrite;\r
+import org.simantics.db.request.DelayedWriteResult;\r
+import org.simantics.db.request.Write;\r
+import org.simantics.db.request.WriteOnly;\r
+import org.simantics.db.request.WriteOnlyResult;\r
+import org.simantics.db.request.WriteResult;\r
+import org.simantics.db.request.WriteTraits;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.utils.Development;\r
+import org.simantics.utils.datastructures.Callback;\r
+import org.simantics.utils.datastructures.Pair;\r
+\r
+import gnu.trove.map.hash.THashMap;\r
+\r
+\r
+final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {\r
+\r
+    final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);\r
+    \r
+    final public WriteSupport writeSupport;\r
+    final public VirtualGraph provider;\r
+    \r
+    private String resourceName(Resource resource) throws DatabaseException {\r
+       if(Development.DEVELOPMENT) {\r
+               Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN); \r
+               if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);\r
+               else return resource.toString();\r
+       }\r
+       throw new IllegalStateException();\r
+    }\r
+    \r
+    private Layer0 getBuiltins() {\r
+        return getService(Layer0.class);\r
+    }\r
+\r
+    private WriteRequestScheduleSupport getWriteRequestScheduler() {\r
+        return (WriteRequestScheduleSupport) getSession();\r
+    }\r
+\r
+    private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,\r
+            WriteSupport writeSupport, VirtualGraph provider) {\r
+        super(parent2, readSupport);\r
+        this.writeSupport = writeSupport;\r
+        this.provider = provider;\r
+    }\r
+\r
+    public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {\r
+\r
+       WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider); \r
+        \r
+        try {\r
+                       writeSupport.setDefaultClusterSet(null);\r
+               } catch (ServiceException e) {\r
+                       Logger.defaultLogError(e);\r
+               }\r
+        \r
+        return impl;\r
+        \r
+    }\r
+\r
+    final public WriteGraphImpl newAsync() {\r
+        return this;\r
+    }\r
+    \r
+    public WriteGraphImpl newSync(final VirtualGraph provider) {\r
+        return new WriteGraphImpl(parent, processor, writeSupport, provider);\r
+    }\r
+    final public WriteGraphImpl newSync(final CacheEntry parent) {\r
+        return new WriteGraphImpl(parent, processor, writeSupport, provider);\r
+    }\r
+\r
+    @Override\r
+    final public ReadGraphImpl withAsyncParent(CacheEntry parent2) {\r
+        return new WriteGraphImpl(parent2, processor, writeSupport, provider);\r
+    }\r
+\r
+    @Override\r
+    public ReadGraphImpl newRestart(ReadGraphImpl impl) {\r
+\r
+        WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);\r
+\r
+        return write;\r
+        \r
+    }\r
+\r
+    @Override\r
+    final public Resource newResource() throws ServiceException {\r
+        \r
+        try {\r
+               \r
+               Resource result = writeSupport.createResource(provider); \r
+\r
+               if(Development.DEVELOPMENT) {\r
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                       WriteLogger.logNewResource(this, result);\r
+               }\r
+               \r
+            return result;\r
+            \r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(e);\r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    final public Resource newResource(long clusterId) throws ServiceException {\r
+        \r
+        try {\r
+            \r
+               Resource result = writeSupport.createResource(provider, clusterId); \r
+\r
+               if(Development.DEVELOPMENT) {\r
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                       WriteLogger.logNewResource(this, result);\r
+               }\r
+               \r
+            return result;\r
+            \r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(e);\r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    public Resource newResource(Resource clusterSet) throws ServiceException {\r
+        try {\r
+               Resource result;\r
+               if (provider != null)\r
+                       result = writeSupport.createResource(provider);\r
+               else\r
+                       result = writeSupport.createResource(provider, clusterSet); \r
+            if(Development.DEVELOPMENT) {\r
+                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                    WriteLogger.logNewResource(this, result);\r
+            }\r
+            return result;\r
+        } catch (ServiceException e) {\r
+            throw e;\r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(e);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void newClusterSet(Resource clusterSet) throws ServiceException {\r
+        try {\r
+               if (provider == null)\r
+                       writeSupport.createClusterSet(provider, clusterSet); \r
+            if(Development.DEVELOPMENT) {\r
+                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                    WriteLogger.logNewResource(this, clusterSet);\r
+            }\r
+        } catch (ServiceException e) {\r
+            throw e;\r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(e);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public Resource setClusterSet4NewResource(Resource clusterSet)\r
+    throws ServiceException {\r
+        return writeSupport.setDefaultClusterSet(clusterSet);\r
+    }\r
+    /**\r
+     * Compares two object for equality, allowing null objects also.\r
+     * \r
+     * @param o1 obj1\r
+     * @param o2 obj2\r
+     * @return true if both arguments are <code>null</code> or equal\r
+     */\r
+    static boolean safeEquals(Object o1, Object o2) {\r
+        if (o1 == o2)\r
+            return true;\r
+        if (o1 == null && o2 == null)\r
+            return true;\r
+        if (o1 == null || o2 == null)\r
+            return false;\r
+        return o1.equals(o2);\r
+    }\r
+\r
+    @Override\r
+    public void asyncRequest(DelayedWrite request) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, new Callback<DatabaseException>() {\r
+\r
+                       @Override\r
+                       public void run(DatabaseException parameter) {\r
+                               if(parameter != null)\r
+                                       Logger.defaultLogError(parameter);\r
+                       }\r
+               \r
+        }, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public void asyncRequest(DelayedWrite request, Callback<DatabaseException> callback) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public void asyncRequest(final Write r) {\r
+        assert (r != null);\r
+        getWriteRequestScheduler().scheduleRequest(r, new Callback<DatabaseException>() {\r
+\r
+                       @Override\r
+                       public void run(DatabaseException parameter) {\r
+                               if(parameter != null)\r
+                                       Logger.defaultLogError(parameter);\r
+                       }\r
+               \r
+        }, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public void asyncRequest(Write request, Callback<DatabaseException> callback) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public void asyncRequest(WriteOnly request) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, new Callback<DatabaseException>() {\r
+\r
+                       @Override\r
+                       public void run(DatabaseException parameter) {\r
+                               if(parameter != null)\r
+                                       Logger.defaultLogError(parameter);\r
+                       }\r
+               \r
+        }, null, Boolean.TRUE);\r
+    }\r
+    \r
+    @Override\r
+    public void asyncRequest(WriteOnly request, Callback<DatabaseException> callback) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {\r
+        assert (request != null);\r
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
+    }\r
+\r
+    @Override\r
+    public void syncRequest(Write request) throws DatabaseException {\r
+        \r
+       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
+       \r
+        WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); \r
+        try {\r
+            writeSupport.performWriteRequest(graph, request);\r
+        } catch (DatabaseException e) {\r
+            throw e;\r
+        } catch (Throwable t) {\r
+            throw new DatabaseException(t);\r
+        } finally {\r
+            setClusterSet4NewResource(defaultClusterSet);\r
+        }\r
+        \r
+        \r
+    }\r
+    \r
+    @Override\r
+    public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {\r
+\r
+       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
+\r
+       WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); \r
+        try {\r
+            return writeSupport.performWriteRequest(graph, request);\r
+        } catch (DatabaseException e) {\r
+            throw e;\r
+        } catch (Throwable t) {\r
+            throw new DatabaseException(t);\r
+        } finally {\r
+            setClusterSet4NewResource(defaultClusterSet);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void syncRequest(final DelayedWrite request) throws DatabaseException {\r
+        \r
+        try {\r
+\r
+               final DelayedWriteGraph dwg = new DelayedWriteGraph(this);\r
+               request.perform(dwg);\r
+\r
+               syncRequest(new WriteOnlyRequest() {\r
+\r
+                       @Override\r
+                       public void perform(WriteOnlyGraph graph) throws DatabaseException {\r
+                               dwg.commit(graph, request);\r
+                       }\r
+\r
+               });\r
+\r
+        } catch (DatabaseException e) {\r
+               \r
+               throw e;\r
+               \r
+        } catch (Throwable e) {\r
+               \r
+               throw new DatabaseException(e);\r
+               \r
+        } finally {\r
+               \r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void syncRequest(WriteOnly request) throws DatabaseException {\r
+       \r
+       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
+       \r
+        try {\r
+            writeSupport.performWriteRequest(this, request);\r
+        } catch (DatabaseException e) {\r
+            throw e;\r
+        } catch (Throwable t) {\r
+            throw new DatabaseException(t);\r
+        }  finally {\r
+            setClusterSet4NewResource(defaultClusterSet);\r
+        }\r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
+\r
+        if(subject == null || predicate == null || object == null) {\r
+               throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");\r
+        }\r
+\r
+        if(processor.isImmutable(object)) {\r
+               claim(subject, predicate, null, object);                \r
+        } else {\r
+               claim(subject, predicate, getPossibleInverse(predicate), object);               \r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {\r
+        \r
+        if (MemWatch.isLowOnMemory())\r
+            writeSupport.gc();\r
+\r
+        if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");\r
+        if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");\r
+        if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");\r
+        if(provider == null && subject.isPersistent() && !object.isPersistent())\r
+            throw new ServiceException("Cannot claim persistent statements where subject is persistent and object is virtual. Persistent database cannot contain statements that refer to virtual graphs.");\r
+\r
+               try {\r
+                       if(Development.DEVELOPMENT) {\r
+                               try {\r
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
+                                               String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";\r
+                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
+                                                       StringWriter writer = new StringWriter();\r
+                                                       PrintWriter out = new PrintWriter(writer);\r
+                                                       new Exception(text).printStackTrace(out);\r
+                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));\r
+                                               } else {\r
+                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));\r
+                                               }\r
+                                       }\r
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                                               WriteLogger.logClaim(this, subject, predicate, inverse, object);\r
+                               } catch (DatabaseException e) {\r
+                                       Logger.defaultLogError(e);\r
+                               }\r
+                       }\r
+                       writeSupport.claim(provider, subject, predicate, object);\r
+               } catch(RuntimeException e) {\r
+                       throw new ServiceException(e);\r
+               }\r
+    \r
+        if(inverse == null) return;\r
+        if(subject.equals(object) && predicate.equals(inverse)) {\r
+            return;\r
+        }\r
+\r
+        // Do as claim(s,p,o) already works -\r
+        // don't write inverse relations if object is immutable.\r
+        if(processor.isImmutable(object))\r
+            return;\r
+\r
+        try {\r
+            writeSupport.claim(provider, object, inverse, subject);\r
+        } catch(RuntimeException e) {\r
+            throw new ServiceException(e);\r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void deny(Resource subject) throws ServiceException {\r
+        \r
+        assert(subject != null);\r
+\r
+        try {\r
+        \r
+            for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {\r
+                if(subject.equals(stm.getSubject())) {\r
+                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
+//                    if(inverse == null) {\r
+//                        try {\r
+//                            String name = adapt(stm.getPredicate(), String.class);\r
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
+//                        } catch (AdaptionException e) {\r
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");\r
+//                        }\r
+//                    }\r
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
+                }\r
+            }\r
+            \r
+        } catch(DatabaseException e) {\r
+            \r
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
+            \r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void deny(Resource subject, Resource predicate) throws ServiceException {\r
+        \r
+        assert(subject != null);\r
+        assert(predicate != null);\r
+\r
+        try {\r
+\r
+            for (Statement stm : getStatements(subject, predicate)) {\r
+                if (subject.equals(stm.getSubject())) {\r
+                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
+//                    if(inverse == null) {\r
+//                        try {\r
+//                            String name = adapt(stm.getPredicate(), String.class);\r
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
+//                        } catch (AdaptionException e) {\r
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");\r
+//                        }\r
+//                    }\r
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
+            }\r
+        }\r
+\r
+        } catch(DatabaseException e) {\r
+            \r
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
+            \r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
+\r
+        assert(subject != null);\r
+        assert(predicate != null);\r
+        assert(object != null);\r
+\r
+        try { \r
+            for (Statement stm : getStatements(subject, predicate)) {\r
+                if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {\r
+                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
+                }\r
+            }\r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
+        \r
+        assert(subject != null);\r
+        assert(predicate != null);\r
+        assert(object != null);\r
+        \r
+        Resource inverse = getPossibleInverse(predicate);\r
+//        if(inverse == null) {\r
+//            try {\r
+//                String name = adapt(predicate, String.class);\r
+//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
+//            } catch (AdaptionException e) {\r
+//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");\r
+//            }\r
+//        }\r
+        deny(subject, predicate, inverse, object);\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void deny(Statement statement) throws ServiceException {\r
+        assert(statement != null);\r
+        denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());\r
+    }\r
+\r
+    @Override\r
+    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {\r
+\r
+        assert(subject != null);\r
+        assert(predicate != null);\r
+        assert(object != null);\r
+        \r
+        VirtualGraph provider = processor.getProvider(subject, predicate, object);\r
+        \r
+        deny(subject, predicate, inverse, object, provider);\r
+        \r
+    }\r
+\r
+    /*\r
+     * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.\r
+     */\r
+    @Override\r
+    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {\r
+        \r
+        assert(subject != null);\r
+        assert(predicate != null);\r
+        assert(object != null);\r
+\r
+               if(Development.DEVELOPMENT) {\r
+                       try {\r
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
+                                       String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";\r
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
+                                               StringWriter writer = new StringWriter();\r
+                                               PrintWriter out = new PrintWriter(writer);\r
+                                               new Exception(text).printStackTrace(out);\r
+                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));\r
+                                       } else {\r
+                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));\r
+                                       }\r
+                               }\r
+                       } catch (DatabaseException e) {\r
+                               Logger.defaultLogError(e);\r
+                       }\r
+               }\r
+        \r
+        try {\r
+            writeSupport.removeStatement(provider, subject, predicate, object);\r
+        } catch(Throwable e) {\r
+            throw new InternalException("bug in deny(s,p,i,o)", e);\r
+        }\r
+    \r
+        if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;\r
+\r
+        // don't deny inverse relations if object is immutable.\r
+        if(processor.isImmutable(object))\r
+            return;\r
+        \r
+        try {\r
+            writeSupport.removeStatement(provider, object, inverse, subject);\r
+        } catch(Throwable e) {\r
+            throw new InternalException("bug in deny(s,p,i,o)", e);\r
+        }\r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claimValue(Resource resource, Object value) throws ServiceException {\r
+        \r
+        try {\r
+            Binding b = Bindings.getBinding(value.getClass());\r
+            claimValue(resource, value, b);\r
+        } catch(BindingConstructionException e) {\r
+            throw new IllegalArgumentException(e);\r
+        }\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {\r
+        \r
+        if(value == null) throw new ServiceException("claimValue does not accept null value");\r
+        if(binding == null) throw new ServiceException("claimValue does not accept null binding");\r
+        \r
+               if(Development.DEVELOPMENT) {\r
+               try {\r
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
+                                       String valueText = Literals.shortString(value.toString());\r
+                                       String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";\r
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
+                                               StringWriter writer = new StringWriter();\r
+                                               PrintWriter out = new PrintWriter(writer);\r
+                                               new Exception(text).printStackTrace(out);\r
+                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));\r
+                                       } else {\r
+                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));\r
+                                       }\r
+                               }\r
+               } catch (DatabaseException e) {\r
+                               Logger.defaultLogError(e);\r
+               }\r
+               }\r
+        \r
+        try {\r
+               Serializer serializer = getSerializer(binding);\r
+            //Serializer serializer = binding.serializer();\r
+            byte[] data = serializer.serialize(value);\r
+\r
+               if(Development.DEVELOPMENT) {\r
+               try {\r
+                   if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
+                       WriteLogger.logValue(this, resource, data);\r
+               } catch (DatabaseException e) {\r
+                               Logger.defaultLogError(e);\r
+               }\r
+               }\r
+            \r
+            writeSupport.claimValue(provider, resource, data);\r
+            \r
+        } catch (DatabaseException e) {\r
+            throw new ServiceException(e);\r
+        } catch (SerializationException e) {\r
+            throw new ServiceException(e);\r
+        } catch (IOException e) {\r
+            throw new ServiceException(e);\r
+        } \r
+        \r
+    }\r
+\r
+    THashMap<Class<?>, Resource> builtinValues = new THashMap<Class<?>, Resource>(32);\r
+\r
+    private void initBuiltinValues(Layer0 b) {\r
+\r
+        if(!builtinValues.isEmpty()) return;\r
+\r
+        builtinValues.put(String.class, b.String);\r
+        builtinValues.put(Double.class, b.Double);\r
+        builtinValues.put(Float.class, b.Float);\r
+        builtinValues.put(Long.class, b.Long);\r
+        builtinValues.put(Integer.class, b.Integer);\r
+        builtinValues.put(Byte.class, b.Byte);\r
+        builtinValues.put(Boolean.class, b.Boolean);\r
+        builtinValues.put(Variant.class, b.Variant);\r
+\r
+        builtinValues.put(String[].class, b.StringArray);\r
+        builtinValues.put(double[].class, b.DoubleArray);\r
+        builtinValues.put(float[].class, b.FloatArray);\r
+        builtinValues.put(long[].class, b.LongArray);\r
+        builtinValues.put(int[].class, b.IntegerArray);\r
+        builtinValues.put(byte[].class, b.ByteArray);\r
+        builtinValues.put(boolean[].class, b.BooleanArray);\r
+\r
+        builtinValues.put(MutableString.class, b.String);\r
+        builtinValues.put(MutableDouble.class, b.Double);\r
+        builtinValues.put(MutableFloat.class, b.Float);\r
+        builtinValues.put(MutableLong.class, b.Long);\r
+        builtinValues.put(MutableInteger.class, b.Integer);\r
+        builtinValues.put(MutableByte.class, b.Byte);\r
+        builtinValues.put(MutableBoolean.class, b.Boolean);\r
+\r
+    }\r
+\r
+    @Override\r
+    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
+\r
+        Layer0 b = getBuiltins();\r
+        Resource valueResource = newResource();\r
+        claim(valueResource, b.InstanceOf, null, type);\r
+        claim(resource, predicate, inverse, valueResource);\r
+        claimValue(valueResource, value, binding);\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
+\r
+        Layer0 b = getBuiltins();\r
+        initBuiltinValues(b);\r
+        Resource literal = newResource();\r
+        \r
+        Class<?> clazz = value.getClass();\r
+        Resource type = builtinValues.get(clazz);\r
+        if (type == null) {\r
+            type = b.Literal;\r
+            Resource dataType = newResource();\r
+            claim(dataType, b.InstanceOf, null, b.DataType);\r
+            claimValue(dataType, binding.type(), DATA_TYPE_BINDING);\r
+            claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);\r
+        }\r
+        \r
+        claim(literal, b.InstanceOf, null, type);\r
+        claim(resource, predicate, inverse, literal);\r
+        claimValue(literal, value, binding);\r
+        \r
+    }\r
+\r
+    @Override\r
+    public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)\r
+    throws DatabaseException {\r
+        Layer0 b = getBuiltins();\r
+        initBuiltinValues(b);       \r
+        Resource literal = newResource();\r
+        claim(literal, b.InstanceOf, null, b.Literal);\r
+        Resource dataType = newResource();\r
+        claim(dataType, b.InstanceOf, null, b.DataType);\r
+        claimValue(dataType, datatype, DATA_TYPE_BINDING);\r
+        claim(literal, b.HasDataType, dataType);\r
+        claim(resource, predicate, literal);\r
+        return createAccessor(literal, datatype, initialValue);\r
+    }\r
+    @Override\r
+    public RandomAccessBinary createRandomAccessBinary\r
+    (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)\r
+    throws DatabaseException {\r
+        Layer0 b = getBuiltins();\r
+        initBuiltinValues(b);       \r
+        Resource literal = newResource();\r
+        claim(literal, b.InstanceOf, null, b.Literal);\r
+        Resource dataType = newResource();\r
+        claim(dataType, b.InstanceOf, null, b.DataType);\r
+        claimValue(dataType, datatype, DATA_TYPE_BINDING);\r
+        claim(literal, b.HasDataType, dataType);\r
+        claim(resource, predicate, literal);\r
+        return createRandomAccessBinary(literal, datatype, initiaValue);\r
+    }\r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
+\r
+        try {\r
+            Binding binding = Bindings.getBinding(value.getClass());\r
+            claimLiteral(resource, predicate, value, binding);\r
+        } catch(BindingConstructionException e) {\r
+            throw new IllegalArgumentException(e);\r
+        } \r
+\r
+    }\r
+\r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
+        \r
+        Layer0 b = getBuiltins();\r
+        initBuiltinValues(b);       \r
+        \r
+        Statement literalStatement = getPossibleStatement(resource, predicate);\r
+\r
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
+\r
+            claimValue(literalStatement.getObject(), value, binding);\r
+\r
+        } else {\r
+\r
+            Class<?> clazz = value.getClass();\r
+            Resource type = builtinValues.get(clazz);\r
+            Resource literal = newResource();\r
+            if (type == null) {\r
+                type = b.Literal;\r
+                Resource dataType = newResource();\r
+                claim(dataType, b.InstanceOf, null, b.DataType);\r
+                claimValue(dataType, binding.type(), DATA_TYPE_BINDING);\r
+                claim(literal, b.HasDataType, null, dataType);\r
+            }\r
+            claim(literal, b.InstanceOf, null, type);\r
+            claimValue(literal, value, binding); \r
+            claim(resource, predicate, literal);\r
+\r
+        }\r
+\r
+        if(Development.DEVELOPMENT) {\r
+               try {\r
+                       getPossibleStatement(resource, predicate);\r
+               } catch (ManyObjectsForFunctionalRelationException e) {\r
+                       System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);\r
+                       getPossibleStatement(resource, predicate);\r
+               }\r
+        }\r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)\r
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
+        \r
+        try {\r
+            Binding binding = Bindings.getBinding(value.getClass());\r
+            claimLiteral(resource, predicate, type, value, binding);\r
+        } catch(BindingConstructionException e) {\r
+            throw new IllegalArgumentException(e);\r
+        } \r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)\r
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
+        \r
+        Layer0 b = getBuiltins();\r
+        \r
+        Statement literalStatement = getPossibleStatement(resource, predicate);\r
+\r
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
+\r
+            claimValue(literalStatement.getObject(), value, binding);\r
+\r
+        } else {\r
+\r
+            Resource literal = newResource();\r
+            claim(literal, b.InstanceOf, null, type);\r
+            claimValue(literal, value, binding); \r
+            claim(resource, predicate, literal);\r
+\r
+        }\r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)\r
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
+\r
+        try {\r
+            Binding binding = Bindings.getBinding(value.getClass());\r
+            claimLiteral(resource, predicate, inverse, type, value, binding);\r
+        } catch(BindingConstructionException e) {\r
+            throw new IllegalArgumentException(e);\r
+        } \r
+        \r
+    }\r
+    \r
+    @Override\r
+    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)\r
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
+        \r
+        Layer0 b = getBuiltins();\r
+        \r
+        Statement literalStatement = getPossibleStatement(resource, predicate);\r
+\r
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
+\r
+            claimValue(literalStatement.getObject(), value, binding);\r
+\r
+        } else {\r
+\r
+            Resource literal = newResource();\r
+            claim(literal, b.InstanceOf, null, type);\r
+            claimValue(literal, value, binding); \r
+            claim(resource, predicate, inverse, literal);\r
+\r
+        }\r
+        \r
+    }\r
+\r
+\r
+    @Override\r
+    public void denyValue(Resource resource) throws ServiceException {\r
+\r
+        denyValue0(resource, null);\r
+\r
+    }\r
+\r
+    @Override\r
+    public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {\r
+\r
+        denyValue0(resource, valueProvider);\r
+\r
+    }\r
+    \r
+    /**\r
+     * @param resource resource to remove value from\r
+     * @param valueProvider <code>null</code> means the caller doesn't know and\r
+     *        this method must find out\r
+     * @throws ServiceException\r
+     */\r
+    private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {\r
+\r
+       if(Development.DEVELOPMENT) {\r
+               try {\r
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
+                                       Object oldValue = getPossibleValue(resource);\r
+                                       String valueText = oldValue == null ? "<no value>" : oldValue.toString();\r
+                                       String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";\r
+                                       if (oldValue != null) {\r
+                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
+                                                       StringWriter writer = new StringWriter();\r
+                                                       PrintWriter out = new PrintWriter(writer);\r
+                                                       new Exception(text).printStackTrace(out);\r
+                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));\r
+                                               } else {\r
+                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));\r
+                                               }\r
+                                       }\r
+                               }\r
+               } catch (DatabaseException e) {\r
+                               Logger.defaultLogError(e);\r
+               }\r
+               }\r
+       \r
+        if (provider != null) {\r
+\r
+            // Can only remove from the specified VirtualGraph\r
+            if (resource.isPersistent())\r
+                throw new ArgumentException("Tried to remove literal value from persistent resource " + resource\r
+                        + " using virtual graph '" + provider.getIdentifier() + "'");\r
+            writeSupport.denyValue(provider, resource);\r
+\r
+        } else {\r
+\r
+            // Can remove from any provider\r
+            if (resource.isPersistent()) {\r
+                // A persistent resource cannot have a virtual literal\r
+                // so this should be safe.\r
+                writeSupport.denyValue(provider, resource);\r
+            } else {\r
+                // A virtual resource cannot have a persistent literal.\r
+                // Must look for a virtual graph for resource.\r
+                Layer0 L0 = getBuiltins();\r
+                if (valueProvider == null) {\r
+                    if (hasStatement(resource, L0.InstanceOf)) {\r
+                        valueProvider = processor.getProvider(resource, L0.InstanceOf);\r
+                    } else if (hasStatement(resource, L0.Inherits)) {\r
+                        valueProvider = processor.getProvider(resource, L0.Inherits);\r
+                    } else if (hasStatement(resource, L0.SubrelationOf)) {\r
+                        valueProvider = processor.getProvider(resource, L0.SubrelationOf);\r
+                    }\r
+                }\r
+                if (valueProvider != null)\r
+                    writeSupport.denyValue(valueProvider, resource);\r
+            }\r
+\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
+\r
+        Statement valueStatement = getPossibleStatement(resource, predicate);\r
+\r
+        if (valueStatement != null && !valueStatement.isAsserted(resource)) {\r
+\r
+            Resource value = valueStatement.getObject();\r
+            Resource inverse = getPossibleInverse(predicate);\r
+\r
+            if (provider != null) {\r
+\r
+                // Can only remove from the specified provider\r
+                deny(resource, predicate, inverse, value, provider);\r
+                writeSupport.denyValue(provider, value);\r
+\r
+            } else {\r
+\r
+                // Can remove from any provider\r
+                VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);\r
+                deny(resource, predicate, inverse, value, statementProvider);\r
+                denyValue0(resource, statementProvider);\r
+\r
+            }\r
+\r
+        }\r
+\r
+    }\r
+\r
+    @Override\r
+    public void flushCluster() {\r
+        \r
+        writeSupport.flushCluster();\r
+        \r
+    }\r
+\r
+    @Override\r
+    public void flushCluster(Resource r) {\r
+        writeSupport.flushCluster(r);\r
+    }\r
+    \r
+    Object serialize(Object value) {\r
+        \r
+        Class<?> clazz = value.getClass();\r
+        \r
+        if(clazz.isArray()) return value;\r
+        \r
+        if(Double.class == clazz) return new double[] { (Double)value };\r
+        else if(String.class == clazz) return new String[] { (String)value };\r
+        else if(Integer.class == clazz) return new int[] { (Integer)value };\r
+        else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };\r
+        else if(Long.class == clazz) return new long[] { (Long)value };\r
+        else if(Byte.class == clazz) return new byte[] { (Byte)value };\r
+        else if(Float.class == clazz) return new float[] { (Float)value };\r
+        else return value;\r
+        \r
+    }\r
+    \r
+    @Override\r
+    public <T> void addMetadata(Metadata data) throws ServiceException {\r
+        writeSupport.addMetadata(data);\r
+    }\r
+\r
+    @Override\r
+    public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {\r
+        return writeSupport.getMetadata(clazz);\r
+    }\r
+\r
+    @Override\r
+    public TreeMap<String, byte[]> getMetadata() {\r
+        return writeSupport.getMetadata();\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+        return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";\r
+    }     \r
+\r
+    public void commitAccessorChanges(WriteTraits writeTraits) {\r
+        try {\r
+            RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);\r
+            if (ravs == null)\r
+                return;\r
+\r
+            for(Pair<Resource, ResourceData> entry : ravs.entries()) {\r
+                ResourceData rd = entry.second;\r
+               if(!rd.isChanged()) continue;\r
+                Resource resource = entry.first;\r
+                try {\r
+                    ExternalValueSupport evs = getService(ExternalValueSupport.class);\r
+                    long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;\r
+                    long bsize = rd.binaryFile.length();\r
+                    final int N = 1<<20;\r
+                    final int LIMIT = 10 * 1000 * 1000;\r
+                    long left = bsize;\r
+                    long offset = 0;\r
+                    byte[] bytes = new byte[N];\r
+                    rd.binaryFile.reset();\r
+                    rd.binaryFile.position(0);\r
+                    int count = 0;\r
+//                    if (LIMIT < left)\r
+//                        evs.moveValue(this, resource);\r
+                    while (left > 0) {\r
+                        int length = N < left ? N : (int)left;\r
+                        rd.binaryFile.readFully(bytes, 0, length);\r
+                        evs.writeValue(this, resource, offset, length, bytes);\r
+                        offset += length;\r
+                        left -= length;\r
+                        count += length;\r
+                        if (count > LIMIT) {\r
+                            count = 0;\r
+//                            evs.commitAndContinue(this, writeTraits, resource);\r
+//                            evs.moveValue(this, resource);\r
+                            // This is needed so we don't create too many requests and run out of heap.\r
+                            evs.wait4RequestsLess(1);\r
+                        }\r
+                    }\r
+                    if (bsize < vsize) // truncate\r
+                        evs.writeValue(this, resource, bsize, 0, new byte[0]);\r
+                } catch (DatabaseException e) {\r
+                    Logger.defaultLogError(e);\r
+                }\r
+            }\r
+        } catch(IOException e) {\r
+            Logger.defaultLogError(e);\r
+        } catch (RuntimeException e) {\r
+          Logger.defaultLogError(e);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public VirtualGraph getProvider() {\r
+        return provider;\r
+    }\r
+\r
+    @Override\r
+    public void clearUndoList(WriteTraits writeTraits) {\r
+        writeSupport.clearUndoList(writeTraits);\r
+    }\r
+    \r
+    public void markUndoPoint() {\r
+       writeSupport.startUndo();\r
+    }\r
+    \r
+}\r