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