1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.db.impl.query;
\r
14 import java.util.Collection;
\r
15 import java.util.concurrent.Semaphore;
\r
17 import org.simantics.databoard.Bindings;
\r
18 import org.simantics.db.DevelopmentKeys;
\r
19 import org.simantics.db.RelationInfo;
\r
20 import org.simantics.db.Resource;
\r
21 import org.simantics.db.common.exception.DebugException;
\r
22 import org.simantics.db.exception.DatabaseException;
\r
23 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
\r
24 import org.simantics.db.impl.graph.ReadGraphImpl;
\r
25 import org.simantics.db.impl.procedure.IntProcedureAdapter;
\r
26 import org.simantics.db.impl.procedure.InternalProcedure;
\r
27 import org.simantics.db.procedure.AsyncMultiProcedure;
\r
28 import org.simantics.db.procedure.ListenerBase;
\r
29 import org.simantics.db.request.RequestFlags;
\r
30 import org.simantics.utils.Development;
\r
36 * statusOrException 4 byte
\r
38 * p2OrParents = 4 byte
\r
45 final public class Objects extends CollectionBinaryQuery<IntProcedure> {
\r
47 public Objects(final int r1, final int r2) {
\r
51 final static Objects entry(final QueryProcessor provider, final int r1, final int r2) {
\r
52 return (Objects)provider.objectsMap.get(r1,r2);
\r
55 final static Collection<Objects> entries(final QueryProcessor processor, final int r1) {
\r
56 return processor.objectsMap.values(r1);
\r
59 public final static void runner(ReadGraphImpl graph, final int r1, final int r2, CacheEntry parent, ListenerBase listener, final IntProcedure procedure) {
\r
61 if(parent == null && listener == null) {
\r
62 Objects.computeForEach(graph, r1, r2, null, procedure);
\r
66 QueryProcessor processor = graph.processor;
\r
68 Objects entry = (Objects)processor.objectsMap.get(r1,r2);
\r
71 entry = new Objects(r1, r2);
\r
73 entry.clearResult(processor.querySupport);
\r
74 entry.putEntry(processor);
\r
76 processor.performForEach(graph, entry, parent, listener, procedure);
\r
80 if(entry.isPending()) {
\r
81 synchronized(entry) {
\r
82 if(entry.isPending()) {
\r
83 processor.registerDependencies(graph, entry, parent, listener, procedure, false);
\r
84 computeForEach(graph, r1, r2, null, procedure);
\r
90 processor.performForEach(graph, entry, parent, listener, procedure);
\r
96 static class Runner2Procedure implements IntProcedure {
\r
98 public int single = 0;
\r
99 public Throwable t = null;
\r
101 public void clear() {
\r
107 public void execute(ReadGraphImpl graph, int i) {
\r
108 if(single == 0) single = i;
\r
113 public void finished(ReadGraphImpl graph) {
\r
114 if(single == -1) single = 0;
\r
118 public void exception(ReadGraphImpl graph, Throwable throwable) {
\r
120 this.t = throwable;
\r
123 public int get() throws DatabaseException {
\r
125 if(t instanceof DatabaseException) throw (DatabaseException)t;
\r
126 else throw new DatabaseException(t);
\r
133 static final Runner2Procedure runner2Procedure = new Runner2Procedure();
\r
135 public final static int runner2(ReadGraphImpl graph, final int r1, final int r2, CacheEntry parent) throws DatabaseException {
\r
137 runner2Procedure.clear();
\r
139 if(parent == null) {
\r
140 Objects.computeForEach(graph, r1, r2, null, runner2Procedure);
\r
141 return runner2Procedure.get();
\r
144 QueryProcessor processor = graph.processor;
\r
146 Objects entry = (Objects)processor.objectsMap.get(r1,r2);
\r
147 if(entry == null) {
\r
149 entry = new Objects(r1, r2);
\r
150 entry.setPending();
\r
151 entry.clearResult(processor.querySupport);
\r
152 entry.putEntry(processor);
\r
154 processor.performForEach(graph, entry, parent, null, runner2Procedure);
\r
155 return runner2Procedure.get();
\r
159 if(entry.isPending()) throw new IllegalStateException();
\r
161 processor.performForEach(graph, entry, parent, null, runner2Procedure);
\r
162 return runner2Procedure.get();
\r
169 public BinaryQuery<IntProcedure> getEntry(QueryProcessor provider) {
\r
170 return provider.objectsMap.get(id);
\r
174 public void putEntry(QueryProcessor provider) {
\r
175 if(Development.DEVELOPMENT) {
\r
176 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_PUT, Bindings.BOOLEAN)) {
\r
177 System.err.println("put " + this);
\r
180 provider.objectsMap.put(id, this);
\r
184 final public void removeEntry(QueryProcessor provider) {
\r
185 provider.objectsMap.remove(id);
\r
188 final static private IntArray getAssertionMap(ReadGraphImpl graph, final int r1, final int r2, final Objects entry) {
\r
190 class AssertionMapProc implements IntProcedure {
\r
192 boolean first = true;
\r
194 private IntArray result;
\r
196 public void addStatement(int s, int p, int o) {
\r
198 if(result.size() == 0) {
\r
203 for(int i = 0;i < result.sizeOrData ; i+=3) {
\r
204 int existingP = result.data[i+1];
\r
205 if(p == existingP) {
\r
206 int existingO = result.data[i+2];
\r
207 if(existingO == o) return;
\r
218 public void execute(ReadGraphImpl graph, int type) {
\r
219 AssertedStatements stms = AssertedStatements.queryEach(graph, type, r2, graph.processor, entry, null, NOPT);
\r
220 if(result == null) {
\r
221 result = stms.getResult();
\r
224 IntArray ia = result;
\r
225 result = new IntArray();
\r
226 if(ia.data != null) {
\r
227 for(int i = 0;i < ia.sizeOrData ; i+=3) addStatement(ia.data[i],ia.data[i+1],ia.data[i+2]);
\r
231 IntArray ia = stms.getResult();
\r
232 if(ia.data != null) {
\r
233 for(int i = 0;i < ia.sizeOrData ; i+=3) addStatement(ia.data[i],ia.data[i+1],ia.data[i+2]);
\r
239 public void finished(ReadGraphImpl graph) {
\r
243 public void exception(ReadGraphImpl graph, Throwable throwable) {
\r
248 AssertionMapProc amp = new AssertionMapProc();
\r
250 // This dependency could be cut
\r
251 PrincipalTypes.queryEach(graph, r1, graph.processor, entry, null, amp);
\r
257 final static private void forSingleAssertion(ReadGraphImpl graph, final int r1, final int r2, final Objects entry, final IntProcedure procedure) {
\r
259 IntArray map = getAssertionMap(graph, r1, r2, entry);
\r
261 if(entry != null) entry.finish(graph, procedure);
\r
262 else procedure.finished(graph);
\r
266 int size = map.size();
\r
269 int value = map.data[2];
\r
271 if(entry != null) {
\r
272 entry.addOrSetFunctional(value);
\r
273 entry.finish(graph, procedure);
\r
275 procedure.execute(graph, value);
\r
276 procedure.finished(graph);
\r
279 } else if(size == 0) {
\r
281 if(entry != null) entry.finish(graph, procedure);
\r
282 else procedure.finished(graph);
\r
286 int candidateS = map.data[0];
\r
287 int candidateO = map.data[2];
\r
289 SuperTypes candidate = SuperTypes.queryEach(graph, candidateS, graph.processor, entry, null, NOP);
\r
290 if(candidate.isExcepted()) {
\r
291 if(entry != null) entry.except((Throwable)candidate.getResult());
\r
292 procedure.exception(graph, (Throwable)candidate.getResult());
\r
295 IntSet candidateIs = candidate.getResult();
\r
297 for(int i=3;i<map.size();i+=3) {
\r
299 int nextS = map.data[i];
\r
300 int nextO = map.data[i+2];
\r
302 if(nextS != candidateS) {
\r
304 if(candidateIs.contains(nextS)) {
\r
306 // Next is a super type of candidate => ignore next
\r
310 SuperTypes next = SuperTypes.queryEach(graph, nextS, graph.processor, entry, null, NOP);
\r
311 if(next.isExcepted()) {
\r
312 if(entry != null) entry.except((Throwable)next.getResult());
\r
313 procedure.exception(graph, (Throwable)next.getResult());
\r
316 IntSet nextIs = next.getResult();
\r
318 if(nextIs.contains(candidateS)) {
\r
320 // Candidate is a super type of next => next is the new candidate
\r
322 candidateS = nextS;
\r
323 candidateO = nextO;
\r
324 candidateIs = nextIs;
\r
328 // candidate and next are unrelated => error
\r
329 ManyObjectsForFunctionalRelationException exception = new ManyObjectsForFunctionalRelationException("Functional relation has conflicting assertions " + r1 + ", " + r2 + " " + map , r1);
\r
331 if(entry != null) entry.except(exception);
\r
332 procedure.exception(graph, exception);
\r
343 if(entry != null) {
\r
344 entry.addOrSetFunctional(candidateO);
\r
345 entry.finish(graph, procedure);
\r
347 procedure.execute(graph, candidateO);
\r
348 procedure.finished(graph);
\r
355 final static InternalProcedure<IntSet> NOP = new InternalProcedure<IntSet>() {
\r
358 public void execute(ReadGraphImpl graph, IntSet result) {
\r
362 public void exception(ReadGraphImpl graph, Throwable throwable) {
\r
367 final static TripleIntProcedure NOPT = new TripleIntProcedure() {
\r
371 public void exception(ReadGraphImpl graph, Throwable throwable) {
\r
375 public void execute(ReadGraphImpl graph, int s, int p, int o) {
\r
379 public void finished(ReadGraphImpl graph) {
\r
384 // Search for one statement
\r
385 final public void computeFunctionalIndex(ReadGraphImpl graph, final QueryProcessor provider, final RelationInfo ri, final IntProcedure procedure) {
\r
386 computeFunctionalIndex(graph, r1(), r2(), this, ri, procedure);
\r
389 // Search for one statement
\r
390 final static public void computeFunctionalIndex(ReadGraphImpl graph, final int r1, final int r2, final Objects entry, final RelationInfo ri, final IntProcedure procedure) {
\r
394 int result = graph.processor.querySupport.getFunctionalObject(r1, r2);
\r
398 // Check for assertions
\r
399 forSingleAssertion(graph, r1, r2, entry, procedure);
\r
401 } else if (result == -1) {
\r
403 graph.processor.querySupport.getObjects(graph, r1, r2, new IntProcedure() {
\r
406 public void execute(ReadGraphImpl graph, int i) {
\r
407 if(entry != null) entry.addOrSetFunctional(i);
\r
408 else procedure.execute(graph, i);
\r
412 public void exception(ReadGraphImpl graph, Throwable t) {
\r
413 if(DebugException.DEBUG) new DebugException(t).printStackTrace();
\r
417 public void finished(ReadGraphImpl graph) {
\r
422 // Check for assertions
\r
423 forSingleAssertion(graph, r1, r2, entry, procedure);
\r
427 // If functional relation was found there is no need to check assertions
\r
428 if(entry != null) {
\r
429 entry.addOrSetFunctional(result);
\r
430 entry.finish(graph, procedure);
\r
432 procedure.execute(graph, result);
\r
433 procedure.finished(graph);
\r
441 // Note! The dependency is intentionally cut!
\r
442 DirectPredicates.queryEach(graph, r1, graph.processor, null, null, new SyncIntProcedure() {
\r
444 boolean found = false;
\r
447 public void run(ReadGraphImpl graph) {
\r
450 if(entry != null) entry.finish(graph, procedure);
\r
451 else procedure.finished(graph);
\r
454 // Check for assertions
\r
455 forSingleAssertion(graph, r1, r2, entry, procedure);
\r
462 public void execute(ReadGraphImpl graph, final int pred) {
\r
468 // Note! The dependency is intentionally cut!
\r
469 DirectObjects.queryEach(graph, r1, pred, graph.processor, null, null, new IntProcedure() {
\r
472 public void execute(ReadGraphImpl graph, int i) {
\r
476 if(entry != null) entry.addOrSetFunctional(i);
\r
477 else procedure.execute(graph, i);
\r
483 ManyObjectsForFunctionalRelationException exception = new ManyObjectsForFunctionalRelationException("Functional relation has more than one statement (r1=" + r1 + ", r2=" + r2 + ").", r1);
\r
484 if(entry != null) entry.except(exception);
\r
485 procedure.exception(graph, exception);
\r
492 public void finished(ReadGraphImpl graph) {
\r
496 public void exception(ReadGraphImpl graph, Throwable t) {
\r
497 procedure.exception(graph, t);
\r
504 SuperRelations.queryEach(graph, pred, graph.processor, entry, null, new InternalProcedure<IntSet>() {
\r
507 public void execute(ReadGraphImpl graph, IntSet result) {
\r
511 if(result.contains(r2)) {
\r
513 // Note! The dependency is intentionally cut!
\r
514 DirectObjects.queryEach(graph, r1, pred, graph.processor, null, null, new IntProcedure() {
\r
517 public void execute(ReadGraphImpl graph, int i) {
\r
521 if(entry != null) entry.addOrSetFunctional(i);
\r
522 else procedure.execute(graph, i);
\r
528 ManyObjectsForFunctionalRelationException exception = new ManyObjectsForFunctionalRelationException("Functional relation has more than one statement (r1=" + r1 + ", r2=" + r2 + ").", r1);
\r
529 if(entry != null) entry.except(exception);
\r
530 procedure.exception(graph, exception);
\r
537 public void finished(ReadGraphImpl graph) {
\r
541 public void exception(ReadGraphImpl graph, Throwable t) {
\r
542 procedure.exception(graph, t);
\r
552 public void exception(ReadGraphImpl graph, Throwable t) {
\r
553 procedure.exception(graph, t);
\r
563 public void finished(ReadGraphImpl graph) {
\r
576 final static private void forAssertions(ReadGraphImpl graph, final int r1, final int r2, final Objects entry, final IntProcedure procedure) {
\r
578 // Note! The dependency is intentionally cut!
\r
579 PrincipalTypes.queryEach(graph, r1, graph.processor, null, null, new SyncIntProcedure() {
\r
582 public void run(ReadGraphImpl graph) {
\r
584 if(entry != null) entry.finish(graph, procedure);
\r
585 else procedure.finished(graph);
\r
589 TripleIntProcedure proc = new TripleIntProcedure() {
\r
592 public void execute(ReadGraphImpl graph, int s, int p, int o) {
\r
593 if(entry != null) entry.addOrSet(o);
\r
594 else procedure.execute(graph, o);
\r
598 public void finished(ReadGraphImpl graph) {
\r
603 public void exception(ReadGraphImpl graph, Throwable t) {
\r
604 if(DebugException.DEBUG) new DebugException(t).printStackTrace();
\r
605 procedure.exception(graph, t);
\r
612 public void execute(ReadGraphImpl graph, int type) {
\r
616 AssertedStatements.queryEach(graph, type, r2, graph.processor, entry, null, proc);
\r
621 public void finished(ReadGraphImpl graph) {
\r
630 final public static void computeNotFunctionalFinalIndex(ReadGraphImpl graph, final int r1, final int r2, final QueryProcessor provider, RelationInfo ri, AsyncMultiProcedure<Resource> procedure) {
\r
636 final public void computeNotFunctionalIndex(ReadGraphImpl graph, RelationInfo ri, final IntProcedure procedure) {
\r
637 computeNotFunctionalIndex(graph, r1(), r2(), this, ri, procedure);
\r
640 final static public void computeNotFunctionalIndex(ReadGraphImpl graph, final int r1, final int r2, final Objects entry, RelationInfo ri, final IntProcedure procedure) {
\r
644 graph.processor.querySupport.getObjects(graph, r1, r2, new IntProcedure() {
\r
647 public void execute(ReadGraphImpl graph, int i) {
\r
648 if(entry != null) entry.addOrSet(i);
\r
649 else procedure.execute(graph, i);
\r
653 public void exception(ReadGraphImpl graph, Throwable t) {
\r
654 if(DebugException.DEBUG) new DebugException(t).printStackTrace();
\r
655 procedure.exception(graph, t);
\r
659 public void finished(ReadGraphImpl graph) {
\r
664 if(ri.isAsserted) {
\r
665 forAssertions(graph, r1, r2, entry, procedure);
\r
667 if(entry != null) entry.finish(graph, procedure);
\r
668 else procedure.finished(graph);
\r
673 // Note! The dependency is intentionally cut!
\r
674 DirectPredicates.queryEach(graph, r1, graph.processor, null, null, new SyncIntProcedure() {
\r
677 public void run(ReadGraphImpl graph) {
\r
679 forAssertions(graph, r1, r2, entry, procedure);
\r
684 public void execute(ReadGraphImpl graph, final int pred) {
\r
690 // Note! The dependency is intentionally cut!
\r
691 DirectObjects.queryEach(graph, r1, pred, graph.processor, null, null, new IntProcedure() {
\r
694 public void execute(ReadGraphImpl graph, int i) {
\r
695 if(entry != null) entry.addOrSet(i);
\r
696 else procedure.execute(graph, i);
\r
700 public void finished(ReadGraphImpl graph) {
\r
705 public void exception(ReadGraphImpl graph, Throwable t) {
\r
706 procedure.exception(graph, t);
\r
716 SuperRelations.queryEach(graph, pred, graph.processor, entry, null, new InternalProcedure<IntSet>() {
\r
719 public void execute(ReadGraphImpl graph, IntSet result) {
\r
721 if(result.contains(r2)) {
\r
725 // Note! The dependency is intentionally cut!
\r
726 DirectObjects.queryEach(graph, r1, pred, graph.processor, null, null, new IntProcedure() {
\r
729 public void execute(ReadGraphImpl graph, int i) {
\r
730 if(entry != null) entry.addOrSet(i);
\r
731 else procedure.execute(graph, i);
\r
735 public void finished(ReadGraphImpl graph) {
\r
740 public void exception(ReadGraphImpl graph, Throwable t) {
\r
741 if(DebugException.DEBUG) new DebugException(t).printStackTrace();
\r
742 procedure.exception(graph, t);
\r
755 public void exception(ReadGraphImpl graph, Throwable t) {
\r
756 procedure.exception(graph, t);
\r
767 public void finished(ReadGraphImpl graph) {
\r
778 public void computeForEach(ReadGraphImpl graph, final QueryProcessor provider, final IntProcedure procedure, final boolean store) {
\r
779 computeForEach(graph, r1(), r2(), this, procedure);
\r
782 public static void computeForEach(ReadGraphImpl graph, final int r1, final int r2, final Objects entry, final IntProcedure procedure) {
\r
784 RelationInfo ri = RelationInfoQuery.queryEach(graph, r2, graph.processor, entry, null, ip);
\r
785 graph.ensureLoaded(r1, r2);
\r
786 if(ri.isFunctional) {
\r
787 computeFunctionalIndex(graph, r1, r2, entry, ri, procedure);
\r
789 computeNotFunctionalIndex(graph, r1, r2, entry, ri, procedure);
\r
794 final static InternalProcedure<RelationInfo> ip = new InternalProcedure<RelationInfo>() {
\r
797 public void execute(ReadGraphImpl graph, RelationInfo result) {
\r
801 public void exception(ReadGraphImpl graph, Throwable throwable) {
\r
807 public String toString() {
\r
808 return "Objects[" + r1() + " - " + r2() + "]";
\r
811 final private void finish(ReadGraphImpl graph, IntProcedure procedure) {
\r
813 assert(assertPending());
\r
815 synchronized(this) {
\r
819 IntArray v = (IntArray)getResult();
\r
821 if(v.data == null) {
\r
822 if(v.sizeOrData != IntArray.NO_DATA) {
\r
823 procedure.execute(graph, v.sizeOrData);
\r
826 for(int i = 0;i < v.sizeOrData ; i++) {
\r
827 procedure.execute(graph, v.data[i]);
\r
831 procedure.finished(graph);
\r
835 final public void addOrSet(int add) {
\r
837 assert(assertPending());
\r
839 IntArray value = (IntArray)getResult();
\r
840 synchronized(value) {
\r
846 final public void addOrSetFunctional(int add) {
\r
848 assert(isPending());
\r
850 IntArray value = (IntArray)getResult();
\r
856 public void performFromCache(ReadGraphImpl graph, QueryProcessor provider, final IntProcedure procedure) {
\r
860 if(handleException(graph, procedure)) return;
\r
862 final IntArray value = (IntArray)getResult();
\r
863 if(value.data == null) {
\r
864 if(value.sizeOrData != IntArray.NO_DATA) procedure.execute(graph, value.sizeOrData);
\r
866 for(int i = 0;i < value.sizeOrData ; i++) procedure.execute(graph, value.data[i]);
\r
869 procedure.finished(graph);
\r
874 public void recompute(ReadGraphImpl graph, QueryProcessor provider) {
\r
876 final Semaphore s = new Semaphore(0);
\r
878 computeForEach(graph, provider, new IntProcedureAdapter() {
\r
881 public void finished(ReadGraphImpl graph) {
\r
886 public void exception(ReadGraphImpl graph, Throwable t) {
\r
888 new Error("Error in recompute.", t).printStackTrace();
\r
893 while(!s.tryAcquire()) {
\r
894 provider.resume(graph);
\r
900 public int type() {
\r
901 return RequestFlags.IMMEDIATE_UPDATE;
\r
905 boolean isImmutable(ReadGraphImpl graph) {
\r
906 return graph.processor.isImmutable(r1());
\r