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.ModelFactory;
011import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
012import de.deepamehta.core.storage.spi.DeepaMehtaStorage;
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 DeepaMehtaStorage storage;
033
034    private final Logger logger = Logger.getLogger(getClass().getName());
035
036    // ---------------------------------------------------------------------------------------------------- Constructors
037
038    StorageDecorator(DeepaMehtaStorage 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>dm4.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>dm4.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 DeepaMehtaObjectModelImpl fetchObject(long id) {
307        return (DeepaMehtaObjectModelImpl) storage.fetchObject(id);
308    }
309
310
311
312    // === Traversal ===
313
314    /**
315     * @return  The fetched associations.
316     *          Note: their child topics are not fetched.
317     */
318    final List<AssociationModelImpl> fetchTopicAssociations(long topicId) {
319        return (List<AssociationModelImpl>) storage.fetchTopicAssociations(topicId);
320    }
321
322    final List<AssociationModelImpl> fetchAssociationAssociations(long assocId) {
323        return (List<AssociationModelImpl>) storage.fetchAssociationAssociations(assocId);
324    }
325
326    // ---
327
328    /**
329     * Convenience method (checks singularity).
330     *
331     * @param   assocTypeUri        may be null
332     * @param   myRoleTypeUri       may be null
333     * @param   othersRoleTypeUri   may be null
334     * @param   othersTopicTypeUri  may be null
335     *
336     * @return  The fetched topic.
337     *          Note: its child topics are not fetched.
338     */
339    final RelatedTopicModelImpl fetchTopicRelatedTopic(long topicId, String assocTypeUri, String myRoleTypeUri,
340                                                       String othersRoleTypeUri, String othersTopicTypeUri) {
341        List<RelatedTopicModelImpl> topics = fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri,
342            othersRoleTypeUri, othersTopicTypeUri);
343        switch (topics.size()) {
344        case 0:
345            return null;
346        case 1:
347            return topics.iterator().next();
348        default:
349            throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (topicId=" +
350                topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
351                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
352        }
353    }
354
355    /**
356     * @param   assocTypeUri        may be null
357     * @param   myRoleTypeUri       may be null
358     * @param   othersRoleTypeUri   may be null
359     * @param   othersTopicTypeUri  may be null
360     *
361     * @return  The fetched topics.
362     *          Note: their child topics are not fetched.
363     */
364    final List<RelatedTopicModelImpl> fetchTopicRelatedTopics(long topicId, String assocTypeUri, String myRoleTypeUri,
365                                                              String othersRoleTypeUri, String othersTopicTypeUri) {
366        return (List<RelatedTopicModelImpl>) storage.fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri,
367            othersRoleTypeUri, othersTopicTypeUri);
368    }
369
370    /**
371     * Convenience method (receives *list* of association types).
372     *
373     * @param   assocTypeUris       may *not* be null
374     * @param   myRoleTypeUri       may be null
375     * @param   othersRoleTypeUri   may be null
376     * @param   othersTopicTypeUri  may be null
377     *
378     * @return  The fetched topics.
379     *          Note: their child topics are not fetched.
380     */
381    final List<RelatedTopicModelImpl> fetchTopicRelatedTopics(long topicId, List<String> assocTypeUris,
382                                            String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) {
383        List<RelatedTopicModelImpl> result = new ArrayList();
384        for (String assocTypeUri : assocTypeUris) {
385            result.addAll(fetchTopicRelatedTopics(topicId, assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
386                othersTopicTypeUri));
387        }
388        return result;
389    }
390
391    // ---
392
393    /**
394     * Convenience method (checks singularity).
395     *
396     * @return  The fetched association.
397     *          Note: its child topics are not fetched.
398     */
399    final RelatedAssociationModelImpl fetchTopicRelatedAssociation(long topicId, String assocTypeUri,
400                                            String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) {
401        List<RelatedAssociationModelImpl> assocs = fetchTopicRelatedAssociations(topicId, assocTypeUri, myRoleTypeUri,
402            othersRoleTypeUri, othersAssocTypeUri);
403        switch (assocs.size()) {
404        case 0:
405            return null;
406        case 1:
407            return assocs.iterator().next();
408        default:
409            throw new RuntimeException("Ambiguity: there are " + assocs.size() + " related associations (topicId=" +
410                topicId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
411                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri + "\")");
412        }
413    }
414
415    /**
416     * @param   assocTypeUri        may be null
417     * @param   myRoleTypeUri       may be null
418     * @param   othersRoleTypeUri   may be null
419     * @param   othersAssocTypeUri  may be null
420     *
421     * @return  The fetched associations.
422     *          Note: their child topics are not fetched.
423     */
424    final List<RelatedAssociationModelImpl> fetchTopicRelatedAssociations(long topicId, String assocTypeUri,
425                                            String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) {
426        return (List<RelatedAssociationModelImpl>) storage.fetchTopicRelatedAssociations(topicId, assocTypeUri,
427            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
428    }
429
430    // ---
431
432    /**
433     * Convenience method (checks singularity).
434     *
435     * @return  The fetched topic.
436     *          Note: its child topics are not fetched.
437     */
438    final RelatedTopicModelImpl fetchAssociationRelatedTopic(long assocId, String assocTypeUri, String myRoleTypeUri,
439                                                             String othersRoleTypeUri, String othersTopicTypeUri) {
440        List<RelatedTopicModelImpl> topics = fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri,
441            othersRoleTypeUri, othersTopicTypeUri);
442        switch (topics.size()) {
443        case 0:
444            return null;
445        case 1:
446            return topics.iterator().next();
447        default:
448            throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (assocId=" +
449                assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
450                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
451        }
452    }
453
454    /**
455     * @return  The fetched topics.
456     *          Note: their child topics are not fetched.
457     */
458    final List<RelatedTopicModelImpl> fetchAssociationRelatedTopics(long assocId, String assocTypeUri,
459                                            String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) {
460        return (List<RelatedTopicModelImpl>) storage.fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri,
461            othersRoleTypeUri, othersTopicTypeUri);
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    final List<RelatedTopicModelImpl> fetchAssociationRelatedTopics(long assocId, List<String> assocTypeUris,
476                                            String myRoleTypeUri, String othersRoleTypeUri, String othersTopicTypeUri) {
477        List<RelatedTopicModelImpl> result = new ArrayList();
478        for (String assocTypeUri : assocTypeUris) {
479            result.addAll(fetchAssociationRelatedTopics(assocId, assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
480                othersTopicTypeUri));
481        }
482        return result;
483    }
484
485    // ---
486
487    /**
488     * Convenience method (checks singularity).
489     *
490     * @return  The fetched association.
491     *          Note: its child topics are not fetched.
492     */
493    final RelatedAssociationModelImpl fetchAssociationRelatedAssociation(long assocId, String assocTypeUri,
494                                            String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) {
495        List<RelatedAssociationModelImpl> assocs = fetchAssociationRelatedAssociations(assocId, assocTypeUri,
496            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
497        switch (assocs.size()) {
498        case 0:
499            return null;
500        case 1:
501            return assocs.iterator().next();
502        default:
503            throw new RuntimeException("Ambiguity: there are " + assocs.size() + " related associations (assocId=" +
504                assocId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
505                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersAssocTypeUri=\"" + othersAssocTypeUri +
506                "\"),\nresult=" + assocs);
507        }
508    }
509
510    /**
511     * @param   assocTypeUri        may be null
512     * @param   myRoleTypeUri       may be null
513     * @param   othersRoleTypeUri   may be null
514     * @param   othersAssocTypeUri  may be null
515     *
516     * @return  The fetched associations.
517     *          Note: their child topics are not fetched.
518     */
519    final List<RelatedAssociationModelImpl> fetchAssociationRelatedAssociations(long assocId, String assocTypeUri,
520                                            String myRoleTypeUri, String othersRoleTypeUri, String othersAssocTypeUri) {
521        return (List<RelatedAssociationModelImpl>) storage.fetchAssociationRelatedAssociations(assocId, assocTypeUri,
522            myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
523    }
524
525    // ---
526
527    /**
528     * Convenience method (checks singularity).
529     *
530     * @param   objectId            id of a topic or an association
531     * @param   assocTypeUri        may be null
532     * @param   myRoleTypeUri       may be null
533     * @param   othersRoleTypeUri   may be null
534     * @param   othersTopicTypeUri  may be null
535     *
536     * @return  The fetched topic.
537     *          Note: its child topics are not fetched.
538     */
539    final RelatedTopicModelImpl fetchRelatedTopic(long objectId, String assocTypeUri, String myRoleTypeUri,
540                                                  String othersRoleTypeUri, String othersTopicTypeUri) {
541        List<RelatedTopicModelImpl> topics = fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri,
542            othersRoleTypeUri, othersTopicTypeUri);
543        switch (topics.size()) {
544        case 0:
545            return null;
546        case 1:
547            return topics.iterator().next();
548        default:
549            throw new RuntimeException("Ambiguity: there are " + topics.size() + " related topics (objectId=" +
550                objectId + ", assocTypeUri=\"" + assocTypeUri + "\", myRoleTypeUri=\"" + myRoleTypeUri + "\", " +
551                "othersRoleTypeUri=\"" + othersRoleTypeUri + "\", othersTopicTypeUri=\"" + othersTopicTypeUri + "\")");
552        }
553    }
554
555    /**
556     * @param   objectId            id of a topic or an association
557     * @param   assocTypeUri        may be null
558     * @param   myRoleTypeUri       may be null
559     * @param   othersRoleTypeUri   may be null
560     * @param   othersTopicTypeUri  may be null
561     *
562     * @return  The fetched topics.
563     *          Note: their child topics are not fetched.
564     */
565    final List<RelatedTopicModelImpl> fetchRelatedTopics(long objectId, String assocTypeUri, String myRoleTypeUri,
566                                                         String othersRoleTypeUri, String othersTopicTypeUri) {
567        return (List<RelatedTopicModelImpl>) storage.fetchRelatedTopics(objectId, assocTypeUri, myRoleTypeUri,
568            othersRoleTypeUri, othersTopicTypeUri);
569    }
570
571    // ### TODO: decorator for fetchRelatedAssociations()
572
573
574
575    // === Properties ===
576
577    final Object fetchProperty(long id, String propUri) {
578        return storage.fetchProperty(id, propUri);
579    }
580
581    final boolean hasProperty(long id, String propUri) {
582        return storage.hasProperty(id, propUri);
583    }
584
585    // ---
586
587    final List<TopicModelImpl> fetchTopicsByProperty(String propUri, Object propValue) {
588        return (List<TopicModelImpl>) storage.fetchTopicsByProperty(propUri, propValue);
589    }
590
591    final List<TopicModelImpl> fetchTopicsByPropertyRange(String propUri, Number from, Number to) {
592        return (List<TopicModelImpl>) storage.fetchTopicsByPropertyRange(propUri, from, to);
593    }
594
595    final List<AssociationModelImpl> fetchAssociationsByProperty(String propUri, Object propValue) {
596        return (List<AssociationModelImpl>) storage.fetchAssociationsByProperty(propUri, propValue);
597    }
598
599    final List<AssociationModelImpl> fetchAssociationsByPropertyRange(String propUri, Number from, Number to) {
600        return (List<AssociationModelImpl>) storage.fetchAssociationsByPropertyRange(propUri, from, to);
601    }
602
603    // ---
604
605    final void storeTopicProperty(long topicId, String propUri, Object propValue, boolean addToIndex) {
606        storage.storeTopicProperty(topicId, propUri, propValue, addToIndex);
607    }
608
609    final void storeAssociationProperty(long assocId, String propUri, Object propValue, boolean addToIndex) {
610        storage.storeAssociationProperty(assocId, propUri, propValue, addToIndex);
611    }
612
613    // ---
614
615    final void indexTopicProperty(long topicId, String propUri, Object propValue) {
616        storage.indexTopicProperty(topicId, propUri, propValue);
617    }
618
619    final void indexAssociationProperty(long assocId, String propUri, Object propValue) {
620        storage.indexAssociationProperty(assocId, propUri, propValue);
621    }
622
623    // ---
624
625    final void removeTopicProperty(long topicId, String propUri) {
626        storage.deleteTopicProperty(topicId, propUri);
627    }
628
629    final void removeAssociationProperty(long assocId, String propUri) {
630        storage.deleteAssociationProperty(assocId, propUri);
631    }
632
633
634
635    // === DB ===
636
637    final DeepaMehtaTransaction beginTx() {
638        return storage.beginTx();
639    }
640
641    /**
642     * Initializes the database.
643     * Prerequisite: there is an open transaction.
644     *
645     * @return  <code>true</code> if a clean install is detected, <code>false</code> otherwise.
646     */
647    final boolean init() {
648        boolean isCleanInstall = storage.setupRootNode();
649        if (isCleanInstall) {
650            logger.info("Clean install detected -- Starting with a fresh DB");
651            storeMigrationNr(0);
652        }
653        return isCleanInstall;
654    }
655
656    final void shutdown() {
657        storage.shutdown();
658    }
659
660    // ---
661
662    final int fetchMigrationNr() {
663        return (Integer) fetchProperty(0, "core_migration_nr");
664    }
665
666    final void storeMigrationNr(int migrationNr) {
667        storage.storeTopicProperty(0, "core_migration_nr", migrationNr, false);     // addToIndex=false
668    }
669
670    // ---
671
672    final Object getDatabaseVendorObject() {
673        return storage.getDatabaseVendorObject();
674    }
675
676    final Object getDatabaseVendorObject(long objectId) {
677        return storage.getDatabaseVendorObject(objectId);
678    }
679}