001package de.deepamehta.core.impl;
002
003import de.deepamehta.core.model.AssociationModel;
004import de.deepamehta.core.model.DeepaMehtaObjectModel;
005import de.deepamehta.core.model.IndexMode;
006import de.deepamehta.core.model.RelatedAssociationModel;
007import de.deepamehta.core.model.RelatedTopicModel;
008import de.deepamehta.core.model.SimpleValue;
009import de.deepamehta.core.model.TopicModel;
010import de.deepamehta.core.service.ResultList;
011import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
012import de.deepamehta.core.storage.spi.DeepaMehtaStorage;
013
014import static java.util.Arrays.asList;
015import java.util.Iterator;
016import java.util.List;
017import java.util.logging.Logger;
018
019
020
021public class StorageDecorator {
022
023    // ---------------------------------------------------------------------------------------------- Instance Variables
024
025    private DeepaMehtaStorage storage;
026
027    private final Logger logger = Logger.getLogger(getClass().getName());
028
029    // ---------------------------------------------------------------------------------------------------- Constructors
030
031    public StorageDecorator(DeepaMehtaStorage storage) {
032        this.storage = storage;
033    }
034
035    // -------------------------------------------------------------------------------------------------- Public Methods
036
037
038
039    // === Topics ===
040
041    /**
042     * @return  The fetched topic.
043     *          Note: its child topics are not fetched.
044     */
045    TopicModel fetchTopic(long topicId) {
046        return storage.fetchTopic(topicId);
047    }
048
049    /**
050     * Looks up a single topic by exact value.
051     * If no such topic exists <code>null</code> is returned.
052     * If more than one topic is found a runtime exception is thrown.
053     * <p>
054     * IMPORTANT: Looking up a topic this way requires the corresponding type to be indexed with indexing mode
055     * <code>dm4.core.key</code>.
056     *
057     * @return  The fetched topic.
058     *          Note: its child topics are not fetched.
059     */
060    TopicModel fetchTopic(String key, SimpleValue value) {
061        return storage.fetchTopic(key, value.value());
062    }
063
064    List<TopicModel> fetchTopics(String key, SimpleValue value) {
065        return storage.fetchTopics(key, value.value());
066    }
067
068    // ---
069
070    /**
071     * @return  The fetched topics.
072     *          Note: their child topics are not fetched.
073     */
074    List<TopicModel> queryTopics(String key, SimpleValue value) {
075        return storage.queryTopics(key, value.value());
076    }
077
078    // ---
079
080    Iterator<TopicModel> fetchAllTopics() {
081        return storage.fetchAllTopics();
082    }
083
084    // ---
085
086    /**
087     * Creates a topic.
088     * <p>
089     * The topic's URI is stored and indexed.
090     *
091     * @return  FIXDOC ### the created topic. Note:
092     *          - the topic URI   is initialzed and     persisted.
093     *          - the topic value is initialzed but not persisted.
094     *          - the type URI    is initialzed but not persisted.
095     */
096    void storeTopic(TopicModel model) {
097        storage.storeTopic(model);
098    }
099
100    /**
101     * Stores and indexes the topic's URI.
102     */
103    void storeTopicUri(long topicId, String uri) {
104        storage.storeTopicUri(topicId, uri);
105    }
106
107    void storeTopicTypeUri(long topicId, String topicTypeUri) {
108        storage.storeTopicTypeUri(topicId, topicTypeUri);
109    }
110
111    // ---
112
113    /**
114     * Convenience method (no indexing).
115     */
116    void storeTopicValue(long topicId, SimpleValue value) {
117        storeTopicValue(topicId, value, asList(IndexMode.OFF), null, null);
118    }
119
120    /**
121     * Stores and indexes the topic's value. ### TODO: separate storing/indexing?
122     */
123    void storeTopicValue(long topicId, SimpleValue value, List<IndexMode> indexModes, String indexKey,
124                                                                                      SimpleValue indexValue) {
125        storage.storeTopicValue(topicId, value, indexModes, indexKey, indexValue);
126    }
127
128    void indexTopicValue(long topicId, IndexMode indexMode, String indexKey, SimpleValue indexValue) {
129        storage.indexTopicValue(topicId, indexMode, indexKey, indexValue);
130    }
131
132    // ---
133
134    /**
135     * Deletes the topic.
136     * <p>
137     * Prerequisite: the topic has no relations.
138     */
139    void deleteTopic(long topicId) {
140        storage.deleteTopic(topicId);
141    }
142
143
144
145    // === Associations ===
146
147    AssociationModel fetchAssociation(long assocId) {
148        return storage.fetchAssociation(assocId);
149    }
150
151    /**
152     * Looks up a single association by exact value.
153     * If no such association exists <code>null</code> is returned.
154     * If more than one association is found a runtime exception is thrown.
155     * <p>
156     * IMPORTANT: Looking up an association this way requires the corresponding type to be indexed with indexing mode
157     * <code>dm4.core.key</code>.
158     *
159     * @return  The fetched association.
160     *          Note: its child topics are not fetched.
161     */
162    AssociationModel fetchAssociation(String key, SimpleValue value) {
163        return storage.fetchAssociation(key, value.value());
164    }
165
166    List<AssociationModel> fetchAssociations(String key, SimpleValue value) {
167        return storage.fetchAssociations(key, value.value());
168    }
169
170    // ---
171
172    /**
173     * Convenience method (checks singularity).
174     *
175     * Returns the association between two topics, qualified by association type and both role types.
176     * If no such association exists <code>null</code> is returned.
177     * If more than one association exist, a runtime exception is thrown.
178     *
179     * @param   assocTypeUri    Association type filter. Pass <code>null</code> to switch filter off.
180     *                          ### FIXME: for methods with a singular return value all filters should be mandatory
181     */
182    AssociationModel fetchAssociation(String assocTypeUri, long topicId1, long topicId2, String roleTypeUri1,
183                                                                                         String roleTypeUri2) {
184        List<AssociationModel> assocs = fetchAssociations(assocTypeUri, topicId1, topicId2, roleTypeUri1, roleTypeUri2);
185        switch (assocs.size()) {
186        case 0:
187            return null;
188        case 1:
189            return assocs.get(0);
190        default:
191            throw new RuntimeException("Ambiguity: there are " + assocs.size() + " \"" + assocTypeUri +
192                "\" associations (topicId1=" + topicId1 + ", topicId2=" + topicId2 + ", " +
193                "roleTypeUri1=\"" + roleTypeUri1 + "\", roleTypeUri2=\"" + roleTypeUri2 + "\")");
194        }
195    }
196
197    /**
198     * Returns the associations between two topics. If no such association exists an empty set is returned.
199     *
200     * @param   assocTypeUri    Association type filter. Pass <code>null</code> to switch filter off.
201     */
202    List<AssociationModel> fetchAssociations(String assocTypeUri, long topicId1, long topicId2,
203                                                                  String roleTypeUri1, String roleTypeUri2) {
204        return storage.fetchAssociations(assocTypeUri, topicId1, topicId2, roleTypeUri1, roleTypeUri2);
205    }
206
207    // ---
208
209    /**
210     * Convenience method (checks singularity).
211     */
212    AssociationModel fetchAssociationBetweenTopicAndAssociation(String assocTypeUri, long topicId, long assocId,
213                                                                     String topicRoleTypeUri, String assocRoleTypeUri) {
214        List<AssociationModel> assocs = fetchAssociationsBetweenTopicAndAssociation(assocTypeUri, topicId, assocId,
215            topicRoleTypeUri, assocRoleTypeUri);
216        switch (assocs.size()) {
217        case 0:
218            return null;
219        case 1:
220            return assocs.get(0);
221        default:
222            throw new RuntimeException("Ambiguity: there are " + assocs.size() + " \"" + assocTypeUri +
223                "\" associations (topicId=" + topicId + ", assocId=" + assocId + ", " +
224                "topicRoleTypeUri=\"" + topicRoleTypeUri + "\", assocRoleTypeUri=\"" + assocRoleTypeUri + "\")");
225        }
226    }
227
228    List<AssociationModel> fetchAssociationsBetweenTopicAndAssociation(String assocTypeUri, long topicId,
229                                                       long assocId, String topicRoleTypeUri, String assocRoleTypeUri) {
230        return storage.fetchAssociationsBetweenTopicAndAssociation(assocTypeUri, topicId, assocId, topicRoleTypeUri,
231            assocRoleTypeUri);
232    }
233
234    // ---
235
236    Iterator<AssociationModel> fetchAllAssociations() {
237        return storage.fetchAllAssociations();
238    }
239
240    long[] fetchPlayerIds(long assocId) {
241        return storage.fetchPlayerIds(assocId);
242    }
243
244    // ---
245
246    /**
247     * Stores and indexes the association's URI.
248     */
249    void storeAssociationUri(long assocId, String uri) {
250        storage.storeAssociationUri(assocId, uri);
251    }
252
253    void storeAssociationTypeUri(long assocId, String assocTypeUri) {
254        storage.storeAssociationTypeUri(assocId, assocTypeUri);
255    }
256
257    void storeRoleTypeUri(long assocId, long playerId, String roleTypeUri) {
258        storage.storeRoleTypeUri(assocId, playerId, roleTypeUri);
259    }
260
261    // ---
262
263    /**
264     * Convenience method (no indexing).
265     */
266    void storeAssociationValue(long assocId, SimpleValue value) {
267        storeAssociationValue(assocId, value, asList(IndexMode.OFF), null, null);
268    }
269
270    /**
271     * Stores and indexes the association's value. ### TODO: separate storing/indexing?
272     */
273    void storeAssociationValue(long assocId, SimpleValue value, List<IndexMode> indexModes, String indexKey,
274                                                                                            SimpleValue indexValue) {
275        storage.storeAssociationValue(assocId, value, indexModes, indexKey, indexValue);
276    }
277
278    void indexAssociationValue(long assocId, IndexMode indexMode, String indexKey, SimpleValue indexValue) {
279        storage.indexAssociationValue(assocId, indexMode, indexKey, indexValue);
280    }
281
282    // ---
283
284    void storeAssociation(AssociationModel model) {
285        storage.storeAssociation(model);
286    }
287
288    void deleteAssociation(long assocId) {
289        storage.deleteAssociation(assocId);
290    }
291
292
293
294    // === Generic Object ===
295
296    DeepaMehtaObjectModel fetchObject(long id) {
297        return storage.fetchObject(id);
298    }
299
300
301
302    // === Traversal ===
303
304    /**
305     * @return  The fetched associations.
306     *          Note: their child topics are not fetched.
307     */
308    List<AssociationModel> fetchTopicAssociations(long topicId) {
309        return storage.fetchTopicAssociations(topicId);
310    }
311
312    List<AssociationModel> fetchAssociationAssociations(long assocId) {
313        return storage.fetchAssociationAssociations(assocId);
314    }
315
316    // ---
317
318    /**
319     * Convenience method (checks singularity).
320     *
321     * @param   assocTypeUri        may be null
322     * @param   myRoleTypeUri       may be null
323     * @param   othersRoleTypeUri   may be null
324     * @param   othersTopicTypeUri  may be null
325     *
326     * @return  The fetched topic.
327     *          Note: its child topics are not fetched.
328     */
329    RelatedTopicModel fetchTopicRelatedTopic(long topicId, String assocTypeUri, String myRoleTypeUri,
330                                                           String othersRoleTypeUri, String othersTopicTypeUri) {
331        ResultList<RelatedTopicModel> topics = fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri,
332            othersRoleTypeUri, othersTopicTypeUri, 0);
333        switch (topics.getSize()) {
334        case 0:
335            return null;
336        case 1:
337            return topics.iterator().next();
338        default:
339            throw new RuntimeException("Ambiguity: there are " + topics.getSize() + " related topics (topicId=" +
340                topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
341                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
342        }
343    }
344
345    /**
346     * @param   assocTypeUri        may be null
347     * @param   myRoleTypeUri       may be null
348     * @param   othersRoleTypeUri   may be null
349     * @param   othersTopicTypeUri  may be null
350     *
351     * @return  The fetched topics.
352     *          Note: their child topics are not fetched.
353     */
354    ResultList<RelatedTopicModel> fetchTopicRelatedTopics(long topicId, String assocTypeUri,
355                                                          String myRoleTypeUri, String othersRoleTypeUri,
356                                                          String othersTopicTypeUri, int maxResultSize) {
357        List<RelatedTopicModel> relTopics = storage.fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri,
358            othersRoleTypeUri, othersTopicTypeUri);
359        // ### TODO: respect maxResultSize
360        return new ResultList(relTopics.size(), relTopics);
361    }
362
363    /**
364     * Convenience method (receives *list* of association types).
365     *
366     * @param   assocTypeUris       may *not* be null
367     * @param   myRoleTypeUri       may be null
368     * @param   othersRoleTypeUri   may be null
369     * @param   othersTopicTypeUri  may be null
370     *
371     * @return  The fetched topics.
372     *          Note: their child topics are not fetched.
373     */
374    ResultList<RelatedTopicModel> fetchTopicRelatedTopics(long topicId, List<String> assocTypeUris,
375                                                          String myRoleTypeUri, String othersRoleTypeUri,
376                                                          String othersTopicTypeUri, int maxResultSize) {
377        ResultList<RelatedTopicModel> result = new ResultList();
378        for (String assocTypeUri : assocTypeUris) {
379            ResultList<RelatedTopicModel> res = fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri,
380                othersRoleTypeUri, othersTopicTypeUri, maxResultSize);
381            result.addAll(res);
382        }
383        return result;
384    }
385
386    // ---
387
388    /**
389     * Convenience method (checks singularity).
390     *
391     * @return  The fetched association.
392     *          Note: its child topics are not fetched.
393     */
394    RelatedAssociationModel fetchTopicRelatedAssociation(long topicId, String assocTypeUri, String myRoleTypeUri,
395                                                         String othersRoleTypeUri, String othersAssocTypeUri) {
396        ResultList<RelatedAssociationModel> assocs = fetchTopicRelatedAssociations(topicId, assocTypeUri, myRoleTypeUri,
397            othersRoleTypeUri, othersAssocTypeUri);
398        switch (assocs.getSize()) {
399        case 0:
400            return null;
401        case 1:
402            return assocs.iterator().next();
403        default:
404            throw new RuntimeException("Ambiguity: there are " + assocs.getSize() + " related associations (topicId=" +
405                topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
406                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri + "\")");
407        }
408    }
409
410    /**
411     * @param   assocTypeUri        may be null
412     * @param   myRoleTypeUri       may be null
413     * @param   othersRoleTypeUri   may be null
414     * @param   othersAssocTypeUri  may be null
415     *
416     * @return  The fetched associations.
417     *          Note: their child topics are not fetched.
418     */
419    ResultList<RelatedAssociationModel> fetchTopicRelatedAssociations(long topicId, String assocTypeUri,
420                                                                      String myRoleTypeUri, String othersRoleTypeUri,
421                                                                      String othersAssocTypeUri) {
422        List<RelatedAssociationModel> relAssocs = storage.fetchTopicRelatedAssociations(topicId, assocTypeUri,
423            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
424        return new ResultList(relAssocs.size(), relAssocs);
425    }
426
427    // ---
428
429    /**
430     * Convenience method (checks singularity).
431     *
432     * @return  The fetched topic.
433     *          Note: its child topics are not fetched.
434     */
435    RelatedTopicModel fetchAssociationRelatedTopic(long assocId, String assocTypeUri, String myRoleTypeUri,
436                                                          String othersRoleTypeUri, String othersTopicTypeUri) {
437        ResultList<RelatedTopicModel> topics = fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri,
438            othersRoleTypeUri, othersTopicTypeUri, 0);
439        switch (topics.getSize()) {
440        case 0:
441            return null;
442        case 1:
443            return topics.iterator().next();
444        default:
445            throw new RuntimeException("Ambiguity: there are " + topics.getSize() + " related topics (assocId=" +
446                assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
447                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
448        }
449    }
450
451    /**
452     * @return  The fetched topics.
453     *          Note: their child topics are not fetched.
454     */
455    ResultList<RelatedTopicModel> fetchAssociationRelatedTopics(long assocId, String assocTypeUri,
456                                                                String myRoleTypeUri, String othersRoleTypeUri,
457                                                                String othersTopicTypeUri, int maxResultSize) {
458        List<RelatedTopicModel> relTopics = storage.fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri,
459            othersRoleTypeUri, othersTopicTypeUri);
460        // ### TODO: respect maxResultSize
461        return new ResultList(relTopics.size(), relTopics);
462    }
463
464    /**
465     * Convenience method (receives *list* of association types).
466     *
467     * @param   assocTypeUris       may be null
468     * @param   myRoleTypeUri       may be null
469     * @param   othersRoleTypeUri   may be null
470     * @param   othersTopicTypeUri  may be null
471     *
472     * @return  The fetched topics.
473     *          Note: their child topics are not fetched.
474     */
475    ResultList<RelatedTopicModel> fetchAssociationRelatedTopics(long assocId, List<String> assocTypeUris,
476                                                                String myRoleTypeUri, String othersRoleTypeUri,
477                                                                String othersTopicTypeUri, int maxResultSize) {
478        ResultList<RelatedTopicModel> result = new ResultList();
479        for (String assocTypeUri : assocTypeUris) {
480            ResultList<RelatedTopicModel> res = fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri,
481                othersRoleTypeUri, othersTopicTypeUri, maxResultSize);
482            result.addAll(res);
483        }
484        return result;
485    }
486
487    // ---
488
489    /**
490     * Convenience method (checks singularity).
491     *
492     * @return  The fetched association.
493     *          Note: its child topics are not fetched.
494     */
495    RelatedAssociationModel fetchAssociationRelatedAssociation(long assocId, String assocTypeUri,
496                                            String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) {
497        ResultList<RelatedAssociationModel> assocs = fetchAssociationRelatedAssociations(assocId, assocTypeUri,
498            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
499        switch (assocs.getSize()) {
500        case 0:
501            return null;
502        case 1:
503            return assocs.iterator().next();
504        default:
505            throw new RuntimeException("Ambiguity: there are " + assocs.getSize() + " related associations (assocId=" +
506                assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
507                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri +
508                "\"),\nresult=" + assocs);
509        }
510    }
511
512    /**
513     * @param   assocTypeUri        may be null
514     * @param   myRoleTypeUri       may be null
515     * @param   othersRoleTypeUri   may be null
516     * @param   othersAssocTypeUri  may be null
517     *
518     * @return  The fetched associations.
519     *          Note: their child topics are not fetched.
520     */
521    ResultList<RelatedAssociationModel> fetchAssociationRelatedAssociations(long assocId, String assocTypeUri,
522                                                                         String myRoleTypeUri, String othersRoleTypeUri,
523                                                                         String othersAssocTypeUri) {
524        List<RelatedAssociationModel> relAssocs = storage.fetchAssociationRelatedAssociations(assocId, assocTypeUri,
525            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
526        return new ResultList(relAssocs.size(), relAssocs);
527    }
528
529    // ---
530
531    /**
532     * Convenience method (checks singularity).
533     *
534     * @param   objectId            id of a topic or an association
535     * @param   assocTypeUri        may be null
536     * @param   myRoleTypeUri       may be null
537     * @param   othersRoleTypeUri   may be null
538     * @param   othersTopicTypeUri  may be null
539     *
540     * @return  The fetched topic.
541     *          Note: its child topics are not fetched.
542     */
543    RelatedTopicModel fetchRelatedTopic(long objectId, String assocTypeUri, String myRoleTypeUri,
544                                                       String othersRoleTypeUri, String othersTopicTypeUri) {
545        ResultList<RelatedTopicModel> topics = fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri,
546            othersRoleTypeUri, othersTopicTypeUri);
547        switch (topics.getSize()) {
548        case 0:
549            return null;
550        case 1:
551            return topics.iterator().next();
552        default:
553            throw new RuntimeException("Ambiguity: there are " + topics.getSize() + " related topics (objectId=" +
554                objectId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
555                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
556        }
557    }
558
559    /**
560     * @param   objectId            id of a topic or an association
561     * @param   assocTypeUri        may be null
562     * @param   myRoleTypeUri       may be null
563     * @param   othersRoleTypeUri   may be null
564     * @param   othersTopicTypeUri  may be null
565     *
566     * @return  The fetched topics.
567     *          Note: their child topics are not fetched.
568     */
569    ResultList<RelatedTopicModel> fetchRelatedTopics(long objectId, String assocTypeUri, String myRoleTypeUri,
570                                                                  String othersRoleTypeUri, String othersTopicTypeUri) {
571        List<RelatedTopicModel> relTopics = storage.fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri,
572            othersRoleTypeUri, othersTopicTypeUri);
573        return new ResultList(relTopics.size(), relTopics);
574    }
575
576    // ### TODO: decorator for fetchRelatedAssociations()
577
578
579
580    // === Properties ===
581
582    Object fetchProperty(long id, String propUri) {
583        return storage.fetchProperty(id, propUri);
584    }
585
586    boolean hasProperty(long id, String propUri) {
587        return storage.hasProperty(id, propUri);
588    }
589
590    // ---
591
592    List<TopicModel> fetchTopicsByProperty(String propUri, Object propValue) {
593        return storage.fetchTopicsByProperty(propUri, propValue);
594    }
595
596    List<TopicModel> fetchTopicsByPropertyRange(String propUri, Number from, Number to) {
597        return storage.fetchTopicsByPropertyRange(propUri, from, to);
598    }
599
600    List<AssociationModel> fetchAssociationsByProperty(String propUri, Object propValue) {
601        return storage.fetchAssociationsByProperty(propUri, propValue);
602    }
603
604    List<AssociationModel> fetchAssociationsByPropertyRange(String propUri, Number from, Number to) {
605        return storage.fetchAssociationsByPropertyRange(propUri, from, to);
606    }
607
608    // ---
609
610    void storeTopicProperty(long topicId, String propUri, Object propValue, boolean addToIndex) {
611        storage.storeTopicProperty(topicId, propUri, propValue, addToIndex);
612    }
613
614    void storeAssociationProperty(long assocId, String propUri, Object propValue, boolean addToIndex) {
615        storage.storeAssociationProperty(assocId, propUri, propValue, addToIndex);
616    }
617
618    // ---
619
620    void removeTopicProperty(long topicId, String propUri) {
621        storage.deleteTopicProperty(topicId, propUri);
622    }
623
624    void removeAssociationProperty(long assocId, String propUri) {
625        storage.deleteAssociationProperty(assocId, propUri);
626    }
627
628
629
630    // === DB ===
631
632    DeepaMehtaTransaction beginTx() {
633        return storage.beginTx();
634    }
635
636    /**
637     * Initializes the database.
638     * Prerequisite: there is an open transaction.
639     *
640     * @return  <code>true</code> if a clean install is detected, <code>false</code> otherwise.
641     */
642    boolean init() {
643        boolean isCleanInstall = storage.setupRootNode();
644        if (isCleanInstall) {
645            logger.info("Starting with a fresh DB -- Setting migration number to 0");
646            storeMigrationNr(0);
647        }
648        return isCleanInstall;
649    }
650
651    void shutdown() {
652        storage.shutdown();
653    }
654
655    // ---
656
657    int fetchMigrationNr() {
658        return (Integer) storage.fetchProperty(0, "core_migration_nr");
659    }
660
661    void storeMigrationNr(int migrationNr) {
662        storage.storeTopicProperty(0, "core_migration_nr", migrationNr, false);     // addToIndex=false
663    }
664
665    // ---
666
667    Object getDatabaseVendorObject() {
668        return storage.getDatabaseVendorObject();
669    }
670
671    Object getDatabaseVendorObject(long objectId) {
672        return storage.getDatabaseVendorObject(objectId);
673    }
674}