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