001    package de.deepamehta.core.impl;
002    
003    import de.deepamehta.core.Association;
004    import de.deepamehta.core.AssociationDefinition;
005    import de.deepamehta.core.DeepaMehtaObject;
006    import de.deepamehta.core.RelatedTopic;
007    import de.deepamehta.core.Topic;
008    import de.deepamehta.core.Type;
009    import de.deepamehta.core.model.ChildTopicsModel;
010    import de.deepamehta.core.model.DeepaMehtaObjectModel;
011    import de.deepamehta.core.model.RelatedTopicModel;
012    import de.deepamehta.core.model.SimpleValue;
013    import de.deepamehta.core.model.TopicModel;
014    import de.deepamehta.core.service.Directive;
015    import de.deepamehta.core.service.Directives;
016    import de.deepamehta.core.service.ResultList;
017    
018    import org.codehaus.jettison.json.JSONObject;
019    
020    import java.util.List;
021    import 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     */
037    abstract 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        @Override
148        public void setChildTopics(ChildTopicsModel childTopics) {
149            try {
150                getChildTopics().update(childTopics);
151            } catch (Exception e) {
152                throw new RuntimeException("Setting the child topics failed (" + childTopics + ")", e);
153            }
154        }
155    
156        // ---
157    
158        @Override
159        public DeepaMehtaObject loadChildTopics() {
160            getChildTopics().loadChildTopics();
161            return this;
162        }
163    
164        @Override
165        public DeepaMehtaObject loadChildTopics(String childTypeUri) {
166            getChildTopics().loadChildTopics(childTypeUri);
167            return this;
168        }
169    
170        // ---
171    
172        @Override
173        public DeepaMehtaObjectModel getModel() {
174            return model;
175        }
176    
177    
178    
179        // === Updating ===
180    
181        @Override
182        public void update(DeepaMehtaObjectModel newModel) {
183            updateUri(newModel.getUri());
184            updateTypeUri(newModel.getTypeUri());
185            updateValue(newModel);
186            //
187            Directives.get().add(getUpdateDirective(), this);
188        }
189    
190        // ---
191    
192        @Override
193        public void updateChildTopic(TopicModel newChildTopic, AssociationDefinition assocDef) {
194            getChildTopics().updateChildTopics(newChildTopic, null, assocDef);      // newChildTopics=null
195        }
196    
197        @Override
198        public void updateChildTopics(List<TopicModel> newChildTopics, AssociationDefinition assocDef) {
199            getChildTopics().updateChildTopics(null, newChildTopics, assocDef);     // newChildTopic=null
200        }
201    
202    
203    
204        // === Deletion ===
205    
206        /**
207         * Deletes all sub-topics of this DeepaMehta object (associated via "dm4.core.composition", recursively) and
208         * deletes all the remaining direct associations of this DeepaMehta object.
209         * <p>
210         * Note: deletion of the object itself is up to the subclasses.
211         */
212        @Override
213        public void delete() {
214            // 1) recursively delete sub-topics
215            ResultList<RelatedTopic> childTopics = getRelatedTopics("dm4.core.composition",
216                "dm4.core.parent", "dm4.core.child", null, 0);
217            for (Topic childTopic : childTopics) {
218                childTopic.delete();
219            }
220            // 2) delete direct associations
221            for (Association assoc : getAssociations()) {       // getAssociations() is abstract
222                assoc.delete();
223            }
224        }
225    
226    
227    
228        // === Traversal ===
229    
230        // --- Topic Retrieval ---
231    
232        @Override
233        public RelatedTopic getRelatedTopic(String assocTypeUri, String myRoleTypeUri, String othersRoleTypeUri,
234                                                                                       String othersTopicTypeUri) {
235            RelatedTopicModel topic = fetchRelatedTopic(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, othersTopicTypeUri);
236            // fetchRelatedTopic() is abstract
237            return topic != null ? dms.instantiateRelatedTopic(topic) : null;
238        }
239    
240        @Override
241        public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, int maxResultSize) {
242            return getRelatedTopics(assocTypeUri, null, null, null, maxResultSize);
243        }
244    
245        @Override
246        public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, String myRoleTypeUri,
247                                                String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize) {
248            ResultList<RelatedTopicModel> topics = fetchRelatedTopics(assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
249                othersTopicTypeUri, maxResultSize);     // fetchRelatedTopics() is abstract
250            return dms.instantiateRelatedTopics(topics);
251        }
252    
253        // Note: this method is implemented in the subclasses (this is an abstract class):
254        //     getRelatedTopics(List assocTypeUris, ...)
255    
256        // --- Association Retrieval ---
257    
258        // Note: these methods are implemented in the subclasses (this is an abstract class):
259        //     getAssociation(...)
260        //     getAssociations()
261    
262    
263    
264        // === Properties ===
265    
266        @Override
267        public Object getProperty(String propUri) {
268            return dms.getProperty(getId(), propUri);
269        }
270    
271        @Override
272        public boolean hasProperty(String propUri) {
273            return dms.hasProperty(getId(), propUri);
274        }
275    
276        // Note: these methods are implemented in the subclasses:
277        //     setProperty(...)
278        //     removeProperty(...)
279    
280    
281    
282        // === Misc ===
283    
284        @Override
285        public Object getDatabaseVendorObject() {
286            return dms.storageDecorator.getDatabaseVendorObject(getId());
287        }
288    
289    
290    
291        // **********************************
292        // *** JSONEnabled Implementation ***
293        // **********************************
294    
295    
296    
297        @Override
298        public JSONObject toJSON() {
299            return model.toJSON();
300        }
301    
302    
303    
304        // ****************
305        // *** Java API ***
306        // ****************
307    
308    
309    
310        @Override
311        public boolean equals(Object o) {
312            return ((AttachedDeepaMehtaObject) o).model.equals(model);
313        }
314    
315        @Override
316        public int hashCode() {
317            return model.hashCode();
318        }
319    
320        @Override
321        public String toString() {
322            return model.toString();
323        }
324    
325    
326    
327        // ----------------------------------------------------------------------------------------- Package Private Methods
328    
329        abstract String className();
330    
331        abstract Directive getUpdateDirective();
332    
333        abstract void storeUri();
334    
335        abstract void storeTypeUri();
336    
337        // ---
338    
339        abstract RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri,
340                                                     String othersRoleTypeUri, String othersTopicTypeUri);
341    
342        abstract ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri,
343                                                    String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize);
344    
345        // ---
346    
347        // ### TODO: add to public interface
348        Type getType() {
349            return dms.valueStorage.getType(getModel());
350        }
351    
352        // ------------------------------------------------------------------------------------------------- Private Methods
353    
354    
355    
356        // === Update ===
357    
358        private void updateUri(String newUri) {
359            // abort if no update is requested
360            if (newUri == null) {
361                return;
362            }
363            //
364            String uri = getUri();
365            if (!uri.equals(newUri)) {
366                logger.info("### Changing URI of " + className() + " " + getId() +
367                    " from \"" + uri + "\" -> \"" + newUri + "\"");
368                setUri(newUri);
369            }
370        }
371    
372        private void updateTypeUri(String newTypeUri) {
373            // abort if no update is requested
374            if (newTypeUri == null) {
375                return;
376            }
377            //
378            String typeUri = getTypeUri();
379            if (!typeUri.equals(newTypeUri)) {
380                logger.info("### Changing type URI of " + className() + " " + getId() +
381                    " from \"" + typeUri + "\" -> \"" + newTypeUri + "\"");
382                setTypeUri(newTypeUri);
383            }
384        }
385    
386        private void updateValue(DeepaMehtaObjectModel newModel) {
387            if (getType().getDataTypeUri().equals("dm4.core.composite")) {
388                getChildTopics().update(newModel.getChildTopicsModel());
389            } else {
390                updateSimpleValue(newModel.getSimpleValue());
391            }
392        }
393    
394        private void updateSimpleValue(SimpleValue newValue) {
395            // abort if no update is requested
396            if (newValue == null) {
397                return;
398            }
399            //
400            SimpleValue value = getSimpleValue();
401            if (!value.equals(newValue)) {
402                logger.info("### Changing simple value of " + className() + " " + getId() +
403                    " from \"" + value + "\" -> \"" + newValue + "\"");
404                setSimpleValue(newValue);
405            }
406        }
407    }