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