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