001package de.deepamehta.core.impl; 002 003import de.deepamehta.core.AssociationDefinition; 004import de.deepamehta.core.JSONEnabled; 005import de.deepamehta.core.model.AssociationDefinitionModel; 006import de.deepamehta.core.model.AssociationModel; 007import de.deepamehta.core.model.DeepaMehtaObjectModel; 008import de.deepamehta.core.model.IndexMode; 009import de.deepamehta.core.model.TopicModel; 010import de.deepamehta.core.model.TypeModel; 011import de.deepamehta.core.model.ViewConfigurationModel; 012import de.deepamehta.core.service.Directive; 013import de.deepamehta.core.service.Directives; 014import de.deepamehta.core.util.SequencedHashMap; 015 016import org.codehaus.jettison.json.JSONArray; 017import org.codehaus.jettison.json.JSONObject; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Iterator; 022import java.util.List; 023import java.util.logging.Logger; 024 025 026 027class TypeModelImpl extends TopicModelImpl implements TypeModel { 028 029 // ---------------------------------------------------------------------------------------------- Instance Variables 030 031 private String dataTypeUri; 032 private List<IndexMode> indexModes; 033 private SequencedHashMap<String, AssociationDefinitionModelImpl> assocDefs; // is never null, may be empty 034 private List<String> labelConfig; // is never null, may be empty 035 private ViewConfigurationModelImpl viewConfig; // is never null 036 037 private Logger logger = Logger.getLogger(getClass().getName()); 038 039 // ---------------------------------------------------------------------------------------------------- Constructors 040 041 TypeModelImpl(TopicModelImpl typeTopic, String dataTypeUri, List<IndexMode> indexModes, 042 List<AssociationDefinitionModel> assocDefs, List<String> labelConfig, 043 ViewConfigurationModelImpl viewConfig) { 044 super(typeTopic); 045 this.dataTypeUri = dataTypeUri; 046 this.indexModes = indexModes; 047 this.assocDefs = toMap(assocDefs); 048 this.labelConfig = labelConfig; 049 this.viewConfig = viewConfig; 050 } 051 052 TypeModelImpl(TypeModelImpl type) { 053 super(type); 054 this.dataTypeUri = type.getDataTypeUri(); 055 this.indexModes = type.getIndexModes(); 056 this.assocDefs = toMap(type.getAssocDefs()); 057 this.labelConfig = type.getLabelConfig(); 058 this.viewConfig = type.getViewConfigModel(); 059 } 060 061 // -------------------------------------------------------------------------------------------------- Public Methods 062 063 064 065 // === Data Type === 066 067 @Override 068 public String getDataTypeUri() { 069 return dataTypeUri; 070 } 071 072 @Override 073 public void setDataTypeUri(String dataTypeUri) { 074 this.dataTypeUri = dataTypeUri; 075 } 076 077 078 079 // === Index Modes === 080 081 @Override 082 public List<IndexMode> getIndexModes() { 083 return indexModes; 084 } 085 086 @Override 087 public void addIndexMode(IndexMode indexMode) { 088 indexModes.add(indexMode); 089 } 090 091 092 093 // === Association Definitions === 094 095 @Override 096 public Collection<AssociationDefinitionModelImpl> getAssocDefs() { 097 return assocDefs.values(); 098 } 099 100 @Override 101 public AssociationDefinitionModelImpl getAssocDef(String assocDefUri) { 102 return getAssocDefOrThrow(assocDefUri); 103 } 104 105 @Override 106 public boolean hasAssocDef(String assocDefUri) { 107 return _getAssocDef(assocDefUri) != null; 108 } 109 110 /** 111 * @param assocDef the assoc def to add. 112 * Note: its ID might be uninitialized (-1). 113 */ 114 @Override 115 public TypeModel addAssocDef(AssociationDefinitionModel assocDef) { 116 return addAssocDefBefore(assocDef, null); // beforeAssocDefUri=null 117 } 118 119 /** 120 * @param assocDef the assoc def to add. 121 * Note: its ID might be uninitialized (-1). 122 * @param beforeAssocDefUri the URI of the assoc def <i>before</i> the given assoc def is inserted. 123 * If <code>null</code> the assoc def is appended at the end. 124 */ 125 @Override 126 public TypeModel addAssocDefBefore(AssociationDefinitionModel assocDef, String beforeAssocDefUri) { 127 try { 128 // error check 129 String assocDefUri = assocDef.getAssocDefUri(); 130 AssociationDefinitionModel existing = _getAssocDef(assocDefUri); 131 if (existing != null) { 132 throw new RuntimeException("Ambiguity: type \"" + getUri() + "\" has more than one \"" + assocDefUri + 133 "\" association definitions"); 134 } 135 // 136 assocDefs.putBefore(assocDefUri, (AssociationDefinitionModelImpl) assocDef, beforeAssocDefUri); 137 return this; 138 } catch (Exception e) { 139 throw new RuntimeException("Adding an association definition to type \"" + getUri() + "\" before \"" + 140 beforeAssocDefUri + "\" failed" + assocDef, e); 141 } 142 } 143 144 @Override 145 public AssociationDefinitionModel removeAssocDef(String assocDefUri) { 146 try { 147 AssociationDefinitionModel assocDef = assocDefs.remove(assocDefUri); 148 if (assocDef == null) { 149 throw new RuntimeException("Association definition \"" + assocDefUri + "\" not found in " + 150 assocDefs.keySet()); 151 } 152 return assocDef; 153 } catch (Exception e) { 154 throw new RuntimeException("Removing association definition \"" + assocDefUri + "\" from type \"" + 155 getUri() + "\" failed", e); 156 } 157 } 158 159 160 161 // === Label Configuration === 162 163 @Override 164 public List<String> getLabelConfig() { 165 return labelConfig; 166 } 167 168 @Override 169 public void setLabelConfig(List<String> labelConfig) { 170 this.labelConfig = labelConfig; 171 } 172 173 174 175 // === View Configuration === 176 177 @Override 178 public ViewConfigurationModelImpl getViewConfigModel() { 179 return viewConfig; 180 } 181 182 // FIXME: server-side operations on the view config settings possibly suggest they are not acually 183 // view config settings but part of the topic type model. Possibly this method should be dropped. 184 @Override 185 public Object getViewConfig(String typeUri, String settingUri) { 186 return viewConfig.getSetting(typeUri, settingUri); 187 } 188 189 @Override 190 public void setViewConfig(ViewConfigurationModel viewConfig) { 191 this.viewConfig = (ViewConfigurationModelImpl) viewConfig; 192 } 193 194 195 196 // === Iterable Implementation === 197 198 /** 199 * Returns an interator which iterates this TypeModel's assoc def URIs. 200 */ 201 @Override 202 public Iterator<String> iterator() { 203 return assocDefs.keySet().iterator(); 204 } 205 206 207 208 // **************************** 209 // *** TopicModel Overrides *** 210 // **************************** 211 212 213 214 @Override 215 public JSONObject toJSON() { 216 try { 217 return super.toJSON() 218 .put("data_type_uri", getDataTypeUri()) 219 .put("index_mode_uris", toJSONArray(indexModes)) 220 .put("assoc_defs", toJSONArray(assocDefs.values())) 221 .put("label_config", new JSONArray(getLabelConfig())) 222 .put("view_config_topics", getViewConfigModel().toJSONArray()); 223 } catch (Exception e) { 224 throw new RuntimeException("Serialization failed (" + this + ")", e); 225 } 226 } 227 228 229 230 // **************** 231 // *** Java API *** 232 // **************** 233 234 235 236 @Override 237 public String toString() { 238 return "id=" + id + ", uri=\"" + uri + "\", value=\"" + value + "\", typeUri=\"" + typeUri + 239 "\", dataTypeUri=\"" + getDataTypeUri() + "\", indexModes=" + getIndexModes() + ", assocDefs=" + 240 getAssocDefs() + ", labelConfig=" + getLabelConfig() + ", " + getViewConfigModel(); 241 } 242 243 // ----------------------------------------------------------------------------------------- Package Private Methods 244 245 246 247 // === Abstract Methods === 248 249 List<? extends DeepaMehtaObjectModelImpl> getAllInstances() { 250 throw new UnsupportedOperationException(); 251 } 252 253 // --- 254 255 Directive getUpdateTypeDirective() { 256 throw new UnsupportedOperationException(); 257 } 258 259 Directive getDeleteTypeDirective() { 260 throw new UnsupportedOperationException(); 261 } 262 263 264 265 // === Core Internal Hooks === 266 267 @Override 268 void preUpdate(DeepaMehtaObjectModel newModel) { 269 super.preUpdate(newModel); 270 // 271 // ### TODO: is it sufficient if we rehash (remove + add) at post-time? 272 if (uriChange(newModel.getUri(), uri)) { 273 removeFromTypeCache(); 274 } 275 } 276 277 @Override 278 void postUpdate(DeepaMehtaObjectModel newModel, DeepaMehtaObjectModel oldModel) { 279 super.postUpdate(newModel, oldModel); 280 // 281 if (uriChange(newModel.getUri(), oldModel.getUri())) { 282 pl.typeStorage.putInTypeCache(this); 283 } 284 // 285 updateType((TypeModel) newModel); 286 // 287 // Note: the UPDATE_TOPIC_TYPE/UPDATE_ASSOCIATION_TYPE directive must be added *before* a possible UPDATE_TOPIC 288 // directive (added by DeepaMehtaObjectModelImpl.update()). In case of a changed type URI the webclient's type 289 // cache must be updated *before* the TopicTypeRenderer/AssociationTypeRenderer can render the type. 290 Directives.get().add(getUpdateTypeDirective(), instantiate()); 291 } 292 293 // --- 294 295 @Override 296 void preDelete() { 297 super.preDelete(); 298 // 299 int size = getAllInstances().size(); 300 if (size > 0) { 301 throw new RuntimeException(size + " \"" + value + "\" instances still exist"); 302 } 303 } 304 305 @Override 306 void postDelete() { 307 super.postDelete(); 308 // 309 removeFromTypeCache(); 310 } 311 312 313 314 // === Update (memory + DB) === 315 316 void updateDataTypeUri(String dataTypeUri) { 317 setDataTypeUri(dataTypeUri); // update memory 318 storeDataTypeUri(); // update DB 319 } 320 321 void updateLabelConfig(List<String> labelConfig) { 322 setLabelConfig(labelConfig); // update memory 323 pl.typeStorage.updateLabelConfig(labelConfig, getAssocDefs()); // update DB 324 } 325 326 void _addIndexMode(IndexMode indexMode) { 327 // update memory 328 addIndexMode(indexMode); 329 // update DB 330 pl.typeStorage.storeIndexMode(uri, indexMode); 331 indexAllInstances(indexMode); 332 } 333 334 // --- 335 336 void _addAssocDefBefore(AssociationDefinitionModel assocDef, String beforeAssocDefUri) { 337 try { 338 long lastAssocDefId = lastAssocDefId(); // must be determined *before* memory is updated 339 // 340 // 1) update memory 341 // Note: the assoc def's custom association type is stored as a child topic. The meta model extension that 342 // adds "Association Type" as a child to the "Composition Definition" and "Aggregation Definition" 343 // association types has itself a custom association type (named "Custom Association Type"), see migration 344 // 5. It would not be stored as storage is model driven and the (meta) model doesn't know about custom 345 // associations as this very concept is introduced only by the assoc def being added here. So, the model 346 // must be updated (in-memory) *before* the assoc def is stored. 347 addAssocDefBefore(assocDef, beforeAssocDefUri); 348 // 349 // 2) update DB 350 pl.typeStorage.storeAssociationDefinition(assocDef); 351 long beforeAssocDefId = beforeAssocDefUri != null ? getAssocDef(beforeAssocDefUri).getId() : -1; 352 long firstAssocDefId = firstAssocDefId(); // must be determined *after* memory is updated 353 pl.typeStorage.addAssocDefToSequence(getId(), assocDef.getId(), beforeAssocDefId, firstAssocDefId, 354 lastAssocDefId); 355 } catch (Exception e) { 356 throw new RuntimeException("Adding an association definition to type \"" + getUri() + "\" before \"" + 357 beforeAssocDefUri + "\" failed" + assocDef, e); 358 } 359 } 360 361 void _removeAssocDef(String assocDefUri) { 362 // We trigger deleting an association definition by deleting the underlying association. This mimics deleting an 363 // association definition interactively in the webclient. Updating this type definition's memory and DB sequence 364 // is triggered then by the Type Editor plugin's preDeleteAssociation() hook. 365 // This way deleting an association definition works for both cases: 1) interactive deletion (when the user 366 // deletes an association), and 2) programmatical deletion (e.g. from a migration). 367 getAssocDef(assocDefUri).delete(); 368 } 369 370 371 372 // === Type Editor Support === 373 374 void _addAssocDef(AssociationModel assoc) { 375 _addAssocDefBefore(pl.typeStorage.newAssociationDefinition(assoc), null); // beforeAssocDefUri=null 376 } 377 378 /** 379 * Note: in contrast to the other "update" methods this one updates the memory only, not the DB! 380 * If you want to update memory and DB use {@link AssociationDefinition#update}. 381 * <p> 382 * This method is here to support a special case: the user retypes an association which results in 383 * a changed type definition. In this case the DB is already up-to-date and only the type's memory 384 * representation must be updated. So, here the DB update is the *cause* for a necessary memory-update. 385 * Normally the situation is vice-versa: the DB update is the necessary *effect* of a memory-update. 386 * 387 * @param assocDef the new association definition. 388 * Note: in contrast to the other "update" methods this one does not support partial updates. 389 * That is all association definition fields must be initialized. ### FIXDOC 390 */ 391 void _updateAssocDef(AssociationModel assoc) { 392 // Note: if the assoc def's custom association type is changed the assoc def URI changes as well. 393 // So we must identify the assoc def to update **by ID** and rehash (that is remove + add). 394 String[] assocDefUris = findAssocDefUris(assoc.getId()); 395 AssociationDefinitionModel oldAssocDef = getAssocDef(assocDefUris[0]); 396 if (assoc == oldAssocDef) { 397 // edited via type topic -- abort 398 return; 399 } 400 // Note: we must not manipulate the assoc model in-place. The Webclient expects by-ID roles. 401 AssociationModel newAssocModel = mf.newAssociationModel(assoc); 402 AssociationModel oldAssocModel = oldAssocDef; 403 // Note: an assoc def expects by-URI roles. 404 newAssocModel.setRoleModel1(oldAssocModel.getRoleModel1()); 405 newAssocModel.setRoleModel2(oldAssocModel.getRoleModel2()); 406 // 407 AssociationDefinitionModel newAssocDef = mf.newAssociationDefinitionModel(newAssocModel, 408 oldAssocDef.getParentCardinalityUri(), 409 oldAssocDef.getChildCardinalityUri(), oldAssocDef.getViewConfigModel() 410 ); 411 String oldAssocDefUri = oldAssocDef.getAssocDefUri(); 412 String newAssocDefUri = newAssocDef.getAssocDefUri(); 413 if (oldAssocDefUri.equals(newAssocDefUri)) { 414 replaceAssocDef(newAssocDef); 415 } else { 416 replaceAssocDef(newAssocDef, oldAssocDefUri, assocDefUris[1]); 417 // 418 // Note: if the custom association type has changed and the assoc def is part the label config 419 // we must replace the assoc def URI in the label config 420 replaceInLabelConfig(newAssocDefUri, oldAssocDefUri); 421 } 422 } 423 424 /** 425 * Removes an association from memory and rebuilds the sequence in DB. Note: the underlying 426 * association is *not* removed from DB. 427 * This method is called (by the Type Editor plugin's preDeleteAssociation() hook) when the 428 * deletion of an association that represents an association definition is imminent. ### FIXDOC 429 */ 430 void _removeAssocDefFromMemoryAndRebuildSequence(AssociationModel assoc) { 431 String[] assocDefUris = findAssocDefUris(assoc.getId()); 432 String assocDefUri = getAssocDef(assocDefUris[0]).getAssocDefUri(); 433 // update memory 434 removeAssocDef(assocDefUri); 435 removeFromLabelConfig(assocDefUri); 436 // update DB 437 pl.typeStorage.rebuildSequence(this); 438 } 439 440 441 442 // === 443 444 void rehashAssocDef(long assocDefId) { 445 String[] assocDefUris = findAssocDefUris(assocDefId); 446 rehashAssocDef(assocDefUris[0], assocDefUris[1]); 447 } 448 449 // ------------------------------------------------------------------------------------------------- Private Methods 450 451 452 453 // === Update (memory + DB) === 454 455 private void updateType(TypeModel newModel) { 456 _updateDataTypeUri(newModel.getDataTypeUri()); 457 _updateAssocDefs(newModel.getAssocDefs()); 458 _updateSequence(newModel.getAssocDefs()); 459 _updateLabelConfig(newModel.getLabelConfig()); 460 } 461 462 // --- 463 464 private void _updateDataTypeUri(String newDataTypeUri) { 465 if (newDataTypeUri != null) { 466 String dataTypeUri = getDataTypeUri(); 467 if (!dataTypeUri.equals(newDataTypeUri)) { 468 logger.info("### Changing data type URI from \"" + dataTypeUri + "\" -> \"" + newDataTypeUri + "\""); 469 updateDataTypeUri(newDataTypeUri); 470 } 471 } 472 } 473 474 private void _updateAssocDefs(Collection<? extends AssociationDefinitionModel> newAssocDefs) { 475 for (AssociationDefinitionModel assocDef : newAssocDefs) { 476 // Note: if the assoc def's custom association type was changed the assoc def URI changes as well. 477 // So we must identify the assoc def to update **by ID**. 478 // ### TODO: drop updateAssocDef() and rehash here (that is remove + add). 479 String[] assocDefUris = findAssocDefUris(assocDef.getId()); 480 getAssocDef(assocDefUris[0]).update(assocDef); 481 } 482 } 483 484 private void _updateSequence(Collection<? extends AssociationDefinitionModel> newAssocDefs) { 485 try { 486 if (getAssocDefs().size() != newAssocDefs.size()) { 487 throw new RuntimeException("adding/removing of assoc defs not yet supported via type update"); 488 } 489 if (hasSameAssocDefSequence(newAssocDefs)) { 490 return; 491 } 492 // update memory 493 logger.info("### Changing assoc def sequence (" + getAssocDefs().size() + " items)"); 494 rehashAssocDefs(newAssocDefs); 495 // update DB 496 pl.typeStorage.rebuildSequence(this); 497 } catch (Exception e) { 498 throw new RuntimeException("Updating the assoc def sequence failed", e); 499 } 500 } 501 502 private void _updateLabelConfig(List<String> newLabelConfig) { 503 try { 504 if (!getLabelConfig().equals(newLabelConfig)) { 505 logger.info("### Changing label configuration from " + getLabelConfig() + " -> " + newLabelConfig); 506 updateLabelConfig(newLabelConfig); 507 } 508 } catch (Exception e) { 509 throw new RuntimeException("Updating label configuration of type \"" + getUri() + "\" failed", e); 510 } 511 } 512 513 514 515 // === Store (DB only) === 516 517 private void storeDataTypeUri() { 518 // remove current assignment 519 getRelatedTopic("dm4.core.aggregation", "dm4.core.type", "dm4.core.default", "dm4.core.data_type") 520 .getRelatingAssociation().delete(); 521 // create new assignment 522 pl.typeStorage.storeDataType(uri, dataTypeUri); 523 } 524 525 private void indexAllInstances(IndexMode indexMode) { 526 List<? extends DeepaMehtaObjectModel> objects = getAllInstances(); 527 // 528 String str = "\"" + value + "\" (" + uri + ") instances"; 529 if (indexModes.size() > 0) { 530 if (objects.size() > 0) { 531 logger.info("### Indexing " + objects.size() + " " + str + " (indexMode=" + indexMode + ")"); 532 } else { 533 logger.info("### Indexing " + str + " ABORTED -- no instances in DB"); 534 } 535 } else { 536 logger.info("### Indexing " + str + " ABORTED -- no index mode set"); 537 } 538 // 539 for (DeepaMehtaObjectModel obj : objects) { 540 ((DeepaMehtaObjectModelImpl) obj).indexSimpleValue(indexMode); 541 } 542 } 543 544 545 546 // === Association Definitions (memory access) === 547 548 /** 549 * Finds an assoc def by ID and returns its URI (at index 0). Returns the URI of the next-in-sequence 550 * assoc def as well (at index 1), or null if the found assoc def is the last one. 551 */ 552 private String[] findAssocDefUris(long assocDefId) { 553 if (assocDefId == -1) { 554 throw new IllegalArgumentException("findAssocDefUris() called with assocDefId=-1"); 555 } 556 String[] assocDefUris = new String[2]; 557 Iterator<String> i = iterator(); 558 while (i.hasNext()) { 559 String assocDefUri = i.next(); 560 long _assocDefId = checkAssocDefId(_getAssocDef(assocDefUri)); 561 if (_assocDefId == assocDefId) { 562 assocDefUris[0] = assocDefUri; 563 if (i.hasNext()) { 564 assocDefUris[1] = i.next(); 565 } 566 break; 567 } 568 } 569 if (assocDefUris[0] == null) { 570 throw new RuntimeException("Assoc def with ID " + assocDefId + " not found in assoc defs of type \"" + 571 getUri() + "\" (" + assocDefs.keySet() + ")"); 572 } 573 return assocDefUris; 574 } 575 576 private boolean hasSameAssocDefSequence(Collection<? extends AssociationDefinitionModel> assocDefs) { 577 Collection<? extends AssociationDefinitionModel> _assocDefs = getAssocDefs(); 578 if (assocDefs.size() != _assocDefs.size()) { 579 return false; 580 } 581 // 582 Iterator<? extends AssociationDefinitionModel> i = assocDefs.iterator(); 583 for (AssociationDefinitionModel _assocDef : _assocDefs) { 584 AssociationDefinitionModel assocDef = i.next(); 585 // Note: if the assoc def's custom association type changedes the assoc def URI changes as well. 586 // So we must identify the assoc defs to compare **by ID**. 587 long assocDefId = checkAssocDefId(assocDef); 588 long _assocDefId = checkAssocDefId(_assocDef); 589 if (assocDefId != _assocDefId) { 590 return false; 591 } 592 } 593 // 594 return true; 595 } 596 597 // --- 598 599 private void rehashAssocDef(String assocDefUri, String beforeAssocDefUri) { 600 AssociationDefinitionModel assocDef = removeAssocDef(assocDefUri); 601 logger.info("### Rehashing assoc def \"" + assocDefUri + "\" -> \"" + assocDef.getAssocDefUri() + 602 "\" (put " + (beforeAssocDefUri != null ? "before \"" + beforeAssocDefUri + "\"" : "at end") + ")"); 603 addAssocDefBefore(assocDef, beforeAssocDefUri); 604 } 605 606 private void rehashAssocDefs(Collection<? extends AssociationDefinitionModel> newAssocDefs) { 607 for (AssociationDefinitionModel assocDef : newAssocDefs) { 608 rehashAssocDef(assocDef.getAssocDefUri(), null); 609 } 610 } 611 612 private void replaceAssocDef(AssociationDefinitionModel assocDef) { 613 replaceAssocDef(assocDef, assocDef.getAssocDefUri(), null); 614 } 615 616 private void replaceAssocDef(AssociationDefinitionModel assocDef, String oldAssocDefUri, String beforeAssocDefUri) { 617 removeAssocDef(oldAssocDefUri); 618 addAssocDefBefore(assocDef, beforeAssocDefUri); 619 } 620 621 // --- 622 623 private AssociationDefinitionModelImpl getAssocDefOrThrow(String assocDefUri) { 624 AssociationDefinitionModelImpl assocDef = _getAssocDef(assocDefUri); 625 if (assocDef == null) { 626 throw new RuntimeException("Association definition \"" + assocDefUri + "\" not found in " + 627 assocDefs.keySet()); 628 } 629 return assocDef; 630 } 631 632 private AssociationDefinitionModelImpl _getAssocDef(String assocDefUri) { 633 return assocDefs.get(assocDefUri); 634 } 635 636 // --- 637 638 /** 639 * Returns the ID of the last association definition of this type or 640 * <code>-1</code> if there are no association definitions. 641 */ 642 private long lastAssocDefId() { 643 long lastAssocDefId = -1; 644 for (AssociationDefinitionModel assocDef : getAssocDefs()) { 645 lastAssocDefId = assocDef.getId(); 646 } 647 return lastAssocDefId; 648 } 649 650 private long firstAssocDefId() { 651 return getAssocDefs().iterator().next().getId(); 652 } 653 654 // --- 655 656 private long checkAssocDefId(AssociationDefinitionModel assocDef) { 657 long assocDefId = assocDef.getId(); 658 if (assocDefId == -1) { 659 throw new RuntimeException("The assoc def ID is uninitialized (-1): " + assocDef); 660 } 661 return assocDefId; 662 } 663 664 // --- 665 666 private SequencedHashMap<String, AssociationDefinitionModelImpl> toMap( 667 Collection<? extends AssociationDefinitionModel> assocDefs) { 668 SequencedHashMap<String, AssociationDefinitionModelImpl> _assocDefs = new SequencedHashMap(); 669 for (AssociationDefinitionModel assocDef : assocDefs) { 670 _assocDefs.put(assocDef.getAssocDefUri(), (AssociationDefinitionModelImpl) assocDef); 671 } 672 return _assocDefs; 673 } 674 675 676 677 // === Label Configuration (memory access) === 678 679 private void replaceInLabelConfig(String newAssocDefUri, String oldAssocDefUri) { 680 List<String> labelConfig = getLabelConfig(); 681 int i = labelConfig.indexOf(oldAssocDefUri); 682 if (i != -1) { 683 logger.info("### Label config: replacing \"" + oldAssocDefUri + "\" -> \"" + newAssocDefUri + 684 "\" (position " + i + ")"); 685 labelConfig.set(i, newAssocDefUri); 686 } 687 } 688 689 private void removeFromLabelConfig(String assocDefUri) { 690 List<String> labelConfig = getLabelConfig(); 691 int i = labelConfig.indexOf(assocDefUri); 692 if (i != -1) { 693 logger.info("### Label config: removing \"" + assocDefUri + "\" (position " + i + ")"); 694 labelConfig.remove(i); 695 } 696 } 697 698 699 700 // === Type Cache (memory access) === 701 702 /** 703 * Removes this type from type cache and adds a DELETE TYPE directive to the given set of directives. 704 */ 705 private void removeFromTypeCache() { 706 pl.typeStorage.removeFromTypeCache(uri); 707 // 708 Directive dir = getDeleteTypeDirective(); // abstract 709 Directives.get().add(dir, new JSONWrapper("uri", uri)); 710 } 711 712 713 714 // === Serialization === 715 716 private JSONArray toJSONArray(List<IndexMode> indexModes) { 717 JSONArray indexModeUris = new JSONArray(); 718 for (IndexMode indexMode : indexModes) { 719 indexModeUris.put(indexMode.toUri()); 720 } 721 return indexModeUris; 722 } 723 724 private JSONArray toJSONArray(Collection<? extends AssociationDefinitionModel> assocDefs) { 725 JSONArray _assocDefs = new JSONArray(); 726 for (AssociationDefinitionModel assocDef : assocDefs) { 727 _assocDefs.put(assocDef.toJSON()); 728 } 729 return _assocDefs; 730 } 731 732 733 734 // ------------------------------------------------------------------------------------------------- Private Classes 735 736 private static final class JSONWrapper implements JSONEnabled { 737 738 private JSONObject wrapped; 739 740 private JSONWrapper(String key, Object value) { 741 try { 742 wrapped = new JSONObject(); 743 wrapped.put(key, value); 744 } catch (Exception e) { 745 throw new RuntimeException("Constructing a JSONWrapper failed", e); 746 } 747 } 748 749 @Override 750 public JSONObject toJSON() { 751 return wrapped; 752 } 753 } 754}