KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > refactoring > RefactoringManager


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.xml.refactoring;
20
21 import java.beans.PropertyChangeEvent JavaDoc;
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Map JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.concurrent.ExecutionException JavaDoc;
31 import javax.swing.undo.CannotRedoException JavaDoc;
32 import javax.swing.undo.CannotUndoException JavaDoc;
33 import javax.swing.undo.UndoManager JavaDoc;
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 /**
48  * Singleton refactoring manager, ensuring atomic refactoring change across
49  * affected models. Provide undo/redo capability of each set of refactoring
50  * changes.
51  *
52  * @author Nam Nguyen
53  */

54 public class RefactoringManager implements Cancellable, PropertyChangeListener JavaDoc {
55     private static RefactoringManager manager = new RefactoringManager();
56     private List JavaDoc<RefactoringEngine> engines;
57     private List JavaDoc<ChangeExecutor> executors;
58     private RefactorRequest lastRefactorRequest;
59     private Map JavaDoc<Model,UndoManager JavaDoc> undoManagers;
60     private UndoManager JavaDoc genericChangeUndoManager;
61     private enum Status { IDLE, RUNNING, CANCELLING }
62     private Status status = Status.IDLE;
63     private FindUsageResult findUsageResult;
64     
65     /** Creates a new instance of RefactoringManager */
66     private RefactoringManager() {
67     }
68     
69     /**
70      * Returns Refactoring manager default instance.
71      */

72     public static RefactoringManager getInstance() {
73         return manager;
74     }
75     
76     /**
77      * Return all usages of the given target component within the specified search scope.
78      */

79     
80     /**
81      * Return all usages of the given target component this refactoring manager can find.
82      */

83     public FindUsageResult findUsages(Referenceable target) {
84         if (target == null) {
85             throw new IllegalArgumentException JavaDoc("Calling find usages with null target"); //NOI18N
86
}
87         return new FindUsageResult(target);
88     }
89     
90     /**
91      * Return local usages of the given target component.
92      */

93     public FindUsageResult findLocalUsages(Referenceable target) {
94         if (target == null || ! (target instanceof Component)) {
95             throw new IllegalArgumentException JavaDoc("Calling find local usages with non-component target"); //NOI18N
96
}
97         Component root = RefactorRequest.getRootOf(target);
98         return new FindUsageResult(target, Collections.singleton(root));
99     }
100     
101     /**
102      * Return all usages of the given target component this refactoring manager can find.
103      */

104     public FindUsageResult findUsages(Referenceable target, Set JavaDoc<Component> searchRoots) {
105         return new FindUsageResult(target, searchRoots);
106     }
107     
108     /**
109      * Return all usages of the given target component this refactoring manager can find.
110      */

111     public FindUsageResult findUsages(Referenceable target, Component searchRoot) {
112         return new FindUsageResult(target, searchRoot);
113     }
114     
115     public boolean canChange(Class JavaDoc<? 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 JavaDoc 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 JavaDoc {
144         if (! canUndo()) {
145             throw new CannotUndoException JavaDoc();
146         }
147         try {
148             Set JavaDoc<Model> excludedFromSave = lastRefactorRequest.getDirtyModels();
149             setStatus(Status.RUNNING);
150             for (UndoManager JavaDoc 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 JavaDoc<Model> excludedFromSave) {
168         if (undoManagers == null) return;
169         for (UndoManager JavaDoc 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 JavaDoc 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 JavaDoc {
198         if (! canRedo()) {
199             throw new CannotRedoException JavaDoc();
200         }
201         try {
202             Set JavaDoc<Model> excludedFromSave = lastRefactorRequest.getDirtyModels();
203             setStatus(Status.RUNNING);
204             for (UndoManager JavaDoc 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 JavaDoc<ChangeExecutor> getExecutors() {
222         if (executors == null) {
223             executors = new ArrayList JavaDoc<ChangeExecutor>();
224             Lookup.Result results = Lookup.getDefault().lookup(
225                     new Lookup.Template(ChangeExecutor.class));
226             for (Object JavaDoc service : results.allInstances()){
227                 executors.add((ChangeExecutor)service);
228             }
229             executors.add(new GeneralChangeExecutor());
230         }
231         return executors;
232     }
233     
234     protected List JavaDoc<RefactoringEngine> getEngines() {
235         if (engines == null) {
236             engines = new ArrayList JavaDoc<RefactoringEngine>();
237             Lookup.Result results = Lookup.getDefault().lookup(
238                     new Lookup.Template(RefactoringEngine.class));
239             for (Object JavaDoc 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 JavaDoc<Model,UndoManager JavaDoc>();
249         }
250         // checking against source to eliminate embedded model case
251
FileObject source = (FileObject) model.getModelSource().getLookup().lookup(FileObject.class);
252         if (source == null) {
253             throw new IllegalArgumentException JavaDoc("Could not get source file from provided model"); //NOI18N
254
}
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 JavaDoc um = new UndoManager JavaDoc();
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 JavaDoc();
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     /**
295      * Returns UI helper to display find usage or refactoring of the given
296      * target component.
297      */

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     /**
309      * Pre-checking if all the pre-conditions are met for the request to be processed.
310      * @param request the refactor request.
311      * @return list of errors prevent the request from completedly processed;
312      * return null if precheck is cancelled.
313      */

314     public synchronized List JavaDoc<ErrorItem> precheckChange(RefactorRequest request) {
315         if (status != Status.IDLE) {
316             throw new IllegalStateException JavaDoc("Invalid state "+status); //NOI18N
317
}
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 JavaDoc<ErrorItem> precheckUsages(RefactorRequest request) {
336         if (status != Status.IDLE) {
337             throw new IllegalStateException JavaDoc("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 JavaDoc e) {
352             request.addError(e.getLocalizedMessage());
353         } catch (ExecutionException JavaDoc e) {
354             request.addError(e.getLocalizedMessage());
355         } finally {
356             setStatus(Status.IDLE);
357             findUsageResult = null;
358         }
359
360         return request.getErrors();
361     }
362     
363     /**
364      * Process refactor request. The processing of the latest request
365      * could be cancelled by calling #cancel().
366      * @param request the refactor request.
367      */

368      public synchronized void process(RefactorRequest request) throws IOException JavaDoc {
369         if (request.hasFatalErrors()) {
370             throw new IllegalStateException JavaDoc(
371                     "Cannot process a request with errors"); //NOI18N
372
}
373         
374         boolean isLocal = request.isScopeLocal();
375         Set JavaDoc<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 { // isLocal
419
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 JavaDoc t) {
447             setStatus(Status.CANCELLING);
448             throw t;
449         } catch (IOException JavaDoc ioe) {
450             setStatus(Status.CANCELLING);
451             throw ioe;
452         } finally {
453             if (! isLocal) {
454                 removeRefactorUndoEventListeners();
455                 removeUndoableListener(request.getChangeExecutor());
456             }
457             if (! isRunning()) { // cancelled
458
_undo(excludedFromSave);
459                 clearLastUndoSupport();
460             }
461             setStatus(Status.IDLE);
462         }
463     }
464     
465     /**
466      * Execute the refactor request quietely if there are no errors.
467      *
468      * @params failsOnUsages if true the execution will fail when there are usages.
469      * @exceptions CannotRefactorException when there are fatal errors or usages and failsOnUsages is set to true.
470      */

471     public void execute(RefactorRequest request, boolean failsOnUsages) throws CannotRefactorException, IOException JavaDoc {
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 JavaDoc evt) {
522         if (UsageSet.VALID_PROPERTY.equals(evt.getPropertyName()) &&
523                 Boolean.FALSE.equals(evt.getNewValue())) {
524             clearLastUndoSupport();
525         }
526     }
527
528 }
529
Popular Tags