1 56 package org.opencrx.kernel.layer.application; 57 58 import java.text.ParseException ; 59 import java.util.ArrayList ; 60 import java.util.Arrays ; 61 import java.util.Date ; 62 import java.util.HashSet ; 63 import java.util.Iterator ; 64 import java.util.List ; 65 import java.util.Map ; 66 import java.util.Set ; 67 import java.util.StringTokenizer ; 68 69 import org.openmdx.base.accessor.jmi.cci.RefPackage_1_0; 70 import org.openmdx.base.exception.ServiceException; 71 import org.openmdx.base.text.format.DateFormat; 72 import org.openmdx.compatibility.base.dataprovider.cci.AttributeSelectors; 73 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject; 74 import org.openmdx.compatibility.base.dataprovider.cci.DataproviderObject_1_0; 75 import org.openmdx.compatibility.base.dataprovider.cci.Directions; 76 import org.openmdx.compatibility.base.dataprovider.cci.RequestCollection; 77 import org.openmdx.compatibility.base.dataprovider.cci.ServiceHeader; 78 import org.openmdx.compatibility.base.dataprovider.cci.SystemAttributes; 79 import org.openmdx.compatibility.base.marshalling.Marshaller; 80 import org.openmdx.compatibility.base.naming.Path; 81 import org.openmdx.kernel.exception.BasicException; 82 import org.openmdx.model1.accessor.basic.cci.ModelElement_1_0; 83 import org.openmdx.model1.accessor.basic.cci.Model_1_0; 84 import org.openmdx.model1.code.AggregationKind; 85 86 public class Cloneable { 87 88 public Cloneable( 90 Model_1_0 model, 91 OpenCrxKernel_1 plugin, 92 RequestCollection delegation, 93 RefPackage_1_0 rootPkg 94 ) { 95 this.model = model; 96 this.delegation = delegation; 97 this.plugin = plugin; 98 this.rootPkg = rootPkg; 99 } 100 101 109 public DataproviderObject cloneAndUpdateReferences( 110 ServiceHeader header, 111 DataproviderObject_1_0 original, 112 Path toContainer, 113 Map objectMarshallers, 114 String referenceFilterAsString, 115 boolean replaceExisting 116 ) throws ServiceException { 117 List referenceFilter = referenceFilterAsString == null ? null : new ArrayList (); 119 if(referenceFilterAsString != null) { 120 StringTokenizer tokenizer = new StringTokenizer (referenceFilterAsString, " ;,", false); 121 while(tokenizer.hasMoreTokens()) { 122 referenceFilter.add( 123 new Path(original.path().toXri() + "/" + tokenizer.nextToken()) 124 ); 125 } 126 } 127 List replacements = new ArrayList (); 128 DataproviderObject cloned = this.cloneObject( 129 header, 130 original, 131 toContainer, 132 CLONE_EXCLUDE_ATTRIBUTES, 133 objectMarshallers, 134 referenceFilter, 135 replacements, 136 replaceExisting 137 ); 138 referenceFilter = referenceFilterAsString == null ? null : new ArrayList (); 140 if(referenceFilterAsString != null) { 141 StringTokenizer tokenizer = new StringTokenizer (referenceFilterAsString, " ;,", false); 142 while(tokenizer.hasMoreTokens()) { 143 referenceFilter.add( 144 new Path(cloned.path().toXri() + "/" + tokenizer.nextToken()) 145 ); 146 } 147 } 148 this.applyReplacements( 149 header, 150 cloned.path(), 151 true, 152 replacements, 153 referenceFilter 154 ); 155 return cloned; 156 } 157 158 166 private DataproviderObject cloneObject( 167 ServiceHeader header, 168 DataproviderObject_1_0 original, 169 Path toContainer, 170 Set excludeAttributes, 171 Map objectMarshallers, 172 List referenceFilter, 173 List replacements, 174 boolean replaceExisting 175 ) throws ServiceException { 176 177 String originalType = (String )original.values(SystemAttributes.OBJECT_CLASS).get(0); 179 DataproviderObject clone = null; 180 if((objectMarshallers != null) && (objectMarshallers.get(originalType) != null)) { 181 clone = (DataproviderObject)((Marshaller)objectMarshallers.get(originalType)).marshal( 182 original 183 ); 184 } 185 else { 186 clone = new DataproviderObject(new Path("")); 187 clone.addClones( 188 original, 189 true 190 ); 191 } 192 clone.attributeNames().remove("owningUser"); 195 clone.attributeNames().remove("owningGroup"); 196 197 clone.path().setTo( 199 toContainer.getChild( 200 replaceExisting 201 ? original.path().getBase() 202 : original.path().getBase().length() < MANUAL_QUALIFIER_THRESHOLD 203 ? original.path().getBase() 204 : this.plugin.getUidAsString() 205 ) 206 ); 207 DataproviderObject replacement = new DataproviderObject(new Path("xri:@openmdx:*")); 209 replacement.values(SystemAttributes.OBJECT_CLASS).add("org:opencrx:kernel:base:ReferenceReplacement"); 210 replacement.values("oldReference").add( 211 new Path((String )original.values(SystemAttributes.OBJECT_IDENTITY).get(0)) 212 ); 213 replacement.values("newReference").add(clone.path()); 214 replacements.add(replacement); 215 if(excludeAttributes != null) { 217 clone.attributeNames().removeAll(excludeAttributes); 218 } 219 try { 221 ModelElement_1_0 classDef = this.model.getElement(originalType); 222 for( 223 Iterator i = clone.attributeNames().iterator(); 224 i.hasNext(); 225 ) { 226 String featureName = (String )i.next(); 227 ModelElement_1_0 featureDef = this.model.getFeatureDef(classDef, featureName, false); 228 String qualifiedFeatureName = featureDef == null 229 ? null 230 : (String )featureDef.values("qualifiedName").get(0); 231 if( 232 !(SystemAttributes.OBJECT_CLASS.equals(featureName) || CLONEABLE_READONLY_FEATURES.contains(qualifiedFeatureName)) && 233 ((featureDef == null) || 234 !((Boolean )featureDef.values("isChangeable").get(0)).booleanValue()) 235 ) { 236 i.remove(); 237 } 238 } 239 } catch(Exception e) {} 240 241 if(replaceExisting) { 243 try { 244 DataproviderObject existing = this.plugin.retrieveObjectForModification(clone.path()); 245 existing.attributeNames().clear(); 246 existing.addClones(clone, true); 247 } 248 catch(ServiceException e) { 249 if(e.getExceptionCode() == BasicException.Code.NOT_FOUND) { 251 this.delegation.addCreateRequest( 252 clone 253 ); 254 } 255 } 256 } 257 else { 258 this.delegation.addCreateRequest( 259 clone 260 ); 261 } 262 Map references = (Map )this.model.getElement( 264 original.values(SystemAttributes.OBJECT_CLASS).get(0) 265 ).values("reference").get(0); 266 for( 267 Iterator i = references.values().iterator(); 268 i.hasNext(); 269 ) { 270 ModelElement_1_0 featureDef = (ModelElement_1_0)i.next(); 271 ModelElement_1_0 referencedEnd = this.model.getElement( 272 featureDef.values("referencedEnd").get(0) 273 ); 274 boolean referenceIsCompositeAndChangeable = 275 this.model.isReferenceType(featureDef) && 276 AggregationKind.COMPOSITE.equals(referencedEnd.values("aggregation").get(0)) && 277 ((Boolean )referencedEnd.values("isChangeable").get(0)).booleanValue(); 278 boolean referenceIsShared = 279 this.model.isReferenceType(featureDef) && 280 AggregationKind.SHARED.equals(referencedEnd.values("aggregation").get(0)); 281 282 if(referenceIsCompositeAndChangeable || referenceIsShared) { 285 String reference = (String )featureDef.values("name").get(0); 286 Path referencePath = original.path().getChild(reference); 287 boolean matches = referenceFilter == null; 288 if(!matches) { 289 for( 290 Iterator k = referenceFilter.iterator(); 291 k.hasNext(); 292 ) { 293 Path f = (Path)k.next(); 294 if(referencePath.isLike(f) && (!f.endsWith(new String []{":*"}) || referenceIsCompositeAndChangeable)) { 296 matches = true; 297 break; 298 } 299 } 300 } 301 if(matches) { 302 List content = this.delegation.addFindRequest( 303 original.path().getChild(reference), 304 null, 305 AttributeSelectors.ALL_ATTRIBUTES, 306 0, 307 Integer.MAX_VALUE, 308 Directions.ASCENDING 309 ); 310 for( 311 Iterator j = content.iterator(); 312 j.hasNext(); 313 ) { 314 DataproviderObject contained = (DataproviderObject)j.next(); 315 Path containedIdentity = new Path((String )contained.values(SystemAttributes.OBJECT_IDENTITY).get(0)); 316 this.cloneObject( 317 header, 318 contained, 319 contained.path().equals(containedIdentity) 323 ? clone.path().getChild(reference) 324 : containedIdentity.getParent(), 325 excludeAttributes, 326 objectMarshallers, 327 referenceFilter, 328 replacements, 329 replaceExisting 330 ); 331 } 332 } 333 } 334 } 335 Set referencedObjectIdentities = new HashSet (); 337 this.plugin.collectReferencedObjects( 338 original, 339 referenceFilter, 340 referencedObjectIdentities 341 ); 342 for( 343 Iterator i = referencedObjectIdentities.iterator(); 344 i.hasNext(); 345 ) { 346 Path referencedObjectPath = (Path)i.next(); 347 DataproviderObject_1_0 referencedObject = null; 348 try { 349 referencedObject = this.plugin.retrieveObjectFromLocal(header, referencedObjectPath); 350 } catch(Exception e) {} 351 if(referencedObject != null) { 352 Path newReference = 353 this.cloneObject( 354 header, 355 this.plugin.retrieveObjectFromLocal(header, referencedObjectPath), 356 referencedObjectPath.getParent(), 357 excludeAttributes, 358 objectMarshallers, 359 referenceFilter, 360 replacements, 361 replaceExisting 362 ).path(); 363 replacement = new DataproviderObject(new Path("xri:@openmdx:*")); 365 replacement.values(SystemAttributes.OBJECT_CLASS).add("org:opencrx:kernel:base:ReferenceReplacement"); 366 replacement.values("oldReference").add(referencedObjectPath); 367 replacement.values("newReference").add(newReference); 368 replacements.add(replacement); 369 } 370 } 371 return clone; 372 } 373 374 public void deleteCompositeAndReferencedObjects( 376 DataproviderObject_1_0 object, 377 String referenceFilterAsString 378 ) throws ServiceException { 379 List referenceFilter = new ArrayList (); 380 if(referenceFilterAsString != null) { 381 StringTokenizer tokenizer = new StringTokenizer (referenceFilterAsString, " ;,", false); 382 while(tokenizer.hasMoreTokens()) { 383 referenceFilter.add( 384 new Path(object.path().toXri() + "/" + tokenizer.nextToken()) 385 ); 386 } 387 } 388 this.deleteReferencedObjects( 389 object, 390 referenceFilter 391 ); 392 this.plugin.removeObject( 395 object.path() 396 ); 397 } 398 399 private void deleteReferencedObjects( 401 DataproviderObject_1_0 object, 402 List referenceFilter 403 ) throws ServiceException { 404 405 Path objectIdentity = object.path(); 406 407 Map references = (Map )this.model.getElement( 409 object.values(SystemAttributes.OBJECT_CLASS).get(0) 410 ).values("reference").get(0); 411 for( 412 Iterator i = references.values().iterator(); 413 i.hasNext(); 414 ) { 415 ModelElement_1_0 featureDef = (ModelElement_1_0)i.next(); 416 ModelElement_1_0 referencedEnd = this.model.getElement( 417 featureDef.values("referencedEnd").get(0) 418 ); 419 if( 420 this.model.isReferenceType(featureDef) && 421 AggregationKind.COMPOSITE.equals(referencedEnd.values("aggregation").get(0)) 422 ) { 423 String reference = (String )featureDef.values("name").get(0); 424 Path referencePath = objectIdentity.getChild(reference); 425 boolean matches = referenceFilter == null; 426 if(!matches) { 427 for( 428 Iterator k = referenceFilter.iterator(); 429 k.hasNext(); 430 ) { 431 if(referencePath.isLike((Path)k.next())) { 432 matches = true; 433 break; 434 } 435 } 436 } 437 if(matches) { 438 List content = this.delegation.addFindRequest( 439 objectIdentity.getChild(reference), 440 null, 441 AttributeSelectors.ALL_ATTRIBUTES, 442 0, 443 Integer.MAX_VALUE, 444 Directions.ASCENDING 445 ); 446 for( 447 Iterator j = content.iterator(); 448 j.hasNext(); 449 ) { 450 DataproviderObject_1_0 composite = (DataproviderObject_1_0)j.next(); 451 this.deleteReferencedObjects( 452 composite, 453 referenceFilter 454 ); 455 } 456 } 457 } 458 } 459 460 Set referencedObjectPaths = new HashSet (); 462 this.plugin.collectReferencedObjects( 463 object, 464 referenceFilter, 465 referencedObjectPaths 466 ); 467 for( 468 Iterator i = referencedObjectPaths.iterator(); 469 i.hasNext(); 470 ) { 471 Path referencedObject = (Path)i.next(); 472 try { 473 this.plugin.removeObject( 474 referencedObject 475 ); 476 } 477 catch(ServiceException e) {} 479 } 480 } 481 482 public int applyReplacements( 484 ServiceHeader header, 485 Path objectIdentity, 486 boolean isChangeable, 487 List replacements, 488 String referenceFilterAsString 489 ) throws ServiceException { 490 List referenceFilter = new ArrayList (); 491 if(referenceFilterAsString != null) { 492 StringTokenizer tokenizer = new StringTokenizer (referenceFilterAsString, " ;,", false); 493 while(tokenizer.hasMoreTokens()) { 494 referenceFilter.add( 495 new Path(objectIdentity.toXri() + "/" + tokenizer.nextToken()) 496 ); 497 } 498 } 499 return this.applyReplacements( 500 header, 501 objectIdentity, 502 isChangeable, 503 replacements, 504 referenceFilter 505 ); 506 } 507 508 519 private int applyReplacements( 520 ServiceHeader header, 521 Path objectIdentity, 522 boolean isChangeable, 523 List replacements, 524 List referenceFilter 525 ) throws ServiceException { 526 527 int numberOfReplacements = 0; 528 DataproviderObject_1_0 object = null; 529 try { 530 object = this.plugin.retrieveObjectFromLocal(header, objectIdentity); 531 } catch(Exception e) {} 532 if(object == null) return 0; 533 534 if(isChangeable) { 535 DataproviderObject replacedObject = null; 536 for( 537 Iterator i = object.attributeNames().iterator(); 538 i.hasNext(); 539 ) { 540 String name = (String )i.next(); 541 Object oldValue = object.values(name).get(0); 542 Object newValue = null; 543 for( 544 Iterator j = replacements.iterator(); 545 j.hasNext(); 546 ) { 547 DataproviderObject_1_0 replacement = (DataproviderObject_1_0)j.next(); 548 String replacementType = (String )replacement.values(SystemAttributes.OBJECT_CLASS).get(0); 549 boolean matches = replacement.getValues("name") != null 551 ? replacement.values("name").contains(name) 552 : true; 553 if( 555 matches && 556 (oldValue instanceof String ) && 557 "org:opencrx:kernel:base:StringReplacement".equals(replacementType) 558 ) { 559 matches &= (replacement.getValues("oldString") != null) && (replacement.getValues("oldString").size() > 0) 560 ? oldValue.equals(replacement.values("oldString").get(0)) 561 : true; 562 newValue = replacement.values("newString").get(0); 563 } 564 else if( 566 matches && 567 (oldValue instanceof Comparable ) && 568 "org:opencrx:kernel:base:NumberReplacement".equals(replacementType) 569 ) { 570 matches &= (replacement.getValues("oldNumber") != null) && (replacement.getValues("oldNumber").size() > 0) 571 ? ((Comparable )oldValue).compareTo(replacement.values("oldNumber").get(0)) == 0 572 : true; 573 newValue = replacement.values("newNumber").get(0); 574 } 575 else if( 577 matches && 578 "org:opencrx:kernel:base:DateTimeReplacement".equals(replacementType) 579 ) { 580 matches &= (replacement.getValues("oldDateTime") != null) && (replacement.getValues("oldDateTime").size() > 0) 581 ? oldValue.equals(replacement.values("oldDateTime").get(0)) 582 : true; 583 if( 584 (replacement.getValues("baseDateTime") != null) && 585 (replacement.values("baseDateTime").size() > 0) 586 ) { 587 try { 588 DateFormat dateFormat = DateFormat.getInstance(); 589 Date baseDate = dateFormat.parse((String )replacement.values("baseDateTime").get(0)); 590 Date oldDate = dateFormat.parse((String )oldValue); 591 Date newDate = dateFormat.parse((String )replacement.values("newDateTime").get(0)); 592 newValue = dateFormat.format(new Date (newDate.getTime() + (oldDate.getTime() - baseDate.getTime()))); 593 } 594 catch(ParseException e) { 595 newValue = replacement.values("newDateTime").get(0); 596 } 597 } 598 else { 599 newValue = replacement.values("newDateTime").get(0); 600 } 601 } 602 else if( 604 matches && 605 (oldValue instanceof Boolean ) && 606 "org:opencrx:kernel:base:BooleanReplacement".equals(replacementType) 607 ) { 608 matches &= (replacement.getValues("oldBoolean") != null) && (replacement.getValues("oldBoolean").size() > 0) 609 ? oldValue.equals(replacement.values("oldBoolean").get(0)) 610 : true; 611 newValue = replacement.values("newBoolean").get(0); 612 } 613 else if( 615 matches && 616 (oldValue instanceof Path) && 617 "org:opencrx:kernel:base:ReferenceReplacement".equals(replacementType) 618 ) { 619 matches &= (replacement.getValues("oldReference") != null) && (replacement.getValues("oldReference").size() > 0) 620 ? oldValue.equals(replacement.values("oldReference").get(0)) 621 : true; 622 newValue = replacement.values("newReference").get(0); 623 } 624 else { 625 matches = false; 626 } 627 if(matches) { 629 if(replacedObject == null) { 630 replacedObject = this.plugin.retrieveObjectForModification(object.path()); 631 } 632 replacedObject.clearValues(name); 633 if(newValue != null) { 634 replacedObject.values(name).add(newValue); 635 numberOfReplacements++; 636 } 637 } 638 } 639 } 640 } 641 642 Map references = (Map )this.model.getElement( 644 object.values(SystemAttributes.OBJECT_CLASS).get(0) 645 ).values("reference").get(0); 646 for( 647 Iterator i = references.values().iterator(); 648 i.hasNext(); 649 ) { 650 ModelElement_1_0 featureDef = (ModelElement_1_0)i.next(); 651 ModelElement_1_0 referencedEnd = this.model.getElement( 652 featureDef.values("referencedEnd").get(0) 653 ); 654 boolean referenceIsCompositeAndChangeable = 655 this.model.isReferenceType(featureDef) && 656 AggregationKind.COMPOSITE.equals(referencedEnd.values("aggregation").get(0)) && 657 ((Boolean )referencedEnd.values("isChangeable").get(0)).booleanValue(); 658 boolean referenceIsSharedAndChangeable = 659 this.model.isReferenceType(featureDef) && 660 AggregationKind.SHARED.equals(referencedEnd.values("aggregation").get(0)) && 661 ((Boolean )referencedEnd.values("isChangeable").get(0)).booleanValue(); 662 if(referenceIsCompositeAndChangeable || referenceIsSharedAndChangeable) { 665 String reference = (String )featureDef.values("name").get(0); 666 Path referencePath = objectIdentity.getChild(reference); 667 boolean matches = referenceFilter == null; 668 if(!matches) { 669 for( 670 Iterator k = referenceFilter.iterator(); 671 k.hasNext(); 672 ) { 673 if(referencePath.isLike((Path)k.next())) { 674 matches = true; 675 break; 676 } 677 } 678 } 679 if(matches) { 680 List content = this.delegation.addFindRequest( 681 objectIdentity.getChild(reference), 682 null, 683 AttributeSelectors.ALL_ATTRIBUTES, 684 0, 685 Integer.MAX_VALUE, 686 Directions.ASCENDING 687 ); 688 for( 689 Iterator j = content.iterator(); 690 j.hasNext(); 691 ) { 692 DataproviderObject_1_0 composite = (DataproviderObject_1_0)j.next(); 693 numberOfReplacements += this.applyReplacements( 694 header, 695 composite.path(), 696 ((Boolean )referencedEnd.values("isChangeable").get(0)).booleanValue(), 697 replacements, 698 referenceFilter 699 ); 700 } 701 } 702 } 703 } 704 705 Set referencedObjectPaths = new HashSet (); 707 this.plugin.collectReferencedObjects( 708 object, 709 referenceFilter, 710 referencedObjectPaths 711 ); 712 for( 713 Iterator i = referencedObjectPaths.iterator(); 714 i.hasNext(); 715 ) { 716 numberOfReplacements += this.applyReplacements( 717 header, 718 (Path)i.next(), 719 true, 720 replacements, 721 referenceFilter 722 ); 723 } 724 return numberOfReplacements; 725 } 726 727 public static final Set CLONE_EXCLUDE_ATTRIBUTES = 731 new HashSet (Arrays.asList(new String []{"activityNumber"})); 732 733 public static final Set CLONEABLE_READONLY_FEATURES = 734 new HashSet (Arrays.asList(new String []{ 735 "org:opencrx:kernel:product1:ProductOfferingPosition:basedOn", 736 "org:opencrx:kernel:product1:ProductBundlePosition:basedOn", 737 "org:opencrx:kernel:product1:BundledProductPosition:basedOn", 738 "org:opencrx:kernel:contract1:ContractPosition:lineItemNumber", 739 "org:opencrx:kernel:account1:AbstractAccount:fullName", 740 "org:opencrx:kernel:product1:ProductConfigurationSet:configType", 741 "org:opencrx:kernel:product1:ProductConfiguration:configType" 742 })); 743 744 public static final int MANUAL_QUALIFIER_THRESHOLD = 10; 745 746 private final Model_1_0 model; 747 private final RequestCollection delegation; 748 private final OpenCrxKernel_1 plugin; 749 private final RefPackage_1_0 rootPkg; 750 } 751 752 | Popular Tags |