001package de.deepamehta.core.impl;
002
003import de.deepamehta.core.ChildTopics;
004import de.deepamehta.core.RelatedTopic;
005import de.deepamehta.core.Topic;
006import de.deepamehta.core.model.ChildTopicsModel;
007import de.deepamehta.core.model.RelatedTopicModel;
008import de.deepamehta.core.model.SimpleValue;
009import de.deepamehta.core.model.TopicModel;
010import de.deepamehta.core.service.ModelFactory;
011
012import java.util.ArrayList;
013import java.util.Iterator;
014import java.util.List;
015import java.util.logging.Logger;
016
017
018
019/**
020 * A child topics model that is attached to the DB.
021 */
022class ChildTopicsImpl implements ChildTopics {
023
024    // ---------------------------------------------------------------------------------------------- Instance Variables
025
026    private ChildTopicsModelImpl model;         // underlying model
027
028    private DeepaMehtaObjectModelImpl parent;   // the parent object this ChildTopics belongs to
029
030    private PersistenceLayer pl;
031    private ModelFactory mf;
032
033    private Logger logger = Logger.getLogger(getClass().getName());
034
035    // ---------------------------------------------------------------------------------------------------- Constructors
036
037    ChildTopicsImpl(ChildTopicsModelImpl model, DeepaMehtaObjectModelImpl parent, PersistenceLayer pl) {
038        this.model = model;
039        this.parent = parent;
040        this.pl = pl;
041        this.mf = pl.mf;
042    }
043
044    // -------------------------------------------------------------------------------------------------- Public Methods
045
046
047
048    // **********************************
049    // *** ChildTopics Implementation ***
050    // **********************************
051
052
053
054    // === Accessors ===
055
056    @Override
057    public RelatedTopic getTopic(String assocDefUri) {
058        loadChildTopics(assocDefUri);
059        return _getTopic(assocDefUri);
060    }
061
062    @Override
063    public RelatedTopic getTopicOrNull(String assocDefUri) {
064        loadChildTopics(assocDefUri);
065        return _getTopicOrNull(assocDefUri);
066    }
067
068    @Override
069    public List<RelatedTopic> getTopics(String assocDefUri) {
070        loadChildTopics(assocDefUri);
071        return _getTopics(assocDefUri);
072    }
073
074    @Override
075    public List<RelatedTopic> getTopicsOrNull(String assocDefUri) {
076        loadChildTopics(assocDefUri);
077        return _getTopicsOrNull(assocDefUri);
078    }
079
080    // ---
081
082    @Override
083    public Object get(String assocDefUri) {
084        Object value = model.get(assocDefUri);
085        // Note: topics just created have no child topics yet
086        if (value == null) {
087            return null;
088        }
089        // Note: no direct recursion takes place here. Recursion is indirect: attached topics are created here, this
090        // implies creating further ChildTopicsImpl objects, which in turn calls this method again but for the next
091        // child-level. Finally attached topics are created for all child-levels. ### FIXME
092        if (value instanceof RelatedTopicModel) {
093            return instantiate((RelatedTopicModel) value);
094        } else if (value instanceof List) {
095            return instantiate((List<RelatedTopicModel>) value);
096        } else {
097            throw new RuntimeException("Unexpected value in a ChildTopicsModel: " + value);
098        }
099    }
100
101    // ---
102
103    @Override
104    public ChildTopicsModel getModel() {
105        return model;
106    }
107
108
109
110    // === Convenience Accessors ===
111
112    @Override
113    public String getString(String assocDefUri) {
114        return getTopic(assocDefUri).getSimpleValue().toString();
115    }
116
117    @Override
118    public String getStringOrNull(String assocDefUri) {
119        Topic topic = getTopicOrNull(assocDefUri);
120        return topic != null ? topic.getSimpleValue().toString() : null;
121    }
122
123    @Override
124    public int getInt(String assocDefUri) {
125        return getTopic(assocDefUri).getSimpleValue().intValue();
126    }
127
128    @Override
129    public Integer getIntOrNull(String assocDefUri) {
130        Topic topic = getTopicOrNull(assocDefUri);
131        return topic != null ? topic.getSimpleValue().intValue() : null;
132    }
133
134    @Override
135    public long getLong(String assocDefUri) {
136        return getTopic(assocDefUri).getSimpleValue().longValue();
137    }
138
139    @Override
140    public Long getLongOrNull(String assocDefUri) {
141        Topic topic = getTopicOrNull(assocDefUri);
142        return topic != null ? topic.getSimpleValue().longValue() : null;
143    }
144
145    @Override
146    public double getDouble(String assocDefUri) {
147        return getTopic(assocDefUri).getSimpleValue().doubleValue();
148    }
149
150    @Override
151    public Double getDoubleOrNull(String assocDefUri) {
152        Topic topic = getTopicOrNull(assocDefUri);
153        return topic != null ? topic.getSimpleValue().doubleValue() : null;
154    }
155
156    @Override
157    public boolean getBoolean(String assocDefUri) {
158        return getTopic(assocDefUri).getSimpleValue().booleanValue();
159    }
160
161    @Override
162    public Boolean getBooleanOrNull(String assocDefUri) {
163        Topic topic = getTopicOrNull(assocDefUri);
164        return topic != null ? topic.getSimpleValue().booleanValue() : null;
165    }
166
167    @Override
168    public Object getObject(String assocDefUri) {
169        return getTopic(assocDefUri).getSimpleValue().value();
170    }
171
172    @Override
173    public Object getObjectOrNull(String assocDefUri) {
174        Topic topic = getTopicOrNull(assocDefUri);
175        return topic != null ? topic.getSimpleValue().value() : null;
176    }
177
178    // ---
179
180    @Override
181    public ChildTopics getChildTopics(String assocDefUri) {
182        return getTopic(assocDefUri).getChildTopics();
183    }
184
185    // Note: there are no convenience accessors for a multiple-valued child.
186
187
188
189    // === Manipulators ===
190
191    // --- Single-valued Childs ---
192
193    @Override
194    public ChildTopics set(String assocDefUri, TopicModel value) {
195        return _updateOne(assocDefUri, mf.newRelatedTopicModel(value));
196    }
197
198    // ---
199
200    @Override
201    public ChildTopics set(String assocDefUri, Object value) {
202        return _updateOne(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), new SimpleValue(value)));
203    }
204
205    @Override
206    public ChildTopics set(String assocDefUri, ChildTopicsModel value) {
207        return _updateOne(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), value));
208    }
209
210    // ---
211
212    @Override
213    public ChildTopics setRef(String assocDefUri, long refTopicId) {
214        return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicId));
215    }
216
217    @Override
218    public ChildTopics setRef(String assocDefUri, long refTopicId, ChildTopicsModel relatingAssocChildTopics) {
219        return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicId, relatingAssocChildTopics));
220    }
221
222    @Override
223    public ChildTopics setRef(String assocDefUri, String refTopicUri) {
224        return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicUri));
225    }
226
227    @Override
228    public ChildTopics setRef(String assocDefUri, String refTopicUri, ChildTopicsModel relatingAssocChildTopics) {
229        return _updateOne(assocDefUri, mf.newTopicReferenceModel(refTopicUri, relatingAssocChildTopics));
230    }
231
232    // ---
233
234    @Override
235    public ChildTopics setDeletionRef(String assocDefUri, long refTopicId) {
236        return _updateOne(assocDefUri, mf.newTopicDeletionModel(refTopicId));
237    }
238
239    @Override
240    public ChildTopics setDeletionRef(String assocDefUri, String refTopicUri) {
241        return _updateOne(assocDefUri, mf.newTopicDeletionModel(refTopicUri));
242    }
243
244    // --- Multiple-valued Childs ---
245
246    @Override
247    public ChildTopics add(String assocDefUri, TopicModel value) {
248        return _updateMany(assocDefUri, mf.newRelatedTopicModel(value));
249    }
250
251    // ---
252
253    @Override
254    public ChildTopics add(String assocDefUri, Object value) {
255        return _updateMany(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), new SimpleValue(value)));
256    }
257
258    @Override
259    public ChildTopics add(String assocDefUri, ChildTopicsModel value) {
260        return _updateMany(assocDefUri, mf.newRelatedTopicModel(mf.childTypeUri(assocDefUri), value));
261    }
262
263    // ---
264
265    @Override
266    public ChildTopics addRef(String assocDefUri, long refTopicId) {
267        return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicId));
268    }
269
270    @Override
271    public ChildTopics addRef(String assocDefUri, long refTopicId, ChildTopicsModel relatingAssocChildTopics) {
272        return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicId, relatingAssocChildTopics));
273    }
274
275    @Override
276    public ChildTopics addRef(String assocDefUri, String refTopicUri) {
277        return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicUri));
278    }
279
280    @Override
281    public ChildTopics addRef(String assocDefUri, String refTopicUri, ChildTopicsModel relatingAssocChildTopics) {
282        return _updateMany(assocDefUri, mf.newTopicReferenceModel(refTopicUri, relatingAssocChildTopics));
283    }
284
285    // ---
286
287    @Override
288    public ChildTopics addDeletionRef(String assocDefUri, long refTopicId) {
289        return _updateMany(assocDefUri, mf.newTopicDeletionModel(refTopicId));
290    }
291
292    @Override
293    public ChildTopics addDeletionRef(String assocDefUri, String refTopicUri) {
294        return _updateMany(assocDefUri, mf.newTopicDeletionModel(refTopicUri));
295    }
296
297
298
299    // === Iterable Implementation ===
300
301    @Override
302    public Iterator<String> iterator() {
303        return model.iterator();
304    }
305
306    // ----------------------------------------------------------------------------------------- Package Private Methods
307
308    boolean has(String assocDefUri) {
309        return model.has(assocDefUri);
310    }
311
312    int size() {
313        return model.size();
314    }
315
316    // ------------------------------------------------------------------------------------------------- Private Methods
317
318    // Note 1: we need to explicitly declare the arg as RelatedTopicModel. When declared as TopicModel instead the
319    // JVM would invoke the ChildTopicsModel's put()/add() which takes a TopicModel object even if at runtime a
320    // RelatedTopicModel or even a TopicReferenceModel is passed. This is because Java method overloading involves
321    // no dynamic dispatch. See the methodOverloading tests in JavaAPITest.java (in module dm4-test). ### still true?
322
323    // Note 2: calling parent.update(..) would not work. The JVM would call the update() method of the base class
324    // (DeepaMehtaObjectImpl), not the subclass's update() method. This is related to Java's (missing) multiple
325    // dispatch. Note that 2 inheritance hierarchies are involved here: the DM object hierarchy and the DM model
326    // hierarchy. See the missingMultipleDispatch tests in JavaAPITest.java (in module dm4-test). ### still true?
327
328    private ChildTopics _updateOne(String assocDefUri, RelatedTopicModel newChildTopic) {
329        parent.updateChildTopics(mf.newChildTopicsModel().put(assocDefUri, newChildTopic));
330        return this;
331    }
332
333    private ChildTopics _updateMany(String assocDefUri, RelatedTopicModel newChildTopic) {
334        parent.updateChildTopics(mf.newChildTopicsModel().add(assocDefUri, newChildTopic));
335        return this;
336    }
337
338    // ---
339
340    /**
341     * Loads the child topics for the given assoc def, provided they are not loaded already.
342     */
343    private void loadChildTopics(String assocDefUri) {
344        parent.loadChildTopics(assocDefUri);
345    }
346
347
348
349    // === Instantiation ===
350
351    private RelatedTopic _getTopic(String assocDefUri) {
352        return instantiate(model.getTopic(assocDefUri));
353    }
354
355    private RelatedTopic _getTopicOrNull(String assocDefUri) {
356        RelatedTopicModel topic = model.getTopicOrNull(assocDefUri);
357        return topic != null ? instantiate(topic) : null;
358    }
359
360    // ---
361
362    private List<RelatedTopic> _getTopics(String assocDefUri) {
363        return instantiate(model.getTopics(assocDefUri));
364    }
365
366    private List<RelatedTopic> _getTopicsOrNull(String assocDefUri) {
367        List<? extends RelatedTopicModel> topics = model.getTopicsOrNull(assocDefUri);
368        return topics != null ? instantiate(topics) : null;
369    }
370
371    // ---
372
373    private List<RelatedTopic> instantiate(List<? extends RelatedTopicModel> models) {
374        List<RelatedTopic> topics = new ArrayList();
375        for (RelatedTopicModel model : models) {
376            topics.add(instantiate(model));
377        }
378        return topics;
379    }
380
381    private RelatedTopic instantiate(RelatedTopicModel model) {
382        try {
383            return new RelatedTopicImpl((RelatedTopicModelImpl) model, pl);
384        } catch (Exception e) {
385            throw new RuntimeException("Instantiating a RelatedTopic failed (" + model + ")", e);
386        }
387    }
388}