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-2007 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.java.spi;
042:
043: import java.util.Collections;
044: import org.netbeans.api.java.source.ModificationResult.Difference;
045: import com.sun.source.tree.CompilationUnitTree;
046: import java.io.IOException;
047: import java.util.Collection;
048: import java.util.HashMap;
049: import java.util.LinkedList;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.Set;
053: import java.util.logging.Logger;
054: import javax.lang.model.element.Element;
055: import javax.lang.model.type.TypeKind;
056: import org.netbeans.api.java.classpath.ClassPath;
057: import org.netbeans.api.java.source.CancellableTask;
058: import org.netbeans.api.java.source.ClasspathInfo;
059: import org.netbeans.api.java.source.CompilationController;
060: import org.netbeans.api.java.source.CompilationInfo;
061: import org.netbeans.api.java.source.JavaSource;
062: import org.netbeans.api.java.source.ModificationResult;
063: import org.netbeans.api.java.source.Task;
064: import org.netbeans.api.java.source.TreePathHandle;
065: import org.netbeans.api.java.source.WorkingCopy;
066: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
067: import org.netbeans.modules.refactoring.api.Problem;
068: import org.netbeans.modules.refactoring.java.RetoucheUtils;
069: import org.netbeans.modules.refactoring.java.plugins.FindVisitor;
070: import org.netbeans.modules.refactoring.java.plugins.RetoucheCommit;
071: import org.netbeans.modules.refactoring.spi.ProgressProviderAdapter;
072: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
073: import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
074: import org.openide.ErrorManager;
075: import org.openide.filesystems.FileObject;
076: import org.openide.util.NbBundle;
077:
078: /**
079: *
080: * @author Jan Becicka
081: */
082: public abstract class JavaRefactoringPlugin extends
083: ProgressProviderAdapter implements RefactoringPlugin {
084:
085: protected enum Phase {
086: PRECHECK, FASTCHECKPARAMETERS, CHECKPARAMETERS, PREPARE
087: };
088:
089: protected volatile boolean cancelRequest = false;
090: private volatile CancellableTask currentTask;
091: private WorkingTask workingTask = new WorkingTask();
092:
093: protected Problem preCheck(CompilationController javac)
094: throws IOException {
095: return null;
096: }
097:
098: protected Problem checkParameters(CompilationController javac)
099: throws IOException {
100: return null;
101: }
102:
103: protected Problem fastCheckParameters(CompilationController javac)
104: throws IOException {
105: return null;
106: }
107:
108: // protected abstract Problem prepare(WorkingCopy wc, RefactoringElementsBag bag) throws IOException;
109:
110: protected abstract JavaSource getJavaSource(Phase p);
111:
112: public Problem preCheck() {
113: return workingTask.run(Phase.PRECHECK);
114: }
115:
116: public Problem checkParameters() {
117: return workingTask.run(Phase.CHECKPARAMETERS);
118: }
119:
120: public Problem fastCheckParameters() {
121: return workingTask.run(Phase.FASTCHECKPARAMETERS);
122: }
123:
124: // public Problem prepare(final RefactoringElementsBag bag) {
125: // this.whatRun = Switch.PREPARE;
126: // this.problem = null;
127: // FileObject fo = getFileObject();
128: // JavaSource js = JavaSource.forFileObject(fo);
129: // try {
130: // js.runModificationTask(new CancellableTask<WorkingCopy>() {
131: // public void cancel() {
132: // }
133: //
134: // public void run(WorkingCopy wc) throws Exception {
135: // prepare(wc, bag);
136: // }
137: // }).commit();
138: // } catch (IOException ex) {
139: // throw new RuntimeException(ex);
140: // }
141: // return problem;
142: // }
143:
144: public void cancelRequest() {
145: cancelRequest = true;
146: if (currentTask != null) {
147: currentTask.cancel();
148: }
149: RetoucheUtils.cancel = true;
150: }
151:
152: protected ClasspathInfo getClasspathInfo(
153: AbstractRefactoring refactoring) {
154: ClasspathInfo cpInfo = refactoring.getContext().lookup(
155: ClasspathInfo.class);
156: if (cpInfo == null) {
157: Collection<? extends TreePathHandle> handles = refactoring
158: .getRefactoringSource().lookupAll(
159: TreePathHandle.class);
160: if (!handles.isEmpty()) {
161: cpInfo = RetoucheUtils.getClasspathInfoFor(handles
162: .toArray(new TreePathHandle[handles.size()]));
163: } else {
164: cpInfo = RetoucheUtils
165: .getClasspathInfoFor((FileObject) null);
166: }
167: refactoring.getContext().add(cpInfo);
168: }
169: return cpInfo;
170: }
171:
172: protected static final Problem createProblem(Problem result,
173: boolean isFatal, String message) {
174: Problem problem = new Problem(isFatal, message);
175: if (result == null) {
176: return problem;
177: } else if (isFatal) {
178: problem.setNext(result);
179: return problem;
180: } else {
181: //problem.setNext(result.getNext());
182: //result.setNext(problem);
183:
184: // [TODO] performance
185: Problem p = result;
186: while (p.getNext() != null)
187: p = p.getNext();
188: p.setNext(problem);
189: return result;
190: }
191: }
192:
193: /**
194: * Checks if the element is still available. Tests if it is still valid.
195: * (Was not deleted by matching mechanism.)
196: * If element is available, returns null, otherwise it creates problem.
197: * (Helper method for refactoring implementation as this problem is
198: * general for all refactorings.)
199: *
200: * @param e element to check
201: * @param info
202: * @return problem message or null if the element is valid
203: */
204: protected static Problem isElementAvail(TreePathHandle e,
205: CompilationInfo info) {
206: if (e == null) {
207: //element is null or is not valid.
208: return new Problem(true, NbBundle.getMessage(
209: FindVisitor.class, "DSC_ElNotAvail")); // NOI18N
210: } else {
211: Element el = e.resolveElement(info);
212: if (el == null || el.asType().getKind() == TypeKind.ERROR) {
213: return new Problem(true, NbBundle.getMessage(
214: FindVisitor.class, "DSC_ElementNotResolved"));
215: }
216:
217: if ("this".equals(el.getSimpleName().toString())
218: || "super".equals(el.getSimpleName().toString())) { // NOI18N
219: return new Problem(true, NbBundle.getMessage(
220: FindVisitor.class, "ERR_CannotRefactorThis", el
221: .getSimpleName()));
222: }
223:
224: // element is still available
225: return null;
226: }
227: }
228:
229: private Iterable<? extends List<FileObject>> groupByRoot(
230: Iterable<? extends FileObject> data) {
231: Map<FileObject, List<FileObject>> result = new HashMap<FileObject, List<FileObject>>();
232: for (FileObject file : data) {
233: if (cancelRequest) {
234: return Collections.emptyList();
235: }
236: ClassPath cp = ClassPath.getClassPath(file,
237: ClassPath.SOURCE);
238: if (cp != null) {
239: FileObject root = cp.findOwnerRoot(file);
240: if (root != null) {
241: List<FileObject> subr = result.get(root);
242: if (subr == null) {
243: subr = new LinkedList<FileObject>();
244: result.put(root, subr);
245: }
246: subr.add(file);
247: }
248: }
249: }
250: return result.values();
251: }
252:
253: protected final Collection<ModificationResult> processFiles(
254: Set<FileObject> files, CancellableTask<WorkingCopy> task) {
255: return processFiles(files, task, null);
256: }
257:
258: protected final Collection<ModificationResult> processFiles(
259: Set<FileObject> files, CancellableTask<WorkingCopy> task,
260: ClasspathInfo info) {
261: currentTask = task;
262: Collection<ModificationResult> results = new LinkedList<ModificationResult>();
263: try {
264: Iterable<? extends List<FileObject>> work = groupByRoot(files);
265: for (List<FileObject> fos : work) {
266: if (cancelRequest) {
267: return Collections.<ModificationResult> emptyList();
268: }
269: final JavaSource javaSource = JavaSource.create(
270: info == null ? ClasspathInfo.create(fos.get(0))
271: : info, fos);
272: try {
273: results.add(javaSource.runModificationTask(task));
274: } catch (IOException ex) {
275: throw (RuntimeException) new RuntimeException()
276: .initCause(ex);
277: }
278: }
279: } finally {
280: currentTask = null;
281: }
282: return results;
283: }
284:
285: protected final void createAndAddElements(Set<FileObject> files,
286: CancellableTask<WorkingCopy> task,
287: RefactoringElementsBag elements,
288: AbstractRefactoring refactoring, ClasspathInfo info) {
289: final Collection<ModificationResult> results = processFiles(
290: files, task, info);
291: elements.registerTransaction(new RetoucheCommit(results));
292: for (ModificationResult result : results) {
293: for (FileObject jfo : result.getModifiedFileObjects()) {
294: for (Difference dif : result.getDifferences(jfo)) {
295: elements.add(refactoring, DiffElement.create(dif,
296: jfo, result));
297: }
298: }
299: }
300: }
301:
302: protected final void createAndAddElements(Set<FileObject> files,
303: CancellableTask<WorkingCopy> task,
304: RefactoringElementsBag elements,
305: AbstractRefactoring refactoring) {
306: createAndAddElements(files, task, elements, refactoring, null);
307: }
308:
309: private class WorkingTask implements Task<CompilationController> {
310:
311: private Phase whatRun;
312: private Problem problem;
313:
314: private Problem run(Phase s) {
315: this .whatRun = s;
316: this .problem = null;
317: JavaSource js = getJavaSource(s);
318: if (js == null) {
319: return null;
320: }
321: try {
322: js.runUserActionTask(this , true);
323: } catch (IOException ex) {
324: throw new RuntimeException(ex);
325: }
326: return problem;
327: }
328:
329: public void run(CompilationController javac) throws Exception {
330: switch (whatRun) {
331: case PRECHECK:
332: this .problem = preCheck(javac);
333: break;
334: case CHECKPARAMETERS:
335: this .problem = checkParameters(javac);
336: break;
337: case FASTCHECKPARAMETERS:
338: this .problem = fastCheckParameters(javac);
339: break;
340: default:
341: throw new IllegalStateException();
342: }
343: }
344:
345: }
346:
347: protected class TransformTask implements
348: CancellableTask<WorkingCopy> {
349:
350: private RefactoringVisitor visitor;
351: private TreePathHandle treePathHandle;
352:
353: public TransformTask(RefactoringVisitor visitor,
354: TreePathHandle searchedItem) {
355: this .visitor = visitor;
356: this .treePathHandle = searchedItem;
357: }
358:
359: public void cancel() {
360: }
361:
362: public void run(WorkingCopy compiler) throws IOException {
363: try {
364: visitor.setWorkingCopy(compiler);
365: } catch (ToPhaseException e) {
366: return;
367: }
368: CompilationUnitTree cu = compiler.getCompilationUnit();
369: if (cu == null) {
370: ErrorManager.getDefault().log(
371: ErrorManager.ERROR,
372: "compiler.getCompilationUnit() is null "
373: + compiler); // NOI18N
374: return;
375: }
376: Element el = null;
377: if (treePathHandle != null) {
378: el = treePathHandle.resolveElement(compiler);
379: if (el == null)
380: Logger.getLogger(
381: "org.netbeans.modules.refactoring.java")
382: .info(
383: "Cannot resolve "
384: + treePathHandle
385: + "in "
386: + compiler.getFileObject()
387: .getPath());
388: }
389:
390: visitor.scan(compiler.getCompilationUnit(), el);
391:
392: fireProgressListenerStep();
393: }
394: }
395: }
|