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(); // ### FIXME: all other writing API methods need transaction as well 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(String childTypeUri) { 166 getCompositeValue().loadChildTopics(childTypeUri); 167 } 168 169 // --- 170 171 @Override 172 public DeepaMehtaObjectModel getModel() { 173 return model; 174 } 175 176 177 178 // === Updating === 179 180 @Override 181 public void update(DeepaMehtaObjectModel newModel, ClientState clientState, Directives directives) { 182 updateUri(newModel.getUri()); 183 updateTypeUri(newModel.getTypeUri()); 184 // 185 if (getType().getDataTypeUri().equals("dm4.core.composite")) { 186 getCompositeValue().update(newModel.getCompositeValueModel(), clientState, directives); 187 } else { 188 updateSimpleValue(newModel.getSimpleValue()); 189 } 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<? extends TopicModel> newChildTopics, AssociationDefinition assocDef, 202 ClientState clientState, Directives directives) { 203 getCompositeValue().updateChildTopics(null, newChildTopics, assocDef, clientState, directives); 204 } 205 206 207 208 // === Traversal === 209 210 // --- Topic Retrieval --- 211 212 @Override 213 public RelatedTopic getRelatedTopic(String assocTypeUri, String myRoleTypeUri, String othersRoleTypeUri, 214 String othersTopicTypeUri, boolean fetchComposite, 215 boolean fetchRelatingComposite) { 216 RelatedTopicModel topic = fetchRelatedTopic(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, othersTopicTypeUri); 217 // fetchRelatedTopic() is abstract 218 return topic != null ? dms.instantiateRelatedTopic(topic, fetchComposite, fetchRelatingComposite) : null; 219 } 220 221 @Override 222 public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, int maxResultSize) { 223 return getRelatedTopics(assocTypeUri, null, null, null, false, false, maxResultSize); 224 } 225 226 @Override 227 public ResultList<RelatedTopic> getRelatedTopics(String assocTypeUri, String myRoleTypeUri, 228 String othersRoleTypeUri, String othersTopicTypeUri, 229 boolean fetchComposite, boolean fetchRelatingComposite, int maxResultSize) { 230 ResultList<RelatedTopicModel> topics = fetchRelatedTopics(assocTypeUri, myRoleTypeUri, othersRoleTypeUri, 231 othersTopicTypeUri, maxResultSize); // fetchRelatedTopics() is abstract 232 return dms.instantiateRelatedTopics(topics, fetchComposite, fetchRelatingComposite); 233 } 234 235 // Note: this method is implemented in the subclasses (this is an abstract class): 236 // getRelatedTopics(List assocTypeUris, ...) 237 238 // --- Association Retrieval --- 239 240 // Note: these methods are implemented in the subclasses (this is an abstract class): 241 // getAssociation(...); 242 // getAssociations(); 243 244 245 246 // === Deletion === 247 248 /** 249 * Deletes all sub-topics of this DeepaMehta object (associated via "dm4.core.composition", recursively) and 250 * deletes all the remaining direct associations of this DeepaMehta object. 251 * <p> 252 * Note: deletion of the object itself is up to the subclasses. 253 */ 254 @Override 255 public void delete(Directives directives) { 256 // Note: directives must be not null. 257 // The subclass's delete() methods add DELETE_TOPIC and DELETE_ASSOCIATION directives to it respectively. 258 if (directives == null) { 259 throw new IllegalArgumentException("directives is null"); 260 } 261 // 1) recursively delete sub-topics 262 ResultList<RelatedTopic> childTopics = getRelatedTopics("dm4.core.composition", 263 "dm4.core.parent", "dm4.core.child", null, false, false, 0); 264 for (Topic childTopic : childTopics) { 265 childTopic.delete(directives); 266 } 267 // 2) delete direct associations 268 for (Association assoc : getAssociations()) { // getAssociations() is abstract 269 assoc.delete(directives); 270 } 271 } 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 abstract void addUpdateDirective(Directives directives); 316 317 abstract void storeUri(); 318 319 abstract void storeTypeUri(); 320 321 // --- 322 323 abstract RelatedTopicModel fetchRelatedTopic(String assocTypeUri, String myRoleTypeUri, 324 String othersRoleTypeUri, String othersTopicTypeUri); 325 326 abstract ResultList<RelatedTopicModel> fetchRelatedTopics(String assocTypeUri, String myRoleTypeUri, 327 String othersRoleTypeUri, String othersTopicTypeUri, int maxResultSize); 328 329 // --- 330 331 Type getType() { 332 return dms.valueStorage.getType(getModel()); 333 } 334 335 // ------------------------------------------------------------------------------------------------- Private Methods 336 337 338 339 // === Update === 340 341 private void updateUri(String newUri) { 342 // abort if no update is requested 343 if (newUri == null) { 344 return; 345 } 346 // 347 String uri = getUri(); 348 if (!uri.equals(newUri)) { 349 logger.info("### Changing URI of " + className() + " " + getId() + 350 " from \"" + uri + "\" -> \"" + newUri + "\""); 351 setUri(newUri); 352 } 353 } 354 355 private void updateTypeUri(String newTypeUri) { 356 // abort if no update is requested 357 if (newTypeUri == null) { 358 return; 359 } 360 // 361 String typeUri = getTypeUri(); 362 if (!typeUri.equals(newTypeUri)) { 363 logger.info("### Changing type URI of " + className() + " " + getId() + 364 " from \"" + typeUri + "\" -> \"" + newTypeUri + "\""); 365 setTypeUri(newTypeUri); 366 } 367 } 368 369 private void updateSimpleValue(SimpleValue newValue) { 370 // abort if no update is requested 371 if (newValue == null) { 372 return; 373 } 374 // 375 SimpleValue value = getSimpleValue(); 376 if (!value.equals(newValue)) { 377 logger.info("### Changing simple value of " + className() + " " + getId() + 378 " from \"" + value + "\" -> \"" + newValue + "\""); 379 setSimpleValue(newValue); 380 } 381 } 382 }