1 17 package org.eclipse.emf.ecore.change.util; 18 19 20 import java.util.Collection ; 21 import java.util.Collections ; 22 import java.util.Iterator ; 23 import java.util.List ; 24 import java.util.Map ; 25 26 import org.eclipse.emf.common.notify.Adapter; 27 import org.eclipse.emf.common.notify.Notification; 28 import org.eclipse.emf.common.notify.Notifier; 29 import org.eclipse.emf.common.util.BasicEList; 30 import org.eclipse.emf.common.util.ECollections; 31 import org.eclipse.emf.common.util.EList; 32 import org.eclipse.emf.ecore.EObject; 33 import org.eclipse.emf.ecore.EReference; 34 import org.eclipse.emf.ecore.EStructuralFeature; 35 import org.eclipse.emf.ecore.change.ChangeDescription; 36 import org.eclipse.emf.ecore.change.ChangeFactory; 37 import org.eclipse.emf.ecore.change.ChangeKind; 38 import org.eclipse.emf.ecore.change.FeatureChange; 39 import org.eclipse.emf.ecore.change.ListChange; 40 import org.eclipse.emf.ecore.change.ResourceChange; 41 import org.eclipse.emf.ecore.change.impl.FeatureChangeImpl; 42 import org.eclipse.emf.ecore.resource.Resource; 43 import org.eclipse.emf.ecore.resource.ResourceSet; 44 import org.eclipse.emf.ecore.util.EObjectContainmentEList; 45 import org.eclipse.emf.ecore.util.InternalEList; 46 47 48 53 public class ChangeRecorder implements Adapter.Internal 54 { 55 protected boolean recording; 56 57 protected ChangeDescription changeDescription; 58 59 protected List targetObjects = new BasicEList.FastCompare(); 60 61 protected boolean loadingTargets; 62 63 public ChangeRecorder() 64 { 65 } 66 67 public ChangeRecorder(EObject rootObject) 68 { 69 beginRecording(Collections.singleton(rootObject)); 70 } 71 72 public ChangeRecorder(Resource resource) 73 { 74 beginRecording(Collections.singleton(resource)); 75 } 76 77 public ChangeRecorder(ResourceSet resourceSet) 78 { 79 beginRecording(Collections.singleton(resourceSet)); 80 } 81 82 public ChangeRecorder(Collection rootObjects) 83 { 84 beginRecording(rootObjects); 85 } 86 87 90 public boolean isRecording() 91 { 92 return recording; 93 } 94 95 102 public void dispose() 103 { 104 recording = false; 105 106 Notifier[] notifiers = (Notifier[])targetObjects.toArray(new Notifier [targetObjects.size()]); 107 targetObjects.clear(); 108 for (int i = 0, length = notifiers.length; i < length; i++) 109 { 110 notifiers[i].eAdapters().remove(this); 111 } 112 changeDescription = null; 113 } 114 115 119 public void beginRecording(Collection rootObjects) 120 { 121 beginRecording(null, rootObjects); 122 } 123 124 137 public void beginRecording(ChangeDescription changeDescription, Collection rootObjects) 138 { 139 this.changeDescription = changeDescription == null ? 140 createChangeDescription() 141 : changeDescription; 142 143 loadingTargets = true; 144 for (Iterator i = rootObjects.iterator(); i.hasNext();) 145 { 146 Notifier notifier = (Notifier)i.next(); 147 addAdapter(notifier); 148 } 149 loadingTargets = false; 150 151 if (changeDescription != null) 152 { 153 prepareChangeDescriptionForResume(); 154 } 155 156 recording = true; 157 } 158 159 167 public ChangeDescription summarize() 168 { 169 if (isRecording()) 170 { 171 consolidateChanges(); 172 return changeDescription; 173 } 174 return null; 175 } 176 177 181 public ChangeDescription endRecording() 182 { 183 if (isRecording()) 184 { 185 recording = false; 186 consolidateChanges(); 187 return changeDescription; 188 } 189 return null; 190 } 191 192 198 protected void prepareChangeDescriptionForResume() 199 { 200 loadingTargets = true; 201 for (Iterator i = changeDescription.getObjectsToAttach().iterator(); i.hasNext();) 202 { 203 Notifier notifier = (Notifier)i.next(); 204 addAdapter(notifier); 205 } 206 loadingTargets = false; 207 208 changeDescription.getObjectsToAttach().clear(); 209 210 for (Iterator i = changeDescription.getObjectChanges().values().iterator(); i.hasNext(); ) 212 { 213 for (Iterator j = ((List )i.next()).iterator(); j.hasNext(); ) 214 { 215 FeatureChange featureChange = (FeatureChange)j.next(); 216 featureChange.getValue(); 217 } 218 } 219 220 for (Iterator i = changeDescription.getResourceChanges().iterator(); i.hasNext();) 221 { 222 ResourceChange resourceChange = (ResourceChange)i.next(); 223 resourceChange.getValue(); 224 } 225 } 226 227 230 protected void consolidateChanges() 231 { 232 List orphanedObjects = changeDescription.getObjectsToAttach(); 233 for (Iterator iter = targetObjects.iterator(); iter.hasNext();) 234 { 235 Object target = iter.next(); 236 if (target instanceof EObject) 237 { 238 EObject eObject = (EObject)target; 239 if (eObject.eContainer() == null && eObject.eResource() == null) 240 { 241 orphanedObjects.add(eObject); 242 } 243 } 244 } 245 246 for (Iterator iter = changeDescription.getObjectChanges().iterator(); iter.hasNext();) 247 { 248 Map.Entry mapEntry = (Map.Entry )iter.next(); 249 EObject eObject = (EObject)mapEntry.getKey(); 250 for (Iterator featureChangeIter = ((List )mapEntry.getValue()).iterator(); featureChangeIter.hasNext();) 251 { 252 finalizeChange((FeatureChange)featureChangeIter.next(), eObject); 253 } 254 } 255 256 for (Iterator iter = changeDescription.getResourceChanges().iterator(); iter.hasNext();) 257 { 258 ResourceChange resourceChange = (ResourceChange)iter.next(); 259 finalizeChange(resourceChange); 260 } 261 } 262 263 public void notifyChanged(Notification notification) 264 { 265 Object notifier = notification.getNotifier(); 266 if (notifier instanceof EObject) 267 { 268 Object feature = notification.getFeature(); 269 if (feature instanceof EReference) 270 { 271 EReference eReference = (EReference)feature; 272 handleFeature(eReference, eReference.isContainment() ? eReference : null, notification, (EObject)notifier); 273 } 274 else if (feature != null) 275 { 276 handleFeature((EStructuralFeature)feature, null, notification, (EObject) notifier); 277 } 278 } 279 else if (notifier instanceof Resource) 280 { 281 int featureID = notification.getFeatureID(Resource.class); 282 if (loadingTargets) 283 { 284 if (featureID == Resource.RESOURCE__IS_LOADED) 285 { 286 Resource resource = (Resource)notification.getNotifier(); 287 for (Iterator i = resource.getContents().iterator(); i.hasNext();) 288 { 289 addAdapter((Notifier)i.next()); 290 } 291 loadingTargets = false; 292 } 293 } 294 else if (featureID == Resource.RESOURCE__CONTENTS) 295 { 296 handleResource(notification); 297 } 298 } 299 else if (notifier instanceof ResourceSet) 300 { 301 if (notification.getFeatureID(ResourceSet.class) == ResourceSet.RESOURCE_SET__RESOURCES) 302 { 303 if (notification.getEventType() == Notification.ADD) 304 { 305 Notifier newValue = (Notifier)notification.getNewValue(); 306 loadingTargets = true; 307 addAdapter(newValue); 308 } 309 } 310 } 311 } 312 313 protected void handleFeature(EStructuralFeature feature, EReference containment, Notification notification, EObject eObject) 314 { 315 boolean shouldRecord = isRecording(); 316 if (feature.isDerived()) 317 { 318 shouldRecord = false; 319 } 320 321 List changes = null; 322 FeatureChange change = null; 323 if (shouldRecord) 324 { 325 changes = getFeatureChanges(eObject); 326 change = getFeatureChange(changes, feature); 327 } 328 329 int event = notification.getEventType(); 330 switch (event) 331 { 332 case Notification.SET: 333 case Notification.UNSET: 334 { 335 if (change == null && shouldRecord) 336 { 337 if (feature.isMany()) 338 { 339 List oldValue = new BasicEList((Collection )eObject.eGet(feature)); 340 int index = notification.getPosition(); 341 if (index != Notification.NO_INDEX) 342 { 343 oldValue.set(index, notification.getOldValue()); 344 } 345 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 346 } 347 else 348 { 349 Object oldValue = notification.getOldValue(); 350 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 351 } 352 ((InternalEList)changes).addUnique(change); 353 } 354 if (containment != null) 355 { 356 Notifier newValue = (Notifier)notification.getNewValue(); 357 if (newValue != null) 358 { 359 addAdapter(newValue); 360 } 361 } 362 break; 363 } 364 case Notification.ADD: 365 { 366 if (change == null && shouldRecord) 367 { 368 List oldValue = new BasicEList((Collection )eObject.eGet(feature)); 369 oldValue.remove(notification.getPosition()); 370 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 371 ((InternalEList)changes).addUnique(change); 372 } 373 if (containment != null) 374 { 375 Notifier newValue = (Notifier)notification.getNewValue(); 376 addAdapter(newValue); 377 } 378 break; 379 } 380 case Notification.ADD_MANY: 381 { 382 if (change == null && shouldRecord) 383 { 384 List oldValue = new BasicEList((Collection )eObject.eGet(feature)); 385 int position = notification.getPosition(); 386 for (int i = ((Collection )notification.getNewValue()).size(); --i >= 0;) 387 { 388 oldValue.remove(position); 389 } 390 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 391 ((InternalEList)changes).addUnique(change); 392 } 393 if (containment != null) 394 { 395 Collection newValues = (Collection )notification.getNewValue(); 396 for (Iterator i = newValues.iterator(); i.hasNext();) 397 { 398 Notifier newValue = (Notifier)i.next(); 399 addAdapter(newValue); 400 } 401 } 402 break; 403 } 404 case Notification.REMOVE: 405 { 406 if (change == null && shouldRecord) 407 { 408 List oldValue = new BasicEList((Collection )eObject.eGet(feature)); 409 410 int position = notification.getPosition(); 413 if (position == Notification.NO_INDEX) 414 { 415 position = 0; 416 } 417 oldValue.add(position, notification.getOldValue()); 418 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 419 ((InternalEList)changes).addUnique(change); 420 } 421 break; 422 } 423 case Notification.REMOVE_MANY: 424 { 425 if (change == null && shouldRecord) 426 { 427 List removedValues = (List )notification.getOldValue(); 428 List oldValue = new BasicEList((Collection )eObject.eGet(feature)); 429 int[] positions = (int[])notification.getNewValue(); 430 if (positions == null) 431 { 432 oldValue.addAll(removedValues); 433 } 434 else 435 { 436 for (int i = 0; i < positions.length; ++i) 437 { 438 oldValue.add(positions[i], removedValues.get(i)); 439 } 440 } 441 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 442 ((InternalEList)changes).addUnique(change); 443 } 444 break; 445 } 446 case Notification.MOVE: 447 { 448 if (change == null && shouldRecord) 449 { 450 EList oldValue = new BasicEList((Collection )eObject.eGet(feature)); 451 int position = notification.getPosition(); 452 int oldPosition = ((Integer )notification.getOldValue()).intValue(); 453 oldValue.move(oldPosition, position); 454 change = createFeatureChange(eObject, feature, oldValue, notification.wasSet()); 455 ((InternalEList)changes).addUnique(change); 456 } 457 break; 458 } 459 } 460 } 461 462 protected void handleResource(Notification notification) 463 { 464 Resource resource = null; 465 ResourceChange change = null; 466 if (isRecording()) 467 { 468 resource = (Resource)notification.getNotifier(); 469 change = getResourceChange(resource); 470 } 471 472 int eventType = notification.getEventType(); 473 switch (eventType) 474 { 475 case Notification.SET: 476 case Notification.UNSET: 477 { 478 if (change == null && isRecording()) 479 { 480 EList oldValue = new BasicEList(resource.getContents()); 481 int index = notification.getPosition(); 482 if (index != Notification.NO_INDEX) 483 { 484 oldValue.set(index, notification.getOldValue()); 485 } 486 change = createResourceChange(resource, oldValue); 487 getResourceChanges().add(change); 488 489 Notifier newValue = (Notifier)notification.getNewValue(); 490 if (newValue != null) 491 { 492 addAdapter(newValue); 493 } 494 } 495 break; 496 } 497 case Notification.ADD: 498 { 499 if (change == null && isRecording()) 500 { 501 EList oldValue = new BasicEList(resource.getContents()); 502 oldValue.remove(notification.getPosition()); 503 change = createResourceChange(resource, oldValue); 504 getResourceChanges().add(change); 505 } 506 Notifier newValue = (Notifier)notification.getNewValue(); 507 addAdapter(newValue); 508 break; 509 } 510 case Notification.ADD_MANY: 511 { 512 if (change == null && isRecording()) 513 { 514 EList oldValue = new BasicEList(resource.getContents()); 515 int position = notification.getPosition(); 516 for (int i = ((Collection )notification.getNewValue()).size(); --i >= 0;) 517 { 518 oldValue.remove(position); 519 } 520 change = createResourceChange(resource, oldValue); 521 getResourceChanges().add(change); 522 } 523 Collection newValues = (Collection )notification.getNewValue(); 524 for (Iterator i = newValues.iterator(); i.hasNext();) 525 { 526 Notifier newValue = (Notifier)i.next(); 527 addAdapter(newValue); 528 } 529 break; 530 } 531 case Notification.REMOVE: 532 { 533 if (change == null && isRecording()) 534 { 535 EList oldValue = new BasicEList(resource.getContents()); 536 537 int position = notification.getPosition(); 540 if (position == Notification.NO_INDEX) 541 { 542 position = 0; 543 } 544 oldValue.add(position, notification.getOldValue()); 545 change = createResourceChange(resource, oldValue); 546 getResourceChanges().add(change); 547 } 548 break; 549 } 550 case Notification.REMOVE_MANY: 551 { 552 if (change == null && isRecording()) 553 { 554 List removedValues = (List )notification.getOldValue(); 555 EList oldValue = new BasicEList(resource.getContents()); 556 int[] positions = (int[])notification.getNewValue(); 557 if (positions == null) 558 { 559 oldValue.addAll(removedValues); 560 } 561 else 562 { 563 for (int i = 0; i < positions.length; ++i) 564 { 565 oldValue.add(positions[i], removedValues.get(i)); 566 } 567 } 568 change = createResourceChange(resource, oldValue); 569 getResourceChanges().add(change); 570 } 571 break; 572 } 573 case Notification.MOVE: 574 { 575 if (change == null && isRecording()) 576 { 577 EList oldValue = new BasicEList(resource.getContents()); 578 int position = notification.getPosition(); 579 int oldPosition = ((Integer )notification.getOldValue()).intValue(); 580 oldValue.move(oldPosition, position); 581 change = createResourceChange(resource, oldValue); 582 getResourceChanges().add(change); 583 } 584 break; 585 } 586 } 587 } 588 589 593 public void setTarget(Notifier target) 594 { 595 if (!targetObjects.add(target)) 596 { 597 throw new IllegalStateException ("The target should not be set more than once"); 598 } 599 600 Collection contents = target instanceof EObject ? ((EObject)target).eContents() : target instanceof ResourceSet 601 ? ((ResourceSet)target).getResources() : target instanceof Resource ? ((Resource)target).getContents() : null; 602 603 if (contents != null) 604 { 605 for (Iterator i = contents.iterator(); i.hasNext();) 606 { 607 Notifier notifier = (Notifier)i.next(); 608 addAdapter(notifier); 609 } 610 } 611 } 612 613 public void unsetTarget(Notifier oldTarget) 614 { 615 targetObjects.remove(oldTarget); 616 } 617 618 protected void addAdapter(Notifier notifier) 619 { 620 if (notifier != changeDescription) 621 { 622 EList eAdapters = notifier.eAdapters(); 623 if (!eAdapters.contains(this)) 624 eAdapters.add(this); 625 } 626 } 627 628 public Notifier getTarget() 629 { 630 return null; 631 } 632 633 public boolean isAdapterForType(Object type) 634 { 635 return false; 636 } 637 638 protected EList getResourceChanges() 639 { 640 return changeDescription.getResourceChanges(); 641 } 642 643 protected ResourceChange getResourceChange(Resource resource) 644 { 645 List resourceChanges = getResourceChanges(); 646 for (int i = 0, size = resourceChanges.size(); i < size;) 647 { 648 ResourceChange resourceChange = (ResourceChange)resourceChanges.get(i++); 649 if (resourceChange.getResource() == resource) 650 { 651 return resourceChange; 652 } 653 } 654 return null; 655 } 656 657 protected List getFeatureChanges(EObject eObject) 658 { 659 List featureChanges = (List )changeDescription.getObjectChanges().get(eObject); 660 if (featureChanges == null) 661 { 662 Map.Entry entry = ChangeFactory.eINSTANCE.createEObjectToChangesMapEntry(eObject); 663 changeDescription.getObjectChanges().add(entry); 664 featureChanges = (EList)entry.getValue(); 665 } 666 return featureChanges; 667 } 668 669 protected FeatureChange getFeatureChange(List featureChanges, EStructuralFeature eStructuralFeature) 670 { 671 EObjectContainmentEList changes = (EObjectContainmentEList)featureChanges; 672 for (int i = 0, size = changes.size(); i < size;) 673 { 674 FeatureChangeImpl featureChange = (FeatureChangeImpl)changes.get(i++); 675 if (featureChange.getFeature() == eStructuralFeature) 676 { 677 return featureChange; 678 } 679 } 680 return null; 681 } 682 683 protected void finalizeChange(ResourceChange change) 684 { 685 EList oldList = new BasicEList.FastCompare(change.getResource().getContents()); 686 EList newList = change.getValue(); 687 change.getListChanges().clear(); 688 createListChanges(oldList, newList, change.getListChanges()); 689 } 690 691 protected void finalizeChange(FeatureChange change, EObject eObject) 692 { 693 if (change.isSet()) 694 { 695 EStructuralFeature feature = change.getFeature(); 696 if (feature.isMany()) 697 { 698 EList oldList = new BasicEList((EList)eObject.eGet(feature)); 699 EList newList = (EList)change.getValue(); 700 EList listChanges = change.getListChanges(); 701 listChanges.clear(); 702 createListChanges(oldList, newList, listChanges); 703 } 704 } 705 } 706 707 protected void createListChanges(EList oldList, EList newList, EList changesList) 708 { 709 int index = 0; 710 for (Iterator objects = newList.iterator(); objects.hasNext(); ++index) 711 { 712 Object newObject = objects.next(); 713 if (oldList.size() <= index) 714 { 715 createAddListChange(oldList, changesList, newObject, index); 716 } 717 else 718 { 719 boolean done; 720 do 721 { 722 done = true; 723 Object targetObject = oldList.get(index); 724 if (targetObject == null ? newObject != null : !targetObject.equals(newObject)) 725 { 726 int position = ECollections.indexOf(oldList, newObject, index); 727 if (position != -1) 728 { 729 int targetIndex = ECollections.indexOf(newList, targetObject, index); 730 if (targetIndex == -1) 731 { 732 createRemoveListChange(oldList, changesList, newObject, index); 733 done = false; 734 } 735 else if (targetIndex > position) 736 { 737 if (oldList.size() <= targetIndex) 738 { 739 targetIndex = oldList.size() - 1; 740 } 741 createMoveListChange(oldList, changesList, newObject, index, targetIndex); 742 done = false; 743 } 744 else 745 { 746 createMoveListChange(oldList, changesList, newObject, position, index); 747 } 748 } 749 else 750 { 751 createAddListChange(oldList, changesList, newObject, index); 752 } 753 } 754 } 755 while (!done); 756 } 757 } 758 for (int i = oldList.size(); i > index;) 759 { 760 createRemoveListChange(oldList, changesList, null, --i); 761 } 762 } 763 764 769 protected void createAddListChange(EList oldList, EList changesList, Object newObject, int index) 770 { 771 ListChange listChange = createListChange(changesList, ChangeKind.ADD_LITERAL, index); 772 listChange.getValues().add(newObject); 773 oldList.add(index, newObject); 774 } 775 776 781 protected void createRemoveListChange(EList oldList, EList changesList, Object newObject, int index) 782 { 783 createListChange(changesList, ChangeKind.REMOVE_LITERAL, index); 784 oldList.remove(index); 785 } 786 787 792 protected void createMoveListChange(EList oldList, EList changesList, Object newObject, int index, int toIndex) 793 { 794 ListChange listChange = createListChange(changesList, ChangeKind.MOVE_LITERAL, index); 795 listChange.setMoveToIndex(toIndex); 796 oldList.move(toIndex, index); 797 } 798 799 protected ListChange createListChange(EList changesList, ChangeKind kind, int index) 800 { 801 ListChange listChange = ChangeFactory.eINSTANCE.createListChange(); 802 listChange.setKind(kind); 803 listChange.setIndex(index); 804 changesList.add(listChange); 805 return listChange; 806 } 807 808 protected FeatureChange createFeatureChange(EObject eObject, EStructuralFeature eStructuralFeature, Object value, boolean isSet) 809 { 810 return ChangeFactory.eINSTANCE.createFeatureChange(eStructuralFeature, value, isSet); 811 } 812 813 protected ResourceChange createResourceChange(Resource resource, EList value) 814 { 815 return ChangeFactory.eINSTANCE.createResourceChange(resource, value); 816 } 817 818 protected ChangeDescription createChangeDescription() 819 { 820 return ChangeFactory.eINSTANCE.createChangeDescription(); 821 } 822 } | Popular Tags |