001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.refactoring.api;
042:
043: import java.util.AbstractCollection;
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Iterator;
047: import java.util.LinkedList;
048: import java.util.ListIterator;
049: import org.netbeans.modules.refactoring.api.impl.ProgressSupport;
050: import org.netbeans.modules.refactoring.api.impl.SPIAccessor;
051: import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
052: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
053: import org.netbeans.modules.refactoring.spi.Transaction;
054: import org.netbeans.modules.refactoring.spi.impl.UndoManager;
055: import org.openide.LifecycleManager;
056:
057: /** Class used to invoke refactorings.
058: *
059: * @author Martin Matula, Daniel Prusa, Jan Becicka
060: */
061: public final class RefactoringSession {
062: private final LinkedList<RefactoringElementImplementation> internalList;
063: private final RefactoringElementsBag bag;
064: private final Collection<RefactoringElement> refactoringElements;
065: private final String description;
066: private ProgressSupport progressSupport;
067: private UndoManager undoManager = UndoManager.getDefault();
068: boolean realcommit = true;
069:
070: private RefactoringSession(String description) {
071: internalList = new LinkedList();
072: bag = SPIAccessor.DEFAULT.createBag(this , internalList);
073: this .description = description;
074: this .refactoringElements = new ElementsCollection();
075: }
076:
077: /**
078: * Creates a new refactoring session.
079: * @param description textual description of this session
080: * @return instance of RefactoringSession
081: */
082: public static RefactoringSession create(String description) {
083: return new RefactoringSession(description);
084: }
085:
086: /**
087: * process all elements from elements bags,
088: * do all fileChanges
089: * and call all commits
090: * @param saveAfterDone save all if true
091: * @return instance of Problem or null, if everything is OK
092: */
093: public Problem doRefactoring(boolean saveAfterDone) {
094: Iterator it = internalList.iterator();
095: fireProgressListenerStart(0, internalList.size() + 1);
096: if (realcommit) {
097: undoManager.transactionStarted();
098: undoManager.setUndoDescription(description);
099: }
100: try {
101: try {
102: while (it.hasNext()) {
103: fireProgressListenerStep();
104: RefactoringElementImplementation element = (RefactoringElementImplementation) it
105: .next();
106: if (element.isEnabled()
107: && !((element.getStatus() == RefactoringElement.GUARDED) || (element
108: .getStatus() == RefactoringElement.READ_ONLY))) {
109: element.performChange();
110: }
111: }
112: } finally {
113: for (Transaction commit : SPIAccessor.DEFAULT
114: .getCommits(bag)) {
115: commit.commit();
116: }
117: }
118: if (saveAfterDone) {
119: LifecycleManager.getDefault().saveAll();
120: }
121: for (RefactoringElementImplementation fileChange : SPIAccessor.DEFAULT
122: .getFileChanges(bag)) {
123: if (fileChange.isEnabled()) {
124: fileChange.performChange();
125: }
126: }
127: fireProgressListenerStep();
128: } finally {
129: fireProgressListenerStop();
130: if (realcommit) {
131: undoManager.addItem(this );
132: undoManager.transactionEnded(false);
133: realcommit = false;
134: }
135: }
136: return null;
137: }
138:
139: /**
140: * do undo of previous doRefactoring()
141: * @param saveAfterDone save all if true
142: * @return instance of Problem or null, if everything is OK
143: */
144: public Problem undoRefactoring(boolean saveAfterDone) {
145: try {
146: ListIterator it = internalList.listIterator(internalList
147: .size());
148: fireProgressListenerStart(0, internalList.size() + 1);
149: ArrayList<RefactoringElementImplementation> fileChanges = SPIAccessor.DEFAULT
150: .getFileChanges(bag);
151: ArrayList<Transaction> commits = SPIAccessor.DEFAULT
152: .getCommits(bag);
153: for (ListIterator<RefactoringElementImplementation> fileChangeIterator = fileChanges
154: .listIterator(fileChanges.size()); fileChangeIterator
155: .hasPrevious();) {
156: RefactoringElementImplementation f = fileChangeIterator
157: .previous();
158: if (f.isEnabled()) {
159: f.undoChange();
160: }
161: }
162: for (ListIterator<Transaction> commitIterator = commits
163: .listIterator(commits.size()); commitIterator
164: .hasPrevious();) {
165: commitIterator.previous().rollback();
166: }
167:
168: while (it.hasPrevious()) {
169: fireProgressListenerStep();
170: RefactoringElementImplementation element = (RefactoringElementImplementation) it
171: .previous();
172: if (element.isEnabled()
173: && !((element.getStatus() == RefactoringElement.GUARDED) || (element
174: .getStatus() == RefactoringElement.READ_ONLY))) {
175: element.undoChange();
176: }
177: }
178: if (saveAfterDone) {
179: LifecycleManager.getDefault().saveAll();
180: }
181: fireProgressListenerStep();
182: } finally {
183: fireProgressListenerStop();
184: }
185: return null;
186: }
187:
188: /**
189: * get elements from session
190: * @return collection of RefactoringElements
191: */
192: public Collection<RefactoringElement> getRefactoringElements() {
193: return refactoringElements;
194: }
195:
196: /**
197: * Adds progress listener to this RefactoringSession
198: * @param listener to add
199: */
200: public synchronized void addProgressListener(
201: ProgressListener listener) {
202: if (progressSupport == null) {
203: progressSupport = new ProgressSupport();
204: }
205: progressSupport.addProgressListener(listener);
206: }
207:
208: /**
209: * Remove progress listener from this RefactoringSession
210: * @param listener to remove
211: */
212: public synchronized void removeProgressListener(
213: ProgressListener listener) {
214: if (progressSupport != null) {
215: progressSupport.removeProgressListener(listener);
216: }
217: }
218:
219: RefactoringElementsBag getElementsBag() {
220: return bag;
221: }
222:
223: private void fireProgressListenerStart(int type, int count) {
224: if (progressSupport != null) {
225: progressSupport
226: .fireProgressListenerStart(this , type, count);
227: }
228: }
229:
230: private void fireProgressListenerStep() {
231: if (progressSupport != null) {
232: progressSupport.fireProgressListenerStep(this );
233: }
234: }
235:
236: private void fireProgressListenerStop() {
237: if (progressSupport != null) {
238: progressSupport.fireProgressListenerStop(this );
239: }
240: }
241:
242: private class ElementsCollection extends
243: AbstractCollection<RefactoringElement> {
244: public Iterator<RefactoringElement> iterator() {
245: return new Iterator() {
246: private final Iterator<RefactoringElementImplementation> inner = internalList
247: .iterator();
248: private final Iterator<RefactoringElementImplementation> inner2 = SPIAccessor.DEFAULT
249: .getFileChanges(bag).iterator();
250:
251: public void remove() {
252: throw new UnsupportedOperationException();
253: }
254:
255: public RefactoringElement next() {
256: if (inner.hasNext()) {
257: return new RefactoringElement(inner.next());
258: } else {
259: return new RefactoringElement(inner2.next());
260: }
261: }
262:
263: public boolean hasNext() {
264: return (inner.hasNext() || inner2.hasNext());
265: }
266: };
267: }
268:
269: public int size() {
270: return internalList.size()
271: + SPIAccessor.DEFAULT.getFileChanges(bag).size();
272: }
273: }
274: }
|