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