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.plugins;
042:
043: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
044: import com.sun.source.tree.CompilationUnitTree;
045: import com.sun.source.tree.Tree;
046: import com.sun.source.util.TreePath;
047: import java.io.IOException;
048: import java.util.*;
049: import java.util.HashSet;
050: import javax.lang.model.element.*;
051: import org.netbeans.api.java.source.*;
052: import org.netbeans.modules.refactoring.api.WhereUsedQuery;
053: import org.netbeans.modules.refactoring.java.WhereUsedElement;
054: import org.netbeans.modules.refactoring.api.Problem;
055: import org.netbeans.modules.refactoring.api.ProgressEvent;
056: import org.netbeans.modules.refactoring.api.WhereUsedQuery;
057: import org.netbeans.modules.refactoring.java.RetoucheUtils;
058: import org.netbeans.modules.refactoring.java.api.WhereUsedQueryConstants;
059: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
060: import org.openide.ErrorManager;
061: import org.openide.filesystems.FileObject;
062: import org.openide.loaders.DataObject;
063: import org.openide.text.CloneableEditorSupport;
064: import org.openide.util.NbBundle;
065:
066: /**
067: *
068: * @author Jan Becicka
069: */
070: public class JavaWhereUsedQueryPlugin extends JavaRefactoringPlugin {
071: private WhereUsedQuery refactoring;
072:
073: /** Creates a new instance of WhereUsedQuery */
074: public JavaWhereUsedQueryPlugin(WhereUsedQuery refactoring) {
075: this .refactoring = refactoring;
076: }
077:
078: protected JavaSource getJavaSource(Phase p) {
079: switch (p) {
080: default:
081: return JavaSource.forFileObject(refactoring
082: .getRefactoringSource()
083: .lookup(TreePathHandle.class).getFileObject());
084: }
085: }
086:
087: @Override
088: public Problem preCheck() {
089: TreePathHandle handle = refactoring.getRefactoringSource()
090: .lookup(TreePathHandle.class);
091: if (!handle.getFileObject().isValid()) {
092: return new Problem(true, NbBundle.getMessage(
093: FindVisitor.class, "DSC_ElNotAvail")); // NOI18N
094: }
095: if (handle.getKind() == Tree.Kind.ARRAY_TYPE) {
096: return new Problem(true, NbBundle.getMessage(
097: FindVisitor.class, "ERR_FindUsagesArrayType"));
098: }
099: return null;
100: }
101:
102: private Set<FileObject> getRelevantFiles(final TreePathHandle tph) {
103: final ClasspathInfo cpInfo = getClasspathInfo(refactoring);
104: final ClassIndex idx = cpInfo.getClassIndex();
105: final Set<FileObject> set = new HashSet<FileObject>();
106:
107: final FileObject file = tph.getFileObject();
108: JavaSource source;
109: if (file != null) {
110: set.add(file);
111: source = JavaSource.create(cpInfo, new FileObject[] { tph
112: .getFileObject() });
113: } else {
114: source = JavaSource.create(cpInfo);
115: }
116: //XXX: This is slow!
117: CancellableTask<CompilationController> task = new CancellableTask<CompilationController>() {
118: public void cancel() {
119: }
120:
121: public void run(CompilationController info)
122: throws Exception {
123: info.toPhase(JavaSource.Phase.RESOLVED);
124: final Element el = tph.resolveElement(info);
125: if (el.getKind().isField()) {
126: //get field references from index
127: set
128: .addAll(idx
129: .getResources(
130: ElementHandle
131: .create((TypeElement) el
132: .getEnclosingElement()),
133: EnumSet
134: .of(ClassIndex.SearchKind.FIELD_REFERENCES),
135: EnumSet
136: .of(ClassIndex.SearchScope.SOURCE)));
137: } else if (el.getKind().isClass()
138: || el.getKind().isInterface()) {
139: if (isFindSubclasses()
140: || isFindDirectSubclassesOnly()) {
141: if (isFindDirectSubclassesOnly()) {
142: //get direct implementors from index
143: EnumSet searchKind = EnumSet
144: .of(ClassIndex.SearchKind.IMPLEMENTORS);
145: set
146: .addAll(idx
147: .getResources(
148: ElementHandle
149: .create((TypeElement) el),
150: searchKind,
151: EnumSet
152: .of(ClassIndex.SearchScope.SOURCE)));
153: } else {
154: //itererate implementors recursively
155: set.addAll(getImplementorsRecursive(idx,
156: cpInfo, (TypeElement) el));
157: }
158: } else {
159: //get type references from index
160: set
161: .addAll(idx
162: .getResources(
163: ElementHandle
164: .create((TypeElement) el),
165: EnumSet
166: .of(
167: ClassIndex.SearchKind.TYPE_REFERENCES,
168: ClassIndex.SearchKind.IMPLEMENTORS),
169: EnumSet
170: .of(ClassIndex.SearchScope.SOURCE)));
171: }
172: } else if (el.getKind() == ElementKind.METHOD
173: && isFindOverridingMethods()) {
174: //Find overriding methods
175: TypeElement type = (TypeElement) el
176: .getEnclosingElement();
177: set.addAll(getImplementorsRecursive(idx, cpInfo,
178: type));
179: }
180: if (el.getKind() == ElementKind.METHOD
181: && isFindUsages()) {
182: //get method references for method and for all it's overriders
183: Set<ElementHandle<TypeElement>> s = RetoucheUtils
184: .getImplementorsAsHandles(idx, cpInfo,
185: (TypeElement) el
186: .getEnclosingElement());
187: for (ElementHandle<TypeElement> eh : s) {
188: TypeElement te = eh.resolve(info);
189: if (te == null) {
190: continue;
191: }
192: for (Element e : te.getEnclosedElements()) {
193: if (e instanceof ExecutableElement) {
194: if (info.getElements().overrides(
195: (ExecutableElement) e,
196: (ExecutableElement) el, te)) {
197: set
198: .addAll(idx
199: .getResources(
200: ElementHandle
201: .create(te),
202: EnumSet
203: .of(ClassIndex.SearchKind.METHOD_REFERENCES),
204: EnumSet
205: .of(ClassIndex.SearchScope.SOURCE)));
206: }
207: }
208: }
209: }
210: set
211: .addAll(idx
212: .getResources(
213: ElementHandle
214: .create((TypeElement) el
215: .getEnclosingElement()),
216: EnumSet
217: .of(ClassIndex.SearchKind.METHOD_REFERENCES),
218: EnumSet
219: .of(ClassIndex.SearchScope.SOURCE))); //?????
220: } else if (el.getKind() == ElementKind.CONSTRUCTOR) {
221: set
222: .addAll(idx
223: .getResources(
224: ElementHandle
225: .create((TypeElement) el
226: .getEnclosingElement()),
227: EnumSet
228: .of(
229: ClassIndex.SearchKind.TYPE_REFERENCES,
230: ClassIndex.SearchKind.IMPLEMENTORS),
231: EnumSet
232: .of(ClassIndex.SearchScope.SOURCE)));
233: }
234:
235: }
236: };
237: try {
238: source.runUserActionTask(task, true);
239: } catch (IOException ioe) {
240: throw (RuntimeException) new RuntimeException()
241: .initCause(ioe);
242: }
243: return set;
244: }
245:
246: private Set<FileObject> getImplementorsRecursive(ClassIndex idx,
247: ClasspathInfo cpInfo, TypeElement el) {
248: Set<FileObject> set = new HashSet<FileObject>();
249: for (ElementHandle<TypeElement> e : RetoucheUtils
250: .getImplementorsAsHandles(idx, cpInfo, el)) {
251: FileObject fo = SourceUtils.getFile(e, cpInfo);
252: assert fo != null : "issue 90196, Cannot find file for "
253: + e + ". cpInfo=" + cpInfo;
254: set.add(fo);
255: }
256: return set;
257: }
258:
259: //@Override
260: public Problem prepare(final RefactoringElementsBag elements) {
261: Set<FileObject> a = getRelevantFiles(refactoring
262: .getRefactoringSource().lookup(TreePathHandle.class));
263: fireProgressListenerStart(ProgressEvent.START, a.size());
264: processFiles(a, new FindTask(elements));
265: fireProgressListenerStop();
266: return null;
267: }
268:
269: @Override
270: public Problem fastCheckParameters() {
271: if (refactoring.getRefactoringSource().lookup(
272: TreePathHandle.class).getKind() == Tree.Kind.METHOD) {
273: return checkParametersForMethod(isFindOverridingMethods(),
274: isFindUsages());
275: }
276: return null;
277: }
278:
279: @Override
280: public Problem checkParameters() {
281: return null;
282: }
283:
284: private Problem checkParametersForMethod(boolean overriders,
285: boolean usages) {
286: if (!(usages || overriders)) {
287: return new Problem(true, NbBundle
288: .getMessage(JavaWhereUsedQueryPlugin.class,
289: "MSG_NothingToFind"));
290: } else
291: return null;
292: }
293:
294: public static CloneableEditorSupport findCloneableEditorSupport(
295: DataObject dob) {
296: Object obj = dob
297: .getCookie(org.openide.cookies.OpenCookie.class);
298: if (obj instanceof CloneableEditorSupport) {
299: return (CloneableEditorSupport) obj;
300: }
301: obj = dob.getCookie(org.openide.cookies.EditorCookie.class);
302: if (obj instanceof CloneableEditorSupport) {
303: return (CloneableEditorSupport) obj;
304: }
305: return null;
306: }
307:
308: private boolean isFindSubclasses() {
309: return refactoring
310: .getBooleanValue(WhereUsedQueryConstants.FIND_SUBCLASSES);
311: }
312:
313: private boolean isFindUsages() {
314: return refactoring
315: .getBooleanValue(WhereUsedQuery.FIND_REFERENCES);
316: }
317:
318: private boolean isFindDirectSubclassesOnly() {
319: return refactoring
320: .getBooleanValue(WhereUsedQueryConstants.FIND_DIRECT_SUBCLASSES);
321: }
322:
323: private boolean isFindOverridingMethods() {
324: return refactoring
325: .getBooleanValue(WhereUsedQueryConstants.FIND_OVERRIDING_METHODS);
326: }
327:
328: private boolean isSearchFromBaseClass() {
329: return false;
330: }
331:
332: private class FindTask implements CancellableTask<WorkingCopy> {
333:
334: private RefactoringElementsBag elements;
335: private volatile boolean cancelled;
336:
337: public FindTask(RefactoringElementsBag elements) {
338: super ();
339: this .elements = elements;
340: }
341:
342: public void cancel() {
343: cancelled = true;
344: }
345:
346: public void run(WorkingCopy compiler) throws IOException {
347: if (cancelled)
348: return;
349: if (compiler.toPhase(JavaSource.Phase.RESOLVED) != JavaSource.Phase.RESOLVED) {
350: return;
351: }
352: CompilationUnitTree cu = compiler.getCompilationUnit();
353: if (cu == null) {
354: ErrorManager.getDefault().log(
355: ErrorManager.ERROR,
356: "compiler.getCompilationUnit() is null "
357: + compiler); // NOI18N
358: return;
359: }
360: TreePathHandle handle = refactoring.getRefactoringSource()
361: .lookup(TreePathHandle.class);
362: Element element = handle.resolveElement(compiler);
363: if (element == null) {
364: ErrorManager.getDefault().log(ErrorManager.ERROR,
365: "element is null for handle " + handle); // NOI18N
366: return;
367: }
368:
369: Collection<TreePath> result = new ArrayList<TreePath>();
370: if (isFindUsages()) {
371: FindUsagesVisitor findVisitor = new FindUsagesVisitor(
372: compiler,
373: refactoring
374: .getBooleanValue(refactoring.SEARCH_IN_COMMENTS));
375: findVisitor
376: .scan(compiler.getCompilationUnit(), element);
377: result.addAll(findVisitor.getUsages());
378: for (FindUsagesVisitor.UsageInComment usageInComment : findVisitor
379: .getUsagesInComments()) {
380: elements.add(refactoring, WhereUsedElement.create(
381: usageInComment.from, usageInComment.to,
382: compiler));
383: }
384: }
385: if (element.getKind() == ElementKind.METHOD
386: && isFindOverridingMethods()) {
387: FindOverridingVisitor override = new FindOverridingVisitor(
388: compiler);
389: override.scan(compiler.getCompilationUnit(), element);
390: result.addAll(override.getUsages());
391: } else if ((element.getKind().isClass() || element
392: .getKind().isInterface())
393: && (isFindSubclasses() || isFindDirectSubclassesOnly())) {
394: FindSubtypesVisitor subtypes = new FindSubtypesVisitor(
395: !isFindDirectSubclassesOnly(), compiler);
396: subtypes.scan(compiler.getCompilationUnit(), element);
397: result.addAll(subtypes.getUsages());
398: }
399: for (TreePath tree : result) {
400: elements.add(refactoring, WhereUsedElement.create(
401: compiler, tree));
402: }
403: fireProgressListenerStep();
404: }
405: }
406:
407: }
|