001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.AssociationDefinition; 004 import de.deepamehta.core.JSONEnabled; 005 import de.deepamehta.core.Type; 006 import de.deepamehta.core.ViewConfiguration; 007 import de.deepamehta.core.model.AssociationDefinitionModel; 008 import de.deepamehta.core.model.IndexMode; 009 import de.deepamehta.core.model.RoleModel; 010 import de.deepamehta.core.model.TypeModel; 011 import de.deepamehta.core.service.ClientState; 012 import de.deepamehta.core.service.Directive; 013 import de.deepamehta.core.service.Directives; 014 import de.deepamehta.core.storage.spi.DeepaMehtaTransaction; 015 016 import org.codehaus.jettison.json.JSONObject; 017 018 import java.util.Collection; 019 import java.util.Iterator; 020 import java.util.LinkedHashMap; 021 import java.util.List; 022 import java.util.Map; 023 import java.util.logging.Logger; 024 025 026 027 abstract class AttachedType extends AttachedTopic implements Type { 028 029 // ---------------------------------------------------------------------------------------------- Instance Variables 030 031 private Map<String, AssociationDefinition> assocDefs; // Attached object cache 032 private ViewConfiguration viewConfig; // Attached object cache 033 034 private Logger logger = Logger.getLogger(getClass().getName()); 035 036 // ---------------------------------------------------------------------------------------------------- Constructors 037 038 AttachedType(TypeModel model, EmbeddedService dms) { 039 super(model, dms); 040 // init attached object cache 041 initAssocDefs(); 042 initViewConfig(); 043 } 044 045 // -------------------------------------------------------------------------------------------------- Public Methods 046 047 048 049 // ****************************************** 050 // *** AttachedDeepaMehtaObject Overrides *** 051 // ****************************************** 052 053 054 055 // === Updating === 056 057 @Override 058 public void update(TypeModel model, ClientState clientState, Directives directives) { 059 boolean uriChanged = hasUriChanged(model.getUri()); 060 if (uriChanged) { 061 removeFromTypeCache(directives); 062 } 063 // 064 super.update(model, clientState, directives); 065 // 066 if (uriChanged) { 067 putInTypeCache(); // abstract 068 } 069 // 070 updateDataTypeUri(model.getDataTypeUri(), directives); 071 updateAssocDefs(model.getAssocDefs(), clientState, directives); 072 updateSequence(model.getAssocDefs()); 073 updateLabelConfig(model.getLabelConfig(), directives); 074 } 075 076 077 078 // === Deletion === 079 080 @Override 081 public void delete(Directives directives) { 082 DeepaMehtaTransaction tx = dms.beginTx(); 083 try { 084 logger.info("Deleting " + className() + " \"" + getUri() + "\""); 085 // 086 super.delete(directives); // delete type topic 087 // 088 removeFromTypeCache(directives); 089 // 090 tx.success(); 091 } catch (Exception e) { 092 logger.warning("ROLLBACK!"); 093 throw new RuntimeException("Deleting " + className() + " \"" + getUri() + "\" failed", e); 094 } finally { 095 tx.finish(); 096 } 097 } 098 099 100 101 // *************************** 102 // *** Type Implementation *** 103 // *************************** 104 105 106 107 // === Model === 108 109 // --- Data Type --- 110 111 @Override 112 public String getDataTypeUri() { 113 return getModel().getDataTypeUri(); 114 } 115 116 @Override 117 public void setDataTypeUri(String dataTypeUri, Directives directives) { 118 // update memory 119 getModel().setDataTypeUri(dataTypeUri); 120 // update DB 121 storeDataTypeUri(dataTypeUri, directives); 122 } 123 124 // --- Index Modes --- 125 126 @Override 127 public List<IndexMode> getIndexModes() { 128 return getModel().getIndexModes(); 129 } 130 131 @Override 132 public void setIndexModes(List<IndexMode> indexModes) { 133 // update memory 134 getModel().setIndexModes(indexModes); 135 // update DB 136 dms.typeStorage.storeIndexModes(getUri(), indexModes); 137 } 138 139 // --- Association Definitions --- 140 141 @Override 142 public Collection<AssociationDefinition> getAssocDefs() { 143 return assocDefs.values(); 144 } 145 146 @Override 147 public AssociationDefinition getAssocDef(String childTypeUri) { 148 AssociationDefinition assocDef = assocDefs.get(childTypeUri); 149 if (assocDef == null) { 150 throw new RuntimeException("Schema violation: association definition \"" + 151 childTypeUri + "\" not found in " + this); 152 } 153 return assocDef; 154 } 155 156 @Override 157 public boolean hasAssocDef(String childTypeUri) { 158 return assocDefs.get(childTypeUri) != null; 159 } 160 161 @Override 162 public void addAssocDef(AssociationDefinitionModel model) { 163 // Note: the predecessor must be determined *before* the memory is updated 164 AssociationDefinitionModel predecessor = lastAssocDef(); 165 // update memory 166 getModel().addAssocDef(model); // update model 167 _addAssocDef(model); // update attached object cache 168 // update DB 169 dms.typeStorage.storeAssociationDefinition(model); 170 dms.typeStorage.appendToSequence(getUri(), model, predecessor); 171 } 172 173 @Override 174 public void updateAssocDef(AssociationDefinitionModel model) { 175 // update memory 176 getModel().updateAssocDef(model); // update model 177 _addAssocDef(model); // update attached object cache 178 // update DB 179 // ### Note: the DB is not updated here! In case of interactive assoc type change the association is 180 // already updated in DB. => See interface comment. 181 } 182 183 @Override 184 public void removeAssocDef(String childTypeUri) { 185 // update memory 186 getModel().removeAssocDef(childTypeUri); // update model 187 AttachedAssociationDefinition assocDef = _removeAssocDef(childTypeUri); // update attached object cache 188 // update DB 189 dms.typeStorage.rebuildSequence(this); 190 } 191 192 // --- Label Configuration --- 193 194 @Override 195 public List<String> getLabelConfig() { 196 return getModel().getLabelConfig(); 197 } 198 199 @Override 200 public void setLabelConfig(List<String> labelConfig, Directives directives) { 201 // update memory 202 getModel().setLabelConfig(labelConfig); 203 // update DB 204 dms.typeStorage.storeLabelConfig(labelConfig, getModel().getAssocDefs(), directives); 205 } 206 207 // --- View Configuration --- 208 209 @Override 210 public ViewConfiguration getViewConfig() { 211 return viewConfig; 212 } 213 214 // FIXME: to be dropped 215 @Override 216 public Object getViewConfig(String typeUri, String settingUri) { 217 return getModel().getViewConfig(typeUri, settingUri); 218 } 219 220 // --- 221 222 @Override 223 public TypeModel getModel() { 224 return (TypeModel) super.getModel(); 225 } 226 227 228 229 // ----------------------------------------------------------------------------------------- Package Private Methods 230 231 abstract void putInTypeCache(); 232 233 abstract void removeFromTypeCache(); 234 235 // --- 236 237 abstract Directive getDeleteTypeDirective(); 238 239 // ------------------------------------------------------------------------------------------------- Private Methods 240 241 242 243 // === Update === 244 245 private boolean hasUriChanged(String newUri) { 246 return newUri != null && !getUri().equals(newUri); 247 } 248 249 // --- 250 251 private void updateDataTypeUri(String newDataTypeUri, Directives directives) { 252 if (newDataTypeUri != null) { 253 String dataTypeUri = getDataTypeUri(); 254 if (!dataTypeUri.equals(newDataTypeUri)) { 255 logger.info("### Changing data type URI from \"" + dataTypeUri + "\" -> \"" + newDataTypeUri + "\""); 256 setDataTypeUri(newDataTypeUri, directives); 257 } 258 } 259 } 260 261 private void storeDataTypeUri(String dataTypeUri, Directives directives) { 262 // remove current assignment 263 getRelatedTopic("dm4.core.aggregation", "dm4.core.type", "dm4.core.default", "dm4.core.data_type", 264 false, false).getRelatingAssociation().delete(directives); 265 // create new assignment 266 dms.typeStorage.storeDataType(getUri(), dataTypeUri); 267 } 268 269 // --- 270 271 private void updateAssocDefs(Collection<AssociationDefinitionModel> newAssocDefs, ClientState clientState, 272 Directives directives) { 273 for (AssociationDefinitionModel assocDef : newAssocDefs) { 274 getAssocDef(assocDef.getChildTypeUri()).update(assocDef, clientState, directives); 275 } 276 } 277 278 // --- 279 280 private void updateSequence(Collection<AssociationDefinitionModel> newAssocDefs) { 281 if (!hasSequenceChanged(newAssocDefs)) { 282 return; 283 } 284 logger.info("### Changing assoc def sequence"); 285 // update memory 286 getModel().removeAllAssocDefs(); 287 for (AssociationDefinitionModel assocDef : newAssocDefs) { 288 getModel().addAssocDef(assocDef); 289 } 290 initAssocDefs(); // attached object cache 291 // update DB 292 dms.typeStorage.rebuildSequence(this); 293 } 294 295 private boolean hasSequenceChanged(Collection<AssociationDefinitionModel> newAssocDefs) { 296 Collection<AssociationDefinition> assocDefs = getAssocDefs(); 297 if (assocDefs.size() != newAssocDefs.size()) { 298 throw new RuntimeException("adding/removing of assoc defs not yet supported via updateTopicType() call"); 299 } 300 // 301 Iterator<AssociationDefinitionModel> i = newAssocDefs.iterator(); 302 for (AssociationDefinition assocDef : assocDefs) { 303 AssociationDefinitionModel newAssocDef = i.next(); 304 if (!assocDef.getChildTypeUri().equals(newAssocDef.getChildTypeUri())) { 305 return true; 306 } 307 } 308 // 309 return false; 310 } 311 312 // --- 313 314 private void updateLabelConfig(List<String> newLabelConfig, Directives directives) { 315 if (!getLabelConfig().equals(newLabelConfig)) { 316 logger.info("### Changing label configuration"); 317 setLabelConfig(newLabelConfig, directives); 318 } 319 } 320 321 322 323 // === Helper === 324 325 /** 326 * Returns the last association definition of this type or 327 * <code>null</code> if there are no association definitions. 328 * 329 * ### TODO: move to class TypeModel? 330 */ 331 private AssociationDefinitionModel lastAssocDef() { 332 AssociationDefinitionModel lastAssocDef = null; 333 for (AssociationDefinitionModel assocDef : getModel().getAssocDefs()) { 334 lastAssocDef = assocDef; 335 } 336 return lastAssocDef; 337 } 338 339 // --- Attached Object Cache --- 340 341 // ### FIXME: make it private 342 protected void initAssocDefs() { 343 this.assocDefs = new LinkedHashMap(); 344 for (AssociationDefinitionModel model : getModel().getAssocDefs()) { 345 _addAssocDef(model); 346 } 347 } 348 349 /** 350 * @param model the new association definition. 351 * Note: all fields must be initialized. 352 */ 353 private void _addAssocDef(AssociationDefinitionModel model) { 354 AttachedAssociationDefinition assocDef = new AttachedAssociationDefinition(model, dms); 355 assocDefs.put(assocDef.getChildTypeUri(), assocDef); 356 } 357 358 private AttachedAssociationDefinition _removeAssocDef(String childTypeUri) { 359 // error check 360 getAssocDef(childTypeUri); 361 // 362 return (AttachedAssociationDefinition) assocDefs.remove(childTypeUri); 363 } 364 365 // --- 366 367 private void initViewConfig() { 368 RoleModel configurable = dms.typeStorage.createConfigurableType(getId()); // ### type ID is uninitialized 369 this.viewConfig = new AttachedViewConfiguration(configurable, getModel().getViewConfigModel(), dms); 370 } 371 372 373 374 // === 375 376 /** 377 * Removes this type from type cache and adds a DELETE TYPE directive to the given set of directives. 378 */ 379 private void removeFromTypeCache(Directives directives) { 380 removeFromTypeCache(); // abstract 381 addDeleteTypeDirective(directives); 382 } 383 384 private void addDeleteTypeDirective(Directives directives) { 385 Directive dir = getDeleteTypeDirective(); // abstract 386 directives.add(dir, new JSONWrapper("uri", getUri())); 387 } 388 389 // ------------------------------------------------------------------------------------------------- Private Classes 390 391 private class JSONWrapper implements JSONEnabled { 392 393 private JSONObject wrapped; 394 395 private JSONWrapper(String key, Object value) { 396 try { 397 wrapped = new JSONObject(); 398 wrapped.put(key, value); 399 } catch (Exception e) { 400 throw new RuntimeException("Constructing a JSONWrapper failed", e); 401 } 402 } 403 404 @Override 405 public JSONObject toJSON() { 406 return wrapped; 407 } 408 } 409 }