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