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