001package systems.dmx.core.impl; 002 003import systems.dmx.core.model.AssociationModel; 004import systems.dmx.core.model.AssociationDefinitionModel; 005import systems.dmx.core.model.AssociationRoleModel; 006import systems.dmx.core.model.AssociationTypeModel; 007import systems.dmx.core.model.ChildTopicsModel; 008import systems.dmx.core.model.DMXObjectModel; 009import systems.dmx.core.model.IndexMode; 010import systems.dmx.core.model.RelatedAssociationModel; 011import systems.dmx.core.model.RelatedTopicModel; 012import systems.dmx.core.model.RoleModel; 013import systems.dmx.core.model.SimpleValue; 014import systems.dmx.core.model.TopicModel; 015import systems.dmx.core.model.TopicDeletionModel; 016import systems.dmx.core.model.TopicReferenceModel; 017import systems.dmx.core.model.TopicRoleModel; 018import systems.dmx.core.model.TopicTypeModel; 019import systems.dmx.core.model.TypeModel; 020import systems.dmx.core.model.ViewConfigurationModel; 021import systems.dmx.core.model.facets.FacetValueModel; 022import systems.dmx.core.model.topicmaps.AssociationViewModel; 023import systems.dmx.core.model.topicmaps.TopicViewModel; 024import systems.dmx.core.model.topicmaps.ViewProperties; 025import systems.dmx.core.service.ModelFactory; 026import systems.dmx.core.util.DMXUtils; 027 028import org.codehaus.jettison.json.JSONArray; 029import org.codehaus.jettison.json.JSONException; 030import org.codehaus.jettison.json.JSONObject; 031 032import java.util.ArrayList; 033import java.util.HashMap; 034import java.util.Iterator; 035import java.util.List; 036import java.util.Map; 037 038 039 040// ### TODO: should methods return model *impl* objects? -> Yes! 041public class ModelFactoryImpl implements ModelFactory { 042 043 // ------------------------------------------------------------------------------------------------------- Constants 044 045 private static final String REF_ID_PREFIX = "ref_id:"; 046 private static final String REF_URI_PREFIX = "ref_uri:"; 047 private static final String DEL_ID_PREFIX = "del_id:"; 048 private static final String DEL_URI_PREFIX = "del_uri:"; 049 050 // ---------------------------------------------------------------------------------------------- Instance Variables 051 052 PersistenceLayer pl; 053 054 // -------------------------------------------------------------------------------------------------- Public Methods 055 056 057 058 // === TopicModel === 059 060 @Override 061 public TopicModelImpl newTopicModel(long id, String uri, String typeUri, SimpleValue value, 062 ChildTopicsModel childTopics) { 063 return new TopicModelImpl(newDMXObjectModel(id, uri, typeUri, value, childTopics)); 064 } 065 066 // TODO: needed? 067 @Override 068 public TopicModelImpl newTopicModel(ChildTopicsModel childTopics) { 069 return newTopicModel(-1, null, null, null, childTopics); 070 } 071 072 @Override 073 public TopicModelImpl newTopicModel(String typeUri) { 074 return newTopicModel(-1, null, typeUri, null, null); 075 } 076 077 @Override 078 public TopicModelImpl newTopicModel(String typeUri, SimpleValue value) { 079 return newTopicModel(-1, null, typeUri, value, null); 080 } 081 082 @Override 083 public TopicModelImpl newTopicModel(String typeUri, ChildTopicsModel childTopics) { 084 return newTopicModel(-1, null, typeUri, null, childTopics); 085 } 086 087 @Override 088 public TopicModelImpl newTopicModel(String uri, String typeUri) { 089 return newTopicModel(-1, uri, typeUri, null, null); 090 } 091 092 @Override 093 public TopicModelImpl newTopicModel(String uri, String typeUri, SimpleValue value) { 094 return newTopicModel(-1, uri, typeUri, value, null); 095 } 096 097 @Override 098 public TopicModelImpl newTopicModel(String uri, String typeUri, ChildTopicsModel childTopics) { 099 return newTopicModel(-1, uri, typeUri, null, childTopics); 100 } 101 102 @Override 103 public TopicModelImpl newTopicModel(long id) { 104 return newTopicModel(id, null, null, null, null); 105 } 106 107 @Override 108 public TopicModelImpl newTopicModel(long id, ChildTopicsModel childTopics) { 109 return newTopicModel(id, null, null, null, childTopics); 110 } 111 112 @Override 113 public TopicModelImpl newTopicModel(TopicModel topic) { 114 return new TopicModelImpl((TopicModelImpl) topic); 115 } 116 117 @Override 118 public TopicModelImpl newTopicModel(JSONObject topic) { 119 try { 120 return new TopicModelImpl(newDMXObjectModel(topic)); 121 } catch (Exception e) { 122 throw parsingFailed(topic, e, "TopicModelImpl"); 123 } 124 } 125 126 127 128 // === AssociationModel === 129 130 @Override 131 public AssociationModelImpl newAssociationModel(long id, String uri, String typeUri, RoleModel roleModel1, 132 RoleModel roleModel2, SimpleValue value, ChildTopicsModel childTopics) { 133 return new AssociationModelImpl(newDMXObjectModel(id, uri, typeUri, value, childTopics), 134 (RoleModelImpl) roleModel1, (RoleModelImpl) roleModel2); 135 } 136 137 @Override 138 public AssociationModelImpl newAssociationModel(String typeUri, RoleModel roleModel1, RoleModel roleModel2) { 139 return newAssociationModel(-1, null, typeUri, roleModel1, roleModel2, null, null); 140 } 141 142 @Override 143 public AssociationModelImpl newAssociationModel(String typeUri, RoleModel roleModel1, RoleModel roleModel2, 144 ChildTopicsModel childTopics) { 145 return newAssociationModel(-1, null, typeUri, roleModel1, roleModel2, null, childTopics); 146 } 147 148 // ### TODO: don't leave the assoc uninitialized. Refactoring needed. See comment in TypeCache#put methods. 149 // ### TODO: make internal? 150 @Override 151 public AssociationModelImpl newAssociationModel() { 152 return newAssociationModel(-1, null, null, null, null, null, null); 153 } 154 155 // ### TODO: don't leave the assoc uninitialized. Refactoring needed. See comment in TypeCache#put methods. 156 // ### TODO: make internal? 157 @Override 158 public AssociationModelImpl newAssociationModel(ChildTopicsModel childTopics) { 159 return newAssociationModel(null, childTopics); 160 } 161 162 // ### TODO: don't leave the assoc uninitialized. Refactoring needed. See comment in TypeCache#put methods. 163 // ### TODO: make internal? 164 @Override 165 public AssociationModelImpl newAssociationModel(String typeUri, ChildTopicsModel childTopics) { 166 return newAssociationModel(typeUri, null, null, childTopics); 167 } 168 169 @Override 170 public AssociationModelImpl newAssociationModel(long id, String uri, String typeUri, RoleModel roleModel1, 171 RoleModel roleModel2) { 172 return newAssociationModel(id, uri, typeUri, roleModel1, roleModel2, null, null); 173 } 174 175 @Override 176 public AssociationModelImpl newAssociationModel(AssociationModel assoc) { 177 return new AssociationModelImpl((AssociationModelImpl) assoc); 178 } 179 180 @Override 181 public AssociationModelImpl newAssociationModel(JSONObject assoc) { 182 try { 183 return new AssociationModelImpl(newDMXObjectModel(assoc), 184 assoc.has("role1") ? parseRole(assoc.getJSONObject("role1")) : null, 185 assoc.has("role2") ? parseRole(assoc.getJSONObject("role2")) : null 186 ); 187 } catch (Exception e) { 188 throw parsingFailed(assoc, e, "AssociationModelImpl"); 189 } 190 } 191 192 // --- 193 194 private RoleModelImpl parseRole(JSONObject roleModel) { 195 try { 196 if (roleModel.has("topicId") || roleModel.has("topicUri")) { 197 return newTopicRoleModel(roleModel); 198 } else if (roleModel.has("assocId")) { 199 return newAssociationRoleModel(roleModel); 200 } else { 201 throw new RuntimeException("One of \"topicId\"/\"topicUri\"/\"assocId\" is expected"); 202 } 203 } catch (Exception e) { 204 throw parsingFailed(roleModel, e, "RoleModelImpl"); 205 } 206 } 207 208 209 210 // === DMXObjectModel === 211 212 /** 213 * @param id Optional (-1 is a valid value and represents "not set"). 214 * @param uri Optional (<code>null</code> is a valid value). 215 * @param typeUri Mandatory in the context of a create operation. 216 * Optional (<code>null</code> is a valid value) in the context of an update operation. 217 * @param value Optional (<code>null</code> is a valid value). 218 * @param childTopics Optional (<code>null</code> is a valid value and is transformed into an empty composite). 219 */ 220 DMXObjectModelImpl newDMXObjectModel(long id, String uri, String typeUri, SimpleValue value, 221 ChildTopicsModel childTopics) { 222 return new DMXObjectModelImpl(id, uri, typeUri, value, (ChildTopicsModelImpl) childTopics, pl()); 223 } 224 225 DMXObjectModelImpl newDMXObjectModel(JSONObject object) throws JSONException { 226 return newDMXObjectModel( 227 object.optLong("id", -1), 228 object.optString("uri", null), 229 object.optString("typeUri", null), 230 object.has("value") ? new SimpleValue(object.get("value")) : null, 231 object.has("childs") ? newChildTopicsModel(object.getJSONObject("childs")) : null 232 ); 233 } 234 235 236 237 // === ChildTopicsModel === 238 239 @Override 240 public ChildTopicsModelImpl newChildTopicsModel() { 241 return new ChildTopicsModelImpl(new HashMap(), this); 242 } 243 244 @Override 245 public ChildTopicsModelImpl newChildTopicsModel(JSONObject values) { 246 try { 247 Map<String, Object> childTopics = new HashMap(); 248 Iterator<String> i = values.keys(); 249 while (i.hasNext()) { 250 String assocDefUri = i.next(); 251 String childTypeUri = childTypeUri(assocDefUri); 252 Object value = values.get(assocDefUri); 253 if (!(value instanceof JSONArray)) { 254 childTopics.put(assocDefUri, createTopicModel(childTypeUri, value)); 255 } else { 256 JSONArray valueArray = (JSONArray) value; 257 List<RelatedTopicModel> topics = new ArrayList(); 258 childTopics.put(assocDefUri, topics); 259 for (int j = 0; j < valueArray.length(); j++) { 260 topics.add(createTopicModel(childTypeUri, valueArray.get(j))); 261 } 262 } 263 } 264 return new ChildTopicsModelImpl(childTopics, this); 265 } catch (Exception e) { 266 throw parsingFailed(values, e, "ChildTopicsModelImpl"); 267 } 268 } 269 270 @Override 271 public String childTypeUri(String assocDefUri) { 272 return assocDefUri.split("#")[0]; 273 } 274 275 // --- 276 277 /** 278 * Creates a topic model from a JSON value. 279 * 280 * Both topic serialization formats are supported: 281 * 1) canonic format -- contains entire topic models. 282 * 2) simplified format -- contains the topic value only (simple or composite). 283 */ 284 private RelatedTopicModel createTopicModel(String childTypeUri, Object value) throws JSONException { 285 if (value instanceof JSONObject) { 286 JSONObject val = (JSONObject) value; 287 // we detect the canonic format by checking for mandatory topic properties 288 if (val.has("value") || val.has("childs")) { 289 // canonic format (topic or topic reference) 290 AssociationModel relatingAssoc = null; 291 if (val.has("assoc")) { 292 relatingAssoc = newAssociationModel(val.getJSONObject("assoc")); 293 } 294 if (val.has("value")) { 295 RelatedTopicModel topicRef = createReferenceModel(val.get("value"), relatingAssoc); 296 if (topicRef != null) { 297 return topicRef; 298 } 299 } 300 // 301 initTypeUri(val, childTypeUri); 302 // 303 TopicModel topic = newTopicModel(val); 304 if (relatingAssoc != null) { 305 return newRelatedTopicModel(topic, relatingAssoc); 306 } else { 307 return newRelatedTopicModel(topic); 308 } 309 } else { 310 // simplified format (composite topic) 311 return newRelatedTopicModel(newTopicModel(childTypeUri, newChildTopicsModel(val))); 312 } 313 } else { 314 // simplified format (simple topic or topic reference) 315 RelatedTopicModel topicRef = createReferenceModel(value, null); 316 if (topicRef != null) { 317 return topicRef; 318 } 319 // simplified format (simple topic) 320 return newRelatedTopicModel(newTopicModel(childTypeUri, new SimpleValue(value))); 321 } 322 } 323 324 private RelatedTopicModel createReferenceModel(Object value, AssociationModel relatingAssoc) { 325 if (value instanceof String) { 326 String val = (String) value; 327 if (val.startsWith(REF_ID_PREFIX)) { 328 long topicId = refTopicId(val); 329 if (relatingAssoc != null) { 330 return newTopicReferenceModel(topicId, relatingAssoc); 331 } else { 332 return newTopicReferenceModel(topicId); 333 } 334 } else if (val.startsWith(REF_URI_PREFIX)) { 335 String topicUri = refTopicUri(val); 336 if (relatingAssoc != null) { 337 return newTopicReferenceModel(topicUri, relatingAssoc); 338 } else { 339 return newTopicReferenceModel(topicUri); 340 } 341 } else if (val.startsWith(DEL_ID_PREFIX)) { 342 return newTopicDeletionModel(delTopicId(val)); 343 } else if (val.startsWith(DEL_URI_PREFIX)) { 344 return newTopicDeletionModel(delTopicUri(val)); 345 } 346 } 347 return null; 348 } 349 350 private void initTypeUri(JSONObject value, String childTypeUri) throws JSONException { 351 if (!value.has("typeUri")) { 352 value.put("typeUri", childTypeUri); 353 } else { 354 // sanity check 355 String typeUri = value.getString("typeUri"); 356 if (!typeUri.equals(childTypeUri)) { 357 throw new IllegalArgumentException("A \"" + childTypeUri + "\" topic model has typeUri=\"" + typeUri + 358 "\""); 359 } 360 } 361 } 362 363 // --- 364 365 private long refTopicId(String val) { 366 return Long.parseLong(val.substring(REF_ID_PREFIX.length())); 367 } 368 369 private String refTopicUri(String val) { 370 return val.substring(REF_URI_PREFIX.length()); 371 } 372 373 private long delTopicId(String val) { 374 return Long.parseLong(val.substring(DEL_ID_PREFIX.length())); 375 } 376 377 private String delTopicUri(String val) { 378 return val.substring(DEL_URI_PREFIX.length()); 379 } 380 381 382 383 // === TopicRoleModel === 384 385 @Override 386 public TopicRoleModelImpl newTopicRoleModel(long topicId, String roleTypeUri) { 387 return new TopicRoleModelImpl(topicId, roleTypeUri, pl()); 388 } 389 390 @Override 391 public TopicRoleModelImpl newTopicRoleModel(String topicUri, String roleTypeUri) { 392 return new TopicRoleModelImpl(topicUri, roleTypeUri, pl()); 393 } 394 395 @Override 396 public TopicRoleModelImpl newTopicRoleModel(JSONObject topicRoleModel) { 397 try { 398 long topicId = topicRoleModel.optLong("topicId", -1); 399 String topicUri = topicRoleModel.optString("topicUri", null); 400 String roleTypeUri = topicRoleModel.getString("roleTypeUri"); 401 // 402 if (topicId == -1 && topicUri == null) { 403 throw new IllegalArgumentException("Neiter \"topicId\" nor \"topicUri\" is set"); 404 } 405 if (topicId != -1 && topicUri != null) { 406 throw new IllegalArgumentException("\"topicId\" and \"topicUri\" must not be set at the same time"); 407 } 408 // 409 if (topicId != -1) { 410 return newTopicRoleModel(topicId, roleTypeUri); 411 } else { 412 return newTopicRoleModel(topicUri, roleTypeUri); 413 } 414 } catch (Exception e) { 415 throw parsingFailed(topicRoleModel, e, "TopicRoleModelImpl"); 416 } 417 } 418 419 420 421 // === AssociationRoleModel === 422 423 @Override 424 public AssociationRoleModelImpl newAssociationRoleModel(long assocId, String roleTypeUri) { 425 return new AssociationRoleModelImpl(assocId, roleTypeUri, pl()); 426 } 427 428 @Override 429 public AssociationRoleModelImpl newAssociationRoleModel(JSONObject assocRoleModel) { 430 try { 431 long assocId = assocRoleModel.getLong("assocId"); 432 String roleTypeUri = assocRoleModel.getString("roleTypeUri"); 433 return newAssociationRoleModel(assocId, roleTypeUri); 434 } catch (Exception e) { 435 throw parsingFailed(assocRoleModel, e, "AssociationRoleModelImpl"); 436 } 437 } 438 439 440 441 // === RelatedTopicModel === 442 443 @Override 444 public RelatedTopicModelImpl newRelatedTopicModel(long topicId) { 445 return new RelatedTopicModelImpl(newTopicModel(topicId), newAssociationModel()); 446 } 447 448 @Override 449 public RelatedTopicModelImpl newRelatedTopicModel(long topicId, AssociationModel relatingAssoc) { 450 return new RelatedTopicModelImpl(newTopicModel(topicId), (AssociationModelImpl) relatingAssoc); 451 } 452 453 @Override 454 public RelatedTopicModelImpl newRelatedTopicModel(String topicUri) { 455 return new RelatedTopicModelImpl(newTopicModel(topicUri, (String) null), newAssociationModel()); 456 // topicTypeUri=null 457 } 458 459 @Override 460 public RelatedTopicModelImpl newRelatedTopicModel(String topicUri, AssociationModel relatingAssoc) { 461 return new RelatedTopicModelImpl(newTopicModel(topicUri, (String) null), (AssociationModelImpl) relatingAssoc); 462 // topicTypeUri=null 463 } 464 465 @Override 466 public RelatedTopicModelImpl newRelatedTopicModel(String topicTypeUri, SimpleValue value) { 467 return new RelatedTopicModelImpl(newTopicModel(topicTypeUri, value), newAssociationModel()); 468 } 469 470 @Override 471 public RelatedTopicModelImpl newRelatedTopicModel(String topicTypeUri, ChildTopicsModel childTopics) { 472 return new RelatedTopicModelImpl(newTopicModel(topicTypeUri, childTopics), newAssociationModel()); 473 } 474 475 @Override 476 public RelatedTopicModelImpl newRelatedTopicModel(TopicModel topic) { 477 return new RelatedTopicModelImpl((TopicModelImpl) topic, newAssociationModel()); 478 } 479 480 @Override 481 public RelatedTopicModelImpl newRelatedTopicModel(TopicModel topic, AssociationModel relatingAssoc) { 482 return new RelatedTopicModelImpl((TopicModelImpl) topic, (AssociationModelImpl) relatingAssoc); 483 } 484 485 486 487 // === RelatedAssociationModel === 488 489 @Override 490 public RelatedAssociationModelImpl newRelatedAssociationModel(AssociationModel assoc, 491 AssociationModel relatingAssoc) { 492 return new RelatedAssociationModelImpl((AssociationModelImpl) assoc, (AssociationModelImpl) relatingAssoc); 493 } 494 495 496 497 // === TopicReferenceModel === 498 499 @Override 500 public TopicReferenceModel newTopicReferenceModel(long topicId) { 501 return new TopicReferenceModelImpl(newRelatedTopicModel(topicId)); 502 } 503 504 @Override 505 public TopicReferenceModel newTopicReferenceModel(long topicId, AssociationModel relatingAssoc) { 506 return new TopicReferenceModelImpl(newRelatedTopicModel(topicId, relatingAssoc)); 507 } 508 509 @Override 510 public TopicReferenceModel newTopicReferenceModel(String topicUri) { 511 return new TopicReferenceModelImpl(newRelatedTopicModel(topicUri)); 512 } 513 514 @Override 515 public TopicReferenceModel newTopicReferenceModel(String topicUri, AssociationModel relatingAssoc) { 516 return new TopicReferenceModelImpl(newRelatedTopicModel(topicUri, relatingAssoc)); 517 } 518 519 @Override 520 public TopicReferenceModel newTopicReferenceModel(long topicId, ChildTopicsModel relatingAssocChildTopics) { 521 return new TopicReferenceModelImpl( 522 newRelatedTopicModel(topicId, newAssociationModel(relatingAssocChildTopics)) 523 ); 524 } 525 526 @Override 527 public TopicReferenceModel newTopicReferenceModel(String topicUri, ChildTopicsModel relatingAssocChildTopics) { 528 return new TopicReferenceModelImpl( 529 newRelatedTopicModel(topicUri, newAssociationModel(relatingAssocChildTopics)) 530 ); 531 } 532 533 @Override 534 public TopicReferenceModel newTopicReferenceModel(Object topicIdOrUri) { 535 RelatedTopicModelImpl relTopic; 536 if (topicIdOrUri instanceof Long) { 537 relTopic = newRelatedTopicModel((Long) topicIdOrUri); 538 } else if (topicIdOrUri instanceof String) { 539 relTopic = newRelatedTopicModel((String) topicIdOrUri); 540 } else { 541 throw new IllegalArgumentException("Tried to build a TopicReferenceModel from a " + 542 topicIdOrUri.getClass().getName() + " (expected are String or Long)"); 543 } 544 return new TopicReferenceModelImpl(relTopic); 545 } 546 547 548 549 // === TopicDeletionModel === 550 551 @Override 552 public TopicDeletionModel newTopicDeletionModel(long topicId) { 553 return new TopicDeletionModelImpl(newRelatedTopicModel(topicId)); 554 } 555 556 @Override 557 public TopicDeletionModel newTopicDeletionModel(String topicUri) { 558 return new TopicDeletionModelImpl(newRelatedTopicModel(topicUri)); 559 } 560 561 562 563 // === TopicTypeModel === 564 565 @Override 566 public TopicTypeModelImpl newTopicTypeModel(TopicModel typeTopic, String dataTypeUri, 567 List<IndexMode> indexModes, List<AssociationDefinitionModel> assocDefs, 568 ViewConfigurationModel viewConfig) { 569 return new TopicTypeModelImpl(newTypeModel(typeTopic, dataTypeUri, indexModes, assocDefs, 570 (ViewConfigurationModelImpl) viewConfig)); 571 } 572 573 @Override 574 public TopicTypeModelImpl newTopicTypeModel(String uri, String value, String dataTypeUri) { 575 return new TopicTypeModelImpl(newTypeModel(uri, "dmx.core.topic_type", new SimpleValue(value), dataTypeUri)); 576 } 577 578 @Override 579 public TopicTypeModelImpl newTopicTypeModel(JSONObject topicType) { 580 try { 581 return new TopicTypeModelImpl(newTypeModel(topicType.put("typeUri", "dmx.core.topic_type"))); 582 } catch (Exception e) { 583 throw parsingFailed(topicType, e, "TopicTypeModelImpl"); 584 } 585 } 586 587 588 589 // === AssociationTypeModel === 590 591 @Override 592 public AssociationTypeModelImpl newAssociationTypeModel(TopicModel typeTopic, String dataTypeUri, 593 List<IndexMode> indexModes, List<AssociationDefinitionModel> assocDefs, 594 ViewConfigurationModel viewConfig) { 595 return new AssociationTypeModelImpl(newTypeModel(typeTopic, dataTypeUri, indexModes, assocDefs, 596 (ViewConfigurationModelImpl) viewConfig)); 597 } 598 599 @Override 600 public AssociationTypeModelImpl newAssociationTypeModel(String uri, String value, String dataTypeUri) { 601 return new AssociationTypeModelImpl(newTypeModel(uri, "dmx.core.assoc_type", new SimpleValue(value), 602 dataTypeUri)); 603 } 604 605 @Override 606 public AssociationTypeModelImpl newAssociationTypeModel(JSONObject assocType) { 607 try { 608 return new AssociationTypeModelImpl(newTypeModel(assocType.put("typeUri", "dmx.core.assoc_type"))); 609 } catch (Exception e) { 610 throw parsingFailed(assocType, e, "AssociationTypeModelImpl"); 611 } 612 } 613 614 615 616 // === TypeModel === 617 618 TypeModelImpl newTypeModel(TopicModel typeTopic, String dataTypeUri, 619 List<IndexMode> indexModes, List<AssociationDefinitionModel> assocDefs, 620 ViewConfigurationModelImpl viewConfig) { 621 return new TypeModelImpl((TopicModelImpl) typeTopic, dataTypeUri, indexModes, assocDefs, viewConfig); 622 } 623 624 TypeModelImpl newTypeModel(String uri, String typeUri, SimpleValue value, String dataTypeUri) { 625 return new TypeModelImpl(newTopicModel(uri, typeUri, value), dataTypeUri, new ArrayList(), new ArrayList(), 626 newViewConfigurationModel()); 627 } 628 629 TypeModelImpl newTypeModel(JSONObject typeModel) throws JSONException { 630 TopicModelImpl typeTopic = newTopicModel(typeModel); 631 return new TypeModelImpl(typeTopic, 632 typeModel.optString("dataTypeUri", null), 633 parseIndexModes(typeModel.optJSONArray("indexModeUris")), // optJSONArray may return null 634 parseAssocDefs(typeModel.optJSONArray("assocDefs"), typeTopic.getUri()), // optJSONArray may return null 635 newViewConfigurationModel(typeModel.optJSONArray("viewConfigTopics"))); // optJSONArray may return null 636 } 637 638 // --- 639 640 private List<IndexMode> parseIndexModes(JSONArray indexModeUris) { 641 try { 642 List<IndexMode> indexModes = new ArrayList(); 643 if (indexModeUris != null) { 644 for (int i = 0; i < indexModeUris.length(); i++) { 645 indexModes.add(IndexMode.fromUri(indexModeUris.getString(i))); 646 } 647 } 648 return indexModes; 649 } catch (Exception e) { 650 throw parsingFailed(indexModeUris, e, "List<IndexMode>"); 651 } 652 } 653 654 private List<AssociationDefinitionModel> parseAssocDefs(JSONArray assocDefs, String parentTypeUri) throws 655 JSONException { 656 List<AssociationDefinitionModel> _assocDefs = new ArrayList(); 657 if (assocDefs != null) { 658 for (int i = 0; i < assocDefs.length(); i++) { 659 JSONObject assocDef = assocDefs.getJSONObject(i) 660 .put("parentTypeUri", parentTypeUri); 661 _assocDefs.add(newAssociationDefinitionModel(assocDef)); 662 } 663 } 664 return _assocDefs; 665 } 666 667 668 669 // === AssociationDefinitionModel === 670 671 @Override 672 public AssociationDefinitionModelImpl newAssociationDefinitionModel(String assocTypeUri, 673 String parentTypeUri, String childTypeUri, 674 String parentCardinalityUri, String childCardinalityUri) { 675 return newAssociationDefinitionModel(-1, null, assocTypeUri, null, false, false, parentTypeUri, childTypeUri, 676 parentCardinalityUri, childCardinalityUri, null); 677 } 678 679 @Override 680 public AssociationDefinitionModelImpl newAssociationDefinitionModel(String assocTypeUri, 681 String parentTypeUri, String childTypeUri, 682 String parentCardinalityUri, String childCardinalityUri, 683 ViewConfigurationModel viewConfig) { 684 return newAssociationDefinitionModel(-1, null, assocTypeUri, null, false, false, parentTypeUri, childTypeUri, 685 parentCardinalityUri, childCardinalityUri, viewConfig); 686 } 687 688 @Override 689 public AssociationDefinitionModelImpl newAssociationDefinitionModel(String assocTypeUri, 690 String customAssocTypeUri, 691 boolean isIdentityAttr, boolean includeInLabel, 692 String parentTypeUri, String childTypeUri, 693 String parentCardinalityUri, String childCardinalityUri) { 694 return newAssociationDefinitionModel(-1, null, assocTypeUri, customAssocTypeUri, isIdentityAttr, includeInLabel, 695 parentTypeUri, childTypeUri, parentCardinalityUri, childCardinalityUri, null); 696 } 697 698 /** 699 * @param assoc the underlying association. 700 * IMPORTANT: the association must identify its players <i>by URI</i> (not by ID). ### still true? 701 */ 702 @Override 703 public AssociationDefinitionModelImpl newAssociationDefinitionModel(AssociationModel assoc, 704 String parentCardinalityUri, String childCardinalityUri, 705 ViewConfigurationModel viewConfig) { 706 return new AssociationDefinitionModelImpl((AssociationModelImpl) assoc, parentCardinalityUri, 707 childCardinalityUri, (ViewConfigurationModelImpl) viewConfig); 708 } 709 710 @Override 711 public AssociationDefinitionModelImpl newAssociationDefinitionModel(JSONObject assocDef) { 712 try { 713 AssociationModelImpl assoc = newAssociationModel(assocDef.optLong("id", -1), null, 714 assocDef.getString("assocTypeUri"), 715 parentRole(assocDef.getString("parentTypeUri")), 716 childRole(assocDef.getString("childTypeUri")), 717 null, childTopics(assocDef) 718 ); 719 // 720 if (!assocDef.has("parentCardinalityUri") && !assoc.getTypeUri().equals("dmx.core.composition_def")) { 721 throw new RuntimeException("\"parentCardinalityUri\" is missing"); 722 } 723 // 724 return new AssociationDefinitionModelImpl(assoc, 725 assocDef.optString("parentCardinalityUri", "dmx.core.one"), 726 assocDef.getString("childCardinalityUri"), 727 newViewConfigurationModel(assocDef.optJSONArray("viewConfigTopics")) 728 ); 729 } catch (Exception e) { 730 throw parsingFailed(assocDef, e, "AssociationDefinitionModelImpl"); 731 } 732 } 733 734 /** 735 * Internal. 736 */ 737 AssociationDefinitionModelImpl newAssociationDefinitionModel(long id, String uri, String assocTypeUri, 738 String customAssocTypeUri, 739 boolean isIdentityAttr, boolean includeInLabel, 740 String parentTypeUri, String childTypeUri, 741 String parentCardinalityUri, String childCardinalityUri, 742 ViewConfigurationModel viewConfig) { 743 return new AssociationDefinitionModelImpl( 744 newAssociationModel(id, uri, assocTypeUri, parentRole(parentTypeUri), childRole(childTypeUri), 745 null, childTopics(customAssocTypeUri, isIdentityAttr, includeInLabel) // value=null 746 ), 747 parentCardinalityUri, childCardinalityUri, 748 (ViewConfigurationModelImpl) viewConfig 749 ); 750 } 751 752 /** 753 * Internal. 754 */ 755 AssociationDefinitionModelImpl newAssociationDefinitionModel(String assocTypeUri, ChildTopicsModel childTopics) { 756 return new AssociationDefinitionModelImpl(newAssociationModel(assocTypeUri, childTopics)); 757 } 758 759 // --- 760 761 private TopicRoleModel parentRole(String parentTypeUri) { 762 return newTopicRoleModel(parentTypeUri, "dmx.core.parent_type"); 763 } 764 765 private TopicRoleModel childRole(String childTypeUri) { 766 return newTopicRoleModel(childTypeUri, "dmx.core.child_type"); 767 } 768 769 // --- 770 771 private ChildTopicsModel childTopics(JSONObject assocDef) throws JSONException { 772 return childTopics( 773 // Note: getString()/optString() on a key with JSON null value would return the string "null" 774 assocDef.isNull("customAssocTypeUri") ? null : assocDef.getString("customAssocTypeUri"), 775 assocDef.optBoolean("isIdentityAttr"), 776 assocDef.optBoolean("includeInLabel") 777 ); 778 } 779 780 private ChildTopicsModel childTopics(String customAssocTypeUri, boolean isIdentityAttr, boolean includeInLabel) { 781 ChildTopicsModel childTopics = newChildTopicsModel() 782 .put("dmx.core.identity_attr", isIdentityAttr) 783 .put("dmx.core.include_in_label", includeInLabel); 784 // 785 if (customAssocTypeUri != null) { 786 if (customAssocTypeUri.startsWith(DEL_URI_PREFIX)) { 787 childTopics.putDeletionRef("dmx.core.assoc_type#dmx.core.custom_assoc_type", 788 delTopicUri(customAssocTypeUri)); 789 } else { 790 childTopics.putRef("dmx.core.assoc_type#dmx.core.custom_assoc_type", customAssocTypeUri); 791 } 792 } 793 // 794 return childTopics; 795 } 796 797 798 799 // === ViewConfigurationModel === 800 801 @Override 802 public ViewConfigurationModelImpl newViewConfigurationModel() { 803 return new ViewConfigurationModelImpl(new HashMap(), pl()); 804 } 805 806 @Override 807 public ViewConfigurationModelImpl newViewConfigurationModel(Iterable<? extends TopicModel> configTopics) { 808 Map<String, TopicModelImpl> _configTopics = new HashMap(); 809 for (TopicModel configTopic : configTopics) { 810 _configTopics.put(configTopic.getTypeUri(), (TopicModelImpl) configTopic); 811 } 812 return new ViewConfigurationModelImpl(_configTopics, pl()); 813 } 814 815 /** 816 * @param configTopics may be null 817 */ 818 @Override 819 public ViewConfigurationModelImpl newViewConfigurationModel(JSONArray configTopics) { 820 try { 821 Map<String, TopicModelImpl> _configTopics = new HashMap(); 822 if (configTopics != null) { 823 for (int i = 0; i < configTopics.length(); i++) { 824 TopicModelImpl configTopic = newTopicModel(configTopics.getJSONObject(i)); 825 _configTopics.put(configTopic.getTypeUri(), configTopic); 826 } 827 } 828 return new ViewConfigurationModelImpl(_configTopics, pl()); 829 } catch (Exception e) { 830 throw parsingFailed(configTopics, e, "ViewConfigurationModelImpl"); 831 } 832 } 833 834 835 836 // === Topicmaps === 837 838 @Override 839 public TopicViewModel newTopicViewModel(TopicModel topic, ViewProperties viewProps) { 840 return new TopicViewModelImpl((TopicModelImpl) topic, viewProps); 841 } 842 843 @Override 844 public AssociationViewModel newAssociationViewModel(AssociationModel assoc, ViewProperties viewProps) { 845 return new AssociationViewModelImpl((AssociationModelImpl) assoc, viewProps); 846 } 847 848 849 850 // === Facets === 851 852 @Override 853 public FacetValueModel newFacetValueModel(String childTypeUri) { 854 return new FacetValueModelImpl(childTypeUri, this); 855 } 856 857 @Override 858 public FacetValueModel newFacetValueModel(JSONObject facetValue) { 859 try { 860 ChildTopicsModelImpl childTopics = newChildTopicsModel(facetValue); 861 if (childTopics.size() != 1) { 862 throw new RuntimeException("There are " + childTopics.size() + " child topic entries (expected is 1)"); 863 } 864 return new FacetValueModelImpl(childTopics); 865 } catch (Exception e) { 866 throw parsingFailed(facetValue, e, "FacetValueModelImpl"); 867 } 868 } 869 870 // ------------------------------------------------------------------------------------------------- Private Methods 871 872 private RuntimeException parsingFailed(JSONObject o, Exception e, String className) { 873 try { 874 return new RuntimeException("JSON parsing failed, " + className + " " + o.toString(4), e); 875 } catch (JSONException je) { 876 // fallback: no prettyprinting 877 return new RuntimeException("JSON parsing failed, " + className + " " + o, e); 878 } 879 } 880 881 private RuntimeException parsingFailed(JSONArray a, Exception e, String className) { 882 try { 883 return new RuntimeException("JSON parsing failed, " + className + " " + a.toString(4), e); 884 } catch (JSONException je) { 885 // fallback: no prettyprinting 886 return new RuntimeException("JSON parsing failed, " + className + " " + a, e); 887 } 888 } 889 890 // --- 891 892 private PersistenceLayer pl() { 893 if (pl == null) { 894 throw new RuntimeException("Before using the ModelFactory a PersistenceLayer must be set"); 895 } 896 return pl; 897 } 898}