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