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