001 package de.deepamehta.core.impl; 002 003 import de.deepamehta.core.Association; 004 import de.deepamehta.core.AssociationType; 005 import de.deepamehta.core.AssociationDefinition; 006 import de.deepamehta.core.RelatedAssociation; 007 import de.deepamehta.core.RelatedTopic; 008 import de.deepamehta.core.Topic; 009 import de.deepamehta.core.TopicType; 010 import de.deepamehta.core.Type; 011 import de.deepamehta.core.model.AssociationDefinitionModel; 012 import de.deepamehta.core.model.AssociationModel; 013 import de.deepamehta.core.model.AssociationRoleModel; 014 import de.deepamehta.core.model.AssociationTypeModel; 015 import de.deepamehta.core.model.IndexMode; 016 import de.deepamehta.core.model.RelatedAssociationModel; 017 import de.deepamehta.core.model.RelatedTopicModel; 018 import de.deepamehta.core.model.RoleModel; 019 import de.deepamehta.core.model.SimpleValue; 020 import de.deepamehta.core.model.TopicModel; 021 import de.deepamehta.core.model.TopicRoleModel; 022 import de.deepamehta.core.model.TopicTypeModel; 023 import de.deepamehta.core.model.TypeModel; 024 import de.deepamehta.core.model.ViewConfigurationModel; 025 import de.deepamehta.core.service.ResultList; 026 import de.deepamehta.core.service.TypeStorage; 027 import de.deepamehta.core.util.DeepaMehtaUtils; 028 029 import static java.util.Arrays.asList; 030 import java.util.ArrayList; 031 import java.util.Collection; 032 import java.util.HashMap; 033 import java.util.List; 034 import java.util.Map; 035 import java.util.logging.Logger; 036 037 038 039 /** 040 * Storage-impl agnostic support for fetching/storing type models. 041 */ 042 class TypeStorageImpl implements TypeStorage { 043 044 // ------------------------------------------------------------------------------------------------------- Constants 045 046 // role types 047 private static final String PARENT_CARDINALITY = "dm4.core.parent_cardinality"; 048 private static final String CHILD_CARDINALITY = "dm4.core.child_cardinality"; 049 050 // ---------------------------------------------------------------------------------------------- Instance Variables 051 052 private Map<String, TypeModel> typeCache = new HashMap(); // type model cache 053 054 private EmbeddedService dms; 055 056 private Logger logger = Logger.getLogger(getClass().getName()); 057 058 // ---------------------------------------------------------------------------------------------------- Constructors 059 060 TypeStorageImpl(EmbeddedService dms) { 061 this.dms = dms; 062 } 063 064 // --------------------------------------------------------------------------------------------------------- Methods 065 066 067 068 // === Type Model Cache === 069 070 TopicTypeModel getTopicType(String topicTypeUri) { 071 TopicTypeModel topicType = (TopicTypeModel) getType(topicTypeUri); 072 return topicType != null ? topicType : fetchTopicType(topicTypeUri); 073 } 074 075 AssociationTypeModel getAssociationType(String assocTypeUri) { 076 AssociationTypeModel assocType = (AssociationTypeModel) getType(assocTypeUri); 077 return assocType != null ? assocType : fetchAssociationType(assocTypeUri); 078 } 079 080 // --- 081 082 private TypeModel getType(String typeUri) { 083 return typeCache.get(typeUri); 084 } 085 086 private void putInTypeCache(TypeModel type) { 087 typeCache.put(type.getUri(), type); 088 } 089 090 void removeFromTypeCache(String typeUri) { 091 typeCache.remove(typeUri); 092 } 093 094 095 096 // === Types === 097 098 // --- Fetch --- 099 100 // ### TODO: unify with next method 101 private TopicTypeModel fetchTopicType(String topicTypeUri) { 102 Topic typeTopic = dms.getTopic("uri", new SimpleValue(topicTypeUri)); 103 checkTopicType(topicTypeUri, typeTopic); 104 // 105 // 1) fetch type components 106 String dataTypeUri = fetchDataTypeTopic(typeTopic.getId(), topicTypeUri, "topic type").getUri(); 107 List<IndexMode> indexModes = fetchIndexModes(typeTopic.getId()); 108 List<AssociationDefinitionModel> assocDefs = fetchAssociationDefinitions(typeTopic); 109 List<String> labelConfig = fetchLabelConfig(assocDefs); 110 ViewConfigurationModel viewConfig = fetchTypeViewConfig(typeTopic); 111 // 112 // 2) build type model 113 TopicTypeModel topicType = new TopicTypeModel(typeTopic.getModel(), dataTypeUri, indexModes, 114 assocDefs, labelConfig, viewConfig); 115 // 116 // 3) put in type model cache 117 putInTypeCache(topicType); 118 // 119 return topicType; 120 } 121 122 // ### TODO: unify with previous method 123 private AssociationTypeModel fetchAssociationType(String assocTypeUri) { 124 Topic typeTopic = dms.getTopic("uri", new SimpleValue(assocTypeUri)); 125 checkAssociationType(assocTypeUri, typeTopic); 126 // 127 // 1) fetch type components 128 String dataTypeUri = fetchDataTypeTopic(typeTopic.getId(), assocTypeUri, "association type").getUri(); 129 List<IndexMode> indexModes = fetchIndexModes(typeTopic.getId()); 130 List<AssociationDefinitionModel> assocDefs = fetchAssociationDefinitions(typeTopic); 131 List<String> labelConfig = fetchLabelConfig(assocDefs); 132 ViewConfigurationModel viewConfig = fetchTypeViewConfig(typeTopic); 133 // 134 // 2) build type model 135 AssociationTypeModel assocType = new AssociationTypeModel(typeTopic.getModel(), dataTypeUri, indexModes, 136 assocDefs, labelConfig, viewConfig); 137 // 138 // 3) put in type model cache 139 putInTypeCache(assocType); 140 // 141 return assocType; 142 } 143 144 // --- 145 146 private void checkTopicType(String topicTypeUri, Topic typeTopic) { 147 if (typeTopic == null) { 148 throw new RuntimeException("Topic type \"" + topicTypeUri + "\" not found in DB"); 149 } else if (!typeTopic.getTypeUri().equals("dm4.core.topic_type") && 150 !typeTopic.getTypeUri().equals("dm4.core.meta_type") && 151 !typeTopic.getTypeUri().equals("dm4.core.meta_meta_type")) { 152 throw new RuntimeException("URI \"" + topicTypeUri + "\" refers to a \"" + typeTopic.getTypeUri() + 153 "\" when the caller expects a \"dm4.core.topic_type\""); 154 } 155 } 156 157 private void checkAssociationType(String assocTypeUri, Topic typeTopic) { 158 if (typeTopic == null) { 159 throw new RuntimeException("Association type \"" + assocTypeUri + "\" not found in DB"); 160 } else if (!typeTopic.getTypeUri().equals("dm4.core.assoc_type")) { 161 throw new RuntimeException("URI \"" + assocTypeUri + "\" refers to a \"" + typeTopic.getTypeUri() + 162 "\" when the caller expects a \"dm4.core.assoc_type\""); 163 } 164 } 165 166 // --- Store --- 167 168 /** 169 * Stores the type-specific parts of the given type model. 170 * Prerequisite: the generic topic parts are stored already. 171 * <p> 172 * Called to store a newly created topic type or association type. 173 */ 174 void storeType(TypeModel type) { 175 // 1) put in type model cache 176 // Note: an association type must be put in type model cache *before* storing its association definitions. 177 // Consider creation of association type "Composition Definition": it has a composition definition itself. 178 putInTypeCache(type); 179 // 180 // 2) store type-specific parts 181 storeDataType(type.getUri(), type.getDataTypeUri()); 182 storeIndexModes(type.getUri(), type.getIndexModes()); 183 storeAssocDefs(type.getId(), type.getAssocDefs()); 184 storeLabelConfig(type.getLabelConfig(), type.getAssocDefs()); 185 storeViewConfig(createConfigurableType(type.getId()), type.getViewConfigModel()); 186 } 187 188 189 190 // === Data Type === 191 192 // --- Fetch --- 193 194 private RelatedTopicModel fetchDataTypeTopic(long typeId, String typeUri, String className) { 195 try { 196 RelatedTopicModel dataType = dms.storageDecorator.fetchTopicRelatedTopic(typeId, "dm4.core.aggregation", 197 "dm4.core.type", "dm4.core.default", "dm4.core.data_type"); 198 if (dataType == null) { 199 throw new RuntimeException("No data type topic is associated to " + className + " \"" + typeUri + "\""); 200 } 201 return dataType; 202 } catch (Exception e) { 203 throw new RuntimeException("Fetching the data type topic of " + className + " \"" + typeUri + "\" failed", 204 e); 205 } 206 } 207 208 // --- Store --- 209 210 // ### TODO: compare to low-level method EmbeddedService._associateDataType(). Remove structural similarity. 211 void storeDataType(String typeUri, String dataTypeUri) { 212 try { 213 dms.createAssociation("dm4.core.aggregation", 214 new TopicRoleModel(typeUri, "dm4.core.type"), 215 new TopicRoleModel(dataTypeUri, "dm4.core.default")); 216 } catch (Exception e) { 217 throw new RuntimeException("Associating type \"" + typeUri + "\" with data type \"" + 218 dataTypeUri + "\" failed", e); 219 } 220 } 221 222 223 224 // === Index Modes === 225 226 // --- Fetch --- 227 228 private List<IndexMode> fetchIndexModes(long typeId) { 229 ResultList<RelatedTopicModel> indexModes = dms.storageDecorator.fetchTopicRelatedTopics(typeId, 230 "dm4.core.aggregation", "dm4.core.type", "dm4.core.default", "dm4.core.index_mode", 0); 231 return IndexMode.fromTopics(indexModes.getItems()); 232 } 233 234 // --- Store --- 235 236 private void storeIndexModes(String typeUri, List<IndexMode> indexModes) { 237 for (IndexMode indexMode : indexModes) { 238 storeIndexMode(typeUri, indexMode); 239 } 240 } 241 242 void storeIndexMode(String typeUri, IndexMode indexMode) { 243 dms.createAssociation("dm4.core.aggregation", 244 new TopicRoleModel(typeUri, "dm4.core.type"), 245 new TopicRoleModel(indexMode.toUri(), "dm4.core.default")); 246 } 247 248 249 250 // === Association Definitions === 251 252 @Override 253 public AssociationDefinitionModel createAssociationDefinition(Association assoc) { 254 // Note: the assoc def's ID is already known. Setting it explicitely prevents 255 // creating the underlying association (see storeAssociationDefinition()). 256 return new AssociationDefinitionModel(assoc.getId(), assoc.getUri(), 257 assoc.getTypeUri(), fetchCustomAssocTypeUri(assoc), 258 fetchParentType(assoc).getUri(), fetchChildType(assoc).getUri(), 259 defaultCardinalityUri(assoc, PARENT_CARDINALITY), 260 defaultCardinalityUri(assoc, CHILD_CARDINALITY), null); // viewConfigModel=null 261 } 262 263 // Note: if the underlying association was an association definition before it have cardinality 264 // assignments already. These assignments are restored. Otherwise "One" is used as default. 265 private String defaultCardinalityUri(Association assoc, String cardinalityRoleTypeUri) { 266 RelatedTopicModel cardinality = fetchCardinality(assoc.getId(), cardinalityRoleTypeUri); 267 if (cardinality != null) { 268 return cardinality.getUri(); 269 } else { 270 return "dm4.core.one"; 271 } 272 } 273 274 @Override 275 public void removeAssociationDefinitionFromMemoryAndRebuildSequence(Type type, String childTypeUri) { 276 ((AttachedType) type).removeAssocDefFromMemoryAndRebuildSequence(childTypeUri); 277 } 278 279 // --- Fetch --- 280 281 private List<AssociationDefinitionModel> fetchAssociationDefinitions(Topic typeTopic) { 282 Map<Long, AssociationDefinitionModel> assocDefs = fetchAssociationDefinitionsUnsorted(typeTopic); 283 List<RelatedAssociationModel> sequence = fetchSequence(typeTopic); 284 // error check 285 if (assocDefs.size() != sequence.size()) { 286 throw new RuntimeException("DB inconsistency: type \"" + typeTopic.getUri() + "\" has " + 287 assocDefs.size() + " association definitions but in sequence are " + sequence.size()); 288 } 289 // 290 return sortAssocDefs(assocDefs, DeepaMehtaUtils.idList(sequence)); 291 } 292 293 private Map<Long, AssociationDefinitionModel> fetchAssociationDefinitionsUnsorted(Topic typeTopic) { 294 Map<Long, AssociationDefinitionModel> assocDefs = new HashMap(); 295 // 296 // 1) fetch child topic types 297 // Note: we must set fetchRelatingComposite to false here. Fetching the composite of association type 298 // Composition Definition would cause an endless recursion. Composition Definition is defined through 299 // Composition Definition itself (child types "Include in Label", "Ordered"). ### FIXDOC: this is obsolete 300 // Note: the "othersTopicTypeUri" filter is not set here (null). We want match both "dm4.core.topic_type" 301 // and "dm4.core.meta_type" (the latter is required e.g. by dm4-mail). ### TODO: add a getRelatedTopics() 302 // method that takes a list of topic types. 303 ResultList<RelatedTopic> childTypes = typeTopic.getRelatedTopics(asList("dm4.core.aggregation_def", 304 "dm4.core.composition_def"), "dm4.core.parent_type", "dm4.core.child_type", null, 0); 305 // othersTopicTypeUri=null, maxResultSize=0 306 // 307 // 2) create association definitions 308 // Note: the returned map is an intermediate, hashed by ID. The actual type model is 309 // subsequently build from it by sorting the assoc def's according to the sequence IDs. 310 for (RelatedTopic childType : childTypes) { 311 AssociationDefinitionModel assocDef = fetchAssociationDefinition(childType.getRelatingAssociation(), 312 typeTopic.getUri(), childType.getUri()); 313 assocDefs.put(assocDef.getId(), assocDef); 314 } 315 return assocDefs; 316 } 317 318 // --- 319 320 @Override 321 public AssociationDefinitionModel fetchAssociationDefinition(Association assoc) { 322 return fetchAssociationDefinition(assoc, fetchParentType(assoc).getUri(), fetchChildType(assoc).getUri()); 323 } 324 325 private AssociationDefinitionModel fetchAssociationDefinition(Association assoc, String parentTypeUri, 326 String childTypeUri) { 327 try { 328 long assocId = assoc.getId(); 329 return new AssociationDefinitionModel( 330 assocId, assoc.getUri(), assoc.getTypeUri(), fetchCustomAssocTypeUri(assoc), 331 parentTypeUri, childTypeUri, 332 fetchCardinalityOrThrow(assocId, PARENT_CARDINALITY).getUri(), 333 fetchCardinalityOrThrow(assocId, CHILD_CARDINALITY).getUri(), 334 fetchAssocDefViewConfig(assoc) 335 ); 336 } catch (Exception e) { 337 throw new RuntimeException("Fetching association definition failed (parentTypeUri=\"" + parentTypeUri + 338 "\", childTypeUri=" + childTypeUri + ", " + assoc + ")", e); 339 } 340 } 341 342 private String fetchCustomAssocTypeUri(Association assoc) { 343 TopicModel assocType = dms.storageDecorator.fetchAssociationRelatedTopic(assoc.getId(), 344 "dm4.core.custom_assoc_type", "dm4.core.parent", "dm4.core.child", "dm4.core.assoc_type"); 345 return assocType != null ? assocType.getUri() : null; 346 } 347 348 // --- 349 350 private List<AssociationDefinitionModel> sortAssocDefs(Map<Long, AssociationDefinitionModel> assocDefs, 351 List<Long> sequence) { 352 List<AssociationDefinitionModel> sortedAssocDefs = new ArrayList(); 353 for (long assocDefId : sequence) { 354 AssociationDefinitionModel assocDef = assocDefs.get(assocDefId); 355 // error check 356 if (assocDef == null) { 357 throw new RuntimeException("DB inconsistency: ID " + assocDefId + 358 " is in sequence but not in the type's association definitions"); 359 } 360 sortedAssocDefs.add(assocDef); 361 } 362 return sortedAssocDefs; 363 } 364 365 // --- Store --- 366 367 private void storeAssocDefs(long typeId, Collection<AssociationDefinitionModel> assocDefs) { 368 for (AssociationDefinitionModel assocDef : assocDefs) { 369 storeAssociationDefinition(assocDef); 370 } 371 storeSequence(typeId, assocDefs); 372 } 373 374 void storeAssociationDefinition(AssociationDefinitionModel assocDef) { 375 try { 376 // 1) create association 377 // Note: if the association definition has been created interactively the underlying association 378 // exists already. Its ID is already known. A possible Custom Association Type assignment is 379 // already stored as well. 380 if (assocDef.getId() == -1) { 381 // 1a) custom association type 382 String customAssocTypeUri = assocDef.getCustomAssocTypeUri(); 383 if (customAssocTypeUri != null) { 384 assocDef.getChildTopicsModel().putRef("dm4.core.assoc_type", customAssocTypeUri); 385 } 386 // 387 dms.createAssociation(assocDef); 388 } 389 // Note: the assoc def ID is definitely known only after creating the association 390 long assocDefId = assocDef.getId(); 391 // 392 // 2) cardinality 393 // Note: if the underlying association was an association definition before it has cardinality 394 // assignments already. These must be removed before assigning new cardinality. 395 removeCardinalityAssignmentIfExists(assocDefId, PARENT_CARDINALITY); 396 removeCardinalityAssignmentIfExists(assocDefId, CHILD_CARDINALITY); 397 associateCardinality(assocDefId, PARENT_CARDINALITY, assocDef.getParentCardinalityUri()); 398 associateCardinality(assocDefId, CHILD_CARDINALITY, assocDef.getChildCardinalityUri()); 399 // 400 // 3) view config 401 storeViewConfig(createConfigurableAssocDef(assocDefId), assocDef.getViewConfigModel()); 402 } catch (Exception e) { 403 throw new RuntimeException("Storing association definition \"" + assocDef.getChildTypeUri() + 404 "\" of type \"" + assocDef.getParentTypeUri() + "\" failed", e); 405 } 406 } 407 408 409 410 // === Parent Type / Child Type === 411 412 // --- Fetch --- 413 414 @Override 415 public Topic fetchParentType(Association assoc) { 416 Topic parentTypeTopic = assoc.getTopic("dm4.core.parent_type"); 417 // error check 418 if (parentTypeTopic == null) { 419 throw new RuntimeException("Invalid association definition: topic role dm4.core.parent_type " + 420 "is missing in " + assoc); 421 } 422 // 423 return parentTypeTopic; 424 } 425 426 @Override 427 public Topic fetchChildType(Association assoc) { 428 Topic childTypeTopic = assoc.getTopic("dm4.core.child_type"); 429 // error check 430 if (childTypeTopic == null) { 431 throw new RuntimeException("Invalid association definition: topic role dm4.core.child_type " + 432 "is missing in " + assoc); 433 } 434 // 435 return childTypeTopic; 436 } 437 438 439 440 // === Cardinality === 441 442 // --- Fetch --- 443 444 private RelatedTopicModel fetchCardinality(long assocDefId, String cardinalityRoleTypeUri) { 445 return dms.storageDecorator.fetchAssociationRelatedTopic(assocDefId, 446 "dm4.core.aggregation", "dm4.core.assoc_def", cardinalityRoleTypeUri, "dm4.core.cardinality"); 447 } 448 449 private RelatedTopicModel fetchCardinalityOrThrow(long assocDefId, String cardinalityRoleTypeUri) { 450 RelatedTopicModel cardinality = fetchCardinality(assocDefId, cardinalityRoleTypeUri); 451 // error check 452 if (cardinality == null) { 453 throw new RuntimeException("Invalid association definition: cardinality is missing (assocDefId=" + 454 assocDefId + ", cardinalityRoleTypeUri=\"" + cardinalityRoleTypeUri + "\")"); 455 } 456 // 457 return cardinality; 458 } 459 460 // --- Store --- 461 462 void storeParentCardinalityUri(long assocDefId, String parentCardinalityUri) { 463 storeCardinalityUri(assocDefId, PARENT_CARDINALITY, parentCardinalityUri); 464 } 465 466 void storeChildCardinalityUri(long assocDefId, String childCardinalityUri) { 467 storeCardinalityUri(assocDefId, CHILD_CARDINALITY, childCardinalityUri); 468 } 469 470 // --- 471 472 private void storeCardinalityUri(long assocDefId, String cardinalityRoleTypeUri, String cardinalityUri) { 473 // remove current assignment 474 RelatedTopicModel cardinality = fetchCardinalityOrThrow(assocDefId, cardinalityRoleTypeUri); 475 removeCardinalityAssignment(cardinality); 476 // create new assignment 477 associateCardinality(assocDefId, cardinalityRoleTypeUri, cardinalityUri); 478 } 479 480 private void removeCardinalityAssignmentIfExists(long assocDefId, String cardinalityRoleTypeUri) { 481 RelatedTopicModel cardinality = fetchCardinality(assocDefId, cardinalityRoleTypeUri); 482 if (cardinality != null) { 483 removeCardinalityAssignment(cardinality); 484 } 485 } 486 487 private void removeCardinalityAssignment(RelatedTopicModel cardinalityAssignment) { 488 long assocId = cardinalityAssignment.getRelatingAssociation().getId(); 489 dms.deleteAssociation(assocId); 490 } 491 492 private void associateCardinality(long assocDefId, String cardinalityRoleTypeUri, String cardinalityUri) { 493 dms.createAssociation("dm4.core.aggregation", 494 new TopicRoleModel(cardinalityUri, cardinalityRoleTypeUri), 495 new AssociationRoleModel(assocDefId, "dm4.core.assoc_def")); 496 } 497 498 499 500 // === Sequence === 501 502 // --- Fetch --- 503 504 // Note: the sequence is fetched in 2 situations: 505 // 1) When fetching a type's association definitions. 506 // In this situation we don't have a Type object at hand but a sole type topic. 507 // 2) When deleting a sequence in order to rebuild it. 508 private List<RelatedAssociationModel> fetchSequence(Topic typeTopic) { 509 try { 510 List<RelatedAssociationModel> sequence = new ArrayList(); 511 // 512 RelatedAssociationModel assocDef = fetchSequenceStart(typeTopic.getId()); 513 if (assocDef != null) { 514 sequence.add(assocDef); 515 while ((assocDef = fetchSuccessor(assocDef.getId())) != null) { 516 sequence.add(assocDef); 517 } 518 } 519 // 520 return sequence; 521 } catch (Exception e) { 522 throw new RuntimeException("Fetching sequence for type \"" + typeTopic.getUri() + "\" failed", e); 523 } 524 } 525 526 // --- 527 528 private RelatedAssociationModel fetchSequenceStart(long typeId) { 529 return dms.storageDecorator.fetchTopicRelatedAssociation(typeId, "dm4.core.aggregation", 530 "dm4.core.type", "dm4.core.sequence_start", null); // othersAssocTypeUri=null 531 } 532 533 private RelatedAssociationModel fetchSuccessor(long assocDefId) { 534 return dms.storageDecorator.fetchAssociationRelatedAssociation(assocDefId, "dm4.core.sequence", 535 "dm4.core.predecessor", "dm4.core.successor", null); // othersAssocTypeUri=null 536 } 537 538 private RelatedAssociationModel fetchPredecessor(long assocDefId) { 539 return dms.storageDecorator.fetchAssociationRelatedAssociation(assocDefId, "dm4.core.sequence", 540 "dm4.core.successor", "dm4.core.predecessor", null); // othersAssocTypeUri=null 541 } 542 543 // --- Store --- 544 545 private void storeSequence(long typeId, Collection<AssociationDefinitionModel> assocDefs) { 546 logger.fine("### Storing " + assocDefs.size() + " sequence segments for type " + typeId); 547 AssociationDefinitionModel lastAssocDef = null; 548 for (AssociationDefinitionModel assocDef : assocDefs) { 549 appendToSequence(typeId, assocDef.getId(), lastAssocDef); 550 lastAssocDef = assocDef; 551 } 552 } 553 554 void appendToSequence(long typeId, long assocDefId, AssociationDefinitionModel lastAssocDef) { 555 if (lastAssocDef == null) { 556 storeSequenceStart(typeId, assocDefId); 557 } else { 558 storeSequenceSegment(lastAssocDef.getId(), assocDefId); 559 } 560 } 561 562 void insertAtSequenceStart(long typeId, long assocDefId) { 563 // delete sequence start 564 RelatedAssociationModel assocDef = fetchSequenceStart(typeId); 565 dms.deleteAssociation(assocDef.getRelatingAssociation().getId()); 566 // reconnect 567 storeSequenceStart(typeId, assocDefId); 568 storeSequenceSegment(assocDefId, assocDef.getId()); 569 } 570 571 void insertIntoSequence(long assocDefId, long beforeAssocDefId) { 572 // delete sequence segment 573 RelatedAssociationModel assocDef = fetchPredecessor(beforeAssocDefId); 574 dms.deleteAssociation(assocDef.getRelatingAssociation().getId()); 575 // reconnect 576 storeSequenceSegment(assocDef.getId(), assocDefId); 577 storeSequenceSegment(assocDefId, beforeAssocDefId); 578 } 579 580 // --- 581 582 private void storeSequenceStart(long typeId, long assocDefId) { 583 dms.createAssociation("dm4.core.aggregation", 584 new TopicRoleModel(typeId, "dm4.core.type"), 585 new AssociationRoleModel(assocDefId, "dm4.core.sequence_start")); 586 } 587 588 private void storeSequenceSegment(long predAssocDefId, long succAssocDefId) { 589 dms.createAssociation("dm4.core.sequence", 590 new AssociationRoleModel(predAssocDefId, "dm4.core.predecessor"), 591 new AssociationRoleModel(succAssocDefId, "dm4.core.successor")); 592 } 593 594 // --- 595 596 void rebuildSequence(Type type) { 597 deleteSequence(type); 598 storeSequence(type.getId(), type.getModel().getAssocDefs()); 599 } 600 601 private void deleteSequence(Topic typeTopic) { 602 List<RelatedAssociationModel> sequence = fetchSequence(typeTopic); 603 logger.info("### Deleting " + sequence.size() + " sequence segments of type \"" + typeTopic.getUri() + "\""); 604 for (RelatedAssociationModel assoc : sequence) { 605 long assocId = assoc.getRelatingAssociation().getId(); 606 dms.deleteAssociation(assocId); 607 } 608 } 609 610 611 612 // === Label Configuration === 613 614 // --- Fetch --- 615 616 private List<String> fetchLabelConfig(List<AssociationDefinitionModel> assocDefs) { 617 List<String> labelConfig = new ArrayList(); 618 for (AssociationDefinitionModel assocDef : assocDefs) { 619 RelatedTopicModel includeInLabel = fetchLabelConfigTopic(assocDef.getId()); 620 if (includeInLabel != null && includeInLabel.getSimpleValue().booleanValue()) { 621 labelConfig.add(assocDef.getChildTypeUri()); 622 } 623 } 624 return labelConfig; 625 } 626 627 private RelatedTopicModel fetchLabelConfigTopic(long assocDefId) { 628 return dms.storageDecorator.fetchAssociationRelatedTopic(assocDefId, "dm4.core.composition", 629 "dm4.core.parent", "dm4.core.child", "dm4.core.include_in_label"); 630 } 631 632 // --- Store --- 633 634 /** 635 * Stores the label configuration of a <i>newly created</i> type. 636 */ 637 private void storeLabelConfig(List<String> labelConfig, Collection<AssociationDefinitionModel> assocDefs) { 638 for (AssociationDefinitionModel assocDef : assocDefs) { 639 boolean includeInLabel = labelConfig.contains(assocDef.getChildTypeUri()); 640 // Note: we don't do the storage in a type-driven fashion here (as in new AttachedAssociationDefinition( 641 // assocDef, dms).getChildTopics().set(...)). A POST_UPDATE_ASSOCIATION event would be fired for the 642 // assoc def and the Type Editor plugin would react and try to access the assoc def's parent type. 643 // This means retrieving a type that is in-mid its storage process. Strange errors would occur. 644 // As a workaround we create the child topic manually. 645 Topic topic = dms.createTopic(new TopicModel("dm4.core.include_in_label", new SimpleValue(includeInLabel))); 646 dms.createAssociation(new AssociationModel("dm4.core.composition", 647 new AssociationRoleModel(assocDef.getId(), "dm4.core.parent"), 648 new TopicRoleModel(topic.getId(), "dm4.core.child") 649 )); 650 } 651 } 652 653 /** 654 * Updates the label configuration of an <i>existing</i> type. 655 */ 656 void updateLabelConfig(List<String> labelConfig, Collection<AssociationDefinition> assocDefs) { 657 for (AssociationDefinition assocDef : assocDefs) { 658 boolean includeInLabel = labelConfig.contains(assocDef.getChildTypeUri()); 659 assocDef.getChildTopics().set("dm4.core.include_in_label", includeInLabel); 660 } 661 } 662 663 664 665 // === View Configurations === 666 667 // --- Fetch --- 668 669 // ### TODO: unify both methods 670 671 private ViewConfigurationModel fetchTypeViewConfig(Topic typeTopic) { 672 try { 673 // Note: othersTopicTypeUri=null, the view config's topic type is unknown (it is client-specific) 674 ResultList<RelatedTopic> configTopics = typeTopic.getRelatedTopics("dm4.core.aggregation", 675 "dm4.core.type", "dm4.core.view_config", null, 0).loadChildTopics(); 676 return new ViewConfigurationModel(DeepaMehtaUtils.toTopicModels(configTopics)); 677 } catch (Exception e) { 678 throw new RuntimeException("Fetching view configuration for type \"" + typeTopic.getUri() + 679 "\" failed", e); 680 } 681 } 682 683 private ViewConfigurationModel fetchAssocDefViewConfig(Association assocDef) { 684 try { 685 // Note: othersTopicTypeUri=null, the view config's topic type is unknown (it is client-specific) 686 ResultList<RelatedTopic> configTopics = assocDef.getRelatedTopics("dm4.core.aggregation", 687 "dm4.core.assoc_def", "dm4.core.view_config", null, 0).loadChildTopics(); 688 return new ViewConfigurationModel(DeepaMehtaUtils.toTopicModels(configTopics)); 689 } catch (Exception e) { 690 throw new RuntimeException("Fetching view configuration for association definition " + assocDef.getId() + 691 " failed", e); 692 } 693 } 694 695 // --- Store --- 696 697 private void storeViewConfig(RoleModel configurable, ViewConfigurationModel viewConfig) { 698 try { 699 for (TopicModel configTopic : viewConfig.getConfigTopics()) { 700 storeViewConfigTopic(configurable, configTopic); 701 } 702 } catch (Exception e) { 703 throw new RuntimeException("Storing view configuration failed (configurable=" + configurable + ")", e); 704 } 705 } 706 707 Topic storeViewConfigTopic(RoleModel configurable, TopicModel configTopic) { 708 Topic topic = dms.createTopic(configTopic); 709 dms.createAssociation("dm4.core.aggregation", configurable, new TopicRoleModel(topic.getId(), 710 "dm4.core.view_config")); 711 return topic; 712 } 713 714 // --- Helper --- 715 716 RoleModel createConfigurableType(long typeId) { 717 return new TopicRoleModel(typeId, "dm4.core.type"); 718 } 719 720 RoleModel createConfigurableAssocDef(long assocDefId) { 721 return new AssociationRoleModel(assocDefId, "dm4.core.assoc_def"); 722 } 723 }