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