06c5ebfed22a0f3c3d51b727e1c0e1dc2338313a
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / DirectQuerySupportImpl.java
1 package fi.vtt.simantics.procore.internal;
2
3 import org.simantics.db.DirectStatements;
4 import org.simantics.db.ReadGraph;
5 import org.simantics.db.RelationInfo;
6 import org.simantics.db.Resource;
7 import org.simantics.db.exception.AssumptionException;
8 import org.simantics.db.exception.DatabaseException;
9 import org.simantics.db.exception.NoSingleResultException;
10 import org.simantics.db.impl.ClusterI;
11 import org.simantics.db.impl.ClusterI.ClusterTypeEnum;
12 import org.simantics.db.impl.ForEachObjectContextProcedure;
13 import org.simantics.db.impl.ForEachObjectProcedure;
14 import org.simantics.db.impl.ForPossibleRelatedValueContextProcedure;
15 import org.simantics.db.impl.ForPossibleRelatedValueProcedure;
16 import org.simantics.db.impl.ResourceImpl;
17 import org.simantics.db.impl.TransientGraph;
18 import org.simantics.db.impl.graph.ReadGraphImpl;
19 import org.simantics.db.procedure.SyncContextMultiProcedure;
20 import org.simantics.db.procedure.SyncContextProcedure;
21 import org.simantics.db.procedure.SyncMultiProcedure;
22 import org.simantics.db.procedure.SyncProcedure;
23 import org.simantics.db.procore.cluster.ClusterBig;
24 import org.simantics.db.procore.cluster.ClusterImpl;
25 import org.simantics.db.procore.cluster.ClusterSmall;
26 import org.simantics.db.procore.cluster.ResourceTableSmall;
27 import org.simantics.db.procore.cluster.ValueTableSmall;
28 import org.simantics.db.service.DirectQuerySupport;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 public class DirectQuerySupportImpl implements DirectQuerySupport {
33
34     private static final Logger LOGGER = LoggerFactory.getLogger(DirectQuerySupportImpl.class);
35
36         final private SessionImplSocket session;
37         
38         DirectQuerySupportImpl(SessionImplSocket session) {
39                 this.session = session;
40         }
41
42         @Override
43         final public DirectStatements getDirectPersistentStatements(ReadGraph graph, final Resource subject) {
44                 ReadGraphImpl impl = (ReadGraphImpl)graph;
45                 return impl.processor.getDirectStatements(impl, subject, true);
46         }
47
48         @Override
49         final public DirectStatements getDirectStatements(ReadGraph graph, final Resource subject) {
50                 ReadGraphImpl impl = (ReadGraphImpl)graph;
51                 return impl.processor.getDirectStatements(impl, subject, false);
52         }
53
54         @Override
55         public RelationInfo getRelationInfo(ReadGraph graph, Resource subject) throws DatabaseException {
56                 ReadGraphImpl impl = (ReadGraphImpl)graph;
57                 return impl.processor.getRelationInfo(impl, subject);
58         }
59
60         @Override
61         public SyncMultiProcedure<Resource> compileForEachObject(ReadGraph graph, final Resource relation, SyncMultiProcedure<Resource> user) throws DatabaseException {
62
63                 RelationInfo info = getRelationInfo(graph, relation);
64                 final int predicateKey = ((ResourceImpl)relation).id;
65                 return new ForEachObjectProcedure(predicateKey, info, session.queryProvider2, user);
66
67         }
68
69         @Override
70         public <C> SyncContextMultiProcedure<C, Resource> compileForEachObject(ReadGraph graph, final Resource relation, SyncContextMultiProcedure<C, Resource> user) throws DatabaseException {
71
72                 RelationInfo info = getRelationInfo(graph, relation);
73                 final int predicateKey = ((ResourceImpl)relation).id;
74                 return new ForEachObjectContextProcedure<C>(predicateKey, info, session.queryProvider2, user);
75
76         }
77
78         @Override
79         public <T> SyncProcedure<T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, SyncProcedure<T> user) throws DatabaseException {
80
81                 RelationInfo info = getRelationInfo(graph, relation);
82                 final int predicateKey = ((ResourceImpl)relation).id;
83                 return new ForPossibleRelatedValueProcedure<T>(predicateKey, info, user);
84
85         }
86
87         @Override
88         public <C, T> SyncContextProcedure<C, T> compilePossibleRelatedValue(ReadGraph graph, final Resource relation, SyncContextProcedure<C, T> user) throws DatabaseException {
89
90                 RelationInfo info = getRelationInfo(graph, relation);
91                 final int predicateKey = ((ResourceImpl)relation).id;
92                 return new ForPossibleRelatedValueContextProcedure<C, T>(predicateKey, info, user);
93
94         }
95
96         @Override
97         public void forEachObjectCompiled(ReadGraph graph, Resource subject, final SyncMultiProcedure<Resource> procedure) {
98                 
99         assert(subject != null);
100         
101         final ForEachObjectProcedure proc = (ForEachObjectProcedure)procedure;
102 //        final RelationInfo info = proc.info;
103
104         final ReadGraphImpl impl = (ReadGraphImpl)graph;
105         final int subjectId = ((ResourceImpl)subject).id;
106
107 //        int callerThread = impl.callerThread;
108 //        int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
109         
110 //        impl.inc();
111         
112 //        if(callerThread == suggestSchedule) {
113                 
114 //              if(info.isFunctional) {
115 //                      querySupport.getObjects4(impl, subjectId, proc);
116 //              } else {
117                 session.querySupport.getObjects4(impl, subjectId, proc);
118 //              }
119                 
120 //        } else {
121 //              
122 //              impl.state.barrier.inc();
123 //            impl.state.barrier.dec(callerThread);
124 //              
125 //              queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
126 //      
127 //                      @Override
128 //                      public void run(int thread) {
129 //                              
130 //                      impl.state.barrier.inc(thread);
131 //                              impl.state.barrier.dec();
132 //
133 //                      if(info.isFunctional) {
134 //                              querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
135 //                      } else {
136 //                              querySupport.getObjects4(impl.newAsync(thread), subjectId, proc);
137 //                      }
138 //                              
139 //                      }
140 //                      
141 //                      @Override
142 //                      public String toString() {
143 //                              return "gaff8";
144 //                      }
145 //      
146 //              });
147 //              
148 //        }
149                 
150         }
151
152         @Override
153         public <C> void forEachObjectCompiled(ReadGraph graph, Resource subject, C context, final SyncContextMultiProcedure<C, Resource> procedure) {
154                 
155                 assert(subject != null);
156
157                 final ForEachObjectContextProcedure<C> proc = (ForEachObjectContextProcedure<C>)procedure;
158                 final RelationInfo info = proc.info;
159
160                 final ReadGraphImpl impl = (ReadGraphImpl)graph;
161                 final int subjectId = ((ResourceImpl)subject).id;
162
163 //              int callerThread = impl.callerThread;
164 //              int suggestSchedule = (subjectId>>16) & queryProvider2.THREAD_MASK;
165
166 //              impl.inc();
167
168                 if(info.isFunctional) {
169                         session.querySupport.getObjects4(impl, subjectId, context, proc);
170                 } else {
171                         session.querySupport.getObjects4(impl, subjectId, context, proc);
172                 }
173                 
174         }
175
176         @Override
177         public <T> void forPossibleRelatedValueCompiled(ReadGraph graph, Resource subject, final SyncProcedure<T> procedure) {
178                 
179                 assert(subject != null);
180
181         final ForPossibleRelatedValueProcedure<T> proc = (ForPossibleRelatedValueProcedure<T>)procedure;
182         final RelationInfo info = proc.info;
183         
184         final ReadGraphImpl impl = (ReadGraphImpl)graph;
185         final int subjectId = ((ResourceImpl)subject).id;
186         
187 //        int callerThread = impl.callerThread;
188 //        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
189         
190 //        impl.inc();
191         
192 //        if(callerThread == suggestSchedule) {
193                 
194 //              if(info.isFunctional) {
195         try {
196                 T result = getRelatedValue4(impl, subjectId, proc);
197                 try {
198                         proc.execute(graph, result);
199                 } catch (DatabaseException e2) {
200                         LOGGER.error("Unexpected exception while handling related value", e2);
201                 }
202         } catch (DatabaseException e) {
203                 try {
204                         proc.exception(graph, e);
205                 } catch (DatabaseException e2) {
206                         LOGGER.error("Unexpected exception while handling related value", e2);
207                 }
208         }
209 //              } else {
210 //                      getRelatedValue4(impl, subjectId, proc);
211 //              }
212                 
213 //        } else {
214 //              
215 //              impl.state.barrier.inc();
216 //            impl.state.barrier.dec(callerThread);
217 //              
218 //              queryProvider2.schedule(callerThread, new SessionTask(suggestSchedule) {
219 //      
220 //                      @Override
221 //                      public void run(int thread) {
222 //
223 //                      impl.state.barrier.inc(thread);
224 //                      impl.state.barrier.dec();
225 //                              
226 //                      if(info.isFunctional) {
227 //                              getRelatedValue4(impl.newAsync(thread), subjectId, proc);
228 //                      } else {
229 //                              getRelatedValue4(impl.newAsync(thread), subjectId, proc);
230 //                      }
231 //                              
232 //                      }
233 //                      
234 //                      @Override
235 //                      public String toString() {
236 //                              return "gaff11";
237 //                      }
238 //      
239 //              });
240 //              
241 //        }
242                 
243         }
244
245         @Override
246         public <C, T> void forPossibleRelatedValueCompiled(ReadGraph graph, Resource subject, C context, final SyncContextProcedure<C, T> procedure) {
247                 
248                 assert(subject != null);
249
250         final ForPossibleRelatedValueContextProcedure<C, T> proc = (ForPossibleRelatedValueContextProcedure<C, T>)procedure;
251         final RelationInfo info = proc.info;
252         
253         final ReadGraphImpl impl = (ReadGraphImpl)graph;
254         final int subjectId = ((ResourceImpl)subject).id;
255         
256 //        int callerThread = impl.callerThread;
257 //        int suggestSchedule = (subjectId>>16) & session.queryProvider2.THREAD_MASK;
258         
259 //        impl.inc();
260                 
261 //        if(info.isFunctional) {
262 //        } else {
263 //              getRelatedValue4(impl, subjectId, context, proc);
264 //        }
265
266                 try {
267                         T result = getRelatedValue4(impl, subjectId, context, proc);
268                 proc.execute(graph, context, result);
269                 } catch (DatabaseException e) {
270                 proc.execute(graph, context, null);
271                 }
272                 
273         }
274         
275 /*      @Override
276         public <T> void forPossibleType(final AsyncReadGraph graph, Resource subject, final AsyncProcedure<Resource> procedure) {
277                 
278         assert(subject != null);
279
280         final ReadGraphImpl impl = (ReadGraphImpl)graph;
281         final int subjectId = ((ResourceImpl)subject).id;
282
283         try {
284                 
285                 final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
286                 if(!cluster.isLoaded()) {
287
288 //                      impl.state.inc(0);
289                         
290                         session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
291                 
292                                         @Override
293                                         public void run() {
294
295                                         try {
296
297                                                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
298                                                 procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
299
300 //                                              impl.state.dec(0);                                              
301                                                 
302                                         } catch (DatabaseException e) {
303                                                 e.printStackTrace();
304                                         }
305                 
306                                         }
307                 
308                                 });
309                                 
310                 } else {
311
312                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
313                         procedure.execute(graph, new ResourceImpl(session.resourceSupport, result));
314                         
315                 }
316                 
317         } catch (DatabaseException e) {
318                 e.printStackTrace();
319         }
320         
321         
322                 
323         }
324         
325         */
326
327         @Override
328         public <C> void forPossibleDirectType(final ReadGraph graph, Resource subject, final C context, final SyncContextProcedure<C, Resource> procedure) {
329                 
330         assert(subject != null);
331
332         final ReadGraphImpl impl = (ReadGraphImpl)graph;
333         final int subjectId = ((ResourceImpl)subject).id;
334         
335         try {
336                 
337                 final ClusterI cluster = session.clusterTable.getClusterByResourceKey(subjectId);
338                 if(!cluster.isLoaded()) {
339
340 //                      impl.state.inc(0);
341                         
342                         session.queryProvider2.requestCluster(impl, cluster.getClusterId(), new Runnable() {
343                 
344                                         @Override
345                                         public void run() {
346
347                                         try {
348
349                                                 ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
350                                                 if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
351                                                         int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
352                                                         procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
353                                                 } else {
354                                                 procedure.execute(graph, context, null);
355                                                 }
356
357 //                                              impl.state.dec(0);
358                                                 
359                                         } catch (DatabaseException e) {
360                                                 LOGGER.error("forPossibleDirectType requestCluster callback failed", e);
361                                         }
362                 
363                                         }
364                 
365                                 });
366                                 
367                 } else {
368
369                         ClusterI.CompleteTypeEnum type = cluster.getCompleteType(subjectId, session.clusterTranslator);
370                         if(ClusterI.CompleteTypeEnum.InstanceOf == type) {
371                                 int result = cluster.getCompleteObjectKey(subjectId, session.clusterTranslator);
372                                 procedure.execute(graph, context, new ResourceImpl(session.resourceSupport, result));
373                         } else {
374                         procedure.execute(graph, context, null);
375                         }
376                         
377                 }
378                 
379         } catch (DatabaseException e) {
380                 
381                 procedure.execute(graph, context, null);
382                 
383         } catch (Throwable t) {
384
385                 LOGGER.error("forPossibleDirectType failed unexpectedly", t);
386                 procedure.execute(graph, context, null);
387                 
388         }
389                 
390         }
391         
392         private <C, T> T getRelatedValue4(final ReadGraphImpl graph, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
393                 
394                 int result = 0;
395                 
396                 final int predicate = procedure.predicateKey;
397
398                 if(subject < 0) {
399
400                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
401                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
402                             return getRelatedValue4(graph, subject, context, procedure);
403                         }
404                         
405                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
406                 for (int id : g.getObjects(subject, predicate)) {
407                         if(result != 0) {
408                                 throw new DatabaseException("Multiple objects");
409                         } else {
410                                 result = id;
411                         }
412                 }
413                         }
414                         
415                         if(result == 0) {
416                                 throw new DatabaseException("No objects for " + subject );
417                         } else {
418                                 return getValue4(graph, null, result, context, procedure);
419                         }
420                         
421                 } 
422                 
423         final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
424         if(!cluster.isLoaded()) {
425                 cluster.load();
426                 return getRelatedValue4(graph, subject, context, procedure);
427         }
428         
429         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
430                         
431                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
432                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
433                                 return getRelatedValue4(graph, subject, context, procedure);
434                         }
435                 
436                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
437                 for (int id : g.getObjects(subject, predicate)) {
438                         if(result != 0) {
439                                 throw new DatabaseException("Multiple objects");
440                         } else {
441                                 result = id;
442                         }
443                 }
444                         }
445                         
446                         return getRelatedDirectValue4(graph, cluster, subject, result, context, procedure);
447                         
448                 } else {
449                         
450                         return getRelatedDirectValue4(graph, cluster, subject, 0, context, procedure);
451                         
452                 }
453                 
454         }
455         
456         private <T> T getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
457                 
458                 Object result = null;
459         
460                 if(subject < 0) {
461
462                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
463                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
464                             return getValue4(graph, containerCluster, subject, procedure);
465                         }
466                         
467                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
468                                 Object value = g.getValue(subject);
469                                 if(value != null) {
470                                         if(result != null) {
471                                                 throw new DatabaseException("Multiple values");
472                                         } else {
473                                                 result = value;
474                                         }
475                                 }
476                         }
477                         
478                         return (T)"name";
479                         
480                 }
481                 
482                 ClusterImpl cluster = containerCluster;
483                 if(!containerCluster.contains(subject)) {
484                         cluster = session.clusterTable.getClusterByResourceKey(subject);
485                         if(!cluster.isLoaded()) {
486                                 cluster.load();
487                                 return getValue4(graph, containerCluster, subject, procedure);
488                         }
489                 }
490                 
491         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
492
493                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
494                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
495                             return getValue4(graph, containerCluster, subject, procedure);
496                         }
497                 
498                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
499                                 Object value = g.getValue(subject);
500                                 if(value != null) {
501                                         if(result != null) {
502                                                 throw new DatabaseException("Multiple values");
503                                         } else {
504                                                 result = value;
505                                         }
506                                 }
507                         }
508                         
509                         if(result != null) {
510                                 return (T)result;
511                         } else {
512                                 if(ClusterTypeEnum.SMALL == cluster.getType())
513                                         return getDirectValue4(graph, (ClusterSmall)cluster, subject);
514                                 else 
515                                         return getDirectValue4(graph, (ClusterBig)cluster, subject);
516                         }
517
518                 } else {
519
520                         if(ClusterTypeEnum.SMALL == cluster.getType())
521                                 return getDirectValue4(graph, (ClusterSmall)cluster, subject);
522                         else 
523                                 return getDirectValue4(graph, (ClusterBig)cluster, subject);
524
525                 }
526         
527         }
528         
529         private <C, T> T getValue4(final ReadGraphImpl graph, final ClusterImpl containerCluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
530                 
531                 Object result = null;
532         
533                 if(subject < 0) {
534
535                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
536                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
537                             return getValue4(graph, containerCluster, subject, context, procedure);
538                         }
539                         
540                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
541                                 Object value = g.getValue(subject);
542                                 if(value != null) {
543                                         if(result != null) {
544                                                 throw new DatabaseException("Multiple values");
545                                         } else {
546                                                 result = value;
547                                         }
548                                 }
549                         }
550
551                         return (T)"name";
552                         
553                 }
554                 
555                 ClusterImpl cluster = containerCluster;
556                 if(!containerCluster.contains(subject)) {
557                         cluster = session.clusterTable.getClusterByResourceKey(subject);
558                         if(!cluster.isLoaded()) {
559                                 cluster.load();
560                                 return getValue4(graph, containerCluster, subject, context, procedure);
561                         }
562                 }
563                 
564         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
565
566                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject)) {
567                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, g -> {});
568                             return getValue4(graph, containerCluster, subject, context, procedure);
569                         }
570                 
571                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
572                                 Object value = g.getValue(subject);
573                                 if(value != null) {
574                                         if(result != null) {
575                                                 throw new DatabaseException("Multiple values");
576                                         } else {
577                                                 result = value;
578                                         }
579                                 }
580                         }
581                         
582                         if(result != null) {
583                                 return (T)result;
584                         } else {
585                                 if(ClusterTypeEnum.SMALL == cluster.getType())
586                                         return getDirectValue4(graph, (ClusterSmall)cluster, subject);
587                                 else 
588                                         return getDirectValue4(graph, (ClusterBig)cluster, subject);
589                         }
590
591                 } else {
592
593                         if(ClusterTypeEnum.SMALL == cluster.getType())
594                                 return getDirectValue4(graph, (ClusterSmall)cluster, subject);
595                         else 
596                                 return getDirectValue4(graph, (ClusterBig)cluster, subject);
597
598                 }
599         
600         }
601
602         private <T> T getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
603
604                 int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
605                 if(so == 0) {
606                         if(result == 0) {
607                                 throw new DatabaseException("No objects " + subject + " " + procedure.predicateKey);
608                         } else {
609                                 return getValue4(graph, cluster, result, procedure);
610                         }
611                 } else {
612                         if(result == 0) {
613                                 return getValue4(graph, cluster, so, procedure);
614                         } else {
615                                 throw new DatabaseException("Multiple objects");
616                         }
617                 }
618
619         }
620         
621         private <C, T> T getRelatedDirectValue4(final ReadGraphImpl graph, final ClusterImpl cluster, final int subject, final int result, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) throws DatabaseException {
622
623                 int so = cluster.getSingleObject(subject, procedure, session.clusterTranslator);
624                 if(so == 0) {
625                         if(result == 0) {
626                                 throw new NoSingleResultException("No objects " + subject + " " + procedure.predicateKey, result);
627                         } else {
628                                 return getValue4(graph, cluster, result, context, procedure);
629                         }
630                 } else {
631                         if(result == 0) {
632                                 return getValue4(graph, cluster, so, context, procedure);
633                         } else {
634                                 throw new NoSingleResultException("Multiple objects for " + subject + " " + procedure.predicateKey, result);
635                         }
636                 }
637                 
638         }
639         
640         public <T> T getRelatedValue4(final ReadGraphImpl graph, final int subject, final ForPossibleRelatedValueProcedure<T> procedure) throws DatabaseException {
641                 
642                 int result = 0;
643                 
644                 final int predicate = procedure.predicateKey;
645
646                 if(subject < 0) {
647
648                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
649                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
650                             return getRelatedValue4(graph, subject, procedure);
651                         }
652                         
653                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
654                 for (int id : g.getObjects(subject, predicate)) {
655                         if(result != 0) {
656                                 throw new DatabaseException("Multiple objects");
657                         } else {
658                                 result = id;
659                         }
660                 }
661                         }
662                         
663                         if(result == 0) {
664                                 throw new DatabaseException("No objects for " + subject );
665                         } else {
666                                 return getValue4(graph, null, result, procedure);
667                         }
668                         
669                 } 
670                 
671         final org.simantics.db.procore.cluster.ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);
672         if(!cluster.isLoaded()) {
673                 cluster.load();
674                 return getRelatedValue4(graph, subject, procedure);
675         }
676         
677         if(cluster.hasVirtual() && session.virtualGraphServerSupport.virtuals.contains(subject)) {
678                         
679                         if(!SessionImplSocket.areVirtualStatementsLoaded(session.virtualGraphServerSupport, subject, predicate)) {
680                             SessionImplSocket.loadVirtualStatements(session.virtualGraphServerSupport, graph, subject, predicate, g -> {});
681                             return getRelatedValue4(graph, subject, procedure);
682                         }
683                 
684                         for(TransientGraph g : session.virtualGraphServerSupport.providers) {
685                 for (int id : g.getObjects(subject, predicate)) {
686                         if(result != 0) {
687                                 throw new DatabaseException("Multiple objects");
688                         } else {
689                                 result = id;
690                         }
691                 }
692                         }
693                         
694                         return getRelatedDirectValue4(graph, cluster, subject, result, procedure);
695                         
696                 } else {
697                         
698                         return getRelatedDirectValue4(graph, cluster, subject, 0, procedure);
699                         
700                 }
701                 
702         }
703
704         /*
705         private <C, T> void getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject, final C context, final ForPossibleRelatedValueContextProcedure<C, T> procedure) {
706                 
707                 try {
708                         byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
709                         T value = (T)utf(bytes);
710                         procedure.execute(graph, context, value);
711                 } catch (DatabaseException e) {
712                         procedure.execute(graph, context, null);
713                 }
714
715 //              graph.dec();
716                 
717         }
718         */
719
720         private <T> T getDirectValue4(final ReadGraphImpl graph, final ClusterBig cluster, final int subject) throws DatabaseException {
721
722                 byte[] bytes = cluster.getValue(subject, session.clusterTranslator);
723                 return (T)utf(bytes);
724
725         }
726
727         private <T> T getDirectValue4(final ReadGraphImpl graph, final ClusterSmall cluster, final int subject) throws DatabaseException {
728
729                 ResourceTableSmall rt = cluster.resourceTable;
730                 ValueTableSmall vt = cluster.valueTable;
731
732                 byte[] bs = vt.table;
733                 long[] ls = rt.table;
734
735                 int index = ((subject&0xFFF) << 1) - 1 + rt.offset;
736
737                 int valueIndex = (int)(ls[index] >>> 24) & 0x3FFFFF + vt.offset;
738
739                 int size = (int)bs[valueIndex++]-1;
740                 if(size <= 0) {
741                         throw new DatabaseException("No value for " + subject); 
742                 }
743                 
744                 char[] chars = new char[size];
745                 valueIndex++;
746                 for(int i=0;i<size;i++) {
747                         chars[i] = (char)bs[valueIndex++];
748                 }
749
750                 return (T)new String(chars);
751
752         }
753
754         private final String utf(byte[] bytes) throws AssumptionException {
755
756                 if(bytes == null) return null;
757
758                 // Read databoard int32 using Length encoding
759                 // https://dev.simantics.org/index.php/Databoard_Specification#Length
760                 int index = 0;
761                 int length = bytes[index++]&0xff; 
762                 if(length >= 0x80) {
763                         if(length >= 0xc0) {
764                                 if(length >= 0xe0) {
765                                         if(length >= 0xf0) {
766                                                 length &= 0x0f;
767                                                 length += ((bytes[index++]&0xff)<<3);
768                                                 length += ((bytes[index++]&0xff)<<11);
769                                                 length += ((bytes[index++]&0xff)<<19);
770                                                 length += 0x10204080;
771                                         }
772                                         else {
773                                                 length &= 0x1f;
774                                                 length += ((bytes[index++]&0xff)<<4);
775                                                 length += ((bytes[index++]&0xff)<<12);
776                                                 length += ((bytes[index++]&0xff)<<20);
777                                                 length += 0x204080;
778                                         }
779                                 }
780                                 else {
781                                         length &= 0x3f;
782                                         length += ((bytes[index++]&0xff)<<5);
783                                         length += ((bytes[index++]&0xff)<<13);
784                                         length += 0x4080;
785                                 }
786                         }
787                         else {
788                                 length &= 0x7f;
789                                 length += ((bytes[index++]&0xff)<<6);
790                                 length += 0x80;
791                         }
792                 }
793
794                 // Copied from DataInputStream
795                 int utflen = length;
796                 char[] chararr = new char[utflen];
797
798                 int c, char2, char3;
799                 int count = index;
800                 int target = index + length;
801                 int chararr_count=0;
802
803                 while (count < target) {
804                         c = (int) bytes[count] & 0xff;
805                         if (c > 127) break;
806                         count++;
807                         chararr[chararr_count++]=(char)c;
808                 }
809
810                 while (count < target) {
811                         c = (int) bytes[count] & 0xff;
812                         switch (c >> 4) {
813                         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
814                                 /* 0xxxxxxx*/
815                                 count++;
816                                 chararr[chararr_count++]=(char)c;
817                                 break;
818                         case 12: case 13:
819                                 /* 110x xxxx   10xx xxxx*/
820                                 count += 2;
821                                 if (count > target)
822                                         throw new AssumptionException(
823                                                         "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
824                                 char2 = (int) bytes[count-1];
825                                 if ((char2 & 0xC0) != 0x80)
826                                         throw new AssumptionException(
827                                                         "malformed input around byte " + count); 
828                                 chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | 
829                                                 (char2 & 0x3F));  
830                                 break;
831                         case 14:
832                                 /* 1110 xxxx  10xx xxxx  10xx xxxx */
833                                 count += 3;
834                                 if (count > target)
835                                         throw new AssumptionException(
836                                                         "malformed input: partial character at end (" + (count-index) + " > " + utflen + ")");
837                                 char2 = (int) bytes[count-2];
838                                 char3 = (int) bytes[count-1];
839                                 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
840                                         throw new AssumptionException(
841                                                         "malformed input around byte " + (count-1));
842                                 chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
843                                                 ((char2 & 0x3F) << 6)  |
844                                                 ((char3 & 0x3F) << 0));
845                                 break;
846                         default:
847                                 /* 10xx xxxx,  1111 xxxx */
848                                 throw new AssumptionException(
849                                                 "malformed input around byte " + count);
850                         }
851                 }
852
853                 // The number of chars produced may be less than utflen
854                 return new String(chararr, 0, chararr_count);
855         }
856
857 }