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:
042: package org.netbeans.modules.refactoring.java.plugins;
043:
044: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
045: import com.sun.source.tree.CompilationUnitTree;
046: import com.sun.source.tree.Tree;
047: import com.sun.source.util.TreePath;
048: import java.io.IOException;
049: import java.util.*;
050: import javax.lang.model.element.ExecutableElement;
051: import javax.lang.model.element.TypeElement;
052: import javax.swing.Action;
053: import org.netbeans.api.java.source.*;
054: import org.netbeans.modules.refactoring.api.*;
055: import org.netbeans.modules.refactoring.spi.ui.UI;
056: import org.netbeans.modules.refactoring.java.api.WhereUsedQueryConstants;
057: import org.netbeans.modules.refactoring.spi.*;
058: import org.netbeans.modules.refactoring.spi.ui.RefactoringUI;
059: import org.netbeans.modules.refactoring.java.ui.WhereUsedQueryUI;
060: import org.netbeans.modules.refactoring.java.ui.tree.ElementGrip;
061: import org.openide.ErrorManager;
062: import org.openide.filesystems.FileObject;
063: import org.openide.util.*;
064: import org.openide.util.lookup.Lookups;
065:
066: /**
067: * The plugin that carries out Safe Delete refactoring.
068: * @author Bharath Ravikumar
069: * @author Jan Becicka
070: */
071: public class SafeDeleteRefactoringPlugin extends JavaRefactoringPlugin {
072: private SafeDeleteRefactoring refactoring;
073: private WhereUsedQuery[] whereUsedQueries;
074:
075: private static final String DOT = "."; //NOI18N
076:
077: /**
078: * Creates the a new instance of the Safe Delete refactoring
079: * plugin.
080: * @param refactoring The refactoring to be used by this plugin
081: */
082: public SafeDeleteRefactoringPlugin(SafeDeleteRefactoring refactoring) {
083: this .refactoring = refactoring;
084: }
085:
086: /**
087: * For each element to be refactored, the corresponding
088: * prepare method of the underlying WhereUsed query is
089: * invoked to check for usages. If none is present, a
090: * <CODE>SafeDeleteRefactoringElement</CODE> is created
091: * with the corresponding element.
092: * @param refactoringElements
093: * @return
094: */
095: public Problem prepare(RefactoringElementsBag refactoringElements) {
096: RefactoringSession inner = RefactoringSession.create("delete"); // NOI18N
097: Collection<ElementGrip> abstractMethHandles = new ArrayList<ElementGrip>();
098: Set<Object> refactoredObjects = new HashSet<Object>();
099: Collection<? extends FileObject> files = refactoring
100: .getRefactoringSource().lookupAll(FileObject.class);
101: fireProgressListenerStart(AbstractRefactoring.PARAMETERS_CHECK,
102: whereUsedQueries.length + 1);
103: for (int i = 0; i < whereUsedQueries.length; ++i) {
104: Object refactoredObject = whereUsedQueries[i]
105: .getRefactoringSource().lookup(Object.class);
106: refactoredObjects.add(refactoredObject);
107:
108: whereUsedQueries[i].prepare(inner);
109: TreePathHandle treePathHandle = grips.get(i);
110: if (Tree.Kind.METHOD == treePathHandle.getKind()) {
111: JavaSource javaSrc = JavaSource
112: .forFileObject(treePathHandle.getFileObject());
113: try {
114: javaSrc
115: .runUserActionTask(
116: new OverriddenAbsMethodFinder(
117: treePathHandle,
118: abstractMethHandles),
119: cancelRequest);
120:
121: } catch (IOException ioException) {
122: ErrorManager.getDefault().notify(ioException);
123: }
124: }
125:
126: if (!files.contains(treePathHandle.getFileObject())) {
127: TransformTask task = new TransformTask(
128: new DeleteTransformer(), grips.get(i));
129: createAndAddElements(Collections.singleton(grips.get(i)
130: .getFileObject()), task, refactoringElements,
131: refactoring);
132: }
133: fireProgressListenerStep();
134: }
135: Problem problemFromWhereUsed = null;
136: for (RefactoringElement refacElem : inner
137: .getRefactoringElements()) {
138: ElementGrip elem = refacElem.getLookup().lookup(
139: ElementGrip.class);
140: if (!isPendingDelete(elem, refactoredObjects)) {
141: problemFromWhereUsed = new Problem(
142: false,
143: getString("ERR_ReferencesFound"),
144: ProblemDetailsFactory
145: .createProblemDetails(new ProblemDetailsImplemen(
146: new WhereUsedQueryUI(
147: elem != null ? elem
148: .getHandle()
149: : null,
150: elem != null ? elem
151: .toString()
152: : "",
153: refactoring), inner)));
154: break;
155: }
156: }
157:
158: for (ElementGrip absMethodGrip : abstractMethHandles) {
159: if (!isPendingDelete(absMethodGrip, refactoredObjects)) {
160: Problem probAbsMethod = new Problem(
161: false,
162: getParameterizedString(
163: "ERR_OverridesAbstractMethod",
164: getMethodName(absMethodGrip.getHandle())));
165: if (problemFromWhereUsed != null) {
166: problemFromWhereUsed.setNext(probAbsMethod);
167: } else {
168: problemFromWhereUsed = probAbsMethod;
169: }
170: break;
171: }
172: }
173:
174: if (problemFromWhereUsed != null) {
175: fireProgressListenerStop();
176: return problemFromWhereUsed;
177: }
178:
179: Collection importStmts = new ArrayList();
180: //If there we no non-import usages, delete the import statements as well.
181: if (importStmts.size() > 0) {
182: for (Iterator it = importStmts.iterator(); it.hasNext();) {
183: RefactoringElementImplementation refacElem = (RefactoringElementImplementation) it
184: .next();
185: refactoringElements.add(refactoring, refacElem);
186: }
187: }
188:
189: fireProgressListenerStop();
190: return null;
191: }
192:
193: private class ProblemDetailsImplemen implements
194: ProblemDetailsImplementation {
195:
196: private RefactoringUI ui;
197: private RefactoringSession rs;
198:
199: public ProblemDetailsImplemen(RefactoringUI ui,
200: RefactoringSession rs) {
201: this .ui = ui;
202: this .rs = rs;
203: }
204:
205: public void showDetails(Action callback, Cancellable parent) {
206: parent.cancel();
207: UI.openRefactoringUI(ui, rs, callback);
208: }
209:
210: public String getDetailsHint() {
211: return getString("LBL_ShowUsages");
212: }
213:
214: }
215:
216: /**
217: * Checks whether the element being refactored is a valid Method/Field/Class
218: * @return Problem returns a generic problem message if the check fails
219: */
220: @Override
221: public Problem preCheck() {
222: // Element[] refElements = refactoring.getRefactoredObjects();
223: // for(int i = 0;i < refElements.length; ++i) {
224: // Element refactoredObject = refElements[i];
225: // boolean validType = refactoredObject instanceof ClassMember
226: // || refactoredObject instanceof LocalVariable
227: // || refactoredObject instanceof Resource;
228: // if(!validType) {
229: // String errMsg = NbBundle.getMessage(SafeDeleteRefactoringPlugin.class,
230: // "ERR_SafeDel_InvalidType"); // NOI18N
231: // return new Problem(true,errMsg);
232: // }
233: //
234: // if (!CheckUtils.isElementInOpenProject(refactoredObject)) {
235: // return new Problem(true, NbBundle.getMessage(SafeDeleteRefactoringPlugin.class, "ERR_ProjectNotOpened"));
236: // }
237: // }
238: return null;
239: }
240:
241: /**
242: * A No-op for this particular refactoring.
243: */
244: @Override
245: public Problem fastCheckParameters() {
246: //Nothing to be done for Safe Delete
247: return null;
248: }
249:
250: private ArrayList<TreePathHandle> grips = new ArrayList<TreePathHandle>();
251:
252: /**
253: * Invokes the checkParameters of each of the underlying
254: * WhereUsed refactorings and returns a Problem (if any)
255: * returned by any of these queries.
256: */
257: @Override
258: public Problem checkParameters() {
259: //This class expects too many details from SafeDeleteRefactoring
260: //But there's no other go I guess.
261: grips.clear();
262: for (final FileObject f : refactoring.getRefactoringSource()
263: .lookupAll(FileObject.class)) {
264: JavaSource source = JavaSource.forFileObject(f);
265: try {
266: source.runUserActionTask(
267: new CancellableTask<CompilationController>() {
268: public void cancel() {
269:
270: }
271:
272: public void run(CompilationController co)
273: throws Exception {
274: co
275: .toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
276: CompilationUnitTree cut = co
277: .getCompilationUnit();
278: for (Tree t : cut.getTypeDecls()) {
279: TreePathHandle handle = TreePathHandle
280: .create(TreePath.getPath(
281: cut, t), co);
282: if (!containsHandle(handle, co))
283: grips.add(handle);
284: }
285: }
286: }, true);
287: } catch (IllegalArgumentException ex) {
288: ex.printStackTrace();
289: } catch (IOException ex) {
290: ex.printStackTrace();
291: }
292: }
293:
294: grips.addAll(refactoring.getRefactoringSource().lookupAll(
295: TreePathHandle.class));
296:
297: whereUsedQueries = new WhereUsedQuery[grips.size()];
298: for (int i = 0; i < whereUsedQueries.length; ++i) {
299: whereUsedQueries[i] = createQuery(grips.get(i));
300:
301: whereUsedQueries[i].putValue(
302: WhereUsedQuery.SEARCH_IN_COMMENTS, refactoring
303: .isCheckInComments());
304: if (Tree.Kind.METHOD.equals(grips.get(i).getKind())) {
305: whereUsedQueries[i]
306: .putValue(
307: WhereUsedQueryConstants.FIND_OVERRIDING_METHODS,
308: true);
309: }
310: }
311:
312: Problem problemFromUsage = null;
313: for (int i = 0; i < whereUsedQueries.length; ++i) {
314: // Fix for issue 63050. Doesn't make sense to check usages of a Resource.Ignore it.
315: // if(whereUsedQueries[i].getRefactoredObject() instanceof Resource)
316: // continue;
317: if ((problemFromUsage = whereUsedQueries[i]
318: .checkParameters()) != null)
319: return problemFromUsage;
320: }
321: return null;
322: }
323:
324: protected JavaSource getJavaSource(Phase p) {
325: return null;
326: }
327:
328: private boolean containsHandle(TreePathHandle handle,
329: CompilationInfo info) {
330: for (TreePathHandle current : refactoring
331: .getRefactoringSource().lookupAll(TreePathHandle.class)) {
332: if (current.resolveElement(info).equals(
333: handle.resolveElement(info))) {
334: return true;
335: }
336: }
337: return false;
338: }
339:
340: private WhereUsedQuery createQuery(TreePathHandle tph) {
341: WhereUsedQuery q = new WhereUsedQuery(Lookups.singleton(tph));
342: for (Object o : refactoring.getContext()
343: .lookupAll(Object.class)) {
344: q.getContext().add(o);
345: }
346: q.getContext().add(refactoring);
347: q.getContext().add(this );
348: return q;
349: }
350:
351: //Private Helper methods
352: private static String getString(String key) {
353: return NbBundle.getMessage(SafeDeleteRefactoringPlugin.class,
354: key);
355: }
356:
357: private static boolean isPendingDelete(ElementGrip elementGrip,
358: Set<Object> refactoredObjects) {
359: ElementGrip parent = elementGrip;
360: if (parent != null) {
361: do {
362: if (refactoredObjects.contains(parent.getHandle())) {
363: return true;
364: }
365: parent = parent.getParent();
366: } while (parent != null);
367: }
368: return false;
369: }
370:
371: private static String getParameterizedString(String key,
372: Object parameter) {
373: return NbBundle.getMessage(SafeDeleteRefactoringPlugin.class,
374: key, parameter);
375: }
376:
377: private static String getMethodName(
378: final TreePathHandle methodHandle) {
379: JavaSource javaSrc = JavaSource.forFileObject(methodHandle
380: .getFileObject());
381: final String[] methodNameString = new String[1];
382: //Ugly hack to return the method name from the anonymous inner class
383: try {
384: javaSrc.runUserActionTask(
385: new CancellableTask<CompilationController>() {
386:
387: public void cancel() {
388: //No op
389: }
390:
391: public void run(
392: CompilationController compilationController)
393: throws Exception {
394: ExecutableElement execElem = (ExecutableElement) methodHandle
395: .resolveElement(compilationController);
396: TypeElement type = (TypeElement) execElem
397: .getEnclosingElement();
398: methodNameString[0] = type
399: .getQualifiedName()
400: + DOT + execElem.toString();
401: }
402:
403: }, true);
404:
405: } catch (IOException ioException) {
406: ErrorManager.getDefault().notify(ioException);
407: }
408:
409: return methodNameString[0];
410: }
411:
412: }
|