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