--- /dev/null
+/*******************************************************************************\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