]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java
410a8fe69623efb2dfe5be462e8c7392be1495b5
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / graph / WriteGraphImpl.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.impl.graph;
13
14 import java.io.IOException;
15 import java.io.PrintWriter;
16 import java.io.StringWriter;
17 import java.util.TreeMap;
18 import java.util.function.Consumer;
19
20 import org.simantics.databoard.Bindings;
21 import org.simantics.databoard.accessor.Accessor;
22 import org.simantics.databoard.binding.Binding;
23 import org.simantics.databoard.binding.error.BindingConstructionException;
24 import org.simantics.databoard.binding.mutable.Variant;
25 import org.simantics.databoard.primitives.MutableBoolean;
26 import org.simantics.databoard.primitives.MutableByte;
27 import org.simantics.databoard.primitives.MutableDouble;
28 import org.simantics.databoard.primitives.MutableFloat;
29 import org.simantics.databoard.primitives.MutableInteger;
30 import org.simantics.databoard.primitives.MutableLong;
31 import org.simantics.databoard.primitives.MutableString;
32 import org.simantics.databoard.serialization.SerializationException;
33 import org.simantics.databoard.serialization.Serializer;
34 import org.simantics.databoard.type.Datatype;
35 import org.simantics.databoard.util.binary.RandomAccessBinary;
36 import org.simantics.db.DevelopmentKeys;
37 import org.simantics.db.ExternalValueSupport;
38 import org.simantics.db.Metadata;
39 import org.simantics.db.Resource;
40 import org.simantics.db.Statement;
41 import org.simantics.db.VirtualGraph;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.WriteOnlyGraph;
44 import org.simantics.db.common.request.WriteOnlyRequest;
45 import org.simantics.db.common.utils.Literals;
46 import org.simantics.db.common.utils.Logger;
47 import org.simantics.db.exception.ArgumentException;
48 import org.simantics.db.exception.DatabaseException;
49 import org.simantics.db.exception.InternalException;
50 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
51 import org.simantics.db.exception.ServiceException;
52 import org.simantics.db.impl.DatabaseUtils;
53 import org.simantics.db.impl.MemWatch;
54 import org.simantics.db.impl.ResourceImpl;
55 import org.simantics.db.impl.internal.RandomAccessValueSupport;
56 import org.simantics.db.impl.internal.ResourceData;
57 import org.simantics.db.impl.query.CacheEntry;
58 import org.simantics.db.impl.query.QueryProcessor;
59 import org.simantics.db.impl.support.WriteRequestScheduleSupport;
60 import org.simantics.db.procedure.Procedure;
61 import org.simantics.db.request.DelayedWrite;
62 import org.simantics.db.request.DelayedWriteResult;
63 import org.simantics.db.request.Write;
64 import org.simantics.db.request.WriteOnly;
65 import org.simantics.db.request.WriteOnlyResult;
66 import org.simantics.db.request.WriteResult;
67 import org.simantics.db.request.WriteTraits;
68 import org.simantics.layer0.Layer0;
69 import org.simantics.utils.Development;
70 import org.simantics.utils.datastructures.Pair;
71
72 import gnu.trove.map.hash.THashMap;
73
74
75 final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {
76
77     final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);
78     
79     final public WriteSupport writeSupport;
80     final public VirtualGraph provider;
81     
82     private String resourceName(Resource resource) throws DatabaseException {
83         if(Development.DEVELOPMENT) {
84                 Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN); 
85                 if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);
86                 else return resource.toString();
87         }
88         throw new IllegalStateException();
89     }
90     
91     private Layer0 getBuiltins() {
92         return getService(Layer0.class);
93     }
94
95     private WriteRequestScheduleSupport getWriteRequestScheduler() {
96         return (WriteRequestScheduleSupport) getSession();
97     }
98
99     private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,
100             WriteSupport writeSupport, VirtualGraph provider) {
101         super(parent2, readSupport);
102         this.writeSupport = writeSupport;
103         this.provider = provider;
104     }
105
106     public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {
107
108         WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider); 
109         
110         try {
111                         writeSupport.setDefaultClusterSet(null);
112                 } catch (ServiceException e) {
113                         Logger.defaultLogError(e);
114                 }
115         
116         return impl;
117         
118     }
119
120     final public WriteGraphImpl newAsync() {
121         return this;
122     }
123     
124     public WriteGraphImpl newSync(final VirtualGraph provider) {
125         return new WriteGraphImpl(parent, processor, writeSupport, provider);
126     }
127     final public WriteGraphImpl newSync(final CacheEntry parent) {
128         return new WriteGraphImpl(parent, processor, writeSupport, provider);
129     }
130
131     @Override
132     public ReadGraphImpl newRestart(ReadGraphImpl impl) {
133
134         WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);
135
136         return write;
137         
138     }
139
140     @Override
141     final public Resource newResource() throws ServiceException {
142         
143         try {
144                 
145                 Resource result = writeSupport.createResource(provider); 
146
147                 if(Development.DEVELOPMENT) {
148                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
149                         WriteLogger.logNewResource(this, result);
150                 }
151                 
152             return result;
153             
154         } catch (DatabaseException e) {
155             throw new ServiceException(e);
156         }
157         
158     }
159
160     @Override
161     final public Resource newResource(long clusterId) throws ServiceException {
162         
163         try {
164             
165                 Resource result = writeSupport.createResource(provider, clusterId); 
166
167                 if(Development.DEVELOPMENT) {
168                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
169                         WriteLogger.logNewResource(this, result);
170                 }
171                 
172             return result;
173             
174         } catch (DatabaseException e) {
175             throw new ServiceException(e);
176         }
177         
178     }
179
180     @Override
181     public Resource newResource(Resource clusterSet) throws ServiceException {
182         try {
183                 Resource result;
184                 if (provider != null)
185                         result = writeSupport.createResource(provider);
186                 else
187                         result = writeSupport.createResource(provider, clusterSet); 
188             if(Development.DEVELOPMENT) {
189                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
190                     WriteLogger.logNewResource(this, result);
191             }
192             return result;
193         } catch (ServiceException e) {
194             throw e;
195         } catch (DatabaseException e) {
196             throw new ServiceException(e);
197         }
198     }
199
200     @Override
201     public void newClusterSet(Resource clusterSet) throws ServiceException {
202         try {
203                 if (provider == null)
204                         writeSupport.createClusterSet(provider, clusterSet); 
205             if(Development.DEVELOPMENT) {
206                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
207                     WriteLogger.logNewResource(this, clusterSet);
208             }
209         } catch (ServiceException e) {
210             throw e;
211         } catch (DatabaseException e) {
212             throw new ServiceException(e);
213         }
214     }
215
216     @Override
217     public Resource setClusterSet4NewResource(Resource clusterSet)
218     throws ServiceException {
219         return writeSupport.setDefaultClusterSet(clusterSet);
220     }
221     /**
222      * Compares two object for equality, allowing null objects also.
223      * 
224      * @param o1 obj1
225      * @param o2 obj2
226      * @return true if both arguments are <code>null</code> or equal
227      */
228     static boolean safeEquals(Object o1, Object o2) {
229         if (o1 == o2)
230             return true;
231         if (o1 == null && o2 == null)
232             return true;
233         if (o1 == null || o2 == null)
234             return false;
235         return o1.equals(o2);
236     }
237
238     @Override
239     public void asyncRequest(DelayedWrite request) {
240         assert (request != null);
241         getWriteRequestScheduler().scheduleRequest(request, e -> {
242             if(e != null)
243                 Logger.defaultLogError(e);
244         }, null, Boolean.TRUE);
245     }
246
247     @Override
248     public void asyncRequest(DelayedWrite request, Consumer<DatabaseException> callback) {
249         assert (request != null);
250         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
251     }
252
253     @Override
254     public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {
255         assert (request != null);
256         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
257     }
258
259     @Override
260     public void asyncRequest(final Write r) {
261         assert (r != null);
262         getWriteRequestScheduler().scheduleRequest(r, e -> {
263             if(e != null)
264                 Logger.defaultLogError(e);
265         }, null, Boolean.TRUE);
266     }
267
268     @Override
269     public void asyncRequest(Write request, Consumer<DatabaseException> callback) {
270         assert (request != null);
271         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
272     }
273
274     @Override
275     public void asyncRequest(WriteOnly request) {
276         assert (request != null);
277         getWriteRequestScheduler().scheduleRequest(request, e -> {
278             if(e != null)
279                 Logger.defaultLogError(e);
280         }, null, Boolean.TRUE);
281     }
282     
283     @Override
284     public void asyncRequest(WriteOnly request, Consumer<DatabaseException> callback) {
285         assert (request != null);
286         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
287     }
288
289     @Override
290     public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {
291         assert (request != null);
292         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
293     }
294
295     @Override
296     public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {
297         assert (request != null);
298         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
299     }
300
301     @Override
302     public void syncRequest(Write request) throws DatabaseException {
303         
304         Resource defaultClusterSet = setClusterSet4NewResource(null);
305         
306         WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
307         try {
308             writeSupport.performWriteRequest(graph, request);
309         } catch (DatabaseException e) {
310             throw e;
311         } catch (Throwable t) {
312             throw new DatabaseException(t);
313         } finally {
314             setClusterSet4NewResource(defaultClusterSet);
315         }
316         
317         
318     }
319     
320     @Override
321     public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {
322
323         Resource defaultClusterSet = setClusterSet4NewResource(null);
324
325         WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
326         try {
327             return writeSupport.performWriteRequest(graph, request);
328         } catch (DatabaseException e) {
329             throw e;
330         } catch (Throwable t) {
331             throw new DatabaseException(t);
332         } finally {
333             setClusterSet4NewResource(defaultClusterSet);
334         }
335     }
336
337     @Override
338     public void syncRequest(final DelayedWrite request) throws DatabaseException {
339         
340         try {
341
342                 final DelayedWriteGraph dwg = new DelayedWriteGraph(this);
343                 request.perform(dwg);
344
345                 syncRequest(new WriteOnlyRequest() {
346
347                         @Override
348                         public void perform(WriteOnlyGraph graph) throws DatabaseException {
349                                 dwg.commit(graph, request);
350                         }
351
352                 });
353
354         } catch (DatabaseException e) {
355                 
356                 throw e;
357                 
358         } catch (Throwable e) {
359                 
360                 throw new DatabaseException(e);
361                 
362         } finally {
363                 
364         }
365         
366     }
367
368     @Override
369     public void syncRequest(WriteOnly request) throws DatabaseException {
370         
371         Resource defaultClusterSet = setClusterSet4NewResource(null);
372         
373         try {
374             writeSupport.performWriteRequest(this, request);
375         } catch (DatabaseException e) {
376             throw e;
377         } catch (Throwable t) {
378             throw new DatabaseException(t);
379         }  finally {
380             setClusterSet4NewResource(defaultClusterSet);
381         }
382         
383     }
384     
385     @Override
386     public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {
387
388         if(subject == null || predicate == null || object == null) {
389                 throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");
390         }
391
392         if(processor.isImmutable(object)) {
393                 claim(subject, predicate, null, object);                
394         } else {
395                 claim(subject, predicate, getPossibleInverse(predicate), object);               
396         }
397
398     }
399
400     @Override
401     public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
402         
403         if (MemWatch.isLowOnMemory())
404             writeSupport.gc();
405
406         if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");
407         if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");
408         if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");
409         if(provider == null && subject.isPersistent() && !object.isPersistent())
410             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.");
411
412                 try {
413                         if(Development.DEVELOPMENT) {
414                                 try {
415                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
416                                                 String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
417                                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
418                                                         StringWriter writer = new StringWriter();
419                                                         PrintWriter out = new PrintWriter(writer);
420                                                         new Exception(text).printStackTrace(out);
421                                                         Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));
422                                                 } else {
423                                                         Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));
424                                                 }
425                                         }
426                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
427                                                 WriteLogger.logClaim(this, subject, predicate, inverse, object);
428                                 } catch (DatabaseException e) {
429                                         Logger.defaultLogError(e);
430                                 }
431                         }
432                         writeSupport.claim(provider, subject, predicate, object);
433                 } catch(RuntimeException e) {
434                         throw new ServiceException(e);
435                 }
436     
437         if(inverse == null) return;
438         if(subject.equals(object) && predicate.equals(inverse)) {
439             return;
440         }
441
442         // Do as claim(s,p,o) already works -
443         // don't write inverse relations if object is immutable.
444         if(processor.isImmutable(object))
445             return;
446
447         try {
448             writeSupport.claim(provider, object, inverse, subject);
449         } catch(RuntimeException e) {
450             throw new ServiceException(e);
451         }
452         
453     }
454
455     @Override
456     public void deny(Resource subject) throws ServiceException {
457         
458         assert(subject != null);
459
460         try {
461         
462             for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {
463                 if(subject.equals(stm.getSubject())) {
464                     Resource inverse = getPossibleInverse(stm.getPredicate());
465 //                    if(inverse == null) {
466 //                        try {
467 //                            String name = adapt(stm.getPredicate(), String.class);
468 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
469 //                        } catch (AdaptionException e) {
470 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
471 //                        }
472 //                    }
473                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
474                 }
475             }
476             
477         } catch(DatabaseException e) {
478             
479             throw new ServiceException(INTERNAL_ERROR_STRING, e);
480             
481         }
482         
483     }
484
485     @Override
486     public void deny(Resource subject, Resource predicate) throws ServiceException {
487         
488         assert(subject != null);
489         assert(predicate != null);
490
491         try {
492
493             for (Statement stm : getStatements(subject, predicate)) {
494                 if (subject.equals(stm.getSubject())) {
495                     Resource inverse = getPossibleInverse(stm.getPredicate());
496 //                    if(inverse == null) {
497 //                        try {
498 //                            String name = adapt(stm.getPredicate(), String.class);
499 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
500 //                        } catch (AdaptionException e) {
501 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
502 //                        }
503 //                    }
504                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
505             }
506         }
507
508         } catch(DatabaseException e) {
509             
510             throw new ServiceException(INTERNAL_ERROR_STRING, e);
511             
512         }
513
514     }
515
516     @Override
517     public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
518
519         assert(subject != null);
520         assert(predicate != null);
521         assert(object != null);
522
523         try { 
524             for (Statement stm : getStatements(subject, predicate)) {
525                 if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {
526                     Resource inverse = getPossibleInverse(stm.getPredicate());
527                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
528                 }
529             }
530         } catch (DatabaseException e) {
531             throw new ServiceException(INTERNAL_ERROR_STRING, e);
532         }
533
534     }
535
536     @Override
537     public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
538         
539         assert(subject != null);
540         assert(predicate != null);
541         assert(object != null);
542         
543         Resource inverse = getPossibleInverse(predicate);
544 //        if(inverse == null) {
545 //            try {
546 //                String name = adapt(predicate, String.class);
547 //                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
548 //            } catch (AdaptionException e) {
549 //                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");
550 //            }
551 //        }
552         deny(subject, predicate, inverse, object);
553         
554     }
555
556     @Override
557     public void deny(Statement statement) throws ServiceException {
558         assert(statement != null);
559         denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());
560     }
561
562     @Override
563     public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
564
565         assert(subject != null);
566         assert(predicate != null);
567         assert(object != null);
568         
569         VirtualGraph provider = processor.getProvider(subject, predicate, object);
570         
571         deny(subject, predicate, inverse, object, provider);
572         
573     }
574
575     /*
576      * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.
577      */
578     @Override
579     public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {
580         
581         assert(subject != null);
582         assert(predicate != null);
583         assert(object != null);
584
585                 if(Development.DEVELOPMENT) {
586                         try {
587                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
588                                         String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
589                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
590                                                 StringWriter writer = new StringWriter();
591                                                 PrintWriter out = new PrintWriter(writer);
592                                                 new Exception(text).printStackTrace(out);
593                                                 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));
594                                         } else {
595                                                 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));
596                                         }
597                                 }
598                         } catch (DatabaseException e) {
599                                 Logger.defaultLogError(e);
600                         }
601                 }
602         
603         try {
604             writeSupport.removeStatement(provider, subject, predicate, object);
605         } catch(Throwable e) {
606             throw new InternalException("bug in deny(s,p,i,o)", e);
607         }
608     
609         if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;
610
611         // don't deny inverse relations if object is immutable.
612         if(processor.isImmutable(object))
613             return;
614         
615         try {
616             writeSupport.removeStatement(provider, object, inverse, subject);
617         } catch(Throwable e) {
618             throw new InternalException("bug in deny(s,p,i,o)", e);
619         }
620         
621     }
622     
623     @Override
624     public void claimValue(Resource resource, Object value) throws ServiceException {
625         
626         try {
627             Binding b = Bindings.getBinding(value.getClass());
628             claimValue(resource, value, b);
629         } catch(BindingConstructionException e) {
630             throw new IllegalArgumentException(e);
631         }
632         
633     }
634
635     @Override
636     public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {
637         
638         if(value == null) throw new ServiceException("claimValue does not accept null value");
639         if(binding == null) throw new ServiceException("claimValue does not accept null binding");
640         
641                 if(Development.DEVELOPMENT) {
642                 try {
643                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
644                                         String valueText = Literals.shortString(value.toString());
645                                         String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";
646                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
647                                                 StringWriter writer = new StringWriter();
648                                                 PrintWriter out = new PrintWriter(writer);
649                                                 new Exception(text).printStackTrace(out);
650                                                 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));
651                                         } else {
652                                                 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));
653                                         }
654                                 }
655                 } catch (DatabaseException e) {
656                                 Logger.defaultLogError(e);
657                 }
658                 }
659         
660         try {
661                 Serializer serializer = getSerializer(binding);
662             //Serializer serializer = binding.serializer();
663             byte[] data = serializer.serialize(value);
664
665                 if(Development.DEVELOPMENT) {
666                 try {
667                     if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
668                         WriteLogger.logValue(this, resource, data);
669                 } catch (DatabaseException e) {
670                                 Logger.defaultLogError(e);
671                 }
672                 }
673             
674             writeSupport.claimValue(provider, resource, data);
675             
676         } catch (DatabaseException e) {
677             throw new ServiceException(e);
678         } catch (SerializationException e) {
679             throw new ServiceException(e);
680         } catch (IOException e) {
681             throw new ServiceException(e);
682         } 
683         
684     }
685
686     THashMap<Class<?>, Resource> builtinValues = new THashMap<Class<?>, Resource>(32);
687
688     private void initBuiltinValues(Layer0 b) {
689
690         if(!builtinValues.isEmpty()) return;
691
692         builtinValues.put(String.class, b.String);
693         builtinValues.put(Double.class, b.Double);
694         builtinValues.put(Float.class, b.Float);
695         builtinValues.put(Long.class, b.Long);
696         builtinValues.put(Integer.class, b.Integer);
697         builtinValues.put(Byte.class, b.Byte);
698         builtinValues.put(Boolean.class, b.Boolean);
699         builtinValues.put(Variant.class, b.Variant);
700
701         builtinValues.put(String[].class, b.StringArray);
702         builtinValues.put(double[].class, b.DoubleArray);
703         builtinValues.put(float[].class, b.FloatArray);
704         builtinValues.put(long[].class, b.LongArray);
705         builtinValues.put(int[].class, b.IntegerArray);
706         builtinValues.put(byte[].class, b.ByteArray);
707         builtinValues.put(boolean[].class, b.BooleanArray);
708
709         builtinValues.put(MutableString.class, b.String);
710         builtinValues.put(MutableDouble.class, b.Double);
711         builtinValues.put(MutableFloat.class, b.Float);
712         builtinValues.put(MutableLong.class, b.Long);
713         builtinValues.put(MutableInteger.class, b.Integer);
714         builtinValues.put(MutableByte.class, b.Byte);
715         builtinValues.put(MutableBoolean.class, b.Boolean);
716
717     }
718
719     @Override
720     public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
721
722         Layer0 b = getBuiltins();
723         Resource valueResource = newResource();
724         claim(valueResource, b.InstanceOf, null, type);
725         claim(resource, predicate, inverse, valueResource);
726         claimValue(valueResource, value, binding);
727         
728     }
729
730     @Override
731     public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
732
733         Layer0 b = getBuiltins();
734         initBuiltinValues(b);
735         Resource literal = newResource();
736         
737         Class<?> clazz = value.getClass();
738         Resource type = builtinValues.get(clazz);
739         if (type == null) {
740             type = b.Literal;
741             Resource dataType = newResource();
742             claim(dataType, b.InstanceOf, null, b.DataType);
743             claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
744             claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);
745         }
746         
747         claim(literal, b.InstanceOf, null, type);
748         claim(resource, predicate, inverse, literal);
749         claimValue(literal, value, binding);
750         
751     }
752
753     @Override
754     public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)
755     throws DatabaseException {
756         Layer0 b = getBuiltins();
757         initBuiltinValues(b);       
758         Resource literal = newResource();
759         claim(literal, b.InstanceOf, null, b.Literal);
760         Resource dataType = newResource();
761         claim(dataType, b.InstanceOf, null, b.DataType);
762         claimValue(dataType, datatype, DATA_TYPE_BINDING);
763         claim(literal, b.HasDataType, dataType);
764         claim(resource, predicate, literal);
765         return createAccessor(literal, datatype, initialValue);
766     }
767     @Override
768     public RandomAccessBinary createRandomAccessBinary
769     (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)
770     throws DatabaseException {
771         Layer0 b = getBuiltins();
772         initBuiltinValues(b);       
773         Resource literal = newResource();
774         claim(literal, b.InstanceOf, null, b.Literal);
775         Resource dataType = newResource();
776         claim(dataType, b.InstanceOf, null, b.DataType);
777         claimValue(dataType, datatype, DATA_TYPE_BINDING);
778         claim(literal, b.HasDataType, dataType);
779         claim(resource, predicate, literal);
780         return createRandomAccessBinary(literal, datatype, initiaValue);
781     }
782     @Override
783     public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {
784
785         try {
786             Binding binding = Bindings.getBinding(value.getClass());
787             claimLiteral(resource, predicate, value, binding);
788         } catch(BindingConstructionException e) {
789             throw new IllegalArgumentException(e);
790         } 
791
792     }
793
794     @Override
795     public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
796         
797         Layer0 b = getBuiltins();
798         initBuiltinValues(b);       
799         
800         if(resource.toString().equals("[id=$319492]") && predicate.toString().equals("[id=$235477]"))
801                 System.err.println("foobar2");
802                 
803         
804         Statement literalStatement = getPossibleStatement(resource, predicate);
805
806         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
807
808             claimValue(literalStatement.getObject(), value, binding);
809
810         } else {
811
812             Class<?> clazz = value.getClass();
813             Resource type = builtinValues.get(clazz);
814             Resource literal = newResource();
815             if (type == null) {
816                 type = b.Literal;
817                 Resource dataType = newResource();
818                 claim(dataType, b.InstanceOf, null, b.DataType);
819                 claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
820                 claim(literal, b.HasDataType, null, dataType);
821             }
822             claim(literal, b.InstanceOf, null, type);
823             claimValue(literal, value, binding); 
824             claim(resource, predicate, literal);
825
826         }
827
828         if(Development.DEVELOPMENT) {
829                 try {
830                         getPossibleStatement(resource, predicate);
831                 } catch (ManyObjectsForFunctionalRelationException e) {
832                         System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);
833                         getPossibleStatement(resource, predicate);
834                 }
835         }
836         
837     }
838     
839     @Override
840     public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)
841             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
842         
843         try {
844             Binding binding = Bindings.getBinding(value.getClass());
845             claimLiteral(resource, predicate, type, value, binding);
846         } catch(BindingConstructionException e) {
847             throw new IllegalArgumentException(e);
848         } 
849         
850     }
851     
852     @Override
853     public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)
854             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
855         
856         Layer0 b = getBuiltins();
857         
858         Statement literalStatement = getPossibleStatement(resource, predicate);
859
860         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
861
862             claimValue(literalStatement.getObject(), value, binding);
863
864         } else {
865
866             Resource literal = newResource();
867             claim(literal, b.InstanceOf, null, type);
868             claimValue(literal, value, binding); 
869             claim(resource, predicate, literal);
870
871         }
872         
873     }
874     
875     @Override
876     public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)
877             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
878
879         try {
880             Binding binding = Bindings.getBinding(value.getClass());
881             claimLiteral(resource, predicate, inverse, type, value, binding);
882         } catch(BindingConstructionException e) {
883             throw new IllegalArgumentException(e);
884         } 
885         
886     }
887     
888     @Override
889     public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)
890             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
891         
892         Layer0 b = getBuiltins();
893         
894         Statement literalStatement = getPossibleStatement(resource, predicate);
895
896         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
897
898             claimValue(literalStatement.getObject(), value, binding);
899
900         } else {
901
902             Resource literal = newResource();
903             claim(literal, b.InstanceOf, null, type);
904             claimValue(literal, value, binding); 
905             claim(resource, predicate, inverse, literal);
906
907         }
908         
909     }
910
911
912     @Override
913     public void denyValue(Resource resource) throws ServiceException {
914
915         denyValue0(resource, null);
916
917     }
918
919     @Override
920     public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {
921
922         denyValue0(resource, valueProvider);
923
924     }
925     
926     /**
927      * @param resource resource to remove value from
928      * @param valueProvider <code>null</code> means the caller doesn't know and
929      *        this method must find out
930      * @throws ServiceException
931      */
932     private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {
933
934         if(Development.DEVELOPMENT) {
935                 try {
936                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
937                                         Object oldValue = getPossibleValue(resource);
938                                         String valueText = oldValue == null ? "<no value>" : oldValue.toString();
939                                         String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";
940                                         if (oldValue != null) {
941                                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
942                                                         StringWriter writer = new StringWriter();
943                                                         PrintWriter out = new PrintWriter(writer);
944                                                         new Exception(text).printStackTrace(out);
945                                                         Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));
946                                                 } else {
947                                                         Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));
948                                                 }
949                                         }
950                                 }
951                 } catch (DatabaseException e) {
952                                 Logger.defaultLogError(e);
953                 }
954                 }
955         
956         if (provider != null) {
957
958             // Can only remove from the specified VirtualGraph
959             if (resource.isPersistent())
960                 throw new ArgumentException("Tried to remove literal value from persistent resource " + resource
961                         + " using virtual graph '" + provider.getIdentifier() + "'");
962             writeSupport.denyValue(provider, resource);
963
964         } else {
965
966             // Can remove from any provider
967             if (resource.isPersistent()) {
968                 // A persistent resource cannot have a virtual literal
969                 // so this should be safe.
970                 writeSupport.denyValue(provider, resource);
971             } else {
972                 // A virtual resource cannot have a persistent literal.
973                 // Must look for a virtual graph for resource.
974                 Layer0 L0 = getBuiltins();
975                 if (valueProvider == null) {
976                     if (hasStatement(resource, L0.InstanceOf)) {
977                         valueProvider = processor.getProvider(resource, L0.InstanceOf);
978                     } else if (hasStatement(resource, L0.Inherits)) {
979                         valueProvider = processor.getProvider(resource, L0.Inherits);
980                     } else if (hasStatement(resource, L0.SubrelationOf)) {
981                         valueProvider = processor.getProvider(resource, L0.SubrelationOf);
982                     }
983                 }
984                 if (valueProvider != null)
985                     writeSupport.denyValue(valueProvider, resource);
986             }
987
988         }
989
990     }
991
992     @Override
993     public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {
994
995         Statement valueStatement = getPossibleStatement(resource, predicate);
996
997         if (valueStatement != null && !valueStatement.isAsserted(resource)) {
998
999             Resource value = valueStatement.getObject();
1000             Resource inverse = getPossibleInverse(predicate);
1001
1002             if (provider != null) {
1003
1004                 // Can only remove from the specified provider
1005                 deny(resource, predicate, inverse, value, provider);
1006                 writeSupport.denyValue(provider, value);
1007
1008             } else {
1009
1010                 // Can remove from any provider
1011                 VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);
1012                 deny(resource, predicate, inverse, value, statementProvider);
1013                 denyValue0(resource, statementProvider);
1014
1015             }
1016
1017         }
1018
1019     }
1020
1021     @Override
1022     public void flushCluster() {
1023         
1024         writeSupport.flushCluster();
1025         
1026     }
1027
1028     @Override
1029     public void flushCluster(Resource r) {
1030         writeSupport.flushCluster(r);
1031     }
1032     
1033     Object serialize(Object value) {
1034         
1035         Class<?> clazz = value.getClass();
1036         
1037         if(clazz.isArray()) return value;
1038         
1039         if(Double.class == clazz) return new double[] { (Double)value };
1040         else if(String.class == clazz) return new String[] { (String)value };
1041         else if(Integer.class == clazz) return new int[] { (Integer)value };
1042         else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };
1043         else if(Long.class == clazz) return new long[] { (Long)value };
1044         else if(Byte.class == clazz) return new byte[] { (Byte)value };
1045         else if(Float.class == clazz) return new float[] { (Float)value };
1046         else return value;
1047         
1048     }
1049     
1050     @Override
1051     public <T> void addMetadata(Metadata data) throws ServiceException {
1052         writeSupport.addMetadata(data);
1053     }
1054
1055     @Override
1056     public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {
1057         return writeSupport.getMetadata(clazz);
1058     }
1059
1060     @Override
1061     public TreeMap<String, byte[]> getMetadata() {
1062         return writeSupport.getMetadata();
1063     }
1064
1065     @Override
1066     public String toString() {
1067         return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";
1068     }     
1069
1070     public void commitAccessorChanges(WriteTraits writeTraits) {
1071         try {
1072             RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);
1073             if (ravs == null)
1074                 return;
1075
1076             for(Pair<Resource, ResourceData> entry : ravs.entries()) {
1077                 ResourceData rd = entry.second;
1078                 if(!rd.isChanged()) continue;
1079                 Resource resource = entry.first;
1080                 try {
1081                     ExternalValueSupport evs = getService(ExternalValueSupport.class);
1082                     long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;
1083                     long bsize = rd.binaryFile.length();
1084                     final int N = 1<<20;
1085                     final int LIMIT = 10 * 1000 * 1000;
1086                     long left = bsize;
1087                     long offset = 0;
1088                     byte[] bytes = new byte[N];
1089                     rd.binaryFile.reset();
1090                     rd.binaryFile.position(0);
1091                     int count = 0;
1092 //                    if (LIMIT < left)
1093 //                        evs.moveValue(this, resource);
1094                     while (left > 0) {
1095                         int length = N < left ? N : (int)left;
1096                         rd.binaryFile.readFully(bytes, 0, length);
1097                         evs.writeValue(this, resource, offset, length, bytes);
1098                         offset += length;
1099                         left -= length;
1100                         count += length;
1101                         if (count > LIMIT) {
1102                             count = 0;
1103 //                            evs.commitAndContinue(this, writeTraits, resource);
1104 //                            evs.moveValue(this, resource);
1105                             // This is needed so we don't create too many requests and run out of heap.
1106                             evs.wait4RequestsLess(1);
1107                         }
1108                     }
1109                     if (bsize < vsize) // truncate
1110                         evs.writeValue(this, resource, bsize, 0, new byte[0]);
1111                 } catch (DatabaseException e) {
1112                     Logger.defaultLogError(e);
1113                 }
1114             }
1115         } catch(IOException e) {
1116             Logger.defaultLogError(e);
1117         } catch (RuntimeException e) {
1118           Logger.defaultLogError(e);
1119         }
1120     }
1121
1122     @Override
1123     public VirtualGraph getProvider() {
1124         return provider;
1125     }
1126
1127     @Override
1128     public void clearUndoList(WriteTraits writeTraits) {
1129         writeSupport.clearUndoList(writeTraits);
1130     }
1131     
1132     public void markUndoPoint() {
1133         writeSupport.startUndo();
1134     }
1135     
1136 }