1 11 package org.eclipse.core.commands.operations; 12 13 import java.util.ArrayList ; 14 import java.util.Collections ; 15 import java.util.HashMap ; 16 import java.util.Iterator ; 17 import java.util.List ; 18 import java.util.Map ; 19 20 import org.eclipse.core.commands.ExecutionException; 21 import org.eclipse.core.commands.util.Tracing; 22 import org.eclipse.core.runtime.Assert; 23 import org.eclipse.core.runtime.IAdaptable; 24 import org.eclipse.core.runtime.IProgressMonitor; 25 import org.eclipse.core.runtime.ISafeRunnable; 26 import org.eclipse.core.runtime.IStatus; 27 import org.eclipse.core.runtime.ListenerList; 28 import org.eclipse.core.runtime.OperationCanceledException; 29 import org.eclipse.core.runtime.SafeRunner; 30 import org.eclipse.core.runtime.Status; 31 32 73 public final class DefaultOperationHistory implements IOperationHistory { 74 80 public static boolean DEBUG_OPERATION_HISTORY_NOTIFICATION = false; 81 82 87 public static boolean DEBUG_OPERATION_HISTORY_UNEXPECTED = false; 88 89 94 public static boolean DEBUG_OPERATION_HISTORY_DISPOSE = false; 95 96 101 public static boolean DEBUG_OPERATION_HISTORY_OPENOPERATION = false; 102 103 108 public static boolean DEBUG_OPERATION_HISTORY_APPROVAL = false; 109 110 static final int DEFAULT_LIMIT = 20; 111 112 115 ListenerList approvers = new ListenerList(ListenerList.IDENTITY); 116 117 120 private Map limits = Collections.synchronizedMap(new HashMap ()); 121 122 125 ListenerList listeners = new ListenerList(ListenerList.IDENTITY); 126 127 130 private List redoList = Collections.synchronizedList(new ArrayList ()); 131 132 135 private List undoList = Collections.synchronizedList(new ArrayList ()); 136 137 141 final Object undoRedoHistoryLock = new Object (); 142 143 149 private ICompositeOperation openComposite = null; 150 151 154 final Object openCompositeLock = new Object (); 155 156 159 public DefaultOperationHistory() { 160 super(); 161 } 162 163 168 public void add(IUndoableOperation operation) { 169 Assert.isNotNull(operation); 170 171 179 synchronized (openCompositeLock) { 180 if (openComposite != null && openComposite != operation) { 181 openComposite.add(operation); 182 return; 183 } 184 } 185 186 if (checkUndoLimit(operation)) { 187 synchronized (undoRedoHistoryLock) { 188 undoList.add(operation); 189 } 190 notifyAdd(operation); 191 192 IUndoContext[] contexts = operation.getContexts(); 194 for (int i = 0; i < contexts.length; i++) { 195 flushRedo(contexts[i]); 196 } 197 } else { 198 operation.dispose(); 200 } 201 } 202 203 221 222 public void addOperationApprover(IOperationApprover approver) { 223 approvers.add(approver); 224 } 225 226 246 public void addOperationHistoryListener(IOperationHistoryListener listener) { 247 listeners.add(listener); 248 } 249 250 255 public boolean canRedo(IUndoContext context) { 256 IUndoableOperation operation = getRedoOperation(context); 258 return (operation != null && operation.canRedo()); 259 } 260 261 266 public boolean canUndo(IUndoContext context) { 267 IUndoableOperation operation = getUndoOperation(context); 269 return (operation != null && operation.canUndo()); 270 } 271 272 281 private boolean checkRedoLimit(IUndoableOperation operation) { 282 IUndoContext[] contexts = operation.getContexts(); 283 for (int i = 0; i < contexts.length; i++) { 284 int limit = getLimit(contexts[i]); 285 if (limit > 0) { 286 forceRedoLimit(contexts[i], limit - 1); 287 } else { 288 operation.removeContext(contexts[i]); 290 } 291 } 292 return operation.getContexts().length > 0; 293 } 294 295 299 private boolean checkUndoLimit(IUndoableOperation operation) { 300 IUndoContext[] contexts = operation.getContexts(); 301 for (int i = 0; i < contexts.length; i++) { 302 int limit = getLimit(contexts[i]); 303 if (limit > 0) { 304 forceUndoLimit(contexts[i], limit - 1); 305 } else { 306 operation.removeContext(contexts[i]); 308 } 309 } 310 return operation.getContexts().length > 0; 311 } 312 313 319 public void dispose(IUndoContext context, boolean flushUndo, 320 boolean flushRedo, boolean flushContext) { 321 if (flushContext) { 324 if (DEBUG_OPERATION_HISTORY_DISPOSE) { 325 Tracing.printTrace("OPERATIONHISTORY", "Flushing context " + context); 327 } 328 flushUndo(context); 329 flushRedo(context); 330 limits.remove(context); 331 return; 332 } 333 if (flushUndo) { 334 flushUndo(context); 335 } 336 if (flushRedo) { 337 flushRedo(context); 338 } 339 340 } 341 342 348 private IStatus doRedo(IProgressMonitor monitor, IAdaptable info, 349 IUndoableOperation operation) throws ExecutionException { 350 351 IStatus status = getRedoApproval(operation, info); 352 if (status.isOK()) { 353 notifyAboutToRedo(operation); 354 try { 355 status = operation.redo(monitor, info); 356 } catch (OperationCanceledException e) { 357 status = Status.CANCEL_STATUS; 358 } catch (ExecutionException e) { 359 notifyNotOK(operation); 360 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 361 Tracing.printTrace("OPERATIONHISTORY", "ExecutionException while redoing " + operation); } 364 throw e; 365 } catch (Exception e) { 366 notifyNotOK(operation); 367 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 368 Tracing.printTrace("OPERATIONHISTORY", "Exception while redoing " + operation); } 371 throw new ExecutionException( 372 "While redoing the operation, an exception occurred", e); } 374 } 375 376 if (status.isOK()) { 379 boolean addedToUndo = true; 380 synchronized (undoRedoHistoryLock) { 381 redoList.remove(operation); 382 if (checkUndoLimit(operation)) { 383 undoList.add(operation); 384 } else { 385 addedToUndo = false; 386 } 387 } 388 if (!addedToUndo) { 391 operation.dispose(); 392 } 393 394 notifyRedone(operation); 396 } else { 397 notifyNotOK(operation, status); 398 } 399 400 return status; 401 } 402 403 409 private IStatus doUndo(IProgressMonitor monitor, IAdaptable info, 410 IUndoableOperation operation) throws ExecutionException { 411 IStatus status = getUndoApproval(operation, info); 412 if (status.isOK()) { 413 notifyAboutToUndo(operation); 414 try { 415 status = operation.undo(monitor, info); 416 } catch (OperationCanceledException e) { 417 status = Status.CANCEL_STATUS; 418 } catch (ExecutionException e) { 419 notifyNotOK(operation); 420 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 421 Tracing.printTrace("OPERATIONHISTORY", "ExecutionException while undoing " + operation); } 424 throw e; 425 } catch (Exception e) { 426 notifyNotOK(operation); 427 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 428 Tracing.printTrace("OPERATIONHISTORY", "Exception while undoing " + operation); } 431 throw new ExecutionException( 432 "While undoing the operation, an exception occurred", e); } 434 } 435 if (status.isOK()) { 438 boolean addedToRedo = true; 439 synchronized (undoRedoHistoryLock) { 440 undoList.remove(operation); 441 if (checkRedoLimit(operation)) { 442 redoList.add(operation); 443 } else { 444 addedToRedo = false; 445 } 446 } 447 if (!addedToRedo) { 450 operation.dispose(); 451 } 452 notifyUndone(operation); 455 } else { 456 notifyNotOK(operation, status); 457 } 458 return status; 459 } 460 461 468 public IStatus execute(IUndoableOperation operation, 469 IProgressMonitor monitor, IAdaptable info) 470 throws ExecutionException { 471 Assert.isNotNull(operation); 472 473 if (!operation.canExecute()) { 475 return IOperationHistory.OPERATION_INVALID_STATUS; 476 } 477 478 IStatus status = getExecuteApproval(operation, info); 480 if (!status.isOK()) { 481 return status; 483 } 484 485 490 boolean merging = false; 491 synchronized (openCompositeLock) { 492 if (openComposite != null) { 493 if (openComposite == operation) { 497 return IOperationHistory.OPERATION_INVALID_STATUS; 498 } 499 openComposite.add(operation); 500 merging = true; 501 } 502 } 503 504 507 if (!merging) { 508 notifyAboutToExecute(operation); 509 } 510 try { 511 status = operation.execute(monitor, info); 512 } catch (OperationCanceledException e) { 513 status = Status.CANCEL_STATUS; 514 } catch (ExecutionException e) { 515 notifyNotOK(operation); 516 throw e; 517 } catch (Exception e) { 518 notifyNotOK(operation); 519 throw new ExecutionException( 520 "While executing the operation, an exception occurred", e); } 522 523 if (!merging) { 526 if (status.isOK()) { 527 notifyDone(operation); 528 add(operation); 529 } else { 530 notifyNotOK(operation, status); 531 operation.dispose(); 534 } 535 } 536 return status; 538 } 539 540 543 private IUndoableOperation[] filter(List list, IUndoContext context) { 544 552 553 List filtered = new ArrayList (); 554 Iterator iterator = list.iterator(); 555 synchronized (undoRedoHistoryLock) { 556 while (iterator.hasNext()) { 557 IUndoableOperation operation = (IUndoableOperation) iterator 558 .next(); 559 if (operation.hasContext(context)) { 560 filtered.add(operation); 561 } 562 } 563 } 564 return (IUndoableOperation[]) filtered 565 .toArray(new IUndoableOperation[filtered.size()]); 566 } 567 568 571 private void flushRedo(IUndoContext context) { 572 if (DEBUG_OPERATION_HISTORY_DISPOSE) { 573 Tracing.printTrace("OPERATIONHISTORY", "Flushing redo history for " + context); 575 } 576 577 Object [] filtered = filter(redoList, context); 578 for (int i = 0; i < filtered.length; i++) { 579 IUndoableOperation operation = (IUndoableOperation) filtered[i]; 580 if (context == GLOBAL_UNDO_CONTEXT 581 || operation.getContexts().length == 1) { 582 redoList.remove(operation); 585 internalRemove(operation); 586 } else { 587 IUndoContext[] contexts = operation.getContexts(); 593 for (int j = 0; j < contexts.length; j++) { 594 if (contexts[j].matches(context)) { 595 operation.removeContext(contexts[j]); 596 } 597 } 598 if (operation.getContexts().length == 0) { 599 redoList.remove(operation); 600 internalRemove(operation); 601 } 602 } 603 } 604 } 605 606 609 private void flushUndo(IUndoContext context) { 610 if (DEBUG_OPERATION_HISTORY_DISPOSE) { 611 Tracing.printTrace("OPERATIONHISTORY", "Flushing undo history for " + context); 613 } 614 615 Object [] filtered = filter(undoList, context); 617 for (int i = 0; i < filtered.length; i++) { 618 IUndoableOperation operation = (IUndoableOperation) filtered[i]; 619 if (context == GLOBAL_UNDO_CONTEXT 620 || operation.getContexts().length == 1) { 621 undoList.remove(operation); 624 internalRemove(operation); 625 } else { 626 IUndoContext[] contexts = operation.getContexts(); 632 for (int j = 0; j < contexts.length; j++) { 633 if (contexts[j].matches(context)) { 634 operation.removeContext(contexts[j]); 635 } 636 } 637 if (operation.getContexts().length == 0) { 638 undoList.remove(operation); 639 internalRemove(operation); 640 } 641 } 642 } 643 649 ICompositeOperation endedComposite = null; 650 synchronized (openCompositeLock) { 651 if (openComposite != null) { 652 if (openComposite.hasContext(context)) { 653 if (context == GLOBAL_UNDO_CONTEXT 654 || openComposite.getContexts().length == 1) { 655 endedComposite = openComposite; 656 openComposite = null; 657 } else { 658 openComposite.removeContext(context); 659 } 660 } 661 } 662 } 663 if (endedComposite != null) { 665 notifyNotOK(endedComposite); 666 } 667 } 668 669 673 private void forceRedoLimit(IUndoContext context, int max) { 674 Object [] filtered = filter(redoList, context); 675 int size = filtered.length; 676 if (size > 0) { 677 int index = 0; 678 while (size > max) { 679 IUndoableOperation removed = (IUndoableOperation) filtered[index]; 680 if (context == GLOBAL_UNDO_CONTEXT 681 || removed.getContexts().length == 1) { 682 686 redoList.remove(removed); 687 internalRemove(removed); 688 } else { 689 694 removed.removeContext(context); 695 } 696 size--; 697 index++; 698 } 699 } 700 } 701 702 706 private void forceUndoLimit(IUndoContext context, int max) { 707 Object [] filtered = filter(undoList, context); 708 int size = filtered.length; 709 if (size > 0) { 710 int index = 0; 711 while (size > max) { 712 IUndoableOperation removed = (IUndoableOperation) filtered[index]; 713 if (context == GLOBAL_UNDO_CONTEXT 714 || removed.getContexts().length == 1) { 715 719 undoList.remove(removed); 720 internalRemove(removed); 721 } else { 722 727 removed.removeContext(context); 728 } 729 size--; 730 index++; 731 } 732 } 733 } 734 735 740 public int getLimit(IUndoContext context) { 741 if (!limits.containsKey(context)) { 742 return DEFAULT_LIMIT; 743 } 744 return ((Integer ) (limits.get(context))).intValue(); 745 } 746 747 751 private IStatus getRedoApproval(IUndoableOperation operation, 752 IAdaptable info) { 753 754 final Object [] approverArray = approvers.getListeners(); 755 756 for (int i = 0; i < approverArray.length; i++) { 757 IOperationApprover approver = (IOperationApprover) approverArray[i]; 758 IStatus approval = approver.proceedRedoing(operation, this, info); 759 if (!approval.isOK()) { 760 if (DEBUG_OPERATION_HISTORY_APPROVAL) { 761 Tracing.printTrace("OPERATIONHISTORY", "Redo not approved by " + approver + "for operation " + operation + " approved by " + approval); } 766 return approval; 767 } 768 } 769 return Status.OK_STATUS; 770 } 771 772 777 public IUndoableOperation[] getRedoHistory(IUndoContext context) { 778 Assert.isNotNull(context); 779 return filter(redoList, context); 780 } 781 782 787 public IUndoableOperation getRedoOperation(IUndoContext context) { 788 Assert.isNotNull(context); 789 synchronized (undoRedoHistoryLock) { 790 for (int i = redoList.size() - 1; i >= 0; i--) { 791 IUndoableOperation operation = (IUndoableOperation) redoList 792 .get(i); 793 if (operation.hasContext(context)) { 794 return operation; 795 } 796 } 797 } 798 return null; 799 } 800 801 805 private IStatus getUndoApproval(IUndoableOperation operation, 806 IAdaptable info) { 807 808 final Object [] approverArray = approvers.getListeners(); 809 810 for (int i = 0; i < approverArray.length; i++) { 811 IOperationApprover approver = (IOperationApprover) approverArray[i]; 812 IStatus approval = approver.proceedUndoing(operation, this, info); 813 if (!approval.isOK()) { 814 if (DEBUG_OPERATION_HISTORY_APPROVAL) { 815 Tracing.printTrace("OPERATIONHISTORY", "Undo not approved by " + approver + "for operation " + operation + " with status " + approval); } 820 return approval; 821 } 822 } 823 return Status.OK_STATUS; 824 } 825 826 831 public IUndoableOperation[] getUndoHistory(IUndoContext context) { 832 Assert.isNotNull(context); 833 return filter(undoList, context); 834 } 835 836 841 public IUndoableOperation getUndoOperation(IUndoContext context) { 842 Assert.isNotNull(context); 843 synchronized (undoRedoHistoryLock) { 844 for (int i = undoList.size() - 1; i >= 0; i--) { 845 IUndoableOperation operation = (IUndoableOperation) undoList 846 .get(i); 847 if (operation.hasContext(context)) { 848 return operation; 849 } 850 } 851 } 852 return null; 853 } 854 855 861 private IStatus getExecuteApproval(IUndoableOperation operation, 862 IAdaptable info) { 863 864 final Object [] approverArray = approvers.getListeners(); 865 866 for (int i = 0; i < approverArray.length; i++) { 867 if (approverArray[i] instanceof IOperationApprover2) { 868 IOperationApprover2 approver = (IOperationApprover2) approverArray[i]; 869 IStatus approval = approver.proceedExecuting(operation, this, 870 info); 871 if (!approval.isOK()) { 872 if (DEBUG_OPERATION_HISTORY_APPROVAL) { 873 Tracing.printTrace("OPERATIONHISTORY", "Execute not approved by " + approver + "for operation " + operation + " with status " + approval); } 878 return approval; 879 } 880 } 881 } 882 return Status.OK_STATUS; 883 } 884 885 888 private void internalRemove(IUndoableOperation operation) { 889 operation.dispose(); 890 notifyRemoved(operation); 891 } 892 893 896 private void notifyListeners(final OperationHistoryEvent event) { 897 if (event.getOperation() instanceof IAdvancedUndoableOperation) { 898 final IAdvancedUndoableOperation advancedOp = (IAdvancedUndoableOperation) event 899 .getOperation(); 900 SafeRunner.run(new ISafeRunnable() { 901 public void handleException(Throwable exception) { 902 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 903 Tracing 904 .printTrace( 905 "OPERATIONHISTORY", "Exception during notification callback " + exception); } 908 } 909 910 public void run() throws Exception { 911 advancedOp.aboutToNotify(event); 912 } 913 }); 914 } 915 final Object [] listenerArray = listeners.getListeners(); 916 for (int i = 0; i < listenerArray.length; i++) { 917 final IOperationHistoryListener listener = (IOperationHistoryListener) listenerArray[i]; 918 SafeRunner.run(new ISafeRunnable() { 919 public void handleException(Throwable exception) { 920 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 921 Tracing 922 .printTrace( 923 "OPERATIONHISTORY", "Exception during notification callback " + exception); } 926 } 927 928 public void run() throws Exception { 929 listener.historyNotification(event); 930 } 931 }); 932 } 933 } 934 935 private void notifyAboutToExecute(IUndoableOperation operation) { 936 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 937 Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_EXECUTE " + operation); 939 } 940 941 notifyListeners(new OperationHistoryEvent( 942 OperationHistoryEvent.ABOUT_TO_EXECUTE, this, operation)); 943 } 944 945 948 private void notifyAboutToRedo(IUndoableOperation operation) { 949 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 950 Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_REDO " + operation); 952 } 953 954 notifyListeners(new OperationHistoryEvent( 955 OperationHistoryEvent.ABOUT_TO_REDO, this, operation)); 956 } 957 958 961 private void notifyAboutToUndo(IUndoableOperation operation) { 962 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 963 Tracing.printTrace("OPERATIONHISTORY", "ABOUT_TO_UNDO " + operation); 965 } 966 967 notifyListeners(new OperationHistoryEvent( 968 OperationHistoryEvent.ABOUT_TO_UNDO, this, operation)); 969 } 970 971 974 private void notifyAdd(IUndoableOperation operation) { 975 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 976 Tracing.printTrace("OPERATIONHISTORY", "OPERATION_ADDED " + operation); 978 } 979 980 notifyListeners(new OperationHistoryEvent( 981 OperationHistoryEvent.OPERATION_ADDED, this, operation)); 982 } 983 984 987 private void notifyDone(IUndoableOperation operation) { 988 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 989 Tracing.printTrace("OPERATIONHISTORY", "DONE " + operation); } 991 992 notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.DONE, 993 this, operation)); 994 } 995 996 1000 private void notifyNotOK(IUndoableOperation operation) { 1001 notifyNotOK(operation, null); 1002 } 1003 1004 1011 private void notifyNotOK(IUndoableOperation operation, IStatus status) { 1012 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 1013 Tracing.printTrace("OPERATIONHISTORY", "OPERATION_NOT_OK " + operation); 1015 } 1016 1017 notifyListeners(new OperationHistoryEvent( 1018 OperationHistoryEvent.OPERATION_NOT_OK, this, operation, status)); 1019 } 1020 1021 1024 private void notifyRedone(IUndoableOperation operation) { 1025 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 1026 Tracing.printTrace("OPERATIONHISTORY", "REDONE " + operation); } 1028 1029 notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.REDONE, 1030 this, operation)); 1031 } 1032 1033 1036 private void notifyRemoved(IUndoableOperation operation) { 1037 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 1038 Tracing.printTrace("OPERATIONHISTORY", "OPERATION_REMOVED " + operation); 1040 } 1041 1042 notifyListeners(new OperationHistoryEvent( 1043 OperationHistoryEvent.OPERATION_REMOVED, this, operation)); 1044 } 1045 1046 1049 private void notifyUndone(IUndoableOperation operation) { 1050 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 1051 Tracing.printTrace("OPERATIONHISTORY", "UNDONE " + operation); } 1053 1054 notifyListeners(new OperationHistoryEvent(OperationHistoryEvent.UNDONE, 1055 this, operation)); 1056 } 1057 1058 1061 private void notifyChanged(IUndoableOperation operation) { 1062 if (DEBUG_OPERATION_HISTORY_NOTIFICATION) { 1063 Tracing.printTrace("OPERATIONHISTORY", "OPERATION_CHANGED " + operation); 1065 } 1066 1067 notifyListeners(new OperationHistoryEvent( 1068 OperationHistoryEvent.OPERATION_CHANGED, this, operation)); 1069 } 1070 1071 1078 public IStatus redo(IUndoContext context, IProgressMonitor monitor, 1079 IAdaptable info) throws ExecutionException { 1080 Assert.isNotNull(context); 1081 IUndoableOperation operation = getRedoOperation(context); 1082 1083 if (operation == null) { 1085 return IOperationHistory.NOTHING_TO_REDO_STATUS; 1086 } 1087 1088 if (!operation.canRedo()) { 1090 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1091 Tracing.printTrace("OPERATIONHISTORY", "Redo operation not valid - " + operation); } 1094 1095 return IOperationHistory.OPERATION_INVALID_STATUS; 1096 } 1097 1098 return doRedo(monitor, info, operation); 1099 } 1100 1101 1108 1109 public IStatus redoOperation(IUndoableOperation operation, 1110 IProgressMonitor monitor, IAdaptable info) 1111 throws ExecutionException { 1112 Assert.isNotNull(operation); 1113 IStatus status; 1114 if (operation.canRedo()) { 1115 status = doRedo(monitor, info, operation); 1116 } else { 1117 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1118 Tracing.printTrace("OPERATIONHISTORY", "Redo operation not valid - " + operation); } 1121 status = IOperationHistory.OPERATION_INVALID_STATUS; 1122 } 1123 return status; 1124 } 1125 1126 1131 public void removeOperationApprover(IOperationApprover approver) { 1132 approvers.remove(approver); 1133 } 1134 1135 1140 public void removeOperationHistoryListener( 1141 IOperationHistoryListener listener) { 1142 listeners.remove(listener); 1143 } 1144 1145 1151 public void replaceOperation(IUndoableOperation operation, 1152 IUndoableOperation[] replacements) { 1153 boolean inUndo = false; 1155 synchronized (undoRedoHistoryLock) { 1156 int index = undoList.indexOf(operation); 1157 if (index > -1) { 1158 inUndo = true; 1159 undoList.remove(operation); 1160 ArrayList allContexts = new ArrayList (replacements.length); 1162 for (int i = 0; i < replacements.length; i++) { 1163 IUndoContext[] opContexts = replacements[i].getContexts(); 1164 for (int j = 0; j < opContexts.length; j++) { 1165 allContexts.add(opContexts[j]); 1166 } 1167 undoList.add(index, replacements[i]); 1168 } 1171 for (int i = 0; i < allContexts.size(); i++) { 1174 IUndoContext context = (IUndoContext) allContexts.get(i); 1175 forceUndoLimit(context, getLimit(context)); 1176 } 1177 } 1178 } 1179 if (inUndo) { 1180 internalRemove(operation); 1182 for (int i = 0; i < replacements.length; i++) { 1183 notifyAdd(replacements[i]); 1184 } 1185 return; 1186 } 1187 1188 1190 synchronized (undoRedoHistoryLock) { 1191 int index = redoList.indexOf(operation); 1192 if (index == -1) { 1193 return; 1194 } 1195 ArrayList allContexts = new ArrayList (replacements.length); 1196 redoList.remove(operation); 1197 for (int i = 0; i < replacements.length; i++) { 1199 IUndoContext[] opContexts = replacements[i].getContexts(); 1200 for (int j = 0; j < opContexts.length; j++) { 1201 allContexts.add(opContexts[j]); 1202 } 1203 redoList.add(index, replacements[i]); 1204 } 1206 for (int i = 0; i < allContexts.size(); i++) { 1209 IUndoContext context = (IUndoContext) allContexts.get(i); 1210 forceRedoLimit(context, getLimit(context)); 1211 } 1212 } 1213 internalRemove(operation); 1215 for (int i = 0; i < replacements.length; i++) { 1216 notifyAdd(replacements[i]); 1217 } 1218 } 1219 1220 1226 public void setLimit(IUndoContext context, int limit) { 1227 Assert.isTrue(limit >= 0); 1228 1235 Assert.isNotNull(context); 1236 limits.put(context, new Integer (limit)); 1237 forceUndoLimit(context, limit); 1238 forceRedoLimit(context, limit); 1239 1240 } 1241 1242 1249 public IStatus undo(IUndoContext context, IProgressMonitor monitor, 1250 IAdaptable info) throws ExecutionException { 1251 Assert.isNotNull(context); 1252 IUndoableOperation operation = getUndoOperation(context); 1253 1254 if (operation == null) { 1256 return IOperationHistory.NOTHING_TO_UNDO_STATUS; 1257 } 1258 1259 if (!operation.canUndo()) { 1261 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1262 Tracing.printTrace("OPERATIONHISTORY", "Undo operation not valid - " + operation); } 1265 return IOperationHistory.OPERATION_INVALID_STATUS; 1266 } 1267 1268 return doUndo(monitor, info, operation); 1269 } 1270 1271 1278 public IStatus undoOperation(IUndoableOperation operation, 1279 IProgressMonitor monitor, IAdaptable info) 1280 throws ExecutionException { 1281 Assert.isNotNull(operation); 1282 IStatus status; 1283 if (operation.canUndo()) { 1284 status = doUndo(monitor, info, operation); 1285 } else { 1286 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1287 Tracing.printTrace("OPERATIONHISTORY", "Undo operation not valid - " + operation); } 1290 status = IOperationHistory.OPERATION_INVALID_STATUS; 1291 } 1292 return status; 1293 } 1294 1295 1300 public void openOperation(ICompositeOperation operation, int mode) { 1301 synchronized (openCompositeLock) { 1302 if (openComposite != null && openComposite != operation) { 1303 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1305 Tracing.printTrace("OPERATIONHISTORY", "Open operation called while another operation is open. old: " + openComposite + "; new: " + operation); } 1309 1310 throw new IllegalStateException ( 1311 "Cannot open an operation while one is already open"); } 1313 openComposite = operation; 1314 } 1315 if (DEBUG_OPERATION_HISTORY_OPENOPERATION) { 1316 Tracing.printTrace("OPERATIONHISTORY", "Opening operation " + openComposite); 1318 } 1319 1320 if (mode == EXECUTE) { 1321 notifyAboutToExecute(openComposite); 1322 } 1323 } 1324 1325 1331 public void closeOperation(boolean operationOK, boolean addToHistory, 1332 int mode) { 1333 ICompositeOperation endedComposite = null; 1334 1335 synchronized (openCompositeLock) { 1336 if (DEBUG_OPERATION_HISTORY_UNEXPECTED) { 1337 if (openComposite == null) { 1338 Tracing.printTrace("OPERATIONHISTORY", "Attempted to close operation when none was open"); return; 1341 } 1342 } 1343 if (openComposite != null) { 1345 if (DEBUG_OPERATION_HISTORY_OPENOPERATION) { 1346 Tracing.printTrace("OPERATIONHISTORY", "Closing operation " + openComposite); 1348 } 1349 endedComposite = openComposite; 1350 openComposite = null; 1351 } 1352 } 1353 if (endedComposite != null) { 1357 if (operationOK) { 1358 if (mode == EXECUTE) { 1359 notifyDone(endedComposite); 1360 } 1361 if (addToHistory) { 1362 add(endedComposite); 1363 } 1364 } else { 1365 if (mode == EXECUTE) { 1366 notifyNotOK(endedComposite); 1367 } 1368 } 1369 } 1370 } 1371 1372 1377 public void operationChanged(IUndoableOperation operation) { 1378 if (undoList.contains(operation) || redoList.contains(operation)) { 1379 notifyChanged(operation); 1380 } 1381 } 1382} 1383 | Popular Tags |