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