001package de.deepamehta.core.impl;
002
003import de.deepamehta.core.Association;
004import de.deepamehta.core.AssociationDefinition;
005import de.deepamehta.core.DeepaMehtaObject;
006import de.deepamehta.core.RelatedTopic;
007import de.deepamehta.core.Topic;
008import de.deepamehta.core.Type;
009import de.deepamehta.core.model.ChildTopicsModel;
010import de.deepamehta.core.model.DeepaMehtaObjectModel;
011import de.deepamehta.core.model.RelatedTopicModel;
012import de.deepamehta.core.model.SimpleValue;
013import de.deepamehta.core.model.TopicModel;
014import de.deepamehta.core.service.Directive;
015import de.deepamehta.core.service.Directives;
016import de.deepamehta.core.service.ResultList;
017
018import org.codehaus.jettison.json.JSONObject;
019
020import java.util.List;
021import java.util.logging.Logger;
022
023
024
025/**
026 * A DeepaMehta object model that is attached to the DB.
027 *
028 * Method name conventions and semantics:
029 *  - getXX()           Reads from memory (model).
030 *  - setXX(arg)        Writes to memory (model) and DB. Elementary operation.
031 *  - updateXX(arg)     Compares arg with current value (model) and calls setXX() method(s) if required.
032 *                      Can be called with arg=null which indicates no update is requested.
033 *                      Typically returns nothing.
034 *  - fetchXX()         Fetches value from DB.              ### FIXDOC
035 *  - storeXX()         Stores current value (model) to DB. ### FIXDOC
036 */
037abstract class AttachedDeepaMehtaObject implements DeepaMehtaObject {
038
039    // ---------------------------------------------------------------------------------------------- Instance Variables
040
041    private DeepaMehtaObjectModel model;        // underlying model
042
043    private AttachedChildTopics childTopics;    // attached object cache
044
045    protected final EmbeddedService dms;
046
047    private Logger logger = Logger.getLogger(getClass().getName());
048
049    // ---------------------------------------------------------------------------------------------------- Constructors
050
051    AttachedDeepaMehtaObject(DeepaMehtaObjectModel model, EmbeddedService dms) {
052        this.model = model;
053        this.dms = dms;
054        this.childTopics = new AttachedChildTopics(model.getChildTopicsModel(), this, dms);
055    }
056
057    // -------------------------------------------------------------------------------------------------- Public Methods
058
059
060
061    // ***************************************
062    // *** DeepaMehtaObject Implementation ***
063    // ***************************************
064
065
066
067    // === Model ===
068
069    // --- ID ---
070
071    @Override
072    public long getId() {
073        return model.getId();
074    }
075
076    // --- URI ---
077
078    @Override
079    public String getUri() {
080        return model.getUri();
081    }
082
083    @Override
084    public void setUri(String uri) {
085        // update memory
086        model.setUri(uri);
087        // update DB
088        storeUri();         // abstract
089    }
090
091    // --- Type URI ---
092
093    @Override
094    public String getTypeUri() {
095        return model.getTypeUri();
096    }
097
098    @Override
099    public void setTypeUri(String typeUri) {
100        // update memory
101        model.setTypeUri(typeUri);
102        // update DB
103        storeTypeUri();     // abstract
104    }
105
106    // --- Simple Value ---
107
108    @Override
109    public SimpleValue getSimpleValue() {
110        return model.getSimpleValue();
111    }
112
113    // ---
114
115    @Override
116    public void setSimpleValue(String value) {
117        setSimpleValue(new SimpleValue(value));
118    }
119
120    @Override
121    public void setSimpleValue(int value) {
122        setSimpleValue(new SimpleValue(value));
123    }
124
125    @Override
126    public void setSimpleValue(long value) {
127        setSimpleValue(new SimpleValue(value));
128    }
129
130    @Override
131    public void setSimpleValue(boolean value) {
132        setSimpleValue(new SimpleValue(value));
133    }
134
135    @Override
136    public void setSimpleValue(SimpleValue value) {
137        dms.valueStorage.setSimpleValue(getModel(), value);
138    }
139
140    // --- Child Topics ---
141
142    @Override
143    public AttachedChildTopics getChildTopics() {
144        return childTopics;
145    }
146
147    // ### FIXME: no UPDATE directives are added. No UPDATE events are fired.
148    // Should we call the abstract updateChildTopics() instead?
149    // Should we drop setChildTopics() completely as its semantics is too unclear?
150    @Override
151    public void setChildTopics(ChildTopicsModel childTopics) {
152        try {
153            getChildTopics().update(childTopics);
154        } catch (Exception e) {
155            throw new RuntimeException("Setting the child topics failed (" + childTopics + ")", e);
156        }
157    }
158
159    // ---
160
161    @Override
162    public DeepaMehtaObject loadChildTopics() {
163        getChildTopics().loadChildTopics();
164        return this;
165    }
166
167    @Override
168    public DeepaMehtaObject loadChildTopics(String childTypeUri) {
169        getChildTopics().loadChildTopics(childTypeUri);
170        return this;
171    }
172
173    // ---
174
175    @Override
176    public DeepaMehtaObjectModel getModel() {
177        return model;
178    }
179
180
181
182    // === Updating ===
183
184    @Override
185    public void update(DeepaMehtaObjectModel newModel) {
186        updateUri(newModel.getUri());
187        updateTypeUri(newModel.getTypeUri());
188        if (getType().getDataTypeUri().equals("dm4.core.composite")) {
189            getChildTopics().update(newModel.getChildTopicsModel());
190        } else {
191            updateSimpleValue(newModel.getSimpleValue());
192        }
193        //
194        Directives.get().add(getUpdateDirective(), this);
195    }
196
197    // ---
198
199    @Override
200    public void updateChildTopic(RelatedTopicModel newChildTopic, AssociationDefinition assocDef) {
201        getChildTopics().updateChildTopics(newChildTopic, null, assocDef);      // newChildTopics=null
202    }
203
204    @Override
205    public void updateChildTopics(List<RelatedTopicModel> newChildTopics, AssociationDefinition assocDef) {
206        getChildTopics().updateChildTopics(null, newChildTopics, assocDef);     // newChildTopic=null
207    }
208
209
210
211    // === Deletion ===
212
213    /**
214     * Deletes 1) this DeepaMehta object's child topics (recursively) which have an underlying association definition of
215     * type "Composition Definition" and 2) deletes all the remaining direct associations of this DeepaMehta object.
216     * <p>
217     * Note: deletion of the object itself is up to the subclasses.
218     */
219    @Override
220    public void delete() {
221        // 1) delete child topics (recursively)
222        for (AssociationDefinition assocDef : getType().getAssocDefs()) {
223            if (assocDef.getTypeUri().equals("dm4.core.composition_def")) {
224                for (Topic childTopic : getRelatedTopics(assocDef.getInstanceLevelAssocTypeUri(),
225                        "dm4.core.parent", "dm4.core.child", assocDef.getChildTypeUri(), 0)) {
226                    childTopic.delete();
227                }
228            }
229        }
230        // 2) delete direct associations
231        for (Association assoc : getAssociations()) {       // getAssociations() is abstract
232            assoc.delete();
233        }
234    }
235
236
237
238    // === Traversal ===
239
240    // --- Topic Retrieval ---
241
242    @Override
243    public RelatedTopic getRelatedTopic(String assocTypeUri, String myRoleTypeUri, String othersRoleTypeUri,
244                                                                                   String othersTopicTypeUri) {
245        RelatedTopicModel topic = fetchRelatedTopic(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, othersTopicTypeUri);
246        // fetchRelatedTopic() is abstract
247        return topic != null ? dms.instantiateRelatedTopic(topic) : null;
248    }
249
250    @Override
251    public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, int maxResultSize) {
252        return getRelatedTopics(assocTypeUri, null, null, null, maxResultSize);
253    }
254
255    @Override
256    public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, String myRoleTypeUri,
257                                            String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize) {
258        ResultList<RelatedTopicModel> topics = fetchRelatedTopics(assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
259            othersTopicTypeUri, maxResultSize);     // fetchRelatedTopics() is abstract
260        return dms.instantiateRelatedTopics(topics);
261    }
262
263    // Note: this method is implemented in the subclasses (this is an abstract class):
264    //     getRelatedTopics(List assocTypeUris, ...)
265
266    // --- Association Retrieval ---
267
268    // Note: these methods are implemented in the subclasses (this is an abstract class):
269    //     getAssociation(...)
270    //     getAssociations()
271
272
273
274    // === Properties ===
275
276    @Override
277    public Object getProperty(String propUri) {
278        return dms.getProperty(getId(), propUri);
279    }
280
281    @Override
282    public boolean hasProperty(String propUri) {
283        return dms.hasProperty(getId(), propUri);
284    }
285
286    // Note: these methods are implemented in the subclasses:
287    //     setProperty(...)
288    //     removeProperty(...)
289
290
291
292    // === Misc ===
293
294    @Override
295    public Object getDatabaseVendorObject() {
296        return dms.storageDecorator.getDatabaseVendorObject(getId());
297    }
298
299
300
301    // **********************************
302    // *** JSONEnabled Implementation ***
303    // **********************************
304
305
306
307    @Override
308    public JSONObject toJSON() {
309        return model.toJSON();
310    }
311
312
313
314    // ****************
315    // *** Java API ***
316    // ****************
317
318
319
320    @Override
321    public boolean equals(Object o) {
322        return ((AttachedDeepaMehtaObject) o).model.equals(model);
323    }
324
325    @Override
326    public int hashCode() {
327        return model.hashCode();
328    }
329
330    @Override
331    public String toString() {
332        return model.toString();
333    }
334
335
336
337    // ----------------------------------------------------------------------------------------- Package Private Methods
338
339    abstract String className();
340
341    abstract void updateChildTopics(ChildTopicsModel childTopics);
342
343    abstract Directive getUpdateDirective();
344
345    abstract void storeUri();
346
347    abstract void storeTypeUri();
348
349    // ---
350
351    abstract RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri,
352                                                 String othersRoleTypeUri, String othersTopicTypeUri);
353
354    abstract ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri,
355                                                String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize);
356
357    // ---
358
359    // ### TODO: add to public interface?
360    abstract Type getType();
361
362    // ------------------------------------------------------------------------------------------------- Private Methods
363
364
365
366    // === Update ===
367
368    private void updateUri(String newUri) {
369        // abort if no update is requested
370        if (newUri == null) {
371            return;
372        }
373        //
374        String uri = getUri();
375        if (!uri.equals(newUri)) {
376            logger.info("### Changing URI of " + className() + " " + getId() +
377                " from \"" + uri + "\" -> \"" + newUri + "\"");
378            setUri(newUri);
379        }
380    }
381
382    private void updateTypeUri(String newTypeUri) {
383        // abort if no update is requested
384        if (newTypeUri == null) {
385            return;
386        }
387        //
388        String typeUri = getTypeUri();
389        if (!typeUri.equals(newTypeUri)) {
390            logger.info("### Changing type URI of " + className() + " " + getId() +
391                " from \"" + typeUri + "\" -> \"" + newTypeUri + "\"");
392            setTypeUri(newTypeUri);
393        }
394    }
395
396    private void updateSimpleValue(SimpleValue newValue) {
397        // abort if no update is requested
398        if (newValue == null) {
399            return;
400        }
401        //
402        SimpleValue value = getSimpleValue();
403        if (!value.equals(newValue)) {
404            logger.info("### Changing simple value of " + className() + " " + getId() +
405                " from \"" + value + "\" -> \"" + newValue + "\"");
406            setSimpleValue(newValue);
407        }
408    }
409}