001    package de.deepamehta.core.impl;
002    
003    import de.deepamehta.core.Association;
004    import de.deepamehta.core.RelatedAssociation;
005    import de.deepamehta.core.RelatedTopic;
006    import de.deepamehta.core.Topic;
007    import de.deepamehta.core.TopicType;
008    import de.deepamehta.core.model.AssociationModel;
009    import de.deepamehta.core.model.RelatedAssociationModel;
010    import de.deepamehta.core.model.RelatedTopicModel;
011    import de.deepamehta.core.model.SimpleValue;
012    import de.deepamehta.core.model.TopicModel;
013    import de.deepamehta.core.service.ClientState;
014    import de.deepamehta.core.service.Directive;
015    import de.deepamehta.core.service.Directives;
016    import de.deepamehta.core.service.ResultList;
017    import de.deepamehta.core.storage.spi.DeepaMehtaTransaction;
018    
019    import java.util.List;
020    import java.util.logging.Logger;
021    
022    
023    
024    /**
025     * A topic that is attached to the {@link DeepaMehtaService}.
026     */
027    class AttachedTopic extends AttachedDeepaMehtaObject implements Topic {
028    
029        // ---------------------------------------------------------------------------------------------- Instance Variables
030    
031        private Logger logger = Logger.getLogger(getClass().getName());
032    
033        // ---------------------------------------------------------------------------------------------------- Constructors
034    
035        AttachedTopic(TopicModel model, EmbeddedService dms) {
036            super(model, dms);
037        }
038    
039        // -------------------------------------------------------------------------------------------------- Public Methods
040    
041    
042    
043        // ******************************************
044        // *** AttachedDeepaMehtaObject Overrides ***
045        // ******************************************
046    
047    
048    
049        // === Updating ===
050    
051        @Override
052        public void update(TopicModel model, ClientState clientState, Directives directives) {
053            _update(model, clientState, directives);
054            //
055            dms.fireEvent(CoreEvent.POST_UPDATE_TOPIC_REQUEST, this);
056        }
057    
058    
059    
060        // === Deletion ===
061    
062        @Override
063        public void delete(Directives directives) {
064            DeepaMehtaTransaction tx = dms.beginTx();   // ### TODO: only resource methods should create a transaction
065            try {
066                dms.fireEvent(CoreEvent.PRE_DELETE_TOPIC, this, directives);
067                //
068                // delete sub-topics and associations
069                super.delete(directives);
070                // delete topic itself
071                logger.info("Deleting " + this);
072                directives.add(Directive.DELETE_TOPIC, this);
073                dms.storageDecorator.deleteTopic(getId());
074                //
075                tx.success();
076                //
077                dms.fireEvent(CoreEvent.POST_DELETE_TOPIC, this, directives);
078            } catch (Exception e) {
079                logger.warning("ROLLBACK!");
080                throw new RuntimeException("Deleting topic failed (" + this + ")", e);
081            } finally {
082                tx.finish();
083            }
084        }
085    
086    
087    
088        // ****************************
089        // *** Topic Implementation ***
090        // ****************************
091    
092    
093    
094        @Override
095        public TopicModel getModel() {
096            return (TopicModel) super.getModel();
097        }
098    
099    
100    
101        // === Traversal ===
102    
103        // --- Association Retrieval ---
104    
105        @Override
106        public RelatedAssociation getRelatedAssociation(String assocTypeUri, String myRoleTypeUri,
107                                                        String othersRoleTypeUri, String othersAssocTypeUri,
108                                                        boolean fetchComposite, boolean fetchRelatingComposite) {
109            RelatedAssociationModel assoc = dms.storageDecorator.fetchTopicRelatedAssociation(getId(), assocTypeUri,
110                myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
111            return assoc != null ? dms.instantiateRelatedAssociation(assoc, fetchComposite, fetchRelatingComposite) : null;
112        }
113    
114        @Override
115        public List<RelatedAssociation> getRelatedAssociations(String assocTypeUri, String myRoleTypeUri,
116                                                               String othersRoleTypeUri, String othersAssocTypeUri,
117                                                               boolean fetchComposite, boolean fetchRelatingComposite) {
118            List<RelatedAssociationModel> assocs = dms.storageDecorator.fetchTopicRelatedAssociations(getId(), assocTypeUri,
119                myRoleTypeUri, othersRoleTypeUri, othersAssocTypeUri);
120            return dms.instantiateRelatedAssociations(assocs, fetchComposite, fetchRelatingComposite);
121        }
122    
123    
124    
125        // ***************************************
126        // *** DeepaMehtaObject Implementation ***
127        // ***************************************
128    
129    
130    
131        // === Traversal ===
132    
133        // --- Topic Retrieval ---
134    
135        @Override
136        public ResultList<RelatedTopic> getRelatedTopics(List assocTypeUris, String myRoleTypeUri, String othersRoleTypeUri,
137                                        String othersTopicTypeUri, boolean fetchComposite, boolean fetchRelatingComposite,
138                                        int maxResultSize) {
139            ResultList<RelatedTopicModel> topics = dms.storageDecorator.fetchTopicRelatedTopics(getId(), assocTypeUris,
140                myRoleTypeUri, othersRoleTypeUri, othersTopicTypeUri, maxResultSize);
141            return dms.instantiateRelatedTopics(topics, fetchComposite, fetchRelatingComposite);
142        }
143    
144        // --- Association Retrieval ---
145    
146        @Override
147        public Association getAssociation(String assocTypeUri, String myRoleTypeUri, String othersRoleTypeUri,
148                                                                                       long othersTopicId) {
149            AssociationModel assoc = dms.storageDecorator.fetchAssociation(assocTypeUri, getId(), othersTopicId,
150                myRoleTypeUri, othersRoleTypeUri);
151            return assoc != null ? dms.instantiateAssociation(assoc, false) : null;     // fetchComposite=false
152        }
153    
154        @Override
155        public List<Association> getAssociations() {
156            return dms.instantiateAssociations(dms.storageDecorator.fetchTopicAssociations(getId()), false);
157                                                                                        // fetchComposite=false
158        }
159    
160    
161    
162        // === Properties ===
163    
164        @Override
165        public Object getProperty(String propUri) {
166            return dms.storageDecorator.fetchTopicProperty(getId(), propUri);
167        }
168    
169        @Override
170        public void setProperty(String propUri, Object propValue, boolean addToIndex) {
171            dms.storageDecorator.storeTopicProperty(getId(), propUri, propValue, addToIndex);
172        }
173    
174        @Override
175        public boolean hasProperty(String propUri) {
176            return dms.storageDecorator.hasTopicProperty(getId(), propUri);
177        }
178    
179        @Override
180        public void removeProperty(String propUri) {
181            dms.storageDecorator.removeTopicProperty(getId(), propUri);
182        }
183    
184    
185    
186        // ----------------------------------------------------------------------------------------- Package Private Methods
187    
188        /**
189         * Convenience method.
190         */
191        TopicType getTopicType() {
192            return (TopicType) getType();
193        }
194    
195        /**
196         * Low-level update method which does not fire the POST_UPDATE_TOPIC_REQUEST event.
197         * <p>
198         * Called multiple times while updating a composite value (see AttachedCompositeValue).
199         * POST_UPDATE_TOPIC_REQUEST on the other hand must be fired only once (per update request).
200         */
201        void _update(TopicModel model, ClientState clientState, Directives directives) {
202            logger.info("Updating topic " + getId() + " (new " + model + ")");
203            //
204            dms.fireEvent(CoreEvent.PRE_UPDATE_TOPIC, this, model, directives);
205            //
206            TopicModel oldModel = getModel().clone();
207            super.update(model, clientState, directives);
208            //
209            addUpdateDirective(directives);
210            //
211            dms.fireEvent(CoreEvent.POST_UPDATE_TOPIC, this, model, oldModel, clientState, directives);
212        }
213    
214    
215    
216        // === Implementation of the abstract methods ===
217    
218        @Override
219        String className() {
220            return "topic";
221        }
222    
223        @Override
224        void addUpdateDirective(Directives directives) {
225            directives.add(Directive.UPDATE_TOPIC, this);
226        }
227    
228        @Override
229        final void storeUri() {
230            dms.storageDecorator.storeTopicUri(getId(), getUri());
231        }
232    
233        @Override
234        final void storeTypeUri() {
235            reassignInstantiation();
236            dms.storageDecorator.storeTopicTypeUri(getId(), getTypeUri());
237        }
238    
239        // ---
240    
241        @Override
242        final RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri,
243                                                  String othersRoleTypeUri, String othersTopicTypeUri) {
244            return dms.storageDecorator.fetchTopicRelatedTopic(getId(), assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
245                othersTopicTypeUri);
246        }
247    
248        @Override
249        final ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri,
250                                                               String othersRoleTypeUri, String othersTopicTypeUri,
251                                                               int maxResultSize) {
252            return dms.storageDecorator.fetchTopicRelatedTopics(getId(), assocTypeUri, myRoleTypeUri, othersRoleTypeUri,
253                othersTopicTypeUri, maxResultSize);
254        }
255    
256    
257    
258        // ------------------------------------------------------------------------------------------------- Private Methods
259    
260        private void reassignInstantiation() {
261            // remove current assignment
262            fetchInstantiation().delete(new Directives());      // ### FIXME: receive directives as argument
263            // create new assignment
264            dms.createTopicInstantiation(getId(), getTypeUri());
265        }
266    
267        // Note: this method works only for instances, not for types.
268        // This is because a type is not of type "dm4.core.topic_type" but of type "dm4.core.meta_type".
269        private Association fetchInstantiation() {
270            RelatedTopic topicType = getRelatedTopic("dm4.core.instantiation", "dm4.core.instance", "dm4.core.type",
271                "dm4.core.topic_type", false, false);
272            //
273            if (topicType == null) {
274                throw new RuntimeException("Topic " + getId() + " is not associated to a topic type");
275            }
276            //
277            return topicType.getRelatingAssociation();
278        }
279    }