1 /*******************************************************************************
2 * Copyright (c) 2007, 2018 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.impl.graph;
14 import java.io.IOException;
15 import java.io.PrintWriter;
16 import java.io.StringWriter;
17 import java.util.IdentityHashMap;
19 import java.util.TreeMap;
20 import java.util.function.Consumer;
22 import org.simantics.databoard.Bindings;
23 import org.simantics.databoard.Datatypes;
24 import org.simantics.databoard.accessor.Accessor;
25 import org.simantics.databoard.binding.Binding;
26 import org.simantics.databoard.binding.error.BindingConstructionException;
27 import org.simantics.databoard.binding.mutable.Variant;
28 import org.simantics.databoard.primitives.MutableBoolean;
29 import org.simantics.databoard.primitives.MutableByte;
30 import org.simantics.databoard.primitives.MutableDouble;
31 import org.simantics.databoard.primitives.MutableFloat;
32 import org.simantics.databoard.primitives.MutableInteger;
33 import org.simantics.databoard.primitives.MutableLong;
34 import org.simantics.databoard.primitives.MutableString;
35 import org.simantics.databoard.serialization.SerializationException;
36 import org.simantics.databoard.serialization.Serializer;
37 import org.simantics.databoard.type.ArrayType;
38 import org.simantics.databoard.type.BooleanType;
39 import org.simantics.databoard.type.ByteType;
40 import org.simantics.databoard.type.Datatype;
41 import org.simantics.databoard.type.DoubleType;
42 import org.simantics.databoard.type.FloatType;
43 import org.simantics.databoard.type.IntegerType;
44 import org.simantics.databoard.type.LongType;
45 import org.simantics.databoard.type.StringType;
46 import org.simantics.databoard.type.VariantType;
47 import org.simantics.databoard.util.binary.RandomAccessBinary;
48 import org.simantics.db.DevelopmentKeys;
49 import org.simantics.db.ExternalValueSupport;
50 import org.simantics.db.Metadata;
51 import org.simantics.db.Resource;
52 import org.simantics.db.Statement;
53 import org.simantics.db.VirtualGraph;
54 import org.simantics.db.WriteGraph;
55 import org.simantics.db.WriteOnlyGraph;
56 import org.simantics.db.common.request.WriteOnlyRequest;
57 import org.simantics.db.common.utils.Literals;
58 import org.simantics.db.common.utils.Logger;
59 import org.simantics.db.exception.ArgumentException;
60 import org.simantics.db.exception.DatabaseException;
61 import org.simantics.db.exception.InternalException;
62 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
63 import org.simantics.db.exception.ServiceException;
64 import org.simantics.db.impl.DatabaseUtils;
65 import org.simantics.db.impl.MemWatch;
66 import org.simantics.db.impl.ResourceImpl;
67 import org.simantics.db.impl.internal.RandomAccessValueSupport;
68 import org.simantics.db.impl.internal.ResourceData;
69 import org.simantics.db.impl.query.CacheEntry;
70 import org.simantics.db.impl.query.QueryProcessor;
71 import org.simantics.db.impl.support.WriteRequestScheduleSupport;
72 import org.simantics.db.procedure.Procedure;
73 import org.simantics.db.request.DelayedWrite;
74 import org.simantics.db.request.DelayedWriteResult;
75 import org.simantics.db.request.Write;
76 import org.simantics.db.request.WriteOnly;
77 import org.simantics.db.request.WriteOnlyResult;
78 import org.simantics.db.request.WriteResult;
79 import org.simantics.db.request.WriteTraits;
80 import org.simantics.layer0.Layer0;
81 import org.simantics.utils.Development;
82 import org.simantics.utils.datastructures.Pair;
85 final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {
87 final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);
89 final public WriteSupport writeSupport;
90 final public VirtualGraph provider;
92 private String resourceName(Resource resource) throws DatabaseException {
93 if(Development.DEVELOPMENT) {
94 Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN);
95 if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);
96 else return resource.toString();
98 throw new IllegalStateException();
101 private Layer0 getBuiltins() {
102 return getService(Layer0.class);
105 private WriteRequestScheduleSupport getWriteRequestScheduler() {
106 return (WriteRequestScheduleSupport) getSession();
109 private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,
110 WriteSupport writeSupport, VirtualGraph provider) {
111 super(null, parent2, readSupport);
112 this.writeSupport = writeSupport;
113 this.provider = provider;
116 public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {
118 WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider);
121 writeSupport.setDefaultClusterSet(null);
122 } catch (ServiceException e) {
123 Logger.defaultLogError(e);
130 final public WriteGraphImpl newAsync() {
134 public WriteGraphImpl newSync(final VirtualGraph provider) {
135 return new WriteGraphImpl(parent, processor, writeSupport, provider);
137 final public WriteGraphImpl newSync(final CacheEntry parent) {
138 return new WriteGraphImpl(parent, processor, writeSupport, provider);
142 public ReadGraphImpl newRestart(ReadGraphImpl impl) {
144 WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);
151 final public Resource newResource() throws ServiceException {
155 Resource result = writeSupport.createResource(provider);
157 if(Development.DEVELOPMENT) {
158 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
159 WriteLogger.logNewResource(this, result);
164 } catch (DatabaseException e) {
165 throw new ServiceException(e);
171 final public Resource newResource(long clusterId) throws ServiceException {
175 Resource result = writeSupport.createResource(provider, clusterId);
177 if(Development.DEVELOPMENT) {
178 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
179 WriteLogger.logNewResource(this, result);
184 } catch (DatabaseException e) {
185 throw new ServiceException(e);
191 public Resource newResource(Resource clusterSet) throws ServiceException {
194 if (provider != null)
195 result = writeSupport.createResource(provider);
197 result = writeSupport.createResource(provider, clusterSet);
198 if(Development.DEVELOPMENT) {
199 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
200 WriteLogger.logNewResource(this, result);
203 } catch (ServiceException e) {
205 } catch (DatabaseException e) {
206 throw new ServiceException(e);
211 public void newClusterSet(Resource clusterSet) throws ServiceException {
213 if (provider == null)
214 writeSupport.createClusterSet(provider, clusterSet);
215 if(Development.DEVELOPMENT) {
216 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
217 WriteLogger.logNewResource(this, clusterSet);
219 } catch (ServiceException e) {
221 } catch (DatabaseException e) {
222 throw new ServiceException(e);
227 public Resource setClusterSet4NewResource(Resource clusterSet)
228 throws ServiceException {
229 return writeSupport.setDefaultClusterSet(clusterSet);
232 * Compares two object for equality, allowing null objects also.
236 * @return true if both arguments are <code>null</code> or equal
238 static boolean safeEquals(Object o1, Object o2) {
241 if (o1 == null && o2 == null)
243 if (o1 == null || o2 == null)
245 return o1.equals(o2);
249 public void asyncRequest(DelayedWrite request) {
250 assert (request != null);
251 getWriteRequestScheduler().scheduleRequest(request, e -> {
253 Logger.defaultLogError(e);
254 }, null, Boolean.TRUE);
258 public void asyncRequest(DelayedWrite request, Consumer<DatabaseException> callback) {
259 assert (request != null);
260 getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
264 public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {
265 assert (request != null);
266 getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
270 public void asyncRequest(final Write r) {
272 getWriteRequestScheduler().scheduleRequest(r, e -> {
274 Logger.defaultLogError(e);
275 }, null, Boolean.TRUE);
279 public void asyncRequest(Write request, Consumer<DatabaseException> callback) {
280 assert (request != null);
281 getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
285 public void asyncRequest(WriteOnly request) {
286 assert (request != null);
287 getWriteRequestScheduler().scheduleRequest(request, e -> {
289 Logger.defaultLogError(e);
290 }, null, Boolean.TRUE);
294 public void asyncRequest(WriteOnly request, Consumer<DatabaseException> callback) {
295 assert (request != null);
296 getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
300 public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {
301 assert (request != null);
302 getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
306 public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {
307 assert (request != null);
308 getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
312 public void syncRequest(Write request) throws DatabaseException {
314 Resource defaultClusterSet = setClusterSet4NewResource(null);
316 WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider());
318 writeSupport.performWriteRequest(graph, request);
319 } catch (DatabaseException e) {
321 } catch (Throwable t) {
322 throw new DatabaseException(t);
324 setClusterSet4NewResource(defaultClusterSet);
331 public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {
333 Resource defaultClusterSet = setClusterSet4NewResource(null);
335 WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider());
337 return writeSupport.performWriteRequest(graph, request);
338 } catch (DatabaseException e) {
340 } catch (Throwable t) {
341 throw new DatabaseException(t);
343 setClusterSet4NewResource(defaultClusterSet);
348 public void syncRequest(final DelayedWrite request) throws DatabaseException {
352 final DelayedWriteGraph dwg = new DelayedWriteGraph(this);
353 request.perform(dwg);
355 syncRequest(new WriteOnlyRequest() {
358 public void perform(WriteOnlyGraph graph) throws DatabaseException {
359 dwg.commit(graph, request);
364 } catch (DatabaseException e) {
368 } catch (Throwable e) {
370 throw new DatabaseException(e);
379 public void syncRequest(WriteOnly request) throws DatabaseException {
381 Resource defaultClusterSet = setClusterSet4NewResource(null);
384 writeSupport.performWriteRequest(this, request);
385 } catch (DatabaseException e) {
387 } catch (Throwable t) {
388 throw new DatabaseException(t);
390 setClusterSet4NewResource(defaultClusterSet);
396 public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {
398 if(subject == null || predicate == null || object == null) {
399 throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");
402 if(processor.isImmutable(object)) {
403 claim(subject, predicate, null, object);
405 claim(subject, predicate, getPossibleInverse(predicate), object);
411 public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
413 if (MemWatch.isLowOnMemory())
416 if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");
417 if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");
418 if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");
419 if(provider == null && subject.isPersistent() && !object.isPersistent())
420 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.");
423 if(Development.DEVELOPMENT) {
425 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
426 String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
427 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
428 StringWriter writer = new StringWriter();
429 PrintWriter out = new PrintWriter(writer);
430 new Exception(text).printStackTrace(out);
431 Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));
433 Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));
436 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
437 WriteLogger.logClaim(this, subject, predicate, inverse, object);
438 } catch (DatabaseException e) {
439 Logger.defaultLogError(e);
442 writeSupport.claim(provider, subject, predicate, object);
443 } catch(RuntimeException e) {
444 throw new ServiceException(e);
447 if(inverse == null) return;
448 if(subject.equals(object) && predicate.equals(inverse)) {
452 // Do as claim(s,p,o) already works -
453 // don't write inverse relations if object is immutable.
454 if(processor.isImmutable(object))
458 writeSupport.claim(provider, object, inverse, subject);
459 } catch(RuntimeException e) {
460 throw new ServiceException(e);
466 public void deny(Resource subject) throws ServiceException {
468 assert(subject != null);
472 for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {
473 if(subject.equals(stm.getSubject())) {
474 Resource inverse = getPossibleInverse(stm.getPredicate());
475 // if(inverse == null) {
477 // String name = adapt(stm.getPredicate(), String.class);
478 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
479 // } catch (AdaptionException e) {
480 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
483 deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
487 } catch(DatabaseException e) {
489 throw new ServiceException(INTERNAL_ERROR_STRING, e);
496 public void deny(Resource subject, Resource predicate) throws ServiceException {
498 assert(subject != null);
499 assert(predicate != null);
503 for (Statement stm : getStatements(subject, predicate)) {
504 if (subject.equals(stm.getSubject())) {
505 Resource inverse = getPossibleInverse(stm.getPredicate());
506 // if(inverse == null) {
508 // String name = adapt(stm.getPredicate(), String.class);
509 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
510 // } catch (AdaptionException e) {
511 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
514 deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
518 } catch(DatabaseException e) {
520 throw new ServiceException(INTERNAL_ERROR_STRING, e);
527 public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
529 assert(subject != null);
530 assert(predicate != null);
531 assert(object != null);
534 for (Statement stm : getStatements(subject, predicate)) {
535 if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {
536 Resource inverse = getPossibleInverse(stm.getPredicate());
537 deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
540 } catch (DatabaseException e) {
541 throw new ServiceException(INTERNAL_ERROR_STRING, e);
547 public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
549 assert(subject != null);
550 assert(predicate != null);
551 assert(object != null);
553 Resource inverse = getPossibleInverse(predicate);
554 // if(inverse == null) {
556 // String name = adapt(predicate, String.class);
557 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
558 // } catch (AdaptionException e) {
559 // throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");
562 deny(subject, predicate, inverse, object);
567 public void deny(Statement statement) throws ServiceException {
568 assert(statement != null);
569 denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());
573 public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
575 assert(subject != null);
576 assert(predicate != null);
577 assert(object != null);
579 VirtualGraph provider = processor.getProvider(subject, predicate, object);
581 deny(subject, predicate, inverse, object, provider);
586 * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.
589 public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {
591 assert(subject != null);
592 assert(predicate != null);
593 assert(object != null);
595 if(Development.DEVELOPMENT) {
597 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
598 String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
599 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
600 StringWriter writer = new StringWriter();
601 PrintWriter out = new PrintWriter(writer);
602 new Exception(text).printStackTrace(out);
603 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));
605 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));
608 } catch (DatabaseException e) {
609 Logger.defaultLogError(e);
614 writeSupport.removeStatement(provider, subject, predicate, object);
615 } catch(Throwable e) {
616 throw new InternalException("bug in deny(s,p,i,o)", e);
619 if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;
621 // don't deny inverse relations if object is immutable.
622 if(processor.isImmutable(object))
626 writeSupport.removeStatement(provider, object, inverse, subject);
627 } catch(Throwable e) {
628 throw new InternalException("bug in deny(s,p,i,o)", e);
634 public void claimValue(Resource resource, Object value) throws ServiceException {
637 Binding b = Bindings.getBinding(value.getClass());
638 claimValue(resource, value, b);
639 } catch(BindingConstructionException e) {
640 throw new IllegalArgumentException(e);
646 public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {
648 if(value == null) throw new ServiceException("claimValue does not accept null value");
649 if(binding == null) throw new ServiceException("claimValue does not accept null binding");
651 if(Development.DEVELOPMENT) {
653 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
654 String valueText = Literals.shortString(value.toString());
655 String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";
656 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
657 StringWriter writer = new StringWriter();
658 PrintWriter out = new PrintWriter(writer);
659 new Exception(text).printStackTrace(out);
660 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));
662 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));
665 } catch (DatabaseException e) {
666 Logger.defaultLogError(e);
671 Serializer serializer = getSerializer(binding);
672 //Serializer serializer = binding.serializer();
673 byte[] data = serializer.serialize(value);
675 if(Development.DEVELOPMENT) {
677 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
678 WriteLogger.logValue(this, resource, data);
679 } catch (DatabaseException e) {
680 Logger.defaultLogError(e);
684 writeSupport.claimValue(provider, resource, data);
686 } catch (DatabaseException e) {
687 throw new ServiceException(e);
688 } catch (SerializationException e) {
689 throw new ServiceException(e);
690 } catch (IOException e) {
691 throw new ServiceException(e);
696 Map<Object, Resource> builtinValues = new IdentityHashMap<>(40);
698 private void initBuiltinValues(Layer0 b) {
700 if(!builtinValues.isEmpty()) return;
702 builtinValues.put(String.class, b.String);
703 builtinValues.put(Double.class, b.Double);
704 builtinValues.put(Float.class, b.Float);
705 builtinValues.put(Long.class, b.Long);
706 builtinValues.put(Integer.class, b.Integer);
707 builtinValues.put(Byte.class, b.Byte);
708 builtinValues.put(Boolean.class, b.Boolean);
709 builtinValues.put(Variant.class, b.Variant);
711 builtinValues.put(String[].class, b.StringArray);
712 builtinValues.put(double[].class, b.DoubleArray);
713 builtinValues.put(float[].class, b.FloatArray);
714 builtinValues.put(long[].class, b.LongArray);
715 builtinValues.put(int[].class, b.IntegerArray);
716 builtinValues.put(byte[].class, b.ByteArray);
717 builtinValues.put(boolean[].class, b.BooleanArray);
719 builtinValues.put(MutableString.class, b.String);
720 builtinValues.put(MutableDouble.class, b.Double);
721 builtinValues.put(MutableFloat.class, b.Float);
722 builtinValues.put(MutableLong.class, b.Long);
723 builtinValues.put(MutableInteger.class, b.Integer);
724 builtinValues.put(MutableByte.class, b.Byte);
725 builtinValues.put(MutableBoolean.class, b.Boolean);
727 builtinValues.put(Datatypes.DOUBLE, b.Double);
728 builtinValues.put(Datatypes.STRING, b.String);
729 builtinValues.put(Datatypes.INTEGER, b.Integer);
730 builtinValues.put(Datatypes.LONG, b.Long);
731 builtinValues.put(Datatypes.FLOAT, b.Float);
732 builtinValues.put(Datatypes.BYTE, b.Byte);
733 builtinValues.put(Datatypes.BOOLEAN, b.Boolean);
734 builtinValues.put(Datatypes.VARIANT, b.Variant);
736 builtinValues.put(Datatypes.DOUBLE_ARRAY, b.DoubleArray);
737 builtinValues.put(Datatypes.STRING_ARRAY, b.StringArray);
738 builtinValues.put(Datatypes.INTEGER_ARRAY, b.IntegerArray);
739 builtinValues.put(Datatypes.LONG_ARRAY, b.LongArray);
740 builtinValues.put(Datatypes.FLOAT_ARRAY, b.FloatArray);
741 builtinValues.put(Datatypes.BYTE_ARRAY, b.ByteArray);
742 builtinValues.put(Datatypes.BOOLEAN_ARRAY, b.BooleanArray);
743 builtinValues.put(Datatypes.VARIANT_ARRAY, b.VariantArray);
746 private static Datatype canonicalizeToBuiltinDatatype(Datatype datatype) {
747 if (datatype instanceof ArrayType) {
748 ArrayType at = (ArrayType) datatype;
749 datatype = at.componentType();
750 if (datatype instanceof ByteType) {
751 return Datatypes.BYTE_ARRAY;
752 } else if (datatype instanceof DoubleType) {
753 return Datatypes.DOUBLE_ARRAY;
754 } else if (datatype instanceof FloatType) {
755 return Datatypes.FLOAT_ARRAY;
756 } else if (datatype instanceof IntegerType) {
757 return Datatypes.INTEGER_ARRAY;
758 } else if (datatype instanceof LongType) {
759 return Datatypes.LONG_ARRAY;
760 } else if (datatype instanceof BooleanType) {
761 return Datatypes.BOOLEAN_ARRAY;
762 } else if (datatype instanceof StringType) {
763 return Datatypes.STRING_ARRAY;
764 } else if (datatype instanceof VariantType) {
765 return Datatypes.VARIANT_ARRAY;
769 if (datatype instanceof ByteType) {
770 return Datatypes.BYTE;
771 } else if (datatype instanceof DoubleType) {
772 return Datatypes.DOUBLE;
773 } else if (datatype instanceof FloatType) {
774 return Datatypes.FLOAT;
775 } else if (datatype instanceof IntegerType) {
776 return Datatypes.INTEGER;
777 } else if (datatype instanceof LongType) {
778 return Datatypes.LONG;
779 } else if (datatype instanceof BooleanType) {
780 return Datatypes.BOOLEAN;
781 } else if (datatype instanceof StringType) {
782 return Datatypes.STRING;
783 } else if (datatype instanceof VariantType) {
784 return Datatypes.VARIANT;
789 private Resource resolveBuiltinResourceType(Class<?> valueClass, Datatype datatype) {
790 Resource type = builtinValues.get(valueClass);
791 return type != null ? type : builtinValues.get(datatype);
794 private Resource resolveBuiltinResourceTypeByCanonicalizedDatatype(Datatype datatype, Resource defaultResult) {
795 Datatype cdt = canonicalizeToBuiltinDatatype(datatype);
796 return cdt != null ? builtinValues.get(cdt) : defaultResult;
800 public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
802 Layer0 b = getBuiltins();
803 Resource valueResource = newResource();
804 claim(valueResource, b.InstanceOf, null, type);
805 claim(resource, predicate, inverse, valueResource);
806 claimValue(valueResource, value, binding);
811 public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
813 Layer0 b = getBuiltins();
814 initBuiltinValues(b);
815 Resource literal = newResource();
817 Datatype dt = binding.type();
818 Resource type = resolveBuiltinResourceType(value.getClass(), dt);
820 type = resolveBuiltinResourceTypeByCanonicalizedDatatype(dt, b.Literal);
821 Resource dataType = newResource();
822 claim(dataType, b.InstanceOf, null, b.DataType);
823 claimValue(dataType, dt, DATA_TYPE_BINDING);
824 claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);
827 claim(literal, b.InstanceOf, null, type);
828 claim(resource, predicate, inverse, literal);
829 claimValue(literal, value, binding);
834 public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)
835 throws DatabaseException {
836 Layer0 b = getBuiltins();
837 initBuiltinValues(b);
838 Resource literal = newResource();
839 claim(literal, b.InstanceOf, null, b.Literal);
840 Resource dataType = newResource();
841 claim(dataType, b.InstanceOf, null, b.DataType);
842 claimValue(dataType, datatype, DATA_TYPE_BINDING);
843 claim(literal, b.HasDataType, dataType);
844 claim(resource, predicate, literal);
845 return createAccessor(literal, datatype, initialValue);
848 public RandomAccessBinary createRandomAccessBinary
849 (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)
850 throws DatabaseException {
851 Layer0 b = getBuiltins();
852 initBuiltinValues(b);
853 Resource literal = newResource();
854 claim(literal, b.InstanceOf, null, b.Literal);
855 Resource dataType = newResource();
856 claim(dataType, b.InstanceOf, null, b.DataType);
857 claimValue(dataType, datatype, DATA_TYPE_BINDING);
858 claim(literal, b.HasDataType, dataType);
859 claim(resource, predicate, literal);
860 return createRandomAccessBinary(literal, datatype, initiaValue);
863 public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {
866 Binding binding = Bindings.getBinding(value.getClass());
867 claimLiteral(resource, predicate, value, binding);
868 } catch(BindingConstructionException e) {
869 throw new IllegalArgumentException(e);
875 public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
877 Layer0 b = getBuiltins();
878 initBuiltinValues(b);
880 Statement literalStatement = getPossibleStatement(resource, predicate);
882 if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
884 claimValue(literalStatement.getObject(), value, binding);
888 Resource literal = newResource();
889 Datatype dt = binding.type();
890 Resource type = resolveBuiltinResourceType(value.getClass(), dt);
892 type = resolveBuiltinResourceTypeByCanonicalizedDatatype(dt, b.Literal);
893 Resource dataType = newResource();
894 claim(dataType, b.InstanceOf, null, b.DataType);
895 claimValue(dataType, dt, DATA_TYPE_BINDING);
896 claim(literal, b.HasDataType, null, dataType);
898 claim(literal, b.InstanceOf, null, type);
899 claimValue(literal, value, binding);
900 claim(resource, predicate, literal);
904 if(Development.DEVELOPMENT) {
906 getPossibleStatement(resource, predicate);
907 } catch (ManyObjectsForFunctionalRelationException e) {
908 System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);
909 getPossibleStatement(resource, predicate);
916 public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)
917 throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
920 Binding binding = Bindings.getBinding(value.getClass());
921 claimLiteral(resource, predicate, type, value, binding);
922 } catch(BindingConstructionException e) {
923 throw new IllegalArgumentException(e);
929 public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)
930 throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
932 Layer0 b = getBuiltins();
934 Statement literalStatement = getPossibleStatement(resource, predicate);
936 if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
938 claimValue(literalStatement.getObject(), value, binding);
942 Resource literal = newResource();
943 claim(literal, b.InstanceOf, null, type);
944 claimValue(literal, value, binding);
945 claim(resource, predicate, literal);
952 public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)
953 throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
956 Binding binding = Bindings.getBinding(value.getClass());
957 claimLiteral(resource, predicate, inverse, type, value, binding);
958 } catch(BindingConstructionException e) {
959 throw new IllegalArgumentException(e);
965 public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)
966 throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
968 Layer0 b = getBuiltins();
970 Statement literalStatement = getPossibleStatement(resource, predicate);
972 if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
974 claimValue(literalStatement.getObject(), value, binding);
978 Resource literal = newResource();
979 claim(literal, b.InstanceOf, null, type);
980 claimValue(literal, value, binding);
981 claim(resource, predicate, inverse, literal);
989 public void denyValue(Resource resource) throws ServiceException {
991 denyValue0(resource, null);
996 public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {
998 denyValue0(resource, valueProvider);
1003 * @param resource resource to remove value from
1004 * @param valueProvider <code>null</code> means the caller doesn't know and
1005 * this method must find out
1006 * @throws ServiceException
1008 private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {
1010 if(Development.DEVELOPMENT) {
1012 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
1013 Object oldValue = getPossibleValue(resource);
1014 String valueText = oldValue == null ? "<no value>" : oldValue.toString();
1015 String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";
1016 if (oldValue != null) {
1017 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
1018 StringWriter writer = new StringWriter();
1019 PrintWriter out = new PrintWriter(writer);
1020 new Exception(text).printStackTrace(out);
1021 Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));
1023 Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));
1027 } catch (DatabaseException e) {
1028 Logger.defaultLogError(e);
1032 if (provider != null) {
1034 // Can only remove from the specified VirtualGraph
1035 if (resource.isPersistent())
1036 throw new ArgumentException("Tried to remove literal value from persistent resource " + resource
1037 + " using virtual graph '" + provider.getIdentifier() + "'");
1038 writeSupport.denyValue(provider, resource);
1042 // Can remove from any provider
1043 if (resource.isPersistent()) {
1044 // A persistent resource cannot have a virtual literal
1045 // so this should be safe.
1046 writeSupport.denyValue(provider, resource);
1048 // A virtual resource cannot have a persistent literal.
1049 // Must look for a virtual graph for resource.
1050 Layer0 L0 = getBuiltins();
1051 if (valueProvider == null) {
1052 if (hasStatement(resource, L0.InstanceOf)) {
1053 valueProvider = processor.getProvider(resource, L0.InstanceOf);
1054 } else if (hasStatement(resource, L0.Inherits)) {
1055 valueProvider = processor.getProvider(resource, L0.Inherits);
1056 } else if (hasStatement(resource, L0.SubrelationOf)) {
1057 valueProvider = processor.getProvider(resource, L0.SubrelationOf);
1060 if (valueProvider != null)
1061 writeSupport.denyValue(valueProvider, resource);
1069 public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {
1071 Statement valueStatement = getPossibleStatement(resource, predicate);
1073 if (valueStatement != null && !valueStatement.isAsserted(resource)) {
1075 Resource value = valueStatement.getObject();
1076 Resource inverse = getPossibleInverse(predicate);
1078 if (provider != null) {
1080 // Can only remove from the specified provider
1081 deny(resource, predicate, inverse, value, provider);
1082 writeSupport.denyValue(provider, value);
1086 // Can remove from any provider
1087 VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);
1088 deny(resource, predicate, inverse, value, statementProvider);
1089 denyValue0(resource, statementProvider);
1098 public void flushCluster() {
1100 writeSupport.flushCluster();
1105 public void flushCluster(Resource r) {
1106 writeSupport.flushCluster(r);
1109 Object serialize(Object value) {
1111 Class<?> clazz = value.getClass();
1113 if(clazz.isArray()) return value;
1115 if(Double.class == clazz) return new double[] { (Double)value };
1116 else if(String.class == clazz) return new String[] { (String)value };
1117 else if(Integer.class == clazz) return new int[] { (Integer)value };
1118 else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };
1119 else if(Long.class == clazz) return new long[] { (Long)value };
1120 else if(Byte.class == clazz) return new byte[] { (Byte)value };
1121 else if(Float.class == clazz) return new float[] { (Float)value };
1127 public <T> void addMetadata(Metadata data) throws ServiceException {
1128 writeSupport.addMetadata(data);
1132 public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {
1133 return writeSupport.getMetadata(clazz);
1137 public TreeMap<String, byte[]> getMetadata() {
1138 return writeSupport.getMetadata();
1142 public String toString() {
1143 return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";
1146 public void commitAccessorChanges(WriteTraits writeTraits) {
1148 RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);
1152 for(Pair<Resource, ResourceData> entry : ravs.entries()) {
1153 ResourceData rd = entry.second;
1154 if(!rd.isChanged()) continue;
1155 Resource resource = entry.first;
1157 ExternalValueSupport evs = getService(ExternalValueSupport.class);
1158 long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;
1159 long bsize = rd.binaryFile.length();
1160 final int N = 1<<20;
1161 final int LIMIT = 10 * 1000 * 1000;
1164 byte[] bytes = new byte[N];
1165 rd.binaryFile.reset();
1166 rd.binaryFile.position(0);
1168 // if (LIMIT < left)
1169 // evs.moveValue(this, resource);
1171 int length = N < left ? N : (int)left;
1172 rd.binaryFile.readFully(bytes, 0, length);
1173 evs.writeValue(this, resource, offset, length, bytes);
1177 if (count > LIMIT) {
1179 // evs.commitAndContinue(this, writeTraits, resource);
1180 // evs.moveValue(this, resource);
1181 // This is needed so we don't create too many requests and run out of heap.
1182 evs.wait4RequestsLess(1);
1185 if (bsize < vsize) // truncate
1186 evs.writeValue(this, resource, bsize, 0, new byte[0]);
1187 } catch (DatabaseException e) {
1188 Logger.defaultLogError(e);
1191 } catch(IOException e) {
1192 Logger.defaultLogError(e);
1193 } catch (RuntimeException e) {
1194 Logger.defaultLogError(e);
1199 public VirtualGraph getProvider() {
1204 public void clearUndoList(WriteTraits writeTraits) {
1205 writeSupport.clearUndoList(writeTraits);
1208 public void markUndoPoint() {
1209 writeSupport.startUndo();