001package de.deepamehta.core.impl; 002 003import de.deepamehta.core.Association; 004import de.deepamehta.core.AssociationDefinition; 005import de.deepamehta.core.DeepaMehtaObject; 006import de.deepamehta.core.RelatedTopic; 007import de.deepamehta.core.Topic; 008import de.deepamehta.core.Type; 009import de.deepamehta.core.model.ChildTopicsModel; 010import de.deepamehta.core.model.DeepaMehtaObjectModel; 011import de.deepamehta.core.model.RelatedTopicModel; 012import de.deepamehta.core.model.SimpleValue; 013import de.deepamehta.core.model.TopicModel; 014import de.deepamehta.core.service.Directive; 015import de.deepamehta.core.service.Directives; 016import de.deepamehta.core.service.ResultList; 017 018import org.codehaus.jettison.json.JSONObject; 019 020import java.util.List; 021import 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 */ 037abstract 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}