1 11 package org.eclipse.ltk.internal.core.refactoring; 12 13 import java.util.Collection ; 14 import java.util.Iterator ; 15 import java.util.Stack ; 16 17 import org.eclipse.core.runtime.Assert; 18 import org.eclipse.core.runtime.CoreException; 19 import org.eclipse.core.runtime.IProgressMonitor; 20 import org.eclipse.core.runtime.ISafeRunnable; 21 import org.eclipse.core.runtime.ListenerList; 22 import org.eclipse.core.runtime.NullProgressMonitor; 23 import org.eclipse.core.runtime.SafeRunner; 24 import org.eclipse.core.runtime.SubProgressMonitor; 25 26 import org.eclipse.core.resources.IWorkspaceRunnable; 27 import org.eclipse.core.resources.ResourcesPlugin; 28 29 import org.eclipse.ltk.core.refactoring.Change; 30 import org.eclipse.ltk.core.refactoring.IUndoManager; 31 import org.eclipse.ltk.core.refactoring.IUndoManagerListener; 32 import org.eclipse.ltk.core.refactoring.IValidationCheckResultQuery; 33 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 34 35 38 public class UndoManager implements IUndoManager { 39 40 private Stack fUndoChanges; 41 private Stack fRedoChanges; 42 private Stack fUndoNames; 43 private Stack fRedoNames; 44 45 private ListenerList fListeners; 46 private static final int MAX_UNDO_REDOS= 5; 48 49 private static class NullQuery implements IValidationCheckResultQuery { 50 public boolean proceed(RefactoringStatus status) { 51 return true; 52 } 53 public void stopped(RefactoringStatus status) { 54 } 56 } 57 58 61 public UndoManager() { 62 flush(); 63 } 64 65 68 public void addListener(IUndoManagerListener listener) { 69 if (fListeners == null) 70 fListeners= new ListenerList(ListenerList.IDENTITY); 71 fListeners.add(listener); 72 } 73 74 77 public void removeListener(IUndoManagerListener listener) { 78 if (fListeners == null) 79 return; 80 fListeners.remove(listener); 81 if (fListeners.size() == 0) 82 fListeners= null; 83 } 84 85 public void aboutToPerformChange(final Change change) { 86 if (fListeners == null) 87 return; 88 Object [] listeners= fListeners.getListeners(); 89 for (int i= 0; i < listeners.length; i++) { 90 final IUndoManagerListener listener= (IUndoManagerListener)listeners[i]; 91 SafeRunner.run(new ISafeRunnable() { 92 public void run() throws Exception { 93 listener.aboutToPerformChange(UndoManager.this, change); 94 } 95 public void handleException(Throwable exception) { 96 RefactoringCorePlugin.log(exception); 97 } 98 }); 99 } 100 } 101 102 public void changePerformed(final Change change) { 103 if (fListeners == null) 104 return; 105 Object [] listeners= fListeners.getListeners(); 106 for (int i= 0; i < listeners.length; i++) { 107 final IUndoManagerListener listener= (IUndoManagerListener)listeners[i]; 108 SafeRunner.run(new ISafeRunnable() { 109 public void run() throws Exception { 110 listener.changePerformed(UndoManager.this, change); 111 } 112 public void handleException(Throwable exception) { 113 RefactoringCorePlugin.log(exception); 114 } 115 }); 116 } 117 } 118 119 public void changePerformed(Change change, boolean successful) { 120 changePerformed(change); 122 } 123 124 127 public void aboutToPerformRefactoring() { 128 } 129 130 133 public void refactoringPerformed(boolean success) { 134 } 135 136 141 public void shutdown() { 142 } 143 144 147 public void flush() { 148 flushUndo(); 149 flushRedo(); 150 } 151 152 private void flushUndo() { 153 if (fUndoChanges != null) { 154 sendDispose(fUndoChanges); 155 } 156 fUndoChanges= new Stack (); 157 fUndoNames= new Stack (); 158 fireUndoStackChanged(); 159 } 160 161 private void flushRedo() { 162 if (fRedoChanges != null) { 163 sendDispose(fRedoChanges); 164 } 165 fRedoChanges= new Stack (); 166 fRedoNames= new Stack (); 167 fireRedoStackChanged(); 168 } 169 170 173 public void addUndo(String refactoringName, Change change) { 174 Assert.isNotNull(refactoringName, "refactoring"); Assert.isNotNull(change, "change"); fUndoNames.push(refactoringName); 177 fUndoChanges.push(change); 178 if (fUndoChanges.size() > MAX_UNDO_REDOS) { 179 Change removedChange= (Change)fUndoChanges.remove(0); 180 fUndoNames.remove(0); 181 removedChange.dispose(); 182 } 183 flushRedo(); 184 fireUndoStackChanged(); 185 } 186 187 190 public void performUndo(IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException { 191 if (pm == null) 192 pm= new NullProgressMonitor(); 193 RefactoringStatus result= new RefactoringStatus(); 194 195 if (fUndoChanges.empty()) 196 return; 197 198 Change change= (Change)fUndoChanges.pop(); 199 if (query == null) 200 query= new NullQuery(); 201 Change redo; 202 try { 203 redo= executeChange(result, change, query, pm); 204 } catch (InterruptedException e) { 205 fUndoChanges.push(change); 206 return; 207 } 208 if (!result.hasFatalError()) { 209 if (redo != null && !fUndoNames.isEmpty()) { 210 fRedoNames.push(fUndoNames.pop()); 211 fRedoChanges.push(redo); 212 fireUndoStackChanged(); 213 fireRedoStackChanged(); 214 } else { 215 flush(); 216 } 217 } else { 218 flush(); 219 } 220 } 221 222 225 public void performRedo(IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException { 226 if (pm == null) 227 pm= new NullProgressMonitor(); 228 RefactoringStatus result= new RefactoringStatus(); 229 230 if (fRedoChanges.empty()) 231 return; 232 233 Change change= (Change)fRedoChanges.pop(); 234 if (query == null) 235 query= new NullQuery(); 236 Change undo; 237 try { 238 undo= executeChange(result, change, query, pm); 239 } catch (InterruptedException e) { 240 fRedoChanges.push(change); 241 return; 242 } 243 if (!result.hasFatalError()) { 244 if (undo != null && !fRedoNames.isEmpty()) { 245 fUndoNames.push(fRedoNames.pop()); 246 fUndoChanges.push(undo); 247 fireRedoStackChanged(); 248 fireUndoStackChanged(); 249 } 250 } else { 251 flush(); 252 } 253 } 254 255 private Change executeChange(final RefactoringStatus status, final Change change, final IValidationCheckResultQuery query, IProgressMonitor pm) throws CoreException, InterruptedException { 256 final Change[] undo= new Change[1]; 257 final boolean[] interrupted= new boolean[1]; 258 IWorkspaceRunnable runnable= new IWorkspaceRunnable() { 259 public void run(IProgressMonitor monitor) throws CoreException { 260 boolean undoInitialized= false; 261 try { 262 monitor.beginTask("", 11); status.merge(change.isValid(new SubProgressMonitor(monitor, 2))); 264 if (status.hasFatalError()) { 265 query.stopped(status); 266 change.dispose(); 267 return; 268 } 269 if (!status.isOK() && !query.proceed(status)) { 270 interrupted[0]= true; 271 return; 272 } 273 ResourcesPlugin.getWorkspace().checkpoint(false); 274 boolean successful= false; 275 try { 276 aboutToPerformChange(change); 277 undo[0]= change.perform(new SubProgressMonitor(monitor, 8)); 278 successful= true; 279 ResourcesPlugin.getWorkspace().checkpoint(false); 280 } finally { 281 changePerformed(change, successful); 282 } 283 change.dispose(); 284 if (undo[0] != null) { 285 undo[0].initializeValidationData(new SubProgressMonitor(monitor, 1)); 286 undoInitialized= true; 287 } 288 } catch (CoreException e) { 289 flush(); 290 if (undo[0] != null && undoInitialized) { 291 Change ch= undo[0]; 292 undo[0]= null; 293 ch.dispose(); 294 } else { 295 undo[0]= null; 296 } 297 throw e; 298 } catch (RuntimeException e) { 299 flush(); 300 if (undo[0] != null && undoInitialized) { 301 Change ch= undo[0]; 302 undo[0]= null; 303 ch.dispose(); 304 } else { 305 undo[0]= null; 306 } 307 throw e; 308 } finally { 309 monitor.done(); 310 } 311 } 312 }; 313 ResourcesPlugin.getWorkspace().run(runnable, pm); 314 if (interrupted[0]) 315 throw new InterruptedException (); 316 return undo[0]; 317 } 318 319 322 public boolean anythingToRedo() { 323 return !fRedoChanges.empty(); 324 } 325 326 329 public boolean anythingToUndo() { 330 return !fUndoChanges.empty(); 331 } 332 333 336 public String peekUndoName() { 337 if (fUndoNames.size() > 0) 338 return (String )fUndoNames.peek(); 339 return null; 340 } 341 342 345 public String peekRedoName() { 346 if (fRedoNames.size() > 0) 347 return (String )fRedoNames.peek(); 348 return null; 349 } 350 351 private void fireUndoStackChanged() { 352 if (fListeners == null) 353 return; 354 Object [] listeners= fListeners.getListeners(); 355 for (int i= 0; i < listeners.length; i++) { 356 ((IUndoManagerListener)listeners[i]).undoStackChanged(this); 357 } 358 } 359 360 private void fireRedoStackChanged() { 361 if (fListeners == null) 362 return; 363 Object [] listeners= fListeners.getListeners(); 364 for (int i= 0; i < listeners.length; i++) { 365 ((IUndoManagerListener)listeners[i]).redoStackChanged(this); 366 } 367 } 368 369 private void sendDispose(Collection collection) { 370 for (Iterator iter= collection.iterator(); iter.hasNext();) { 371 final Change change= (Change)iter.next(); 372 ISafeRunnable r= new ISafeRunnable() { 373 public void run() { 374 change.dispose(); 375 } 376 public void handleException(Throwable exception) { 377 RefactoringCorePlugin.log(exception); 378 } 379 }; 380 SafeRunner.run(r); 381 } 382 } 383 384 386 public boolean testHasNumberOfUndos(int number) { 387 return fUndoChanges.size() == number; 388 } 389 390 public boolean testHasNumberOfRedos(int number) { 391 return fRedoChanges.size() == number; 392 } 393 } 394 | Popular Tags |