1 19 package org.netbeans.modules.xml.refactoring; 20 21 import java.beans.PropertyChangeEvent ; 22 import java.beans.PropertyChangeListener ; 23 import java.io.IOException ; 24 import java.util.ArrayList ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.List ; 28 import java.util.Map ; 29 import java.util.Set ; 30 import java.util.concurrent.ExecutionException ; 31 import javax.swing.undo.CannotRedoException ; 32 import javax.swing.undo.CannotUndoException ; 33 import javax.swing.undo.UndoManager ; 34 import org.netbeans.modules.xml.refactoring.impl.GeneralChangeExecutor; 35 import org.netbeans.modules.xml.refactoring.impl.RefactoringUtil; 36 import org.netbeans.modules.xml.refactoring.spi.ChangeExecutor; 37 import org.netbeans.modules.xml.refactoring.spi.RefactoringEngine; 38 import org.netbeans.modules.xml.refactoring.spi.UIHelper; 39 import org.netbeans.modules.xml.xam.Component; 40 import org.netbeans.modules.xml.xam.Model; 41 import org.netbeans.modules.xml.xam.Referenceable; 42 import org.openide.filesystems.FileObject; 43 import org.openide.util.Cancellable; 44 import org.openide.util.Lookup; 45 import org.openide.util.NbBundle; 46 47 54 public class RefactoringManager implements Cancellable, PropertyChangeListener { 55 private static RefactoringManager manager = new RefactoringManager(); 56 private List <RefactoringEngine> engines; 57 private List <ChangeExecutor> executors; 58 private RefactorRequest lastRefactorRequest; 59 private Map <Model,UndoManager > undoManagers; 60 private UndoManager genericChangeUndoManager; 61 private enum Status { IDLE, RUNNING, CANCELLING } 62 private Status status = Status.IDLE; 63 private FindUsageResult findUsageResult; 64 65 66 private RefactoringManager() { 67 } 68 69 72 public static RefactoringManager getInstance() { 73 return manager; 74 } 75 76 79 80 83 public FindUsageResult findUsages(Referenceable target) { 84 if (target == null) { 85 throw new IllegalArgumentException ("Calling find usages with null target"); } 87 return new FindUsageResult(target); 88 } 89 90 93 public FindUsageResult findLocalUsages(Referenceable target) { 94 if (target == null || ! (target instanceof Component)) { 95 throw new IllegalArgumentException ("Calling find local usages with non-component target"); } 97 Component root = RefactorRequest.getRootOf(target); 98 return new FindUsageResult(target, Collections.singleton(root)); 99 } 100 101 104 public FindUsageResult findUsages(Referenceable target, Set <Component> searchRoots) { 105 return new FindUsageResult(target, searchRoots); 106 } 107 108 111 public FindUsageResult findUsages(Referenceable target, Component searchRoot) { 112 return new FindUsageResult(target, searchRoot); 113 } 114 115 public boolean canChange(Class <? extends RefactorRequest> type, Referenceable target) { 116 for (ChangeExecutor executor : getExecutors()) { 117 if (executor.canChange(type, target)) { 118 return true; 119 } 120 } 121 return false; 122 } 123 124 public RefactorRequest getLastRefactorRequest() { 125 return lastRefactorRequest; 126 } 127 128 public synchronized boolean canUndo() { 129 if (undoManagers == null || undoManagers.isEmpty()) { 130 return false; 131 } 132 for (UndoManager um : undoManagers.values()) { 133 if (! um.canUndo()) { 134 return false; 135 } 136 } 137 if (genericChangeUndoManager != null && ! genericChangeUndoManager.canUndo()) { 138 return false; 139 } 140 return true; 141 } 142 143 public synchronized void undo() throws CannotUndoException { 144 if (! canUndo()) { 145 throw new CannotUndoException (); 146 } 147 try { 148 Set <Model> excludedFromSave = lastRefactorRequest.getDirtyModels(); 149 setStatus(Status.RUNNING); 150 for (UndoManager um : undoManagers.values()) { 151 while (um.canUndo()) { 152 um.undo(); 153 } 154 } 155 if (genericChangeUndoManager != null && genericChangeUndoManager.canUndo()) { 156 genericChangeUndoManager.undo(); 157 } 158 159 if (lastRefactorRequest.getAutosave()) { 160 RefactoringUtil.save(lastRefactorRequest, excludedFromSave); 161 } 162 } finally { 163 setStatus(Status.IDLE); 164 } 165 } 166 167 private void _undo(Set <Model> excludedFromSave) { 168 if (undoManagers == null) return; 169 for (UndoManager um : undoManagers.values()) { 170 while (um.canUndo()) { 171 um.undo(); 172 } 173 } 174 if (genericChangeUndoManager != null && genericChangeUndoManager.canUndo()) { 175 genericChangeUndoManager.undo(); 176 } 177 if (lastRefactorRequest.getAutosave()) { 178 RefactoringUtil.save(lastRefactorRequest, excludedFromSave); 179 } 180 } 181 182 public synchronized boolean canRedo() { 183 if (undoManagers == null || undoManagers.isEmpty()) { 184 return false; 185 } 186 for (UndoManager um : undoManagers.values()) { 187 if (! um.canRedo()) { 188 return false; 189 } 190 } 191 if (genericChangeUndoManager != null && ! genericChangeUndoManager.canRedo()) { 192 return false; 193 } 194 return true; 195 } 196 197 public synchronized void redo() throws CannotRedoException { 198 if (! canRedo()) { 199 throw new CannotRedoException (); 200 } 201 try { 202 Set <Model> excludedFromSave = lastRefactorRequest.getDirtyModels(); 203 setStatus(Status.RUNNING); 204 for (UndoManager um : undoManagers.values()) { 205 while (um.canRedo()) { 206 um.redo(); 207 } 208 } 209 if (genericChangeUndoManager != null && genericChangeUndoManager.canRedo()) { 210 genericChangeUndoManager.redo(); 211 } 212 213 if (lastRefactorRequest.getAutosave()) { 214 RefactoringUtil.save(lastRefactorRequest, excludedFromSave); 215 } 216 } finally { 217 setStatus(Status.IDLE); 218 } 219 } 220 221 protected List <ChangeExecutor> getExecutors() { 222 if (executors == null) { 223 executors = new ArrayList <ChangeExecutor>(); 224 Lookup.Result results = Lookup.getDefault().lookup( 225 new Lookup.Template(ChangeExecutor.class)); 226 for (Object service : results.allInstances()){ 227 executors.add((ChangeExecutor)service); 228 } 229 executors.add(new GeneralChangeExecutor()); 230 } 231 return executors; 232 } 233 234 protected List <RefactoringEngine> getEngines() { 235 if (engines == null) { 236 engines = new ArrayList <RefactoringEngine>(); 237 Lookup.Result results = Lookup.getDefault().lookup( 238 new Lookup.Template(RefactoringEngine.class)); 239 for (Object service : results.allInstances()){ 240 engines.add((RefactoringEngine)service); 241 } 242 } 243 return engines; 244 } 245 246 private synchronized void addUndoableRefactorListener(Model model) { 247 if (undoManagers == null) { 248 undoManagers = new HashMap <Model,UndoManager >(); 249 } 250 FileObject source = (FileObject) model.getModelSource().getLookup().lookup(FileObject.class); 252 if (source == null) { 253 throw new IllegalArgumentException ("Could not get source file from provided model"); } 255 for (Model m : undoManagers.keySet()) { 256 FileObject s = (FileObject) m.getModelSource().getLookup().lookup(FileObject.class); 257 if (source.equals(s)) { 258 return; 259 } 260 } 261 262 if (undoManagers.get(model) == null) { 263 UndoManager um = new UndoManager (); 264 model.addUndoableRefactorListener(um); 265 undoManagers.put(model, um); 266 } 267 } 268 269 private synchronized void removeRefactorUndoEventListeners() { 270 if (undoManagers == null) return; 271 for(Model model : undoManagers.keySet()) { 272 model.removeUndoableRefactorListener(undoManagers.get(model)); 273 } 274 if (lastRefactorRequest != null) { 275 removeUndoableListener(lastRefactorRequest.getChangeExecutor()); 276 } 277 } 278 279 private synchronized void addUndoableListener(GeneralChangeExecutor executor) { 280 genericChangeUndoManager = new UndoManager (); 281 executor.addUndoableEditListener(genericChangeUndoManager); 282 } 283 284 private synchronized void removeUndoableListener(ChangeExecutor exec) { 285 if (! (exec instanceof GeneralChangeExecutor) || 286 genericChangeUndoManager == null || exec == null) { 287 return; 288 } 289 290 GeneralChangeExecutor executor = (GeneralChangeExecutor) exec; 291 executor.removeUndoableEditListener(genericChangeUndoManager); 292 } 293 294 298 public UIHelper getTargetComponentUIHelper(Referenceable target) { 299 for (ChangeExecutor ce : RefactoringManager.getInstance().getExecutors()) { 300 if (ce.canChange(RenameRequest.class, target) || 301 ce.canChange(DeleteRequest.class, target)) { 302 return ce.getUIHelper(); 303 } 304 } 305 return null; 306 } 307 308 314 public synchronized List <ErrorItem> precheckChange(RefactorRequest request) { 315 if (status != Status.IDLE) { 316 throw new IllegalStateException ("Invalid state "+status); } 318 setStatus(Status.RUNNING); 319 try { 320 request.precheckChange(); 321 for (ChangeExecutor ce : getExecutors()) { 322 if (! isRunning()) return null; 323 if (ce.canChange(request.getType(), request.getTarget())) { 324 request.setChangeExecutor(ce); 325 ce.precheck(request); 326 break; 327 } 328 } 329 return request.getErrors(); 330 } finally { 331 setStatus(Status.IDLE); 332 } 333 } 334 335 public synchronized List <ErrorItem> precheckUsages(RefactorRequest request) { 336 if (status != Status.IDLE) { 337 throw new IllegalStateException ("Invalid state "+status); 338 } 339 setStatus(Status.RUNNING); 340 341 try { 342 if (request.getUsages() == null) { 343 findUsageResult = findUsages(request.getTarget(), request.getScope()); 344 request.setUsages(findUsageResult.get()); 345 } 346 request.precheckUsages(); 347 for (RefactoringEngine engine : getEngines()) { 348 if (! isRunning()) return null; 349 engine.precheck(request); 350 } 351 } catch (InterruptedException e) { 352 request.addError(e.getLocalizedMessage()); 353 } catch (ExecutionException e) { 354 request.addError(e.getLocalizedMessage()); 355 } finally { 356 setStatus(Status.IDLE); 357 findUsageResult = null; 358 } 359 360 return request.getErrors(); 361 } 362 363 368 public synchronized void process(RefactorRequest request) throws IOException { 369 if (request.hasFatalErrors()) { 370 throw new IllegalStateException ( 371 "Cannot process a request with errors"); } 373 374 boolean isLocal = request.isScopeLocal(); 375 Set <Model> excludedFromSave = request.getDirtyModels(); 376 setStatus(Status.RUNNING); 377 try { 378 clearLastUndoSupport(); 379 lastRefactorRequest = request; 380 381 if (request.getChangeExecutor() == null) { 382 for (ChangeExecutor ce : getExecutors()) { 383 if (ce.canChange(request.getType(), request.getTarget())) { 384 request.setChangeExecutor(ce); 385 break; 386 } 387 } 388 } 389 390 if (! isRunning()) return; 391 UsageSet usageSet = request.getUsages(); 392 393 if (! isLocal) { 394 if (request.getChangeExecutor() instanceof GeneralChangeExecutor) { 395 addUndoableListener((GeneralChangeExecutor) request.getChangeExecutor()); 396 } else { 397 addUndoableRefactorListener(request.getTargetModel()); 398 } 399 request.getChangeExecutor().doChange(request); 400 if (! request.confirmChangePerformed()) { 401 return; 402 } 403 404 for (UsageGroup u : usageSet.getUsages()) { 405 if (u.getItems() != null && ! u.getItems().isEmpty()) { 406 addUndoableRefactorListener(u.getModel()); 407 } 408 } 409 410 for (RefactoringEngine engine : getEngines()) { 411 if (! isRunning()) return; 412 engine.refactorUsages(request); 413 } 414 415 if (request.getAutosave()) { 416 RefactoringUtil.save(request, excludedFromSave); 417 } 418 } else { Model model = request.getTargetModel(); 420 if (model != null) { 421 try { 422 model.startTransaction(); 423 request.getChangeExecutor().doChange(request); 424 425 if (! request.confirmChangePerformed()) { 426 return; 427 } 428 429 for (UsageGroup u : usageSet.getUsages()) { 430 if (! isRunning()) return; 431 if (u.getModel().equals(model)) { 432 u.getEngine().refactorUsages(request); 433 } 434 } 435 436 } finally { 437 if (model.isIntransaction()) { 438 model.endTransaction(); 439 } 440 } 441 } 442 } 443 444 usageSet.addPropertyChangeListener(this); 445 446 } catch (RuntimeException t) { 447 setStatus(Status.CANCELLING); 448 throw t; 449 } catch (IOException ioe) { 450 setStatus(Status.CANCELLING); 451 throw ioe; 452 } finally { 453 if (! isLocal) { 454 removeRefactorUndoEventListeners(); 455 removeUndoableListener(request.getChangeExecutor()); 456 } 457 if (! isRunning()) { _undo(excludedFromSave); 459 clearLastUndoSupport(); 460 } 461 setStatus(Status.IDLE); 462 } 463 } 464 465 471 public void execute(RefactorRequest request, boolean failsOnUsages) throws CannotRefactorException, IOException { 472 precheckChange(request); 473 if (request.hasFatalErrors()) { 474 throw new CannotRefactorException(request.getErrors().get(0).getMessage()); 475 } 476 precheckUsages(request); 477 if (failsOnUsages && ! request.getUsages().isEmpty(false)) { 478 throw new CannotRefactorException(NbBundle.getMessage(RefactoringUtil.class, "MSG_HasUsages")); 479 } 480 if (request.hasFatalErrors()) { 481 throw new CannotRefactorException(request.getErrors().get(0).getMessage()); 482 } 483 process(request); 484 } 485 486 public synchronized boolean cancel() { 487 if (! isRunning()) { 488 return false; 489 } 490 setStatus(Status.CANCELLING); 491 findUsageResult.cancel(); 492 return true; 493 } 494 495 public boolean isRunning() { 496 return getStatus() == Status.RUNNING; 497 } 498 499 public boolean isRunning(UsageSet usages) { 500 return isRunning() && 501 lastRefactorRequest != null && lastRefactorRequest.getUsages() == usages; 502 } 503 504 private synchronized Status getStatus() { 505 return status; 506 } 507 508 private synchronized void setStatus(Status s) { 509 status = s; 510 } 511 512 synchronized void clearLastUndoSupport() { 513 undoManagers = null; 514 genericChangeUndoManager = null; 515 if (lastRefactorRequest != null && lastRefactorRequest.getUsages() != null) { 516 lastRefactorRequest.getUsages().removePropertyChangeListener(this); 517 } 518 lastRefactorRequest = null; 519 } 520 521 public void propertyChange(PropertyChangeEvent evt) { 522 if (UsageSet.VALID_PROPERTY.equals(evt.getPropertyName()) && 523 Boolean.FALSE.equals(evt.getNewValue())) { 524 clearLastUndoSupport(); 525 } 526 } 527 528 } 529 | Popular Tags |