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