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