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        // === Misc ===
265    
266        @Override
267        public Object getDatabaseVendorObject() {
268            return dms.storageDecorator.getDatabaseVendorObject(getId());
269        }
270    
271    
272    
273        // **********************************
274        // *** JSONEnabled Implementation ***
275        // **********************************
276    
277    
278    
279        @Override
280        public JSONObject toJSON() {
281            return model.toJSON();
282        }
283    
284    
285    
286        // ****************
287        // *** Java API ***
288        // ****************
289    
290    
291    
292        @Override
293        public boolean equals(Object o) {
294            return ((AttachedDeepaMehtaObject) o).model.equals(model);
295        }
296    
297        @Override
298        public int hashCode() {
299            return model.hashCode();
300        }
301    
302        @Override
303        public String toString() {
304            return model.toString();
305        }
306    
307    
308    
309        // ----------------------------------------------------------------------------------------- Package Private Methods
310    
311        abstract String className();
312    
313        abstract Directive getUpdateDirective();
314    
315        abstract void storeUri();
316    
317        abstract void storeTypeUri();
318    
319        // ---
320    
321        abstract RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri,
322                                                     String othersRoleTypeUri, String othersTopicTypeUri);
323    
324        abstract ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri,
325                                                    String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize);
326    
327        // ---
328    
329        // ### TODO: add to public interface
330        Type getType() {
331            return dms.valueStorage.getType(getModel());
332        }
333    
334        // ------------------------------------------------------------------------------------------------- Private Methods
335    
336    
337    
338        // === Update ===
339    
340        private void updateUri(String newUri) {
341            // abort if no update is requested
342            if (newUri == null) {
343                return;
344            }
345            //
346            String uri = getUri();
347            if (!uri.equals(newUri)) {
348                logger.info("### Changing URI of " + className() + " " + getId() +
349                    " from \"" + uri + "\" -> \"" + newUri + "\"");
350                setUri(newUri);
351            }
352        }
353    
354        private void updateTypeUri(String newTypeUri) {
355            // abort if no update is requested
356            if (newTypeUri == null) {
357                return;
358            }
359            //
360            String typeUri = getTypeUri();
361            if (!typeUri.equals(newTypeUri)) {
362                logger.info("### Changing type URI of " + className() + " " + getId() +
363                    " from \"" + typeUri + "\" -> \"" + newTypeUri + "\"");
364                setTypeUri(newTypeUri);
365            }
366        }
367    
368        private void updateValue(DeepaMehtaObjectModel newModel) {
369            if (getType().getDataTypeUri().equals("dm4.core.composite")) {
370                getChildTopics().update(newModel.getChildTopicsModel());
371            } else {
372                updateSimpleValue(newModel.getSimpleValue());
373            }
374        }
375    
376        private void updateSimpleValue(SimpleValue newValue) {
377            // abort if no update is requested
378            if (newValue == null) {
379                return;
380            }
381            //
382            SimpleValue value = getSimpleValue();
383            if (!value.equals(newValue)) {
384                logger.info("### Changing simple value of " + className() + " " + getId() +
385                    " from \"" + value + "\" -> \"" + newValue + "\"");
386                setSimpleValue(newValue);
387            }
388        }
389    }