]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java
Multiple readers and variable optimization
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / QueryProcessor.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.query;
13
14 import java.io.BufferedOutputStream;
15 import java.io.File;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.PrintStream;
19 import java.lang.ref.WeakReference;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.IdentityHashMap;
27 import java.util.Iterator;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.concurrent.Semaphore;
33 import java.util.concurrent.atomic.AtomicBoolean;
34 import java.util.concurrent.atomic.AtomicInteger;
35 import java.util.concurrent.locks.Condition;
36 import java.util.concurrent.locks.ReentrantLock;
37
38 import org.simantics.databoard.Bindings;
39 import org.simantics.db.AsyncReadGraph;
40 import org.simantics.db.DevelopmentKeys;
41 import org.simantics.db.DirectStatements;
42 import org.simantics.db.ReadGraph;
43 import org.simantics.db.RelationInfo;
44 import org.simantics.db.Resource;
45 import org.simantics.db.Session;
46 import org.simantics.db.Statement;
47 import org.simantics.db.VirtualGraph;
48 import org.simantics.db.common.procedure.adapter.AsyncMultiProcedureAdapter;
49 import org.simantics.db.common.utils.Logger;
50 import org.simantics.db.debug.ListenerReport;
51 import org.simantics.db.exception.DatabaseException;
52 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
53 import org.simantics.db.exception.NoInverseException;
54 import org.simantics.db.exception.ResourceNotFoundException;
55 import org.simantics.db.impl.DebugPolicy;
56 import org.simantics.db.impl.ResourceImpl;
57 import org.simantics.db.impl.graph.MultiIntProcedure;
58 import org.simantics.db.impl.graph.ReadGraphImpl;
59 import org.simantics.db.impl.graph.ReadGraphSupport;
60 import org.simantics.db.impl.graph.WriteGraphImpl;
61 import org.simantics.db.impl.procedure.IntProcedureAdapter;
62 import org.simantics.db.impl.procedure.InternalProcedure;
63 import org.simantics.db.impl.procedure.TripleIntProcedureAdapter;
64 import org.simantics.db.impl.support.ResourceSupport;
65 import org.simantics.db.procedure.AsyncMultiListener;
66 import org.simantics.db.procedure.AsyncMultiProcedure;
67 import org.simantics.db.procedure.AsyncProcedure;
68 import org.simantics.db.procedure.AsyncSetListener;
69 import org.simantics.db.procedure.ListenerBase;
70 import org.simantics.db.procedure.MultiProcedure;
71 import org.simantics.db.procedure.Procedure;
72 import org.simantics.db.procedure.StatementProcedure;
73 import org.simantics.db.request.AsyncMultiRead;
74 import org.simantics.db.request.AsyncRead;
75 import org.simantics.db.request.ExternalRead;
76 import org.simantics.db.request.MultiRead;
77 import org.simantics.db.request.Read;
78 import org.simantics.db.request.RequestFlags;
79 import org.simantics.db.request.WriteTraits;
80 import org.simantics.layer0.Layer0;
81 import org.simantics.utils.DataContainer;
82 import org.simantics.utils.Development;
83 import org.simantics.utils.datastructures.Pair;
84 import org.simantics.utils.datastructures.collections.CollectionUtils;
85 import org.simantics.utils.datastructures.disposable.AbstractDisposable;
86
87 import gnu.trove.procedure.TIntProcedure;
88 import gnu.trove.procedure.TLongProcedure;
89 import gnu.trove.procedure.TObjectProcedure;
90 import gnu.trove.set.hash.THashSet;
91 import gnu.trove.set.hash.TIntHashSet;
92
93 @SuppressWarnings({"rawtypes", "unchecked"})
94 final public class QueryProcessor extends AbstractDisposable implements ReadGraphSupport {
95
96         public static int                                       indent                = 0;
97
98         
99         // Garbage collection
100         
101         public int                                              boundQueries          = 0;
102
103
104         final private int                                       functionalRelation;
105
106         final private int                                       superrelationOf;
107
108         final private int                                       instanceOf;
109
110         final private int                                       inverseOf;
111
112         final private int                                       asserts;
113
114         final private int                                       hasPredicate;
115
116         final private int                                       hasPredicateInverse;
117
118         final private int                                       hasObject;
119
120         final private int                                       inherits;
121
122         final private int                                       subrelationOf;
123
124         final private int                                       rootLibrary;
125
126         /**
127          * A cache for the root library resource. Initialized in
128          * {@link #getRootLibraryResource()}.
129          */
130         private volatile ResourceImpl                           rootLibraryResource;
131
132         final private int                                       library;
133
134         final private int                                       consistsOf;
135
136         final private int                                       hasName;
137
138         AtomicInteger                                       sleepers = new AtomicInteger(0);
139
140         private boolean                                         updating              = false;
141
142
143         private boolean                                         firingListeners       = false;
144
145         final public QueryCache                                 cache;
146         final public QuerySupport                               querySupport;
147         final public Session                                    session;
148         final public ResourceSupport                            resourceSupport;
149
150         private THashSet<ListenerEntry>                         scheduledListeners    = new THashSet<ListenerEntry>();
151
152         QueryThread[]                                   executors;
153
154         public ArrayList<SessionTask>[]                           queues;
155
156         enum ThreadState {
157
158                 INIT, RUN, SLEEP, DISPOSED
159
160         }
161
162         public ThreadState[]                                                                    threadStates;
163         public ReentrantLock[]                                                                  threadLocks;
164         public Condition[]                                                                          threadConditions;
165
166         public ArrayList<SessionTask>[]                           ownTasks;
167
168         public ArrayList<SessionTask>[]                           ownSyncTasks;
169
170         ArrayList<SessionTask>[]                           delayQueues;
171         
172         public boolean synch = true;
173
174         final Object querySupportLock;
175         
176         public Long modificationCounter = 0L;
177
178         public void close() {
179         }
180
181         final public void scheduleOwn(int caller, SessionTask request) {
182                 ownTasks[caller].add(request);
183         }
184
185         final public void scheduleAlways(int caller, SessionTask request) {
186
187                 int performer = request.thread;
188                 if(caller == performer) {
189                         ownTasks[caller].add(request);
190                 } else {
191                         schedule(caller, request);
192                 }
193
194         }
195
196         final public void schedule(int caller, SessionTask request) {
197
198                 int performer = request.thread;
199
200                 if(DebugPolicy.SCHEDULE)
201                         System.out.println("schedule " + request + " " + caller + " -> " + performer);
202
203                 assert(performer >= 0);
204
205                 assert(request != null);
206
207                 if(caller == performer) {
208                         request.run(caller);
209                 } else {
210                         ReentrantLock queueLock = threadLocks[performer];
211                         queueLock.lock();
212                         queues[performer].add(request);
213                         // This thread could have been sleeping
214                         if(queues[performer].size() == 1) {
215                                 if(ThreadState.SLEEP == threadStates[performer]) sleepers.decrementAndGet();
216                                 threadConditions[performer].signalAll();
217                         }
218                         queueLock.unlock();
219                 }
220
221         }
222
223
224         final int THREADS;
225         final public int  THREAD_MASK;
226         
227         final public static ThreadGroup QueryThreadGroup = new ThreadGroup("Query Thread Group"); 
228
229         public static abstract class SessionTask {
230
231                 final public int thread;
232                 final public int syncCaller;
233                 final public Object object;
234
235                 public SessionTask(WriteTraits object, int thread) {
236                         this.thread = thread;
237                         this.syncCaller = -1;
238                         this.object = object;
239                 }
240
241                 public SessionTask(Object object, int thread, int syncCaller) {
242                         this.thread = thread;
243                         this.syncCaller = syncCaller;
244                         this.object = object;
245                 }
246
247                 public abstract void run(int thread);
248
249                 @Override
250                 public String toString() {
251                         return "SessionTask[" + object + "]";
252                 }
253
254         }
255
256         public static abstract class SessionRead extends SessionTask {
257
258                 final public Semaphore notify;
259                 final public DataContainer<Throwable> throwable; 
260
261                 public SessionRead(Object object, DataContainer<Throwable> throwable, Semaphore notify, int thread) {
262                         super(object, thread, thread);
263                         this.throwable = throwable;
264                         this.notify = notify;
265                 }
266
267                 public SessionRead(Object object, DataContainer<Throwable> throwable, Semaphore notify, int thread, int syncThread) {
268                         super(object, thread, syncThread);
269                         this.throwable = throwable;
270                         this.notify = notify;
271                 }
272
273         }
274
275         long waitingTime = 0;
276
277         static int koss = 0;
278         static int koss2 = 0;
279
280         public boolean resume(ReadGraphImpl graph) {
281                 return executors[0].runSynchronized();
282         }
283         
284         //private WeakReference<GarbageTracker> garbageTracker;
285         
286         private class GarbageTracker    {
287                 
288                 @Override
289                 protected void finalize() throws Throwable {
290                         
291 //                      System.err.println("GarbageTracker");
292 //                      
293 //                      garbageTracker = new WeakReference<GarbageTracker>(new GarbageTracker());
294                         
295                         super.finalize();
296                         
297                 }
298                 
299         }
300
301         public QueryProcessor(final int threads, QuerySupport core, Set<Thread> threadSet)
302                         throws DatabaseException {
303
304                 //garbageTracker = new WeakReference<GarbageTracker>(new GarbageTracker());
305                 
306                 THREADS = threads;
307                 THREAD_MASK = threads - 1;
308
309                 querySupport = core;
310                 cache = new QueryCache(core, threads);
311                 session = querySupport.getSession();
312                 resourceSupport = querySupport.getSupport();
313                 querySupportLock = core.getLock();
314
315                 executors = new QueryThread[THREADS];
316                 queues = new ArrayList[THREADS];
317                 threadLocks = new ReentrantLock[THREADS];
318                 threadConditions = new Condition[THREADS];
319                 threadStates = new ThreadState[THREADS];
320                 ownTasks = new ArrayList[THREADS];
321                 ownSyncTasks = new ArrayList[THREADS];
322                 delayQueues = new ArrayList[THREADS * THREADS];
323
324                 //        freeSchedule = new AtomicInteger(0);
325
326                 for (int i = 0; i < THREADS * THREADS; i++) {
327                         delayQueues[i] = new ArrayList<SessionTask>();
328                 }
329
330                 for (int i = 0; i < THREADS; i++) {
331
332                         //            tasks[i] = new ArrayList<Runnable>();
333                         ownTasks[i] = new ArrayList<SessionTask>();
334                         ownSyncTasks[i] = new ArrayList<SessionTask>();
335                         queues[i] = new ArrayList<SessionTask>();
336                         threadLocks[i] = new ReentrantLock();
337                         threadConditions[i] = threadLocks[i].newCondition();
338                         //            limits[i] = false;
339                         threadStates[i] = ThreadState.INIT;
340
341                 }
342
343                 for (int i = 0; i < THREADS; i++) {
344
345                         final int index = i;
346
347                         executors[i] = new QueryThread(session, this, index, "Query Thread " + index);
348
349                         threadSet.add(executors[i]);
350
351                 }
352
353                 // Now start threads
354                 for (int i = 0; i < THREADS; i++) {
355                         executors[i].start();
356                 }
357
358                 // Make sure that query threads are up and running
359                 while(sleepers.get() != THREADS) {
360                         try {
361                                 Thread.sleep(5);
362                         } catch (InterruptedException e) {
363                                 e.printStackTrace();
364                         }
365                 }
366
367                 rootLibrary = core.getBuiltin("http:/");
368                 boolean builtinsInstalled = rootLibrary != 0;
369
370                 if (builtinsInstalled) {
371                         functionalRelation = core.getBuiltin(Layer0.URIs.FunctionalRelation);
372                         assert (functionalRelation != 0);
373                 } else
374                         functionalRelation = 0;
375
376                 if (builtinsInstalled) {
377                         instanceOf = core.getBuiltin(Layer0.URIs.InstanceOf);
378                         assert (instanceOf != 0);
379                 } else
380                         instanceOf = 0;
381
382                 if (builtinsInstalled) {
383                         inverseOf = core.getBuiltin(Layer0.URIs.InverseOf);
384                         assert (inverseOf != 0);
385                 } else
386                         inverseOf = 0;
387
388
389                 if (builtinsInstalled) {
390                         inherits = core.getBuiltin(Layer0.URIs.Inherits);
391                         assert (inherits != 0);
392                 } else
393                         inherits = 0;
394
395                 if (builtinsInstalled) {
396                         asserts = core.getBuiltin(Layer0.URIs.Asserts);
397                         assert (asserts != 0);
398                 } else
399                         asserts = 0;
400
401                 if (builtinsInstalled) {
402                         hasPredicate = core.getBuiltin(Layer0.URIs.HasPredicate);
403                         assert (hasPredicate != 0);
404                 } else
405                         hasPredicate = 0;
406
407                 if (builtinsInstalled) {
408                         hasPredicateInverse = core.getBuiltin(Layer0.URIs.HasPredicateInverse);
409                         assert (hasPredicateInverse != 0);
410                 } else
411                         hasPredicateInverse = 0;
412
413                 if (builtinsInstalled) {
414                         hasObject = core.getBuiltin(Layer0.URIs.HasObject);
415                         assert (hasObject != 0);
416                 } else
417                         hasObject = 0;
418
419                 if (builtinsInstalled) {
420                         subrelationOf = core.getBuiltin(Layer0.URIs.SubrelationOf);
421                         assert (subrelationOf != 0);
422                 } else
423                         subrelationOf = 0;
424
425                 if (builtinsInstalled) {
426                         superrelationOf = core.getBuiltin(Layer0.URIs.SuperrelationOf);
427                         assert (superrelationOf != 0);
428                 } else
429                         superrelationOf = 0;
430
431                 if (builtinsInstalled) {
432                         library = core.getBuiltin(Layer0.URIs.Library);
433                         assert (library != 0);
434                 } else
435                         library = 0;
436
437                 if (builtinsInstalled) {
438                         consistsOf = core.getBuiltin(Layer0.URIs.ConsistsOf);
439                         assert (consistsOf != 0);
440                 } else
441                         consistsOf = 0;
442
443                 if (builtinsInstalled) {
444                         hasName = core.getBuiltin(Layer0.URIs.HasName);
445                         assert (hasName != 0);
446                 } else
447                         hasName = 0;
448
449         }
450
451         final public void releaseWrite(ReadGraphImpl graph) {
452                 performDirtyUpdates(graph);
453                 modificationCounter++;
454         }
455
456         final public int getId(final Resource r) {
457                 return querySupport.getId(r);
458         }
459
460         public QuerySupport getCore() {
461                 return querySupport;
462         }
463
464         public int getFunctionalRelation() {
465                 return functionalRelation;
466         }
467
468         public int getInherits() {
469                 return inherits;
470         }
471
472         public int getInstanceOf() {
473                 return instanceOf;
474         }
475
476         public int getInverseOf() {
477                 return inverseOf;
478         }
479
480         public int getSubrelationOf() {
481                 return subrelationOf;
482         }
483
484         public int getSuperrelationOf() {
485                 return superrelationOf;
486         }
487
488         public int getAsserts() {
489                 return asserts;
490         }
491
492         public int getHasPredicate() {
493                 return hasPredicate;
494         }
495
496         public int getHasPredicateInverse() {
497                 return hasPredicateInverse;
498         }
499
500         public int getHasObject() {
501                 return hasObject;
502         }
503
504         public int getRootLibrary() {
505                 return rootLibrary;
506         }
507
508         public Resource getRootLibraryResource() {
509                 if (rootLibraryResource == null) {
510                         // Synchronization is not needed here, it doesn't matter if multiple
511                         // threads simultaneously set rootLibraryResource once.
512                         int root = getRootLibrary();
513                         if (root == 0)
514                                 throw new UnsupportedOperationException("database is not initialized, cannot get root library resource");
515                         this.rootLibraryResource = new ResourceImpl(querySupport.getSupport(), root);
516                 }
517                 return rootLibraryResource;
518         }
519
520         public int getLibrary() {
521                 return library;
522         }
523
524         public int getConsistsOf() {
525                 return consistsOf;
526         }
527
528         public int getHasName() {
529                 return hasName;
530         }
531
532         public void forResource(ReadGraphImpl graph, final String id, CacheEntry parent, final InternalProcedure<Integer> procedure) {
533
534                 try {
535                         
536                         QueryCache.runnerURIToResource(graph, id, parent, null, new InternalProcedure<Integer>() {
537
538                                 @Override
539                                 public void execute(ReadGraphImpl graph, Integer result) throws DatabaseException {
540
541                                         if (result != null && result != 0) {
542                                                 procedure.execute(graph, result);
543                                                 return;
544                                         }
545
546                                         // Fall back to using the fixed builtins.
547 //                                      result = querySupport.getBuiltin(id);
548 //                                      if (result != 0) {
549 //                                              procedure.execute(graph, result);
550 //                                              return;
551 //                                      } 
552
553 //                                      try {
554 //                                              result = querySupport.getRandomAccessReference(id);
555 //                                      } catch (ResourceNotFoundException e) {
556 //                                              procedure.exception(graph, e);
557 //                                              return;
558 //                                      }
559
560                                         if (result != 0) {
561                                                 procedure.execute(graph, result);
562                                         } else {
563                                                 procedure.exception(graph, new ResourceNotFoundException(id));
564                                         }
565
566                                 }
567
568                                 @Override
569                                 public void exception(ReadGraphImpl graph, Throwable t) throws DatabaseException {
570                                         procedure.exception(graph, t);
571                                 }
572
573                         });
574                 } catch (DatabaseException e) {
575                         Logger.defaultLogError(e);
576                 }
577
578         }
579
580         public void forBuiltin(ReadGraphImpl graph, final String id, CacheEntry parent, final InternalProcedure<Integer> procedure) throws DatabaseException {
581
582                 Integer result = querySupport.getBuiltin(id);
583                 if (result != 0) {
584                         procedure.execute(graph, result);
585                 } else {
586                         procedure.exception(graph, new ResourceNotFoundException(id));
587                 }
588
589         }
590
591         final <T> void runMultiRead(final ReadGraphImpl graph, MultiReadEntry cached, final MultiRead<T> query, final CacheEntry parent, final QueryProcessor provider, final ListenerBase listener, final AsyncMultiProcedure<T> procedure) {
592
593                 try {
594                         QueryCache.runnerMultiReadEntry(graph, query, parent, listener, procedure);
595                 } catch (DatabaseException e) {
596                         throw new IllegalStateException(e);
597                 }
598
599         }
600
601         public final <T> void runAsyncMultiRead(final ReadGraphImpl graph, final AsyncMultiRead<T> query, final CacheEntry parent, final ListenerBase listener, final AsyncMultiProcedure<T> procedure) {
602
603                 
604                 try {
605                         QueryCache.runnerAsyncMultiReadEntry(graph, query, parent, listener, procedure);
606                 } catch (DatabaseException e) {
607                         throw new IllegalStateException(e);
608                 }
609
610         }
611
612         final <T> void runPrimitiveRead(ReadGraphImpl graph, ExternalReadEntry cached, final ExternalRead<T> query, final CacheEntry parent, final QueryProcessor provider, final ListenerBase listener, final AsyncProcedure<T> procedure) throws DatabaseException {
613                 QueryCache.runnerExternalReadEntry(graph, query, parent, listener, procedure);
614         }
615
616 //    @Override
617 //      public <T> T query(final ReadGraphImpl graph, final Read<T> query, final CacheEntry parent, final AsyncProcedure<T> procedure, final ListenerBase listener) throws DatabaseException {
618 //      
619 //      return QueryCache.resultReadEntry(graph, query, parent, listener, procedure);
620 //
621 //      }
622
623         public <T> void queryMultiRead(final ReadGraphImpl graph, final MultiRead<T> query, final CacheEntry parent, final ListenerBase listener, final AsyncMultiProcedure<T> procedure) throws DatabaseException {
624
625                 QueryCache.runnerMultiReadEntry(graph, query, parent, listener, procedure);
626
627         }
628
629         public <T> void queryPrimitiveRead(final ReadGraphImpl graph, final ExternalRead<T> query, final CacheEntry parent, final ListenerBase listener, final AsyncProcedure<T> procedure) throws DatabaseException {
630
631                 QueryCache.runnerExternalReadEntry(graph, query, parent, listener, procedure);
632
633         }
634
635         boolean isBound(ExternalReadEntry<?> entry) {
636                 if(entry.hasParents()) return true;
637                 else if(hasListener(entry)) return true;
638                 else return false;
639         }
640
641         synchronized public ListenerEntry registerDependencies(ReadGraphImpl graph, CacheEntry child, CacheEntry parent, ListenerBase listener, Object procedure, boolean inferred) {
642
643                 if (parent != null && !inferred) {
644                         try {
645                                 if(!child.isImmutable(graph))
646                                         child.addParent(parent);
647                         } catch (DatabaseException e) {
648                                 Logger.defaultLogError(e);
649                         }
650                         if(DebugPolicy.DEPENDENCIES) System.out.println(child + " -> " + parent);
651                 }
652
653                 if (listener != null) {
654                         return registerListener(child, listener, procedure);
655                 } else {
656                         return null;
657                 }
658
659         }
660
661         
662         static class Dummy implements InternalProcedure<Object>, IntProcedure {
663
664                 @Override
665                 public void execute(ReadGraphImpl graph, int i) {
666                 }
667
668                 @Override
669                 public void finished(ReadGraphImpl graph) {
670                 }
671
672                 @Override
673                 public void execute(ReadGraphImpl graph, Object result) {
674                 }
675
676                 @Override
677                 public void exception(ReadGraphImpl graph, Throwable throwable) {
678                 }
679                 
680         }
681         
682         private static final Dummy dummy = new Dummy();
683
684         /*
685     public <Procedure> Object performForEach2(ReadGraphImpl graph, UnaryQuery<Procedure> query, CacheEntry parent, ListenerBase listener, Procedure procedure) throws Throwable {
686
687         if (DebugPolicy.PERFORM)
688             System.out.println("PE[ " + (query.hashCode() &  THREAD_MASK) + "] " + query);
689
690         assert (!dirty);
691         assert (!collecting);
692
693         assert(query.assertNotDiscarded());
694
695         registerDependencies(graph, query, parent, listener, procedure, false);
696
697         // FRESH, REFUTED, EXCEPTED go here 
698         if (!query.isReady()) {
699
700             size++;
701             misses++;
702
703             query.computeForEach(graph, this, (Procedure)dummy, true);
704             return query.get(graph, this, null);
705
706         } else {
707
708             hits++;
709
710             return query.get(graph, this, procedure);
711
712         }
713
714     }
715         */
716         
717
718         interface QueryCollectorSupport {
719                 public CacheCollectionResult allCaches();
720                 public Collection<CacheEntry> getRootList();
721                 public int getCurrentSize();
722                 public int calculateCurrentSize();
723                 public CacheEntryBase iterate(int level);
724                 public void remove();
725                 public void setLevel(CacheEntryBase entry, int level);
726                 public boolean start(boolean flush);
727         }
728
729         interface QueryCollector {
730
731                 public void collect(int youngTarget, int allowedTimeInMs);
732
733         }
734
735         class QueryCollectorSupportImpl implements QueryCollectorSupport {
736
737                 private static final boolean DEBUG = false;
738                 private static final double ITERATION_RATIO = 0.2;
739                 
740                 private CacheCollectionResult iteration = new CacheCollectionResult();
741                 private boolean fresh = true;
742                 private boolean needDataInStart = true;
743                 
744                 QueryCollectorSupportImpl() {
745                         iteration.restart();
746                 }
747
748                 public CacheCollectionResult allCaches() {
749                         CacheCollectionResult result = new CacheCollectionResult();
750                         QueryProcessor.this.allCaches(result);
751                         result.restart();
752                         return result;
753                 }
754                 
755                 public boolean start(boolean flush) {
756                         // We need new data from query maps
757                         fresh = true;
758                         if(needDataInStart || flush) {
759                                 // Last run ended after processing all queries => refresh data
760                                 restart(flush ? 0.0 : ITERATION_RATIO);
761                         } else {
762                                 // continue with previous big data
763                         }
764                         // Notify caller about iteration situation
765                         return iteration.isAtStart();
766                 }
767
768                 private void restart(double targetRatio) {
769                         
770                         needDataInStart = true;
771
772                         long start = System.nanoTime();
773                         if(fresh) {
774                                 
775                                 // We need new data from query maps
776                                 
777                                 int iterationSize = iteration.size()+1;
778                                 int diff = calculateCurrentSize()-iterationSize;
779                                 
780                                 double ratio = (double)diff / (double)iterationSize;
781                                 boolean dirty = Math.abs(ratio) >= targetRatio;
782                                 
783                                 if(dirty) {
784                                         iteration = allCaches();
785                                         if(DEBUG) {
786                                                 System.err.print("iterate: allCaches in " + 1e-9*(System.nanoTime()-start) + "s. (" + iteration.size() + ") ");
787                                                 for(int i=0;i<CacheCollectionResult.LEVELS;i++)
788                                                         System.err.print(" " + iteration.levels[i].size());
789                                                 System.err.println("");
790                                         }
791                                 } else {
792                                         iteration.restart();
793                                 }
794                                 
795                                 fresh = false;
796                                 needDataInStart = false;
797                         } else {
798                                 // We are returning here within the same GC round - reuse the cache table
799                                 iteration.restart();
800                         }
801                         
802                         return;
803                         
804                 }
805                 
806                 @Override
807                 public CacheEntryBase iterate(int level) {
808                         
809                         CacheEntryBase entry = iteration.next(level);
810                         if(entry == null) {
811                                 restart(ITERATION_RATIO);
812                                 return null;
813                         }
814                         
815                         while(entry != null && entry.isDiscarded()) {
816                                 entry = iteration.next(level);
817                         }
818                         
819                         return entry;
820                         
821                 }
822                 
823                 @Override
824                 public void remove() {
825                         iteration.remove();
826                 }
827                 
828                 @Override
829                 public void setLevel(CacheEntryBase entry, int level) {
830                         iteration.setLevel(entry, level);
831                 }
832
833                 public Collection<CacheEntry> getRootList() {
834                         return cache.getRootList();
835                 }
836
837                 @Override
838                 public int calculateCurrentSize() {
839                         return cache.calculateCurrentSize();
840                 }
841
842                 @Override
843                 public int getCurrentSize() {
844                         return cache.size;
845                 }
846
847         }
848         //    final private static int MINIMUM_SIZE = (int)(Runtime.getRuntime().maxMemory() / 600);
849
850         private QueryCollectorSupport collectorSupport = new QueryCollectorSupportImpl();
851         private QueryCollector collector = new QueryCollectorImpl(this, collectorSupport);
852
853     public int querySize() {
854         return cache.size;
855     }
856
857         public void gc(int youngTarget, int allowedTimeInMs) {
858
859                 collector.collect(youngTarget, allowedTimeInMs);
860
861         }
862
863         public ListenerEntry registerListener(final CacheEntry entry, final ListenerBase base, final Object procedure) {
864
865                 assert (entry != null);
866
867                 if (base.isDisposed())
868                         return null;
869
870                 return addListener(entry, base, procedure);
871
872         }
873
874         private void primeListenerEntry(final ListenerEntry entry, final Object result) {
875                 entry.setLastKnown(result);
876         }
877
878         private ListenerEntry addListener(CacheEntry entry, ListenerBase base, Object procedure) {
879
880                 assert (entry != null);
881                 assert (procedure != null);
882
883                 ArrayList<ListenerEntry> list = cache.listeners.get(entry);
884                 if (list == null) {
885                         list = new ArrayList<ListenerEntry>(1);
886                         cache.listeners.put(entry, list);
887                 }
888
889                 ListenerEntry result = new ListenerEntry(entry, base, procedure);
890                 int currentIndex = list.indexOf(result);
891                 // There was already a listener
892                 if(currentIndex > -1) {
893                         ListenerEntry current = list.get(currentIndex);
894                         if(!current.base.isDisposed()) return null;
895                         list.set(currentIndex, result);
896                 } else {
897                         list.add(result);
898                 }
899
900                 if(DebugPolicy.LISTENER) {
901                         new Exception().printStackTrace();
902                         System.out.println("addListener -> " + list.size() + " " + entry + " " + base + " " + procedure);
903                 }
904
905                 return result;
906
907         }
908
909         private void scheduleListener(ListenerEntry entry) {
910                 assert (entry != null);
911                 if(DebugPolicy.LISTENER) System.out.println("Scheduled " + entry.procedure);
912                 scheduledListeners.add(entry);
913         }
914
915         private void removeListener(ListenerEntry entry) {
916                 assert (entry != null);
917                 ArrayList<ListenerEntry> list = cache.listeners.get(entry.entry);
918                 if(list == null) return;
919                 boolean success = list.remove(entry);
920                 assert (success);
921                 if (list.isEmpty())
922                         cache.listeners.remove(entry.entry);
923         }
924
925         private boolean hasListener(CacheEntry entry) {
926                 if(cache.listeners.get(entry) != null) return true;
927                 return false;
928         }
929
930         boolean hasListenerAfterDisposing(CacheEntry entry) {
931                 if(cache.listeners.get(entry) != null) {
932                         ArrayList<ListenerEntry> entries = cache.listeners.get(entry);
933                         ArrayList<ListenerEntry> list = null;
934                         for (ListenerEntry e : entries) {
935                                 if (e.base.isDisposed()) {
936                                         if(list == null) list = new ArrayList<ListenerEntry>();
937                                         list.add(e);
938                                 }
939                         }
940                         if(list != null) {
941                                 for (ListenerEntry e : list) {
942                                         entries.remove(e);
943                                 }
944                         }
945                         if (entries.isEmpty()) {
946                                 cache.listeners.remove(entry);
947                                 return false;
948                         }
949                         return true;
950                 }
951                 return false;
952         }
953
954         List<ListenerEntry> getListenerEntries(CacheEntry entry) {
955                 hasListenerAfterDisposing(entry);
956                 if(cache.listeners.get(entry) != null)
957                         return cache.listeners.get(entry);
958                 else 
959                         return Collections.emptyList();
960         }
961
962         void processListenerReport(CacheEntry<?> entry, Map<CacheEntry, Set<ListenerBase>> workarea) {
963
964                 if(!workarea.containsKey(entry)) {
965
966                         HashSet<ListenerBase> ls = new HashSet<ListenerBase>();
967                         for(ListenerEntry e : getListenerEntries(entry))
968                                 ls.add(e.base);
969
970                         workarea.put(entry, ls);
971
972                         for(CacheEntry parent : entry.getParents(this)) {
973                                 processListenerReport(parent, workarea);
974                                 ls.addAll(workarea.get(parent));
975                         }
976
977                 }
978
979         }
980
981         public synchronized ListenerReport getListenerReport() throws IOException {
982
983                 class ListenerReportImpl implements ListenerReport {
984
985                         Map<CacheEntry, Set<ListenerBase>> workarea = new HashMap<CacheEntry, Set<ListenerBase>>();
986
987                         @Override
988                         public void print(PrintStream b) {
989                                 Map<ListenerBase, Integer> hist = new HashMap<ListenerBase, Integer>();
990                                 for(Map.Entry<CacheEntry, Set<ListenerBase>> e : workarea.entrySet()) {
991                                         for(ListenerBase l : e.getValue()) {
992                                                 Integer i = hist.get(l);
993                                                 hist.put(l, i != null ? i-1 : -1);
994                                         }
995                                 }
996
997                                 for(Pair<ListenerBase, Integer> p : CollectionUtils.valueSortedEntries(hist)) {
998                                         b.print("" + -p.second + " " + p.first + "\n");
999                                 }
1000
1001                                 b.flush();
1002                         }
1003
1004                 }
1005
1006                 ListenerReportImpl result = new ListenerReportImpl();
1007
1008                 Collection<CacheEntryBase> all = allCaches(new CacheCollectionResult()).toCollection();
1009                 for(CacheEntryBase entry : all) {
1010                         hasListenerAfterDisposing(entry);
1011                 }
1012                 for(CacheEntryBase entry : all) {
1013                         processListenerReport(entry, result.workarea);
1014                 }
1015
1016                 return result;
1017
1018         }
1019
1020         public synchronized String reportListeners(File file) throws IOException {
1021
1022                 if (!isAlive())
1023                         return "Disposed!";
1024
1025                 PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
1026                 ListenerReport report = getListenerReport();
1027                 report.print(b);
1028
1029                 return "Done reporting listeners.";
1030
1031         }
1032
1033         void processParentReport(CacheEntry entry, Map<CacheEntry, Set<CacheEntry>> workarea) {
1034
1035                 if(entry.isDiscarded()) return;
1036                 if(workarea.containsKey(entry)) return;
1037                 
1038                 Iterable<CacheEntry> parents = entry.getParents(this);
1039                 HashSet<CacheEntry> ps = new HashSet<CacheEntry>();
1040                 for(CacheEntry e : parents) {
1041                         if(e.isDiscarded()) continue;
1042                         ps.add(e);
1043                         processParentReport(e, workarea);
1044                 }
1045                 workarea.put(entry, ps);
1046
1047         }
1048
1049         public synchronized String reportQueryActivity(File file) throws IOException {
1050                 
1051                 System.err.println("reportQueries " + file.getAbsolutePath());
1052
1053                 if (!isAlive())
1054                         return "Disposed!";
1055
1056                 PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
1057
1058                 List<Pair<String,Integer>> entries = CollectionUtils.valueSortedEntries(Development.histogram);
1059                 Collections.reverse(entries);
1060                 
1061                 for(Pair<String,Integer> entry : entries) {
1062                         b.println(entry.first + ": " + entry.second);
1063                 }
1064
1065                 b.close();
1066                 
1067                 Development.histogram.clear();
1068
1069                 return "OK";
1070
1071         }
1072         
1073         public synchronized String reportQueries(File file) throws IOException {
1074
1075                 System.err.println("reportQueries " + file.getAbsolutePath());
1076
1077                 if (!isAlive())
1078                         return "Disposed!";
1079
1080                 PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
1081
1082                 long start = System.nanoTime();
1083
1084 //              ArrayList<CacheEntry> all = ;
1085                 
1086                 Map<CacheEntry, Set<CacheEntry>> workarea = new HashMap<CacheEntry, Set<CacheEntry>>();
1087                 Collection<CacheEntryBase> caches = allCaches(new CacheCollectionResult()).toCollection();
1088                 for(CacheEntryBase entry : caches) {
1089                         processParentReport(entry, workarea);
1090                 }
1091                 
1092                 //        for(CacheEntry e : all) System.err.println("entry: " + e);
1093
1094                 long duration = System.nanoTime() - start;
1095                 System.err.println("Query root set in " + 1e-9*duration + "s.");
1096
1097                 start = System.nanoTime();
1098
1099                 HashMap<CacheEntry, Integer> flagMap = new HashMap<CacheEntry, Integer>(); 
1100
1101                 int listeners = 0;
1102
1103                 for(CacheEntry entry : workarea.keySet()) {
1104                         boolean listener = hasListenerAfterDisposing(entry);
1105                         boolean hasParents = entry.getParents(this).iterator().hasNext();
1106                         if(listener) {
1107                                 // Bound
1108                                 flagMap.put(entry, 0);
1109                         } else if (!hasParents) {
1110                                 // Unbound
1111                                 flagMap.put(entry, 1);
1112                         } else {
1113                                 // Unknown
1114                                 flagMap.put(entry, 2);
1115                         }
1116                         //              // Write leaf bit
1117                         //              entry.flags |= 4;
1118                 }
1119
1120                 boolean done = true;
1121                 int loops = 0;
1122
1123                 do {
1124
1125                         done = true;
1126
1127                         long start2 = System.nanoTime();
1128
1129                         int boundCounter = 0;
1130                         int unboundCounter = 0;
1131                         int unknownCounter = 0;
1132
1133                         for(CacheEntry<?> entry : workarea.keySet()) {
1134
1135                                 //System.err.println("process " + entry);
1136
1137                                 int flags = flagMap.get(entry);
1138                                 int bindStatus = flags & 3;
1139
1140                                 if(bindStatus == 0) boundCounter++;
1141                                 else if(bindStatus == 1) unboundCounter++;
1142                                 else if(bindStatus == 2) unknownCounter++;
1143
1144                                 if(bindStatus < 2) continue;
1145
1146                                 int newStatus = 1;
1147                                 for(CacheEntry parent : entry.getParents(this)) {
1148
1149                                         if(parent.isDiscarded()) flagMap.put(parent, 1);
1150
1151                                         int flags2 = flagMap.get(parent);
1152                                         int bindStatus2 = flags2 & 3;
1153                                         // Parent is bound => child is bound
1154                                         if(bindStatus2 == 0) {
1155                                                 newStatus = 0;
1156                                                 break;
1157                                         }
1158                                         // Parent is unknown => child is unknown
1159                                         else if (bindStatus2 == 2) {
1160                                                 newStatus = 2;
1161                                                 done = false;
1162                                                 break;
1163                                         }
1164                                 }
1165
1166                                 flagMap.put(entry, newStatus);
1167
1168                         }
1169
1170                         duration = System.nanoTime() - start2;
1171                         System.err.println("Query analysis pass (" + boundCounter + "/" + unboundCounter + "/" + unknownCounter + ") in "+ 1e-9*duration + "s.");
1172                         b.println("Query analysis pass (" + boundCounter + "/" + unboundCounter + "/" + unknownCounter + ") in "+ 1e-9*duration + "s.");
1173
1174                 } while(!done && loops++ < 20);
1175
1176                 if(loops >= 20) {
1177
1178                         for(CacheEntry entry : workarea.keySet()) {
1179
1180                                 int bindStatus = flagMap.get(entry);
1181                                 if(bindStatus == 2) System.err.println("Undefined bind status for " + entry);
1182
1183                         }
1184
1185                 }
1186
1187                 duration = System.nanoTime() - start;
1188                 System.err.println("Query analysis in " + 1e-9*duration + "s.");
1189
1190                 Map<Class<?>, Integer> counts = new HashMap<Class<?>, Integer>();
1191
1192                 for(CacheEntry entry : workarea.keySet()) {
1193                         Class<?> clazz = entry.getClass();
1194                         if(entry instanceof ReadEntry) clazz = ((ReadEntry)entry).request.getClass(); 
1195                         else if(entry instanceof MultiReadEntry) clazz = ((MultiReadEntry)entry).request.getClass(); 
1196                         else if(entry instanceof AsyncReadEntry) clazz = ((AsyncReadEntry)entry).request.getClass(); 
1197                         else if(entry instanceof AsyncMultiReadEntry) clazz = ((AsyncMultiReadEntry)entry).request.getClass(); 
1198                         else if(entry instanceof ExternalReadEntry) clazz = ((ExternalReadEntry)entry).request.getClass(); 
1199                         Integer c = counts.get(clazz);
1200                         if(c == null) counts.put(clazz, -1);
1201                         else counts.put(clazz, c-1);
1202                 }
1203
1204                 b.print("// Simantics DB client query report file\n");
1205                 b.print("// This file contains the following information\n");
1206                 b.print("// -The amount of cached query instances per query class\n");
1207                 b.print("// -The sizes of retained child sets\n");
1208                 b.print("// -List of parents for each query (search for 'P <query name>')\n");
1209                 b.print("//  -Followed by status, where\n");
1210                 b.print("//   -0=bound\n");
1211                 b.print("//   -1=free\n");
1212                 b.print("//   -2=unknown\n");
1213                 b.print("//   -L=has listener\n");
1214                 b.print("// -List of children for each query (search for 'C <query name>')\n");
1215
1216                 b.print("----------------------------------------\n");
1217
1218                 b.print("// Queries by class\n");
1219                 for(Pair<Class<?>, Integer> p : CollectionUtils.valueSortedEntries(counts)) {
1220                         b.print(-p.second + " " + p.first.getName() + "\n");
1221                 }
1222
1223                 Map<CacheEntry, Integer> hist = new HashMap<CacheEntry, Integer>();
1224                 for(CacheEntry e : workarea.keySet())
1225                         hist.put(e, -1);
1226                 
1227                 boolean changed = true;
1228                 int iter = 0;
1229                 while(changed && iter++<50) {
1230                         
1231                         changed = false;
1232                         
1233                         Map<CacheEntry, Integer> newHist = new HashMap<CacheEntry, Integer>();
1234                         for(CacheEntry e : workarea.keySet())
1235                                 newHist.put(e, -1);
1236
1237                         for(Map.Entry<CacheEntry, Set<CacheEntry>> e : workarea.entrySet()) {
1238                                 Integer c = hist.get(e.getKey());
1239                                 for(CacheEntry p : e.getValue()) {
1240                                         Integer i = newHist.get(p);
1241                                         newHist.put(p, i+c);
1242                                 }
1243                         }
1244                         for(CacheEntry e : workarea.keySet()) {
1245                                 Integer value = newHist.get(e);
1246                                 Integer old = hist.get(e);
1247                                 if(!value.equals(old)) {
1248                                         hist.put(e, value);
1249 //                                      System.err.println("hist " + e + ": " + old + " => " + value);
1250                                         changed = true;
1251                                 }
1252                         }
1253                         
1254                         System.err.println("Retained set iteration " + iter);
1255
1256                 }
1257
1258                 b.print("// Queries by retained set\n");
1259                 for(Pair<CacheEntry, Integer> p : CollectionUtils.valueSortedEntries(hist)) {
1260                         b.print("" + -p.second + " " + p.first + "\n");
1261                 }
1262
1263                 HashMap<CacheEntry, Collection<CacheEntry>> inverse = new HashMap<CacheEntry, Collection<CacheEntry>>();
1264
1265                 b.print("// Entry parent listing\n");
1266                 for(CacheEntry entry : workarea.keySet()) {
1267                         int status = flagMap.get(entry);
1268                         boolean hasListener = hasListenerAfterDisposing(entry);
1269                         b.print("Q " + entry.toString());
1270                         if(hasListener) {
1271                                 b.print(" (L" + status + ")");
1272                                 listeners++;
1273                         } else {
1274                                 b.print(" (" + status + ")");
1275                         }
1276                         b.print("\n");
1277                         for(CacheEntry parent : workarea.get(entry)) {
1278                                 Collection<CacheEntry> inv = inverse.get(parent);
1279                                 if(inv == null) {
1280                                         inv = new ArrayList<CacheEntry>();
1281                                         inverse.put(parent, inv);
1282                                 }
1283                                 inv.add(entry);
1284                                 b.print("  " + parent.toString());
1285                                 b.print("\n");
1286                         }
1287                 }
1288
1289                 b.print("// Entry child listing\n");
1290                 for(Map.Entry<CacheEntry, Collection<CacheEntry>> entry : inverse.entrySet()) {
1291                         b.print("C " + entry.getKey().toString());
1292                         b.print("\n");
1293                         for(CacheEntry child : entry.getValue()) {
1294                                 Integer h = hist.get(child);
1295                                 if(h != null) {
1296                                         b.print("  " + h);
1297                                 } else {
1298                                         b.print("  <no children>");
1299                                 }
1300                                 b.print("  " + child.toString());
1301                                 b.print("\n");
1302                         }
1303                 }
1304
1305                 b.print("#queries: " + workarea.keySet().size() + "\n");
1306                 b.print("#listeners: " + listeners + "\n");
1307
1308                 b.close();
1309
1310                 return "Dumped " + workarea.keySet().size() + " queries.";
1311
1312         }
1313
1314         class UpdateEntry {
1315
1316                 public CacheEntry caller;
1317
1318                 public CacheEntry entry;
1319
1320                 public int         indent;
1321
1322                 public UpdateEntry(CacheEntry caller, CacheEntry entry, int indent) {
1323                         this.caller = caller;
1324                         this.entry = entry;
1325                         this.indent = indent;
1326                 }
1327
1328         };
1329
1330         boolean removeQuery(CacheEntry entry) {
1331
1332                 // This entry has been removed before. No need to do anything here.
1333                 if(entry.isDiscarded()) return false;
1334
1335                 assert (!entry.isDiscarded());
1336
1337                 Query query = entry.getQuery();
1338
1339                 query.removeEntry(this);
1340
1341                 cache.updates++;
1342                 cache.size--;
1343
1344                 if((entry.getGCStatus() & CacheEntry.HAS_BEEN_BOUND) != 0)
1345                         boundQueries--;
1346                 
1347                 entry.discard();
1348
1349                 return true;
1350
1351         }
1352
1353         /**
1354          * 
1355          * @return true if this entry is being listened
1356          */
1357         private boolean updateQuery(UpdateEntry e, LinkedList<UpdateEntry> todo, IdentityHashMap<CacheEntry, CacheEntry> immediates) throws DatabaseException {
1358
1359                 assert (e != null);
1360
1361                 CacheEntry entry = e.entry;
1362
1363                 System.err.println("updateQuery " + entry);
1364                 
1365                 /*
1366                  * If the dependency graph forms a DAG, some entries are inserted in the
1367                  * todo list many times. They only need to be processed once though.
1368                  */
1369                 if (entry.isDiscarded()) {
1370                         if (Development.DEVELOPMENT) {
1371                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1372                                         System.out.print("D");
1373                                         for (int i = 0; i < e.indent; i++)
1374                                                 System.out.print(" ");
1375                                         System.out.println(entry.getQuery());
1376                                 }
1377                         }
1378 //                      System.err.println(" => DISCARDED");
1379                         return false;
1380                 }
1381
1382                 if (entry.isRefuted()) {
1383                         if (Development.DEVELOPMENT) {
1384                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1385                                         System.out.print("R");
1386                                         for (int i = 0; i < e.indent; i++)
1387                                                 System.out.print(" ");
1388                                         System.out.println(entry.getQuery());
1389                                 }
1390                         }
1391                         return false;
1392                 }
1393
1394                 if (entry.isExcepted()) {
1395                         if (Development.DEVELOPMENT) {
1396                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1397                                         System.out.print("E");
1398                                 }
1399                         }
1400                 }
1401
1402                 if (entry.isPending()) {
1403                         if (Development.DEVELOPMENT) {
1404                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1405                                         System.out.print("P");
1406                                 }
1407                         }
1408                 }
1409
1410                 cache.updates++;
1411
1412                 if (Development.DEVELOPMENT) {
1413                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1414                                 System.out.print("U ");
1415                                 for (int i = 0; i < e.indent; i++)
1416                                         System.out.print(" ");
1417                                 System.out.print(entry.getQuery());
1418                         }
1419                 }
1420
1421                 Query query = entry.getQuery();
1422                 int type = query.type();
1423
1424                 boolean hasListener = hasListener(entry); 
1425
1426                 if (Development.DEVELOPMENT) {
1427                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1428                                 if(hasListener(entry)) {
1429                                         System.out.println(" (L)");
1430                                 } else {
1431                                         System.out.println("");
1432                                 }
1433                         }
1434                 }
1435
1436                 if(entry.isPending() || entry.isExcepted()) {
1437
1438                         // If updated
1439                         if ((type & RequestFlags.UPDATE_MASK) == RequestFlags.IMMEDIATE_UPDATE) {
1440
1441                                 immediates.put(entry, entry);
1442
1443                         } else {
1444
1445                                 if(hasListener) {
1446                                         entry.refute();
1447                                 } else {
1448                                         removeQuery(entry);
1449                                 }
1450
1451                         }
1452
1453                 } else {
1454
1455                         // If updated
1456                         if ((type & RequestFlags.UPDATE_MASK) == RequestFlags.IMMEDIATE_UPDATE) {
1457
1458                                 immediates.put(entry, entry);
1459
1460                         } else {
1461
1462                                 if(hasListener) {
1463                                         entry.refute();
1464                                 } else {
1465                                         removeQuery(entry);
1466                                 }
1467
1468                         }
1469
1470                 }
1471
1472 //              System.err.println(" => FOO " + type);
1473
1474                 if (hasListener) {
1475                         ArrayList<ListenerEntry> entries = cache.listeners.get(entry);
1476                         if(entries != null) {
1477                                 for (ListenerEntry le : entries) {
1478                                         scheduleListener(le);
1479                                 }
1480                         }
1481                 }
1482
1483                 // If invalid, update parents
1484                 if (type == RequestFlags.INVALIDATE) {
1485                         updateParents(e.indent, entry, todo);
1486                 }
1487
1488                 return hasListener;
1489
1490         }
1491
1492         private void updateParents(int indent, CacheEntry entry, LinkedList<UpdateEntry> todo) {
1493
1494                 Iterable<CacheEntry> oldParents = entry.getParents(this);
1495                 for (CacheEntry parent : oldParents) {
1496 //                      System.err.println("updateParents " + entry + " => " + parent);
1497                         if(!parent.isDiscarded())
1498                                 todo.push(new UpdateEntry(entry, parent, indent + 2));
1499                 }
1500
1501         }
1502
1503         private boolean pruneListener(ListenerEntry entry) {
1504                 if (entry.base.isDisposed()) {
1505                         removeListener(entry);
1506                         return true;
1507                 } else {
1508                         return false;
1509                 }
1510         }
1511
1512         /**
1513          * @param av1 an array (guaranteed)
1514          * @param av2 any object
1515          * @return <code>true</code> if the two arrays are equal
1516          */
1517         private final boolean arrayEquals(Object av1, Object av2) {
1518                 if (av2 == null)
1519                         return false;
1520                 Class<?> c1 = av1.getClass().getComponentType();
1521                 Class<?> c2 = av2.getClass().getComponentType();
1522                 if (c2 == null || !c1.equals(c2))
1523                         return false;
1524                 boolean p1 = c1.isPrimitive();
1525                 boolean p2 = c2.isPrimitive();
1526                 if (p1 != p2)
1527                         return false;
1528                 if (!p1)
1529                         return Arrays.equals((Object[]) av1, (Object[]) av2);
1530                 if (boolean.class.equals(c1))
1531                         return Arrays.equals((boolean[]) av1, (boolean[]) av2);
1532                 else if (byte.class.equals(c1))
1533                         return Arrays.equals((byte[]) av1, (byte[]) av2);
1534                 else if (int.class.equals(c1))
1535                         return Arrays.equals((int[]) av1, (int[]) av2);
1536                 else if (long.class.equals(c1))
1537                         return Arrays.equals((long[]) av1, (long[]) av2);
1538                 else if (float.class.equals(c1))
1539                         return Arrays.equals((float[]) av1, (float[]) av2);
1540                 else if (double.class.equals(c1))
1541                         return Arrays.equals((double[]) av1, (double[]) av2);
1542                 throw new RuntimeException("??? Contact application querySupport.");
1543         }
1544
1545
1546
1547         final private Object compareTo(ReadGraphImpl graph, final CacheEntry entry, final Object oldValue) {
1548
1549                 try {
1550
1551                         Query query = entry.getQuery();
1552
1553                         if(DebugPolicy.RECOMPUTE) System.out.println("R " + query);
1554
1555                         entry.prepareRecompute(querySupport);
1556                         
1557                         ReadGraphImpl parentGraph = graph.withParent(entry);
1558
1559                         query.recompute(parentGraph);
1560
1561                         if(entry.isExcepted()) return ListenerEntry.NO_VALUE;
1562
1563                         Object newValue = entry.getResult();
1564
1565                         if (ListenerEntry.NO_VALUE == oldValue) {
1566                                 if(DebugPolicy.CHANGES) {
1567                                         System.out.println("C " + query);
1568                                         System.out.println("- " + oldValue);
1569                                         System.out.println("- " + newValue);
1570                                 }
1571                                 return newValue;
1572                         }
1573
1574                         boolean changed = false;
1575
1576                         if (newValue != null) {
1577                                 if (newValue.getClass().isArray()) {
1578                                         changed = !arrayEquals(newValue, oldValue);
1579                                 } else {
1580                                         changed = !newValue.equals(oldValue);
1581                                 }
1582                         } else
1583                                 changed = (oldValue != null);
1584
1585                         if(DebugPolicy.CHANGES && changed) {
1586                                 System.out.println("C " + query);
1587                                 System.out.println("- " + oldValue);
1588                                 System.out.println("- " + newValue);
1589                         }
1590
1591                         return changed ? newValue : ListenerEntry.NOT_CHANGED;
1592
1593                 } catch (Throwable t) {
1594
1595                         Logger.defaultLogError(t);
1596                         entry.except(t);
1597                         return ListenerEntry.NO_VALUE;
1598
1599                 }
1600
1601         }
1602
1603         public boolean hasScheduledUpdates() {
1604                 return !scheduledListeners.isEmpty();
1605         }
1606
1607         public void performScheduledUpdates(WriteGraphImpl graph) {
1608
1609                 assert (!updating);
1610                 assert (!cache.collecting);
1611                 assert (!firingListeners);
1612
1613                 firingListeners = true;
1614
1615                 try {
1616
1617                         // Performing may cause further events to be scheduled.
1618                         while (!scheduledListeners.isEmpty()) {
1619
1620 //                              graph.restart();
1621 //                              graph.state.barrier.inc();
1622
1623                                 // Clone current events to make new entries possible during
1624                                 // firing.
1625                                 THashSet<ListenerEntry> entries = scheduledListeners;
1626                                 scheduledListeners = new THashSet<ListenerEntry>();
1627
1628                                 ArrayList<ListenerEntry> schedule = new ArrayList<ListenerEntry>();
1629
1630                                 for (ListenerEntry listenerEntry : entries) {
1631
1632                                         if (pruneListener(listenerEntry)) {
1633                                                 if(DebugPolicy.LISTENER) System.out.println("Pruned " + listenerEntry.procedure);
1634                                                 continue;
1635                                         }
1636
1637                                         final CacheEntry entry = listenerEntry.entry;
1638                                         assert (entry != null);
1639
1640                                         Object newValue = compareTo(graph, entry, listenerEntry.getLastKnown());
1641
1642                                         if (newValue != ListenerEntry.NOT_CHANGED) {
1643                                                 if(DebugPolicy.LISTENER)
1644                                                         System.out.println("Add to schedule " + listenerEntry.procedure + " with " + newValue);
1645                                                 schedule.add(listenerEntry);
1646                                                 listenerEntry.setLastKnown(entry.getResult());
1647                                         }
1648
1649                                 }
1650
1651                                 for(ListenerEntry listenerEntry : schedule) {
1652                                         final CacheEntry entry = listenerEntry.entry;
1653                                         if(DebugPolicy.LISTENER)
1654                                                 System.out.println("Firing " + listenerEntry.procedure);
1655                                         try {
1656                                                 if(DebugPolicy.LISTENER)
1657                                                         System.out.println("Firing " + listenerEntry.procedure + " for " + listenerEntry.entry);
1658                                                 entry.performFromCache(graph, listenerEntry.procedure);
1659                                         } catch (Throwable t) {
1660                                                 t.printStackTrace();
1661                                         }
1662                                 }
1663
1664 //                              graph.state.barrier.dec();
1665 //                              graph.waitAsync(null);
1666 //                              graph.state.barrier.assertReady();
1667
1668                         }
1669
1670                 } finally {
1671                         firingListeners = false;
1672                 }
1673
1674         }
1675
1676         /**
1677          * 
1678          * @return true if this entry still has listeners
1679          */
1680         public boolean update(final ReadGraphImpl graph, final CacheEntry entry) {
1681
1682                 assert (!cache.collecting);
1683                 assert (!updating);
1684                 updating = true;
1685
1686                 boolean hadListeners = false;
1687                 boolean listenersUnknown = false;
1688
1689                 try {
1690
1691                         assert(entry != null);
1692                         LinkedList<UpdateEntry> todo = new LinkedList<UpdateEntry>();
1693                         IdentityHashMap<CacheEntry, CacheEntry> immediates = new IdentityHashMap<CacheEntry, CacheEntry>();
1694                         todo.add(new UpdateEntry(null, entry, 0));
1695
1696                         while(true) {
1697
1698                                 // Walk the tree and collect immediate updates
1699                                 while (!todo.isEmpty()) {
1700                                         UpdateEntry e = todo.pop();
1701                                         hadListeners |= updateQuery(e, todo, immediates);
1702                                 }
1703
1704                                 if(immediates.isEmpty()) break;
1705
1706                                 // Evaluate all immediate updates and collect parents to update
1707                                 for(CacheEntry immediate : immediates.values()) {
1708
1709                                         if(immediate.isDiscarded()) {
1710                                                 continue;
1711                                         }
1712
1713                                         if(immediate.isExcepted()) {
1714
1715                                                 Object newValue = compareTo(graph, immediate, ListenerEntry.NO_VALUE);
1716                                                 if (newValue != ListenerEntry.NOT_CHANGED)
1717                                                         updateParents(0, immediate, todo);
1718
1719                                         } else {
1720
1721                                                 Object oldValue = immediate.getResult();
1722                                                 Object newValue = compareTo(graph, immediate, oldValue);
1723
1724                                                 if (newValue != ListenerEntry.NOT_CHANGED) {
1725                                                         updateParents(0, immediate, todo);
1726                                                 } else {
1727                                                         // If not changed, keep the old value
1728                                                         immediate.setResult(oldValue);
1729                                                         listenersUnknown = true;
1730                                                 }
1731
1732                                         }
1733
1734                                 }
1735                                 immediates.clear();
1736
1737                         }
1738
1739                 } catch (Throwable t) {
1740                         Logger.defaultLogError(t);
1741                 }
1742
1743                 assert (updating);
1744                 updating = false;
1745
1746                 return hadListeners | listenersUnknown;
1747
1748         }
1749
1750         private ObjectUpdateSet scheduledObjectUpdates = new ObjectUpdateSet();
1751         private ValueUpdateSet scheduledValueUpdates = new ValueUpdateSet();
1752         private ValueUpdateSet scheduledInvalidates = new ValueUpdateSet();
1753         // Maybe use a mutex from util.concurrent?
1754         private Object primitiveUpdateLock = new Object();
1755         private THashSet scheduledPrimitiveUpdates = new THashSet();
1756
1757         public void performDirtyUpdates(final ReadGraphImpl graph) {
1758
1759                 cache.dirty = false;
1760                 lastInvalidate = 0;
1761
1762                 if (Development.DEVELOPMENT) {
1763                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1764                                 System.err.println("== Query update ==");
1765                         }
1766                 }
1767
1768                 // Special case - one statement
1769                 if(scheduledObjectUpdates.size() == 1 && scheduledValueUpdates.size() == 0 && scheduledPrimitiveUpdates.size() == 0 && scheduledInvalidates.size() == 0) {
1770
1771                         long arg0 = scheduledObjectUpdates.getFirst();
1772
1773                         final int subject = (int)(arg0 >>> 32);
1774                         final int predicate = (int)(arg0 & 0xffffffff);
1775
1776                         for(Objects o : QueryCache.entriesObjects(QueryProcessor.this, subject)) update(graph, o);
1777                         for(DirectObjects o : QueryCache.entriesDirectObjects(QueryProcessor.this, subject)) update(graph, o);
1778                         for(Statements o : QueryCache.entriesStatements(QueryProcessor.this, subject)) update(graph, o);
1779
1780                         if(predicate == instanceOf || predicate == inherits || predicate == subrelationOf) {
1781                                 PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, subject);
1782                                 if(principalTypes != null) update(graph, principalTypes);
1783                                 Types types = QueryCache.entryTypes(QueryProcessor.this, subject);
1784                                 if(types != null) update(graph, types);
1785                         }
1786
1787                         if(predicate == subrelationOf) {
1788                                 SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, subject);
1789                                 if(superRelations != null) update(graph, superRelations);
1790                         }
1791
1792                         DirectPredicates dp = QueryCache.entryDirectPredicates(QueryProcessor.this, subject);
1793                         if(dp != null) update(graph, dp);
1794                         OrderedSet os = QueryCache.entryOrderedSet(QueryProcessor.this, predicate);
1795                         if(os != null) update(graph, os);
1796
1797                         scheduledObjectUpdates.clear();
1798                         return;
1799
1800                 }
1801
1802                 // Special case - one value
1803                 if(scheduledObjectUpdates.size() == 0 && scheduledValueUpdates.size() == 1 && scheduledPrimitiveUpdates.size() == 0 && scheduledInvalidates.size() == 0) {
1804
1805                         int arg0 = scheduledValueUpdates.getFirst();
1806
1807                         ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, arg0);
1808                         if(valueQuery != null) update(graph, valueQuery);
1809
1810                         scheduledValueUpdates.clear();
1811                         return;
1812
1813                 }
1814
1815                 final TIntHashSet predicates = new TIntHashSet();
1816                 final TIntHashSet orderedSets = new TIntHashSet();
1817
1818                 THashSet primitiveUpdates;
1819                 synchronized (primitiveUpdateLock) {
1820                         primitiveUpdates = scheduledPrimitiveUpdates;
1821                         scheduledPrimitiveUpdates = new THashSet();
1822                 }
1823
1824                 primitiveUpdates.forEach(new TObjectProcedure() {
1825
1826                         @Override
1827                         public boolean execute(Object arg0) {
1828
1829                                 ExternalReadEntry query = (ExternalReadEntry)cache.externalReadEntryMap.get(arg0);
1830                                 if (query != null) {
1831                                         boolean listening = update(graph, query);
1832                                         if (!listening && !query.hasParents()) {
1833                                                 cache.externalReadEntryMap.remove(arg0);
1834                                                 query.discard();
1835                                         }
1836                                 }
1837                                 return true;
1838                         }
1839
1840                 });
1841
1842                 scheduledValueUpdates.forEach(new TIntProcedure() {
1843
1844                         @Override
1845                         public boolean execute(int arg0) {
1846                                 ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, arg0);
1847                                 if(valueQuery != null) update(graph, valueQuery);
1848                                 return true;
1849                         }
1850
1851                 });
1852
1853                 scheduledInvalidates.forEach(new TIntProcedure() {
1854
1855                         @Override
1856                         public boolean execute(int resource) {
1857                                 
1858                                 ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, resource);
1859                                 if(valueQuery != null) update(graph, valueQuery);
1860                                 
1861                                 PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, resource);
1862                                 if(principalTypes != null) update(graph, principalTypes);
1863                                 Types types = QueryCache.entryTypes(QueryProcessor.this, resource);
1864                                 if(types != null) update(graph, types);
1865
1866                                 SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, resource);
1867                                 if(superRelations != null) update(graph, superRelations);
1868
1869                                 predicates.add(resource);
1870                                 
1871                                 return true;
1872                         }
1873
1874                 });
1875
1876                 scheduledObjectUpdates.forEach(new TLongProcedure() {
1877
1878                         @Override
1879                         public boolean execute(long arg0) {
1880
1881                                 final int subject = (int)(arg0 >>> 32);
1882                                 final int predicate = (int)(arg0 & 0xffffffff);
1883
1884                                 if(predicate == instanceOf || predicate == inherits || predicate == subrelationOf) {
1885                                         PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, subject);
1886                                         if(principalTypes != null) update(graph, principalTypes);
1887                                         Types types = QueryCache.entryTypes(QueryProcessor.this, subject);
1888                                         if(types != null) update(graph, types);
1889                                 }
1890
1891                                 if(predicate == subrelationOf) {
1892                                         SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, subject);
1893                                         if(superRelations != null) update(graph, superRelations);
1894                                 }
1895
1896                                 predicates.add(subject);
1897                                 orderedSets.add(predicate);
1898
1899                                 return true;
1900
1901                         }
1902
1903                 });
1904
1905                 predicates.forEach(new TIntProcedure() {
1906
1907                         @Override
1908                         public boolean execute(final int subject) {
1909
1910                                 for(Objects o : QueryCache.entriesObjects(QueryProcessor.this, subject)) update(graph, o);
1911                                 for(DirectObjects o : QueryCache.entriesDirectObjects(QueryProcessor.this, subject)) update(graph, o);
1912                                 for(Statements o : QueryCache.entriesStatements(QueryProcessor.this, subject)) update(graph, o);
1913
1914                                 DirectPredicates entry = QueryCache.entryDirectPredicates(QueryProcessor.this, subject);
1915                                 if(entry != null) update(graph, entry);
1916
1917                                 return true;
1918
1919                         }
1920
1921                 });
1922
1923                 orderedSets.forEach(new TIntProcedure() {
1924
1925                         @Override
1926                         public boolean execute(int orderedSet) {
1927
1928                                 OrderedSet entry = QueryCache.entryOrderedSet(QueryProcessor.this, orderedSet);
1929                                 if(entry != null) update(graph, entry);
1930
1931                                 return true;
1932
1933                         }
1934
1935                 });
1936
1937                 //              for (Integer subject : predicates) {
1938                 //                      DirectPredicates entry = DirectPredicates.entry(QueryProcessor.this, subject);
1939                 //                      if(entry != null) update(graph, entry);
1940                 //              }
1941
1942
1943                 if (Development.DEVELOPMENT) {
1944                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1945                                 System.err.println("== Query update ends ==");
1946                         }
1947                 }
1948
1949                 scheduledValueUpdates.clear();
1950                 scheduledObjectUpdates.clear();
1951                 scheduledInvalidates.clear();
1952
1953         }
1954
1955         public void updateValue(final int resource) {
1956                 scheduledValueUpdates.add(resource);
1957                 cache.dirty = true;
1958         }
1959
1960         public void updateStatements(final int resource, final int predicate) {
1961                 scheduledObjectUpdates.add((((long)resource) << 32) + predicate);
1962                 cache.dirty = true;
1963         }
1964         
1965         private int lastInvalidate = 0;
1966         
1967         public void invalidateResource(final int resource) {
1968                 if(lastInvalidate == resource) return;
1969                 scheduledValueUpdates.add(resource);
1970                 lastInvalidate = resource;
1971                 cache.dirty = true;
1972         }
1973
1974         public void updatePrimitive(final ExternalRead primitive) {
1975
1976                 // External reads may be updated from arbitrary threads.
1977                 // Synchronize to prevent race-conditions.
1978                 synchronized (primitiveUpdateLock) {
1979                         scheduledPrimitiveUpdates.add(primitive);
1980                 }
1981                 querySupport.dirtyPrimitives();
1982
1983         }
1984
1985         @Override
1986         public synchronized String toString() {
1987                 return "QueryProvider [size = " + cache.size + ", hits = " + cache.hits + " misses = " + cache.misses + ", updates = " + cache.updates + "]";
1988         }
1989
1990         @Override
1991         protected void doDispose() {
1992
1993                 for(int index = 0; index < THREADS; index++) { 
1994                         executors[index].dispose();
1995                 }
1996
1997                 // First just wait
1998                 for(int i=0;i<100;i++) {
1999
2000                         boolean alive = false;
2001                         for(int index = 0; index < THREADS; index++) { 
2002                                 alive |= executors[index].isAlive();
2003                         }
2004                         if(!alive) return;
2005                         try {
2006                                 Thread.sleep(5);
2007                         } catch (InterruptedException e) {
2008                                 Logger.defaultLogError(e);
2009                         }
2010
2011                 }
2012
2013                 // Then start interrupting
2014                 for(int i=0;i<100;i++) {
2015
2016                         boolean alive = false;
2017                         for(int index = 0; index < THREADS; index++) { 
2018                                 alive |= executors[index].isAlive();
2019                         }
2020                         if(!alive) return;
2021                         for(int index = 0; index < THREADS; index++) {
2022                                 executors[index].interrupt();
2023                         }
2024                 }
2025
2026                 //              // Then just destroy
2027                 //              for(int index = 0; index < THREADS; index++) {
2028                 //                      executors[index].destroy();
2029                 //              }
2030
2031                 for(int index = 0; index < THREADS; index++) {
2032                         try {
2033                                 executors[index].join(5000);
2034                         } catch (InterruptedException e) {
2035                                 Logger.defaultLogError("QueryThread " + index + " will not die.", e);
2036                         }
2037                         executors[index] = null;
2038                 }
2039
2040         }
2041
2042         public int getHits() {
2043                 return cache.hits;
2044         }
2045
2046         public int getMisses() {
2047                 return cache.misses;
2048         }
2049
2050         public int getSize() {
2051                 return cache.size;
2052         }
2053
2054         public Set<Long> getReferencedClusters() {
2055                 HashSet<Long> result = new HashSet<Long>();
2056                 for (CacheEntry entry : QueryCache.entriesObjects(this)) {
2057                         Objects query = (Objects) entry.getQuery();
2058                         result.add(querySupport.getClusterId(query.r1()));
2059                 }
2060                 for (CacheEntry entry : QueryCache.entriesDirectPredicates(this)) {
2061                         DirectPredicates query = (DirectPredicates) entry.getQuery();
2062                         result.add(querySupport.getClusterId(query.id));
2063                 }
2064                 for (CacheEntry entry : cache.valueQueryMap.values()) {
2065                         ValueQuery query = (ValueQuery) entry.getQuery();
2066                         result.add(querySupport.getClusterId(query.id));
2067                 }
2068                 return result;
2069         }
2070
2071         public void assertDone() {
2072         }
2073
2074         CacheCollectionResult allCaches(CacheCollectionResult result) {
2075                 
2076                 return cache.allCaches(result);
2077
2078         }
2079
2080         public void printDiagnostics() {
2081         }
2082
2083         public void requestCluster(ReadGraphImpl graph, long clusterId, Runnable runnable) {
2084                 querySupport.requestCluster(graph, clusterId, runnable);
2085         }
2086
2087         public int clean() {
2088                 collector.collect(0, Integer.MAX_VALUE);
2089                 return cache.size;
2090         }
2091
2092         public void clean(final Collection<ExternalRead<?>> requests) {
2093                 QueryCollectorSupport collectorSupport = new QueryCollectorSupport() {
2094                         Iterator<ExternalRead<?>> iterator = requests.iterator();
2095                         @Override
2096                         public CacheCollectionResult allCaches() {
2097                                 throw new UnsupportedOperationException();
2098                         }
2099                         @Override
2100                         public CacheEntryBase iterate(int level) {
2101                                 if(iterator.hasNext()) {
2102                                         ExternalRead<?> request = iterator.next();
2103                                         ExternalReadEntry entry = cache.externalReadEntryMap.get(request);
2104                                         if (entry != null) return entry;
2105                                         else return iterate(level);
2106                                 } else {
2107                                         iterator = requests.iterator();
2108                                         return null;
2109                                 }
2110                         }
2111                         @Override
2112                         public void remove() {
2113                                 throw new UnsupportedOperationException();
2114                         }
2115                         @Override
2116                         public void setLevel(CacheEntryBase entry, int level) {
2117                                 throw new UnsupportedOperationException();
2118                         }
2119                         @Override
2120                         public Collection<CacheEntry> getRootList() {
2121                                 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>(requests.size());
2122                                 for (ExternalRead<?> request : requests) {
2123                                         ExternalReadEntry entry = cache.externalReadEntryMap.get(request);
2124                                         if (entry != null)
2125                                                 result.add(entry);
2126                                 }
2127                                 return result;
2128                         }
2129                         @Override
2130                         public int getCurrentSize() {
2131                                 return cache.size;
2132                         }
2133                         @Override
2134                         public int calculateCurrentSize() {
2135                                 // This tells the collector to attempt collecting everything.
2136                                 return Integer.MAX_VALUE;
2137                         }
2138                         @Override
2139                         public boolean start(boolean flush) {
2140                                 return true;
2141                         }
2142                 };
2143                 new QueryCollectorImpl2(this, collectorSupport).collect(0, Integer.MAX_VALUE);
2144         }
2145
2146         public void scanPending() {
2147                 
2148                 cache.scanPending();
2149
2150         }
2151
2152         public ReadGraphImpl graphForVirtualRequest() {
2153                 return ReadGraphImpl.createAsync(this);
2154         }
2155
2156         
2157         private HashMap<Resource, Class<?>> builtinValues;
2158         
2159         public Class<?> getBuiltinValue(Resource r) {
2160                 if(builtinValues == null) initBuiltinValues();
2161                 return builtinValues.get(r);
2162         }
2163
2164         Exception callerException = null;
2165
2166         public interface AsyncBarrier {
2167                 public void inc(); 
2168                 public void dec();
2169                 //        public void inc(String debug); 
2170                 //        public void dec(String debug);
2171         }
2172
2173 //      final public QueryProcessor processor;
2174 //      final public QuerySupport support;
2175
2176         //    boolean disposed = false;
2177
2178         private void initBuiltinValues() {
2179
2180                 Layer0 b = getSession().peekService(Layer0.class);
2181                 if(b == null) return;
2182
2183                 builtinValues = new HashMap<Resource, Class<?>>();
2184
2185                 builtinValues.put(b.String, String.class);
2186                 builtinValues.put(b.Double, Double.class);
2187                 builtinValues.put(b.Float, Float.class);
2188                 builtinValues.put(b.Long, Long.class);
2189                 builtinValues.put(b.Integer, Integer.class);
2190                 builtinValues.put(b.Byte, Byte.class);
2191                 builtinValues.put(b.Boolean, Boolean.class);
2192
2193                 builtinValues.put(b.StringArray, String[].class);
2194                 builtinValues.put(b.DoubleArray, double[].class);
2195                 builtinValues.put(b.FloatArray, float[].class);
2196                 builtinValues.put(b.LongArray, long[].class);
2197                 builtinValues.put(b.IntegerArray, int[].class);
2198                 builtinValues.put(b.ByteArray, byte[].class);
2199                 builtinValues.put(b.BooleanArray, boolean[].class);
2200
2201         }
2202
2203 //      public ReadGraphSupportImpl(final QueryProcessor provider2) {
2204 //
2205 //              if (null == provider2) {
2206 //                      this.processor = null;
2207 //                      support = null;
2208 //                      return;
2209 //              }
2210 //              this.processor = provider2;
2211 //              support = provider2.getCore();
2212 //              initBuiltinValues();
2213 //
2214 //      }
2215
2216 //      final static public ReadGraphSupportImpl basedOn(ReadGraphSupportImpl impl) {
2217 //              return new ReadGraphSupportImpl(impl.processor);
2218 //      }
2219
2220         @Override
2221         final public Session getSession() {
2222                 return session;
2223         }
2224         
2225         final public ResourceSupport getResourceSupport() {
2226                 return resourceSupport;
2227         }
2228
2229         @Override
2230         final public void forEachPredicate(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
2231
2232                 throw new UnsupportedOperationException();
2233
2234 //              assert(subject != null);
2235 //              assert(procedure != null);
2236 //
2237 //              final ListenerBase listener = getListenerBase(procedure);
2238 //
2239 //              IntProcedure ip = new IntProcedure() {
2240 //
2241 //                      AtomicBoolean first = new AtomicBoolean(true);
2242 //
2243 //                      @Override
2244 //                      public void execute(ReadGraphImpl graph, int i) {
2245 //                              try {
2246 //                                      if(first.get()) {
2247 //                                              procedure.execute(graph, querySupport.getResource(i));
2248 //                                      } else {
2249 //                                              procedure.execute(impl.newRestart(graph), querySupport.getResource(i));
2250 //                                      }
2251 //                              } catch (Throwable t2) {
2252 //                                      Logger.defaultLogError(t2);
2253 //                              }
2254 //                      }
2255 //
2256 //                      @Override
2257 //                      public void finished(ReadGraphImpl graph) {
2258 //                              try {
2259 //                                      if(first.compareAndSet(true, false)) {
2260 //                                              procedure.finished(graph);
2261 ////                                            impl.state.barrier.dec(this);
2262 //                                      } else {
2263 //                                              procedure.finished(impl.newRestart(graph));
2264 //                                      }
2265 //
2266 //                              } catch (Throwable t2) {
2267 //                                      Logger.defaultLogError(t2);
2268 //                              }
2269 //                      }
2270 //
2271 //                      @Override
2272 //                      public void exception(ReadGraphImpl graph, Throwable t) {
2273 //                              try {
2274 //                                      if(first.compareAndSet(true, false)) {
2275 //                                              procedure.exception(graph, t);
2276 //                                      } else {
2277 //                                              procedure.exception(impl.newRestart(graph), t);
2278 //                                      }
2279 //                              } catch (Throwable t2) {
2280 //                                      Logger.defaultLogError(t2);
2281 //                              }
2282 //                      }
2283 //
2284 //              };
2285 //
2286 //              int sId = querySupport.getId(subject);
2287 //
2288 //              try {
2289 //                      QueryCache.runnerPredicates(impl, sId, impl.parent, listener, ip);
2290 //              } catch (DatabaseException e) {
2291 //                      Logger.defaultLogError(e);
2292 //              }
2293
2294         }
2295
2296         @Override
2297         final public void forEachPredicate(final ReadGraphImpl impl, final Resource subject, final MultiProcedure<Resource> procedure) {
2298                 
2299                 throw new UnsupportedOperationException();
2300
2301 //              assert(subject != null);
2302 //              assert(procedure != null);
2303 //
2304 //              final ListenerBase listener = getListenerBase(procedure);
2305 //
2306 //              try {
2307 //                      QueryCache.runnerPredicates(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
2308 //
2309 //                              @Override
2310 //                              public void execute(ReadGraphImpl graph, int i) {
2311 //                                      try {
2312 //                                              procedure.execute(querySupport.getResource(i));
2313 //                                      } catch (Throwable t2) {
2314 //                                              Logger.defaultLogError(t2);
2315 //                                      }
2316 //                              }
2317 //
2318 //                              @Override
2319 //                              public void finished(ReadGraphImpl graph) {
2320 //                                      try {
2321 //                                              procedure.finished();
2322 //                                      } catch (Throwable t2) {
2323 //                                              Logger.defaultLogError(t2);
2324 //                                      }
2325 ////                            impl.state.barrier.dec();
2326 //                              }
2327 //
2328 //                              @Override
2329 //                              public void exception(ReadGraphImpl graph, Throwable t) {
2330 //                                      try {
2331 //                                              procedure.exception(t);
2332 //                                      } catch (Throwable t2) {
2333 //                                              Logger.defaultLogError(t2);
2334 //                                      }
2335 ////                            impl.state.barrier.dec();
2336 //                              }
2337 //
2338 //                      });
2339 //              } catch (DatabaseException e) {
2340 //                      Logger.defaultLogError(e);
2341 //              }
2342
2343         }
2344         
2345         @Override
2346         final public IntSet getPredicates(final ReadGraphImpl impl, final Resource subject) throws Throwable {
2347                 return QueryCacheBase.resultPredicates(impl, querySupport.getId(subject), impl.parent, null); 
2348         }
2349
2350         @Override
2351         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2352                         final Resource predicate, final MultiProcedure<Statement> procedure) {
2353
2354                 assert(subject != null);
2355                 assert(predicate != null);
2356                 assert(procedure != null);
2357
2358                 final ListenerBase listener = getListenerBase(procedure);
2359
2360 //              impl.state.barrier.inc();
2361
2362                 try {
2363                         Statements.queryEach(impl, querySupport.getId(subject), querySupport.getId(predicate), this, impl.parent, listener, new TripleIntProcedureAdapter() {
2364
2365                                 @Override
2366                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
2367                                         try {
2368                                                 procedure.execute(querySupport.getStatement(s, p, o));
2369                                         } catch (Throwable t2) {
2370                                                 Logger.defaultLogError(t2);
2371                                         }
2372                                 }
2373
2374                                 @Override
2375                                 public void finished(ReadGraphImpl graph) {
2376                                         try {
2377                                                 procedure.finished();
2378                                         } catch (Throwable t2) {
2379                                                 Logger.defaultLogError(t2);
2380                                         }
2381 //                              impl.state.barrier.dec();
2382                                 }
2383
2384                                 @Override
2385                                 public void exception(ReadGraphImpl graph, Throwable t) {
2386                                         try {
2387                                                 procedure.exception(t);
2388                                         } catch (Throwable t2) {
2389                                                 Logger.defaultLogError(t2);
2390                                         }
2391 //                              impl.state.barrier.dec();
2392                                 }
2393
2394                         });
2395                 } catch (DatabaseException e) {
2396                         Logger.defaultLogError(e);
2397                 }
2398
2399         }
2400
2401         @Override
2402         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2403                         final Resource predicate, final AsyncMultiProcedure<Statement> procedure) {
2404
2405                 assert(subject != null);
2406                 assert(predicate != null);
2407                 assert(procedure != null);
2408
2409                 final ListenerBase listener = getListenerBase(procedure);
2410
2411                 TripleIntProcedureAdapter proc = new TripleIntProcedureAdapter() {
2412
2413                         boolean first = true;
2414
2415                         @Override
2416                         public void execute(ReadGraphImpl graph, int s, int p, int o) {
2417                                 try {
2418                                         if(first) {
2419                                                 procedure.execute(graph, querySupport.getStatement(s, p, o));
2420                                         } else {
2421                                                 procedure.execute(impl.newRestart(graph), querySupport.getStatement(s, p, o));
2422                                         }
2423                                 } catch (Throwable t2) {
2424                                         Logger.defaultLogError(t2);
2425                                 }
2426                         }
2427
2428                         @Override
2429                         public void finished(ReadGraphImpl graph) {
2430
2431                                 try {
2432                                         if(first) {
2433                                                 first = false;
2434                                                 procedure.finished(graph);
2435 //                                              impl.state.barrier.dec(this);
2436                                         } else {
2437                                                 procedure.finished(impl.newRestart(graph));
2438                                         }
2439                                 } catch (Throwable t2) {
2440                                         Logger.defaultLogError(t2);
2441                                 }
2442
2443                         }
2444
2445                         @Override
2446                         public void exception(ReadGraphImpl graph, Throwable t) {
2447
2448                                 try {
2449                                         if(first) {
2450                                                 first = false;
2451                                                 procedure.exception(graph, t);
2452 //                                              impl.state.barrier.dec(this);
2453                                         } else {
2454                                                 procedure.exception(impl.newRestart(graph), t);
2455                                         }
2456                                 } catch (Throwable t2) {
2457                                         Logger.defaultLogError(t2);
2458                                 }
2459
2460                         }
2461
2462                 };
2463
2464                 int sId = querySupport.getId(subject);
2465                 int pId = querySupport.getId(predicate);
2466
2467 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(proc, "#Statements" + sId + "#" + pId);
2468 //              else impl.state.barrier.inc(null, null);
2469
2470                 try {
2471                         Statements.queryEach(impl, sId, pId, this, impl.parent, listener, proc);
2472                 } catch (DatabaseException e) {
2473                         Logger.defaultLogError(e);
2474                 }
2475
2476         }
2477
2478         @Override
2479         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2480                         final Resource predicate, final StatementProcedure procedure) {
2481
2482                 assert(subject != null);
2483                 assert(predicate != null);
2484                 assert(procedure != null);
2485
2486                 final ListenerBase listener = getListenerBase(procedure);
2487
2488                 TripleIntProcedureAdapter proc = new TripleIntProcedureAdapter() {
2489
2490                         boolean first = true;
2491
2492                         @Override
2493                         public void execute(ReadGraphImpl graph, int s, int p, int o) {
2494                                 try {
2495                                         if(first) {
2496                                                 procedure.execute(graph, s, p, o);
2497                                         } else {
2498                                                 procedure.execute(impl.newRestart(graph), s, p, o);
2499                                         }
2500                                 } catch (Throwable t2) {
2501                                         Logger.defaultLogError(t2);
2502                                 }
2503                         }
2504
2505                         @Override
2506                         public void finished(ReadGraphImpl graph) {
2507
2508                                 try {
2509                                         if(first) {
2510                                                 first = false;
2511                                                 procedure.finished(graph);
2512 //                                              impl.state.barrier.dec(this);
2513                                         } else {
2514                                                 procedure.finished(impl.newRestart(graph));
2515                                         }
2516                                 } catch (Throwable t2) {
2517                                         Logger.defaultLogError(t2);
2518                                 }
2519
2520                         }
2521
2522                         @Override
2523                         public void exception(ReadGraphImpl graph, Throwable t) {
2524
2525                                 try {
2526                                         if(first) {
2527                                                 first = false;
2528                                                 procedure.exception(graph, t);
2529 //                                              impl.state.barrier.dec(this);
2530                                         } else {
2531                                                 procedure.exception(impl.newRestart(graph), t);
2532                                         }
2533                                 } catch (Throwable t2) {
2534                                         Logger.defaultLogError(t2);
2535                                 }
2536
2537                         }
2538
2539                 };
2540
2541                 int sId = querySupport.getId(subject);
2542                 int pId = querySupport.getId(predicate);
2543
2544 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(proc, "#Statements" + sId + "#" + pId);
2545 //              else impl.state.barrier.inc(null, null);
2546
2547                 try {
2548                         Statements.queryEach(impl, sId, pId, this, impl.parent, listener, proc);
2549                 } catch (DatabaseException e) {
2550                         Logger.defaultLogError(e);
2551                 }
2552
2553         }
2554         
2555         @Override
2556         final public void forStatementSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Statement> procedure) {
2557
2558                 assert(subject != null);
2559                 assert(predicate != null);
2560                 assert(procedure != null);
2561
2562                 forEachStatement(impl, subject, predicate, new AsyncMultiListener<Statement>() {
2563
2564                         private Set<Statement> current = null;
2565                         private Set<Statement> run = new HashSet<Statement>();
2566
2567                         @Override
2568                         public void execute(AsyncReadGraph graph, Statement result) {
2569
2570                                 boolean found = false;
2571
2572                                 if(current != null) {
2573
2574                                         found = current.remove(result);
2575
2576                                 }
2577
2578                                 if(!found) procedure.add(graph, result);
2579
2580                                 run.add(result);
2581
2582                         }
2583
2584                         @Override
2585                         public void finished(AsyncReadGraph graph) {
2586
2587                                 if(current != null) { 
2588                                         for(Statement r : current) procedure.remove(graph, r);
2589                                 }
2590
2591                                 current = run;
2592
2593                                 run = new HashSet<Statement>();
2594
2595                         }
2596
2597                         @Override
2598                         public void exception(AsyncReadGraph graph, Throwable t) {
2599                                 procedure.exception(graph, t);
2600                         }
2601
2602                         @Override
2603                         public boolean isDisposed() {
2604                                 return procedure.isDisposed();
2605                         }
2606
2607                 });
2608
2609         }
2610
2611         @Override
2612         final public void forEachAssertedStatement(final ReadGraphImpl impl, final Resource subject,
2613                         final Resource predicate, final AsyncMultiProcedure<Statement> procedure) {
2614
2615                 assert(subject != null);
2616                 assert(predicate != null);
2617                 assert(procedure != null);
2618
2619                 final ListenerBase listener = getListenerBase(procedure);
2620
2621 //              impl.state.barrier.inc();
2622
2623                 try {
2624                         QueryCache.runnerAssertedStatements(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new TripleIntProcedureAdapter() {
2625
2626                                 @Override
2627                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
2628                                         try {
2629                                                 procedure.execute(graph, querySupport.getStatement(s, p, o));
2630                                         } catch (Throwable t2) {
2631                                                 Logger.defaultLogError(t2);
2632                                         }
2633                                 }
2634
2635                                 @Override
2636                                 public void finished(ReadGraphImpl graph) {
2637                                         try {
2638                                                 procedure.finished(graph);
2639                                         } catch (Throwable t2) {
2640                                                 Logger.defaultLogError(t2);
2641                                         }
2642 //                              impl.state.barrier.dec();
2643                                 }
2644
2645                                 @Override
2646                                 public void exception(ReadGraphImpl graph, Throwable t) {
2647                                         try {
2648                                                 procedure.exception(graph, t);
2649                                         } catch (Throwable t2) {
2650                                                 Logger.defaultLogError(t2);
2651                                         }
2652 //                              impl.state.barrier.dec();
2653                                 }
2654
2655                         });
2656                 } catch (DatabaseException e) {
2657                         Logger.defaultLogError(e);
2658                 }
2659
2660         }
2661
2662         private static ListenerBase getListenerBase(Object procedure) {
2663                 if(procedure instanceof ListenerBase) return (ListenerBase)procedure;
2664                 else return null;
2665         }
2666
2667         @Override
2668         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final MultiProcedure<Resource> procedure) {
2669
2670                 assert(subject != null);
2671                 assert(predicate != null);
2672                 assert(procedure != null);
2673
2674                 final ListenerBase listener = getListenerBase(procedure);
2675
2676 //              impl.state.barrier.inc();
2677
2678                 try {
2679                         QueryCache.runnerObjects(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new IntProcedure() {
2680
2681                                 @Override
2682                                 public void execute(ReadGraphImpl graph, int i) {
2683                                         try {
2684                                                 procedure.execute(querySupport.getResource(i));
2685                                         } catch (Throwable t2) {
2686                                                 Logger.defaultLogError(t2);
2687                                         }
2688                                 }
2689
2690                                 @Override
2691                                 public void finished(ReadGraphImpl graph) {
2692                                         try {
2693                                                 procedure.finished();
2694                                         } catch (Throwable t2) {
2695                                                 Logger.defaultLogError(t2);
2696                                         }
2697 //                              impl.state.barrier.dec();
2698                                 }
2699
2700                                 @Override
2701                                 public void exception(ReadGraphImpl graph, Throwable t) {
2702                                         System.out.println("forEachObject exception " + t);
2703                                         try {
2704                                                 procedure.exception(t);
2705                                         } catch (Throwable t2) {
2706                                                 Logger.defaultLogError(t2);
2707                                         }
2708 //                              impl.state.barrier.dec();
2709                                 }
2710
2711                         });
2712                 } catch (DatabaseException e) {
2713                         Logger.defaultLogError(e);
2714                 }
2715
2716         }
2717
2718         @Override
2719         final public void forEachDirectPredicate(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
2720
2721                 throw new UnsupportedOperationException();
2722                 
2723 //              assert(subject != null);
2724 //              assert(procedure != null);
2725 //
2726 //              final ListenerBase listener = getListenerBase(procedure);
2727 //
2728 //              MultiIntProcedure proc = new MultiIntProcedure(procedure, impl, querySupport);
2729 //
2730 //              int sId = querySupport.getId(subject);
2731 //
2732 //              try {
2733 //                      QueryCache.runnerDirectPredicates(impl, sId, impl.parent, listener, proc);
2734 //              } catch (DatabaseException e) {
2735 //                      Logger.defaultLogError(e);
2736 //              }
2737
2738         }
2739
2740         @Override
2741         final public void forEachDirectStatement(final ReadGraphImpl impl, final Resource subject, final Procedure<DirectStatements> procedure) {
2742
2743                 assert(subject != null);
2744                 assert(procedure != null);
2745
2746                 final ListenerBase listener = getListenerBase(procedure);
2747
2748                 org.simantics.db.impl.query.DirectStatements.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure);
2749
2750         }
2751
2752         @Override
2753         final public void forEachDirectStatement(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<DirectStatements> procedure, boolean ignoreVirtual) {
2754
2755                 assert(subject != null);
2756                 assert(procedure != null);
2757
2758                 final ListenerBase listener = getListenerBase(procedure);
2759
2760                 org.simantics.db.impl.query.DirectStatements.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure, ignoreVirtual);
2761
2762         }
2763
2764         private static final Resource INVALID_RESOURCE = new ResourceImpl(null, Integer.MIN_VALUE);
2765
2766         @Override
2767         final public void forPossibleObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncProcedure<Resource> procedure) {
2768
2769                 forEachObject(impl, subject, predicate, new AsyncMultiProcedure<Resource>() {
2770
2771                         private Resource single = null;
2772
2773                         @Override
2774                         public synchronized void execute(AsyncReadGraph graph, Resource result) {
2775                                 if(single == null) {
2776                                         single = result;
2777                                 } else {
2778                                         single = INVALID_RESOURCE;
2779                                 }
2780                         }
2781
2782                         @Override
2783                         public synchronized void finished(AsyncReadGraph graph) {
2784                                 if(single == null || single == INVALID_RESOURCE) procedure.execute(graph, null);
2785                                 else procedure.execute(graph, single);
2786                         }
2787
2788                         @Override
2789                         public synchronized void exception(AsyncReadGraph graph, Throwable throwable) {
2790                                 procedure.exception(graph, throwable);
2791                         }
2792
2793                 });
2794
2795         }
2796
2797         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final ListenerBase listener, final IntProcedure procedure) {
2798                 
2799                 final int sId = querySupport.getId(subject);
2800                 final int pId = querySupport.getId(predicate);
2801
2802                 try {
2803                         QueryCache.runnerObjects(impl, sId, pId, impl.parent, listener, procedure);
2804                 } catch (DatabaseException e) {
2805                         Logger.defaultLogError(e);
2806                 }
2807                 
2808         }
2809         
2810         static class Runner2Procedure implements IntProcedure {
2811             
2812             public int single = 0;
2813             public Throwable t = null;
2814
2815             public void clear() {
2816                 single = 0;
2817                 t = null;
2818             }
2819             
2820         @Override
2821         public void execute(ReadGraphImpl graph, int i) {
2822             if(single == 0) single = i;
2823             else single = -1;
2824         }
2825
2826         @Override
2827         public void finished(ReadGraphImpl graph) {
2828             if(single == -1) single = 0;
2829         }
2830
2831         @Override
2832         public void exception(ReadGraphImpl graph, Throwable throwable) {
2833             single = 0;
2834             this.t = throwable;
2835         }
2836         
2837         public int get() throws DatabaseException {
2838             if(t != null) {
2839                 if(t instanceof DatabaseException) throw (DatabaseException)t;
2840                 else throw new DatabaseException(t);
2841             }
2842             return single;
2843         }
2844             
2845         }
2846         
2847         final public int getSingleObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate) throws DatabaseException {
2848                 
2849                 final int sId = querySupport.getId(subject);
2850                 final int pId = querySupport.getId(predicate);
2851
2852                 Runner2Procedure proc = new Runner2Procedure();
2853                 QueryCache.runnerObjects(impl, sId, pId, impl.parent, null, proc);
2854                 return proc.get();
2855             
2856         }
2857
2858         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncMultiProcedure<Resource> procedure) {
2859
2860                 assert(subject != null);
2861                 assert(predicate != null);
2862
2863                 final ListenerBase listener = getListenerBase(procedure);
2864
2865                 if(impl.parent != null || listener != null) {
2866
2867                         IntProcedure ip = new IntProcedure() {
2868
2869                                 AtomicBoolean first = new AtomicBoolean(true);
2870
2871                                 @Override
2872                                 public void execute(ReadGraphImpl graph, int i) {
2873                                         try {
2874                                                 if(first.get()) {
2875                                                         procedure.execute(impl, querySupport.getResource(i));
2876                                                 } else {
2877                                                         procedure.execute(impl.newRestart(graph), querySupport.getResource(i));
2878                                                 }
2879                                         } catch (Throwable t2) {
2880                                                 Logger.defaultLogError(t2);
2881                                         }
2882
2883                                 }
2884
2885                                 @Override
2886                                 public void finished(ReadGraphImpl graph) {
2887                                         try {
2888                                                 if(first.compareAndSet(true, false)) {
2889                                                         procedure.finished(impl);
2890 //                                                      impl.state.barrier.dec(this);
2891                                                 } else {
2892                                                         procedure.finished(impl.newRestart(graph));
2893                                                 }
2894                                         } catch (Throwable t2) {
2895                                                 Logger.defaultLogError(t2);
2896                                         }
2897                                 }
2898
2899                                 @Override
2900                                 public void exception(ReadGraphImpl graph, Throwable t) {
2901                                         try {
2902                                                 procedure.exception(graph, t);
2903                                         } catch (Throwable t2) {
2904                                                 Logger.defaultLogError(t2);
2905                                         }
2906 //                                      impl.state.barrier.dec(this);
2907                                 }
2908
2909                                 @Override
2910                                 public String toString() {
2911                                         return "forEachObject with " + procedure;
2912                                 }
2913
2914                         };
2915
2916 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Objects" + subject + "#" + predicate);
2917 //                      else impl.state.barrier.inc(null, null);
2918
2919                         forEachObject(impl, subject, predicate, listener, ip);
2920
2921                 } else {
2922
2923                         IntProcedure ip = new IntProcedure() {
2924
2925                                 @Override
2926                                 public void execute(ReadGraphImpl graph, int i) {
2927                                         procedure.execute(graph, querySupport.getResource(i));
2928                                 }
2929
2930                                 @Override
2931                                 public void finished(ReadGraphImpl graph) {
2932                                         procedure.finished(graph);
2933                                 }
2934
2935                                 @Override
2936                                 public void exception(ReadGraphImpl graph, Throwable t) {
2937                                         procedure.exception(graph, t);
2938                                 }
2939
2940                                 @Override
2941                                 public String toString() {
2942                                         return "forEachObject with " + procedure;
2943                                 }
2944
2945                         };
2946
2947                         forEachObject(impl, subject, predicate, listener, ip);
2948
2949                 }
2950
2951         }
2952
2953         @Override
2954         final public void forObjectSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Resource> procedure) {
2955
2956                 assert(subject != null);
2957                 assert(predicate != null);
2958                 assert(procedure != null);
2959
2960                 forEachObject(impl, subject, predicate, new AsyncMultiListener<Resource>() {
2961
2962                         private Set<Resource> current = null;
2963                         private Set<Resource> run = new HashSet<Resource>();
2964
2965                         @Override
2966                         public void execute(AsyncReadGraph graph, Resource result) {
2967
2968                                 boolean found = false;
2969
2970                                 if(current != null) {
2971
2972                                         found = current.remove(result);
2973
2974                                 }
2975
2976                                 if(!found) procedure.add(graph, result);
2977
2978                                 run.add(result);
2979
2980                         }
2981
2982                         @Override
2983                         public void finished(AsyncReadGraph graph) {
2984
2985                                 if(current != null) { 
2986                                         for(Resource r : current) procedure.remove(graph, r);
2987                                 }
2988
2989                                 current = run;
2990
2991                                 run = new HashSet<Resource>();
2992
2993                         }
2994
2995                         @Override
2996                         public boolean isDisposed() {
2997                                 return procedure.isDisposed();
2998                         }
2999
3000                         @Override
3001                         public void exception(AsyncReadGraph graph, Throwable t) {
3002                                 procedure.exception(graph, t);
3003                         }
3004
3005                         @Override
3006                         public String toString() {
3007                                 return "forObjectSet " + procedure;
3008                         }
3009
3010                 });
3011
3012         }
3013
3014         @Override
3015         final public void forPredicateSet(final ReadGraphImpl impl, final Resource subject, final AsyncSetListener<Resource> procedure) {
3016
3017                 assert(subject != null);
3018                 assert(procedure != null);
3019
3020                 forEachPredicate(impl, subject, new AsyncMultiListener<Resource>() {
3021
3022                         private Set<Resource> current = null;
3023                         private Set<Resource> run = new HashSet<Resource>();
3024
3025                         @Override
3026                         public void execute(AsyncReadGraph graph, Resource result) {
3027
3028                                 boolean found = false;
3029
3030                                 if(current != null) {
3031
3032                                         found = current.remove(result);
3033
3034                                 }
3035
3036                                 if(!found) procedure.add(graph, result);
3037
3038                                 run.add(result);
3039
3040                         }
3041
3042                         @Override
3043                         public void finished(AsyncReadGraph graph) {
3044
3045                                 if(current != null) { 
3046                                         for(Resource r : current) procedure.remove(graph, r);
3047                                 }
3048
3049                                 current = run;
3050
3051                                 run = new HashSet<Resource>();
3052
3053                         }
3054
3055                         @Override
3056                         public boolean isDisposed() {
3057                                 return procedure.isDisposed();
3058                         }
3059
3060                         @Override
3061                         public void exception(AsyncReadGraph graph, Throwable t) {
3062                                 procedure.exception(graph, t);
3063                         }
3064
3065                         @Override
3066                         public String toString() {
3067                                 return "forPredicateSet " + procedure;
3068                         }
3069
3070                 });
3071
3072         }
3073
3074         @Override
3075         final public void forPrincipalTypeSet(final ReadGraphImpl impl, final Resource subject, final AsyncSetListener<Resource> procedure) {
3076
3077                 assert(subject != null);
3078                 assert(procedure != null);
3079
3080                 forEachPrincipalType(impl, subject, new AsyncMultiListener<Resource>() {
3081
3082                         private Set<Resource> current = null;
3083                         private Set<Resource> run = new HashSet<Resource>();
3084
3085                         @Override
3086                         public void execute(AsyncReadGraph graph, Resource result) {
3087
3088                                 boolean found = false;
3089
3090                                 if(current != null) {
3091
3092                                         found = current.remove(result);
3093
3094                                 }
3095
3096                                 if(!found) procedure.add(graph, result);
3097
3098                                 run.add(result);
3099
3100                         }
3101
3102                         @Override
3103                         public void finished(AsyncReadGraph graph) {
3104
3105                                 if(current != null) { 
3106                                         for(Resource r : current) procedure.remove(graph, r);
3107                                 }
3108
3109                                 current = run;
3110
3111                                 run = new HashSet<Resource>();
3112
3113                         }
3114
3115                         @Override
3116                         public boolean isDisposed() {
3117                                 return procedure.isDisposed();
3118                         }
3119
3120                         @Override
3121                         public void exception(AsyncReadGraph graph, Throwable t) {
3122                                 procedure.exception(graph, t);
3123                         }
3124
3125                         @Override
3126                         public String toString() {
3127                                 return "forPrincipalTypeSet " + procedure;
3128                         }
3129
3130                 });
3131
3132         }
3133
3134         @Override
3135         final public void forAssertedObjectSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Resource> procedure) {
3136
3137                 assert(subject != null);
3138                 assert(predicate != null);
3139                 assert(procedure != null);
3140
3141                 forEachAssertedObject(impl, subject, predicate, new AsyncMultiListener<Resource>() {
3142
3143                         private Set<Resource> current = null;
3144                         private Set<Resource> run = new HashSet<Resource>();
3145
3146                         @Override
3147                         public void execute(AsyncReadGraph graph, Resource result) {
3148
3149                                 boolean found = false;
3150
3151                                 if(current != null) {
3152
3153                                         found = current.remove(result);
3154
3155                                 }
3156
3157                                 if(!found) procedure.add(graph, result);
3158
3159                                 run.add(result);
3160
3161                         }
3162
3163                         @Override
3164                         public void finished(AsyncReadGraph graph) {
3165
3166                                 if(current != null) { 
3167                                         for(Resource r : current) procedure.remove(graph, r);
3168                                 }
3169
3170                                 current = run;
3171
3172                                 run = new HashSet<Resource>();
3173
3174                         }
3175
3176                         @Override
3177                         public boolean isDisposed() {
3178                                 return procedure.isDisposed();
3179                         }
3180
3181                         @Override
3182                         public void exception(AsyncReadGraph graph, Throwable t) {
3183                                 procedure.exception(graph, t);
3184                         }
3185
3186                         @Override
3187                         public String toString() {
3188                                 return "forObjectSet " + procedure;
3189                         }
3190
3191                 });
3192
3193         }
3194
3195         @Override
3196         final public void forAssertedStatementSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Statement> procedure) {
3197
3198                 assert(subject != null);
3199                 assert(predicate != null);
3200                 assert(procedure != null);
3201
3202                 forEachAssertedStatement(impl, subject, predicate, new AsyncMultiListener<Statement>() {
3203
3204                         private Set<Statement> current = null;
3205                         private Set<Statement> run = new HashSet<Statement>();
3206
3207                         @Override
3208                         public void execute(AsyncReadGraph graph, Statement result) {
3209
3210                                 boolean found = false;
3211
3212                                 if(current != null) {
3213
3214                                         found = current.remove(result);
3215
3216                                 }
3217
3218                                 if(!found) procedure.add(graph, result);
3219
3220                                 run.add(result);
3221
3222                         }
3223
3224                         @Override
3225                         public void finished(AsyncReadGraph graph) {
3226
3227                                 if(current != null) { 
3228                                         for(Statement s : current) procedure.remove(graph, s);
3229                                 }
3230
3231                                 current = run;
3232
3233                                 run = new HashSet<Statement>();
3234
3235                         }
3236
3237                         @Override
3238                         public boolean isDisposed() {
3239                                 return procedure.isDisposed();
3240                         }
3241
3242                         @Override
3243                         public void exception(AsyncReadGraph graph, Throwable t) {
3244                                 procedure.exception(graph, t);
3245                         }
3246
3247                         @Override
3248                         public String toString() {
3249                                 return "forStatementSet " + procedure;
3250                         }
3251
3252                 });
3253
3254         }
3255
3256         @Override
3257         final public void forEachAssertedObject(final ReadGraphImpl impl, final Resource subject,
3258                         final Resource predicate, final AsyncMultiProcedure<Resource> procedure) {
3259
3260                 assert(subject != null);
3261                 assert(predicate != null);
3262                 assert(procedure != null);
3263
3264                 final ListenerBase listener = getListenerBase(procedure);
3265
3266                 try {
3267                         QueryCache.runnerAssertedStatements(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new TripleIntProcedure() {
3268
3269                                 @Override
3270                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
3271                                         try {
3272                                                 procedure.execute(graph, querySupport.getResource(o));
3273                                         } catch (Throwable t2) {
3274                                                 Logger.defaultLogError(t2);
3275                                         }
3276                                 }
3277
3278                                 @Override
3279                                 public void finished(ReadGraphImpl graph) {
3280                                         try {               
3281                                                 procedure.finished(graph);
3282                                         } catch (Throwable t2) {
3283                                                 Logger.defaultLogError(t2);
3284                                         }
3285 //                              impl.state.barrier.dec();
3286                                 }
3287
3288                                 @Override
3289                                 public void exception(ReadGraphImpl graph, Throwable t) {
3290                                         try {
3291                                                 procedure.exception(graph, t);
3292                                         } catch (Throwable t2) {
3293                                                 Logger.defaultLogError(t2);
3294                                         }
3295 //                              impl.state.barrier.dec();
3296                                 }
3297
3298                         });
3299                 } catch (DatabaseException e) {
3300                         Logger.defaultLogError(e);
3301                 }
3302
3303         }
3304
3305         @Override
3306         final public void forEachPrincipalType(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
3307
3308                 assert(subject != null);
3309                 assert(procedure != null);
3310
3311                 final ListenerBase listener = getListenerBase(procedure);
3312
3313                 IntProcedure ip = new IntProcedure() {
3314
3315                         @Override
3316                         public void execute(ReadGraphImpl graph, int i) {
3317                                 try {
3318                                         procedure.execute(graph, querySupport.getResource(i));
3319                                 } catch (Throwable t2) {
3320                                         Logger.defaultLogError(t2);
3321                                 }
3322                         }
3323
3324                         @Override
3325                         public void finished(ReadGraphImpl graph) {
3326                                 try {
3327                                         procedure.finished(graph);
3328                                 } catch (Throwable t2) {
3329                                         Logger.defaultLogError(t2);
3330                                 }
3331 //                              impl.state.barrier.dec(this);
3332                         }
3333
3334                         @Override
3335                         public void exception(ReadGraphImpl graph, Throwable t) {
3336                                 try {
3337                                         procedure.exception(graph, t);
3338                                 } catch (Throwable t2) {
3339                                         Logger.defaultLogError(t2);
3340                                 }
3341 //                              impl.state.barrier.dec(this);
3342                         }
3343
3344                 };
3345
3346                 int sId = querySupport.getId(subject);
3347
3348 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#PrincipalTypes#" + sId);
3349 //              else impl.state.barrier.inc(null, null);
3350
3351                 try {
3352                         QueryCache.runnerPrincipalTypes(impl, sId, impl.parent, listener, ip);
3353                 } catch (DatabaseException e) {
3354                         Logger.defaultLogError(e);
3355                 }
3356
3357         }
3358
3359         @Override
3360         final public void forEachPrincipalType(final ReadGraphImpl impl, final Resource subject, final MultiProcedure<Resource> procedure) {
3361
3362                 assert(subject != null);
3363                 assert(procedure != null);
3364
3365                 final ListenerBase listener = getListenerBase(procedure);
3366
3367 //              impl.state.barrier.inc();
3368
3369                 try {
3370                         QueryCache.runnerPrincipalTypes(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
3371
3372                                 @Override
3373                                 public void execute(ReadGraphImpl graph, int i) {
3374                                         try {
3375                                                 procedure.execute(querySupport.getResource(i));
3376                                         } catch (Throwable t2) {
3377                                                 Logger.defaultLogError(t2);
3378                                         }
3379                                 }
3380
3381                                 @Override
3382                                 public void finished(ReadGraphImpl graph) {
3383                                         try {
3384                                                 procedure.finished();
3385                                         } catch (Throwable t2) {
3386                                                 Logger.defaultLogError(t2);
3387                                         }
3388 //                              impl.state.barrier.dec();
3389                                 }
3390
3391                                 @Override
3392                                 public void exception(ReadGraphImpl graph, Throwable t) {
3393                                         try {
3394                                                 procedure.exception(t);
3395                                         } catch (Throwable t2) {
3396                                                 Logger.defaultLogError(t2);
3397                                         }
3398 //                              impl.state.barrier.dec();
3399                                 }
3400
3401                         });
3402                 } catch (DatabaseException e) {
3403                         Logger.defaultLogError(e);
3404                 }
3405         }
3406
3407     final public void forTypes(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3408
3409         assert(subject != null);
3410         assert(procedure != null);
3411
3412         final ListenerBase listener = getListenerBase(procedure);
3413         assert(listener == null);
3414
3415         InternalProcedure<IntSet> ip = new InternalProcedure<IntSet>() {
3416
3417             @Override
3418             public void execute(final ReadGraphImpl graph, IntSet set) {
3419                 procedure.execute(graph, set);
3420             }
3421
3422             @Override
3423             public void exception(ReadGraphImpl graph, Throwable t) {
3424                 procedure.exception(graph, t);
3425             }
3426
3427         };
3428
3429         int sId = querySupport.getId(subject);
3430
3431         try {
3432                         QueryCache.runnerTypes(impl, sId, impl.parent, listener, ip);
3433                 } catch (DatabaseException e) {
3434                         Logger.defaultLogError(e);
3435                 }
3436
3437     }
3438     
3439         @Override
3440         final public IntSet getTypes(final ReadGraphImpl impl, final Resource subject) throws Throwable {
3441
3442                 assert(subject != null);
3443                 
3444                 return QueryCacheBase.resultTypes(impl, querySupport.getId(subject), impl.parent, null);
3445
3446         }
3447
3448         @Override
3449         final public void forRelationInfo(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<RelationInfo> procedure) {
3450                 
3451                 assert(subject != null);
3452                 assert(procedure != null);
3453
3454                 final ListenerBase listener = getListenerBase(procedure);
3455                 assert(listener == null);
3456
3457                 try {
3458                         
3459                         QueryCache.runnerRelationInfoQuery(impl, querySupport.getId(subject), impl.parent, listener, new InternalProcedure<RelationInfo>() {
3460
3461                                 @Override
3462                                 public void execute(final ReadGraphImpl graph, RelationInfo set) {
3463                                         procedure.execute(graph, set);                                  
3464                                 }
3465
3466                                 @Override
3467                                 public void exception(ReadGraphImpl graph, Throwable t) {
3468                                         procedure.exception(graph, t);
3469                                 }
3470
3471                         });
3472                 } catch (DatabaseException e) {
3473                         Logger.defaultLogError(e);
3474                 }
3475
3476         }
3477
3478         @Override
3479         final public void forSupertypes(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3480
3481                 assert(subject != null);
3482                 assert(procedure != null);
3483
3484                 final ListenerBase listener = getListenerBase(procedure);
3485
3486                 try {
3487                         QueryCache.runnerSuperTypes(impl, querySupport.getId(subject), impl.parent, listener, new InternalProcedure<IntSet>() {
3488
3489                                 AtomicBoolean first = new AtomicBoolean(true);
3490
3491                                 @Override
3492                                 public void execute(final ReadGraphImpl graph, IntSet set) {
3493 //                              final HashSet<Resource> result = new HashSet<Resource>();
3494 //                              set.forEach(new TIntProcedure() {
3495 //
3496 //                                      @Override
3497 //                                      public boolean execute(int type) {
3498 //                                              result.add(querySupport.getResource(type));
3499 //                                              return true;
3500 //                                      }
3501 //
3502 //                              });
3503                                         try {
3504                                                 if(first.compareAndSet(true, false)) {
3505                                                         procedure.execute(graph, set);
3506 //                                              impl.state.barrier.dec();
3507                                                 } else {
3508                                                         procedure.execute(impl.newRestart(graph), set);
3509                                                 }
3510                                         } catch (Throwable t2) {
3511                                                 Logger.defaultLogError(t2);
3512                                         }
3513                                 }
3514
3515                                 @Override
3516                                 public void exception(ReadGraphImpl graph, Throwable t) {
3517                                         try {
3518                                                 if(first.compareAndSet(true, false)) {
3519                                                         procedure.exception(graph, t);
3520 //                                              impl.state.barrier.dec();
3521                                                 } else {
3522                                                         procedure.exception(impl.newRestart(graph), t);
3523                                                 }
3524                                         } catch (Throwable t2) {
3525                                                 Logger.defaultLogError(t2);
3526                                         }
3527                                 }
3528
3529                         });
3530                 } catch (DatabaseException e) {
3531                         Logger.defaultLogError(e);
3532                 }
3533
3534         }
3535
3536         @Override
3537         final public void forDirectSuperrelations(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
3538
3539                 assert(subject != null);
3540                 assert(procedure != null);
3541
3542                 final ListenerBase listener = getListenerBase(procedure);
3543
3544                 IntProcedure ip = new IntProcedureAdapter() {
3545
3546                         @Override
3547                         public void execute(final ReadGraphImpl graph, int superRelation) {
3548                                 try {
3549                                         procedure.execute(graph, querySupport.getResource(superRelation));
3550                                 } catch (Throwable t2) {
3551                                         Logger.defaultLogError(t2);
3552                                 }
3553                         }
3554
3555                         @Override
3556                         public void finished(final ReadGraphImpl graph) {
3557                                 try {
3558                                         procedure.finished(graph);
3559                                 } catch (Throwable t2) {
3560                                         Logger.defaultLogError(t2);
3561                                 }
3562 //                              impl.state.barrier.dec(this);
3563                         }
3564
3565
3566                         @Override
3567                         public void exception(ReadGraphImpl graph, Throwable t) {
3568                                 try {
3569                                         procedure.exception(graph, t);
3570                                 } catch (Throwable t2) {
3571                                         Logger.defaultLogError(t2);
3572                                 }
3573 //                              impl.state.barrier.dec(this);
3574                         }
3575
3576                 };
3577
3578                 int sId = querySupport.getId(subject); 
3579
3580 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#DirectSuperRelations#" + sId);
3581 //              else impl.state.barrier.inc(null, null);
3582
3583                 try {
3584                         QueryCache.runnerDirectSuperRelations(impl, sId, impl.parent, listener, ip);
3585                 } catch (DatabaseException e) {
3586                         Logger.defaultLogError(e);
3587                 }
3588                 
3589 //              DirectSuperRelations.queryEach(impl, sId, this, impl.parent, listener, ip);
3590
3591         }
3592
3593         @Override
3594         final public void forPossibleSuperrelation(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Resource> procedure) {
3595
3596                 assert(subject != null);
3597                 assert(procedure != null);
3598
3599                 final ListenerBase listener = getListenerBase(procedure);
3600
3601 //              impl.state.barrier.inc();
3602
3603                 PossibleSuperRelation.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure);
3604
3605         }
3606
3607         @Override
3608         final public void forSuperrelations(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3609
3610                 assert(subject != null);
3611                 assert(procedure != null);
3612
3613                 final ListenerBase listener = getListenerBase(procedure);
3614
3615                 InternalProcedure<IntSet> ip = new InternalProcedure<IntSet>() {
3616
3617                         @Override
3618                         public void execute(final ReadGraphImpl graph, IntSet set) {
3619 //                              final HashSet<Resource> result = new HashSet<Resource>();
3620 //                              set.forEach(new TIntProcedure() {
3621 //
3622 //                                      @Override
3623 //                                      public boolean execute(int type) {
3624 //                                              result.add(querySupport.getResource(type));
3625 //                                              return true;
3626 //                                      }
3627 //
3628 //                              });
3629                                 try {
3630                                         procedure.execute(graph, set);
3631                                 } catch (Throwable t2) {
3632                                         Logger.defaultLogError(t2);
3633                                 }
3634 //                              impl.state.barrier.dec(this);
3635                         }
3636
3637                         @Override
3638                         public void exception(ReadGraphImpl graph, Throwable t) {
3639                                 try {
3640                                         procedure.exception(graph, t);
3641                                 } catch (Throwable t2) {
3642                                         Logger.defaultLogError(t2);
3643                                 }
3644 //                              impl.state.barrier.dec(this);
3645                         }
3646
3647                 };
3648
3649                 int sId = querySupport.getId(subject);
3650
3651 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#SuperRelations#" + sId);
3652 //              else impl.state.barrier.inc(null, null);
3653
3654                 try {
3655                         QueryCache.runnerSuperRelations(impl, sId, impl.parent, listener, ip);
3656                 } catch (DatabaseException e) {
3657                         Logger.defaultLogError(e);
3658                 }
3659
3660         }
3661
3662         final public byte[] getValue(final ReadGraphImpl impl, final Resource subject) throws DatabaseException {
3663           return getValue(impl, querySupport.getId(subject));
3664         }
3665
3666         final public byte[] getValue(final ReadGraphImpl impl, final int subject) throws DatabaseException {
3667                 return QueryCache.resultValueQuery(impl, subject, impl.parent, null); 
3668         }
3669
3670         @Override
3671         final public void forValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<byte[]> procedure) {
3672
3673                 assert(subject != null);
3674                 assert(procedure != null);
3675
3676                 int sId = querySupport.getId(subject);
3677
3678 //              if(procedure != null) {
3679                 
3680                         final ListenerBase listener = getListenerBase(procedure);
3681
3682                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3683
3684                                 AtomicBoolean first = new AtomicBoolean(true);
3685
3686                                 @Override
3687                                 public void execute(ReadGraphImpl graph, byte[] result) {
3688                                         try {
3689                                                 if(first.compareAndSet(true, false)) {
3690                                                         procedure.execute(graph, result);
3691 //                                                      impl.state.barrier.dec(this);
3692                                                 } else {
3693                                                         procedure.execute(impl.newRestart(graph), result);
3694                                                 }
3695                                         } catch (Throwable t2) {
3696                                                 Logger.defaultLogError(t2);
3697                                         }
3698                                 }
3699
3700                                 @Override
3701                                 public void exception(ReadGraphImpl graph, Throwable t) {
3702                                         try {
3703                                                 if(first.compareAndSet(true, false)) {
3704                                                         procedure.exception(graph, t);
3705 //                                                      impl.state.barrier.dec(this);
3706                                                 } else {
3707                                                         procedure.exception(impl.newRestart(graph), t);
3708                                                 }
3709                                         } catch (Throwable t2) {
3710                                                 Logger.defaultLogError(t2);
3711                                         }
3712                                 }
3713
3714                         };
3715
3716 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Value" + sId);
3717 //                      else impl.state.barrier.inc(null, null);
3718
3719                         try {
3720                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3721                         } catch (DatabaseException e) {
3722                                 throw new IllegalStateException("Internal error");
3723                         }
3724
3725 //              } else {
3726 //
3727 //                      return QueryCacheBase.runnerValueQuery(impl, sId, impl.parent, null, null);
3728 //                      
3729 //              }
3730 //              
3731 //              throw new IllegalStateException("Internal error");
3732
3733         }
3734
3735         @Override
3736         final public void forPossibleValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<byte[]> procedure) {
3737
3738                 assert(subject != null);
3739                 assert(procedure != null);
3740
3741                 final ListenerBase listener = getListenerBase(procedure);
3742
3743                 if(impl.parent != null || listener != null) {
3744
3745                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3746
3747                                 AtomicBoolean first = new AtomicBoolean(true);
3748
3749                                 @Override
3750                                 public void execute(ReadGraphImpl graph, byte[] result) {
3751                                         try {
3752                                                 if(first.compareAndSet(true, false)) {
3753                                                         procedure.execute(graph, result);
3754 //                                                      impl.state.barrier.dec(this);
3755                                                 } else {
3756                                                         procedure.execute(impl.newRestart(graph), result);
3757                                                 }
3758                                         } catch (Throwable t2) {
3759                                                 Logger.defaultLogError(t2);
3760                                         }
3761                                 }
3762
3763                                 @Override
3764                                 public void exception(ReadGraphImpl graph, Throwable t) {
3765                                         try {
3766                                                 if(first.compareAndSet(true, false)) {
3767                                                         procedure.exception(graph, t);
3768 //                                                      impl.state.barrier.dec(this);
3769                                                 } else {
3770                                                         procedure.exception(impl.newRestart(graph), t);
3771                                                 }
3772                                         } catch (Throwable t2) {
3773                                                 Logger.defaultLogError(t2);
3774                                         }
3775                                 }
3776
3777                         };
3778
3779                         int sId = querySupport.getId(subject);
3780
3781 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Value" + sId);
3782 //                      else impl.state.barrier.inc(null, null);
3783
3784                         try {
3785                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3786                         } catch (DatabaseException e) {
3787                                 Logger.defaultLogError(e);
3788                         }
3789
3790                 } else {
3791
3792                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3793
3794                                 @Override
3795                                 public void execute(ReadGraphImpl graph, byte[] result) {
3796
3797                                         procedure.execute(graph, result);
3798
3799                                 }
3800
3801                                 @Override
3802                                 public void exception(ReadGraphImpl graph, Throwable t) {
3803
3804                                         procedure.exception(graph, t);
3805
3806                                 }
3807
3808                         };
3809
3810                         int sId = querySupport.getId(subject);
3811
3812                         try {
3813                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3814                         } catch (DatabaseException e) {
3815                                 Logger.defaultLogError(e);
3816                         }
3817
3818                 }
3819
3820         }
3821
3822         @Override
3823         final public void forInverse(final ReadGraphImpl impl, final Resource relation, final AsyncProcedure<Resource> procedure) {
3824
3825                 assert(relation != null);
3826                 assert(procedure != null);
3827
3828                 final ListenerBase listener = getListenerBase(procedure);
3829
3830                 IntProcedure ip = new IntProcedure() {
3831
3832                         private int result = 0;
3833                         
3834                     final AtomicBoolean found = new AtomicBoolean(false);
3835                     final AtomicBoolean done = new AtomicBoolean(false);
3836
3837                         @Override
3838                         public void finished(ReadGraphImpl graph) {
3839                                 
3840                         // Shall fire exactly once!
3841                         if(done.compareAndSet(false, true)) {
3842                                 try {
3843                                         if(result == 0) {
3844                                                         procedure.exception(graph, new NoInverseException(""));
3845 //                                              impl.state.barrier.dec(this);
3846                                         } else {
3847                                                 procedure.execute(graph, querySupport.getResource(result));
3848 //                                              impl.state.barrier.dec(this);
3849                                         }
3850                                 } catch (Throwable t) {
3851                                         Logger.defaultLogError(t);
3852                                 }
3853                         }
3854                         
3855                         }
3856
3857                         @Override
3858                         public void execute(ReadGraphImpl graph, int i) {
3859                                 
3860                                 if(found.compareAndSet(false, true)) {
3861                                         this.result = i;
3862                                 } else {
3863                                         // Shall fire exactly once!
3864                                         if(done.compareAndSet(false, true)) {
3865                                                 try {
3866                                                         procedure.exception(graph, new ManyObjectsForFunctionalRelationException("Multiple items e.g. " + this.result + " and " + result));
3867 //                                                      impl.state.barrier.dec(this);
3868                                                 } catch (Throwable t) {
3869                                                 Logger.defaultLogError(t);
3870                                                 }
3871                                         }
3872                                 }
3873                                 
3874                         }
3875
3876                         @Override
3877                         public void exception(ReadGraphImpl graph, Throwable t) {
3878                                 // Shall fire exactly once!
3879                                 if(done.compareAndSet(false, true)) {
3880                                         try {
3881                                                 procedure.exception(graph, t);
3882 //                                              impl.state.barrier.dec(this);
3883                                         } catch (Throwable t2) {
3884                                         Logger.defaultLogError(t2);
3885                                         }
3886                                 }
3887                         }
3888
3889                 };
3890
3891                 int sId = querySupport.getId(relation);
3892
3893 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#DirectObjects#" + sId);
3894 //              else impl.state.barrier.inc(null, null);
3895
3896                 try {
3897                         QueryCache.runnerObjects(impl, sId, getInverseOf(), impl.parent, listener, ip);
3898                 } catch (DatabaseException e) {
3899                         Logger.defaultLogError(e);
3900                 }
3901
3902         }
3903
3904         @Override
3905         final public void forResource(final ReadGraphImpl impl, final String id, final AsyncProcedure<Resource> procedure) {
3906
3907                 assert(id != null);
3908                 assert(procedure != null);
3909
3910                 InternalProcedure<Integer> ip = new InternalProcedure<Integer>() {
3911
3912                         @Override
3913                         public void execute(ReadGraphImpl graph, Integer result) {
3914                                 try {
3915                                         procedure.execute(graph, querySupport.getResource(result));
3916                                 } catch (Throwable t2) {
3917                                         Logger.defaultLogError(t2);
3918                                 }
3919 //                              impl.state.barrier.dec(this);
3920                         }   
3921
3922                         @Override
3923                         public void exception(ReadGraphImpl graph, Throwable t) {
3924
3925                                 try {
3926                                         procedure.exception(graph, t);
3927                                 } catch (Throwable t2) {
3928                                         Logger.defaultLogError(t2);
3929                                 }
3930 //                              impl.state.barrier.dec(this);
3931                         }
3932
3933                 };
3934
3935 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "Resource");
3936 //              else impl.state.barrier.inc(null, null);
3937
3938                 forResource(impl, id, impl.parent, ip);
3939
3940         }
3941
3942         @Override
3943         final public void forBuiltin(final ReadGraphImpl impl, final String id, final AsyncProcedure<Resource> procedure) {
3944
3945                 assert(id != null);
3946                 assert(procedure != null);
3947
3948 //              impl.state.barrier.inc();
3949
3950                 try {
3951                         forBuiltin(impl, id, impl.parent, new InternalProcedure<Integer>() {
3952
3953                                 @Override
3954                                 public void execute(ReadGraphImpl graph, Integer result) {
3955                                         try {
3956                                                 procedure.execute(graph, querySupport.getResource(result)); 
3957                                         } catch (Throwable t2) {
3958                                                 Logger.defaultLogError(t2);
3959                                         }
3960 //                              impl.state.barrier.dec();
3961                                 }   
3962
3963                                 @Override
3964                                 public void exception(ReadGraphImpl graph, Throwable t) {
3965                                         try {
3966                                                 procedure.exception(graph, t);
3967                                         } catch (Throwable t2) {
3968                                                 Logger.defaultLogError(t2);
3969                                         }
3970 //                              impl.state.barrier.dec();
3971                                 }
3972
3973                         });
3974                 } catch (DatabaseException e) {
3975                         Logger.defaultLogError(e);
3976                 }
3977
3978         }
3979
3980         @Override
3981         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Boolean> procedure) {
3982
3983                 assert(subject != null);
3984                 assert(procedure != null);
3985
3986                 final ListenerBase listener = getListenerBase(procedure);
3987
3988                 try {
3989                         IntSet result = QueryCache.resultDirectPredicates(impl, querySupport.getId(subject), impl.parent, listener);
3990                         procedure.execute(impl, !result.isEmpty());
3991                 } catch (DatabaseException e) {
3992                         procedure.exception(impl, e);
3993                 }
3994                         
3995         }
3996
3997         @Override
3998         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncProcedure<Boolean> procedure) {
3999
4000                 assert(subject != null);
4001                 assert(predicate != null);
4002                 assert(procedure != null);
4003
4004                 AsyncMultiProcedure<Resource> ip = new AsyncMultiProcedureAdapter<Resource>() {
4005
4006                         boolean found = false;
4007
4008                         @Override
4009                         synchronized public void execute(AsyncReadGraph graph, Resource resource) {
4010                                 found = true;
4011                         }
4012
4013                         @Override
4014                         synchronized public void finished(AsyncReadGraph graph) {
4015                                 try {
4016                                         procedure.execute(graph, found);
4017                                 } catch (Throwable t2) {
4018                                         Logger.defaultLogError(t2);
4019                                 }
4020 //                              impl.state.barrier.dec(this);
4021                         }
4022
4023                         @Override
4024                         public void exception(AsyncReadGraph graph, Throwable t) {
4025                                 try {
4026                                         procedure.exception(graph, t);
4027                                 } catch (Throwable t2) {
4028                                         Logger.defaultLogError(t2);
4029                                 }
4030 //                              impl.state.barrier.dec(this);
4031                         }
4032
4033                 };
4034
4035 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#ForEachObject#" + subject + "#" + predicate);
4036 //              else impl.state.barrier.inc(null, null);
4037
4038                 forEachObject(impl, subject, predicate, ip);
4039
4040         }
4041
4042         @Override
4043         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final Resource object, final AsyncProcedure<Boolean> procedure) {
4044
4045                 assert(subject != null);
4046                 assert(predicate != null);
4047                 assert(procedure != null);
4048
4049 //              impl.state.barrier.inc();
4050
4051                 forEachObject(impl, subject, predicate, new AsyncMultiProcedureAdapter<Resource>() {
4052
4053                         boolean found = false;
4054
4055                         @Override
4056                         synchronized public void execute(AsyncReadGraph graph, Resource resource) {
4057                                 if(resource.equals(object)) found = true;
4058                         }
4059
4060                         @Override
4061                         synchronized public void finished(AsyncReadGraph graph) {
4062                                 try {
4063                                         procedure.execute(graph, found);
4064                                 } catch (Throwable t2) {
4065                                         Logger.defaultLogError(t2);
4066                                 }
4067 //                              impl.state.barrier.dec();
4068                         }
4069
4070                         @Override
4071                         public void exception(AsyncReadGraph graph, Throwable t) {
4072                                 try {
4073                                         procedure.exception(graph, t);
4074                                 } catch (Throwable t2) {
4075                                         Logger.defaultLogError(t2);
4076                                 }
4077 //                              impl.state.barrier.dec();
4078                         }
4079
4080                 });
4081
4082         }
4083
4084         @Override
4085         final public void forHasValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Boolean> procedure) {
4086
4087                 assert(subject != null);
4088                 assert(procedure != null);
4089
4090                 final ListenerBase listener = getListenerBase(procedure);
4091
4092 //              impl.state.barrier.inc();
4093
4094                 try {
4095                         QueryCache.runnerValueQuery(impl, querySupport.getId(subject), impl.parent, listener, new InternalProcedure<byte[]>() {
4096
4097                                 @Override
4098                                 public void execute(ReadGraphImpl graph, byte[] object) {
4099                                         boolean result = object != null;
4100                                         try {
4101                                                 procedure.execute(graph, result);
4102                                         } catch (Throwable t2) {
4103                                                 Logger.defaultLogError(t2);
4104                                         }
4105 //                              impl.state.barrier.dec();
4106                                 }
4107
4108                                 @Override
4109                                 public void exception(ReadGraphImpl graph, Throwable t) {
4110                                         try {
4111                                                 procedure.exception(graph, t);
4112                                         } catch (Throwable t2) {
4113                                                 Logger.defaultLogError(t2);
4114                                         }
4115 //                              impl.state.barrier.dec();
4116                                 }
4117
4118                         });
4119                 } catch (DatabaseException e) {
4120                         Logger.defaultLogError(e);
4121                 }
4122
4123         }
4124
4125         @Override
4126         final public void forOrderedSet(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
4127
4128                 assert(subject != null);
4129                 assert(procedure != null);
4130
4131                 final ListenerBase listener = getListenerBase(procedure);
4132
4133                 try {
4134                         
4135                         QueryCache.runnerOrderedSet(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
4136
4137                                 @Override
4138                                 public void exception(ReadGraphImpl graph, Throwable t) {
4139                                         try {
4140                                                 procedure.exception(graph, t);
4141                                         } catch (Throwable t2) {
4142                                                 Logger.defaultLogError(t2);
4143                                         }
4144 //                              impl.state.barrier.dec();
4145                                 }
4146
4147                                 @Override
4148                                 public void execute(ReadGraphImpl graph, int i) {
4149                                         try {
4150                                                 procedure.execute(graph, querySupport.getResource(i));
4151                                         } catch (Throwable t2) {
4152                                                 Logger.defaultLogError(t2);
4153                                         }
4154                                 }
4155
4156                                 @Override
4157                                 public void finished(ReadGraphImpl graph) {
4158                                         try {
4159                                                 procedure.finished(graph);
4160                                         } catch (Throwable t2) {
4161                                                 Logger.defaultLogError(t2);
4162                                         }
4163 //                              impl.state.barrier.dec();
4164                                 }
4165
4166                         });
4167                 } catch (DatabaseException e) {
4168                         Logger.defaultLogError(e);
4169                 }
4170
4171         }
4172
4173 //      @Override
4174 //      final public <T> void query(final ReadGraphImpl impl, final AsyncRead<T> request, final CacheEntry parent, final AsyncProcedure<T> procedure, ListenerBase listener) throws DatabaseException {
4175 //
4176 //              assert(request != null);
4177 //              assert(procedure != null);
4178 //
4179 //              QueryCache.runnerAsyncReadEntry(impl, request, parent, listener, procedure);
4180 //
4181 //      }
4182
4183 //      @Override
4184 //      final public <T> T tryQuery(final ReadGraphImpl graph, final Read<T> request) throws DatabaseException {
4185 //
4186 //              assert(graph != null);
4187 //              assert(request != null);
4188 //
4189 //              final ReadEntry entry = (ReadEntry)cache.getCached(request);
4190 //              if(entry != null && entry.isReady()) {
4191 //                  return (T)entry.get(graph, this, null);
4192 //              } else {
4193 //                      return request.perform(graph);
4194 //              }
4195 //
4196 //      }
4197
4198 //    final public <T> T tryQuery(final ReadGraphImpl graph, final ExternalRead<T> request) throws DatabaseException {
4199 //
4200 //        assert(graph != null);
4201 //        assert(request != null);
4202 //
4203 //        final ExternalReadEntry<T> entry = cache.externalReadMap.get(request);
4204 //        if(entry != null && entry.isReady()) {
4205 //            if(entry.isExcepted()) {
4206 //                Throwable t = (Throwable)entry.getResult();
4207 //                if(t instanceof DatabaseException) throw (DatabaseException)t;
4208 //                else throw new DatabaseException(t);
4209 //            } else {
4210 //                return (T)entry.getResult();
4211 //            }            
4212 //        } else {
4213 //
4214 //            final DataContainer<T> result = new DataContainer<T>();
4215 //            final DataContainer<Throwable> exception = new DataContainer<Throwable>();
4216 //            
4217 //            request.register(graph, new Listener<T>() {
4218 //                
4219 //                @Override
4220 //                public void exception(Throwable t) {
4221 //                    exception.set(t);
4222 //                }
4223 //
4224 //                @Override
4225 //                public void execute(T t) {
4226 //                    result.set(t);
4227 //                }
4228 //
4229 //                @Override
4230 //                public boolean isDisposed() {
4231 //                    return true;
4232 //                }
4233 //            
4234 //            });
4235 //            
4236 //            Throwable t = exception.get();
4237 //            if(t != null) {
4238 //                if(t instanceof DatabaseException) throw (DatabaseException)t;
4239 //                else throw new DatabaseException(t);
4240 //            }
4241 //            
4242 //            return result.get();
4243 //
4244 //        }
4245 //
4246 //    }
4247         
4248 //      @Override
4249 //      final public <T> void tryQuery(final ReadGraphImpl graph, final AsyncRead<T> request, AsyncProcedure<T> procedure) {
4250 //
4251 //              assert(graph != null);
4252 //              assert(request != null);
4253 //
4254 //              final AsyncReadEntry entry = cache.asyncReadMap.get(request);
4255 //              if(entry != null && entry.isReady()) {
4256 //                      if(entry.isExcepted()) {
4257 //                              procedure.exception(graph, (Throwable)entry.getResult());
4258 //                      } else {
4259 //                              procedure.execute(graph, (T)entry.getResult());
4260 //                      }
4261 //              } else {
4262 //                      request.perform(graph, procedure);
4263 //              }
4264 //
4265 //      }
4266
4267         @Override
4268         final public <T> void query(final ReadGraphImpl impl, final MultiRead<T> request, final CacheEntry parent, final AsyncMultiProcedure<T> procedure, ListenerBase listener) {
4269
4270                 assert(request != null);
4271                 assert(procedure != null);
4272
4273                 try {
4274
4275                         queryMultiRead(impl, request, parent, listener, procedure);
4276                         
4277                 } catch (DatabaseException e) {
4278                         
4279                         throw new IllegalStateException(e);
4280                         
4281                 }
4282
4283         }
4284
4285         @Override
4286         final public <T> void query(final ReadGraphImpl impl, final AsyncMultiRead<T> request, final CacheEntry parent, final AsyncMultiProcedure<T> procedure, ListenerBase listener) {
4287
4288                 assert(request != null);
4289                 assert(procedure != null);
4290
4291 //              impl.state.barrier.inc();
4292
4293                 runAsyncMultiRead(impl, request, parent, listener, new AsyncMultiProcedure<T>() {
4294
4295                         public void execute(AsyncReadGraph graph, T result) {
4296
4297                                 try {
4298                                         procedure.execute(graph, result);
4299                                 } catch (Throwable t2) {
4300                                         Logger.defaultLogError(t2);
4301                                 }
4302                         }
4303
4304                         @Override
4305                         public void finished(AsyncReadGraph graph) {
4306
4307                                 try {
4308                                         procedure.finished(graph);
4309                                 } catch (Throwable t2) {
4310                                         Logger.defaultLogError(t2);
4311                                 }
4312
4313 //                              impl.state.barrier.dec();
4314
4315                         }
4316
4317                         @Override
4318                         public String toString() {
4319                                 return procedure.toString();
4320                         }
4321
4322                         @Override
4323                         public void exception(AsyncReadGraph graph, Throwable t) {
4324
4325                                 try {
4326                                         procedure.exception(graph, t);
4327                                 } catch (Throwable t2) {
4328                                         Logger.defaultLogError(t2);
4329                                 }
4330
4331 //                              impl.state.barrier.dec();
4332
4333                         }
4334
4335                 });
4336
4337         }
4338
4339         @Override
4340         final public <T> void query(final ReadGraphImpl impl, final ExternalRead<T> request, final CacheEntry parent, final Procedure<T> procedure, ListenerBase listener) {
4341
4342                 assert(request != null);
4343                 assert(procedure != null);
4344
4345                 try {
4346                 
4347                         queryPrimitiveRead(impl, request, parent, listener, new AsyncProcedure<T>() {
4348         
4349                                 @Override
4350                                 public String toString() {
4351                                         return procedure.toString();
4352                                 }
4353         
4354                                 @Override
4355                                 public void execute(AsyncReadGraph graph, T result) {
4356                                         try {
4357                                                 procedure.execute(result);
4358                                         } catch (Throwable t2) {
4359                                                 Logger.defaultLogError(t2);
4360                                         }
4361                                 }
4362
4363                                 @Override
4364                                 public void exception(AsyncReadGraph graph, Throwable throwable) {
4365                                         try {
4366                                                 procedure.exception(throwable);
4367                                         } catch (Throwable t2) {
4368                                                 Logger.defaultLogError(t2);
4369                                         }
4370                                 }
4371         
4372                         });
4373                         
4374                 } catch (DatabaseException e) {
4375                         
4376                         throw new IllegalStateException(e);
4377                         
4378                 }
4379
4380         }
4381
4382         @Override
4383         public VirtualGraph getProvider(Resource subject, Resource predicate, Resource object) {
4384                 
4385                 return querySupport.getProvider(querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));
4386                 
4387         }
4388         
4389         @Override
4390         public VirtualGraph getProvider(Resource subject, Resource predicate) {
4391                 
4392                 return querySupport.getProvider(querySupport.getId(subject), querySupport.getId(predicate));
4393                 
4394         }
4395
4396         @Override
4397         public VirtualGraph getValueProvider(Resource subject) {
4398                 
4399                 return querySupport.getValueProvider(querySupport.getId(subject));
4400                 
4401         }
4402
4403         public boolean resumeTasks(ReadGraphImpl graph) {
4404
4405                 return querySupport.resume(graph);
4406
4407         }
4408         
4409         public boolean isImmutable(int resourceId) {
4410                 return querySupport.isImmutable(resourceId);
4411         }
4412
4413         public boolean isImmutable(Resource resource) {
4414                 ResourceImpl impl = (ResourceImpl)resource;
4415                 return isImmutable(impl.id);
4416         }
4417         
4418         private Layer0 L0;
4419         
4420         public Layer0 getL0(ReadGraph graph) {
4421                 if(L0 == null) {
4422                         L0 = Layer0.getInstance(graph);
4423                 }
4424                 return L0;
4425         }
4426         
4427 }