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