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.RefactoringVisitor;
045: import com.sun.source.tree.*;
046: import com.sun.source.util.TreePath;
047: import com.sun.source.util.Trees;
048: import java.io.IOException;
049: import java.util.*;
050: import javax.lang.model.element.*;
051: import javax.lang.model.type.TypeMirror;
052: import javax.lang.model.util.Elements;
053: import javax.lang.model.util.Types;
054: import org.netbeans.api.java.source.*;
055: import org.netbeans.api.java.source.ModificationResult.Difference;
056: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
057: import org.netbeans.modules.refactoring.api.Problem;
058: import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
059: import org.netbeans.modules.refactoring.java.spi.DiffElement;
060: import org.netbeans.modules.refactoring.java.api.UseSuperTypeRefactoring;
061: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
062: import org.netbeans.modules.refactoring.java.spi.ToPhaseException;
063: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
064: import org.openide.ErrorManager;
065: import org.openide.filesystems.FileObject;
066: import org.openide.util.Exceptions;
067: import org.openide.util.NbBundle;
068:
069: /*
070: * UseSuperTypeRefactoringPlugin.java
071: *
072: * Created on June 22, 2005
073: *
074: * @author Bharath Ravi Kumar
075: */
076: /**
077: * The plugin that performs the actual work on
078: * behalf of the use super type refactoring
079: */
080: public class UseSuperTypeRefactoringPlugin extends
081: JavaRefactoringPlugin {
082:
083: private final UseSuperTypeRefactoring refactoring;
084:
085: /**
086: * Creates a new instance of UseSuperTypeRefactoringPlugin
087: * @param refactoring The refactoring to be used by this plugin
088: */
089: public UseSuperTypeRefactoringPlugin(
090: UseSuperTypeRefactoring refactoring) {
091: this .refactoring = refactoring;
092: }
093:
094: /**
095: * Prepares the underlying where used query & checks
096: * for the visibility of the target type.
097: */
098: public Problem prepare(RefactoringElementsBag refactoringElements) {
099: TreePathHandle subClassHandle = refactoring.getTypeElement();
100: replaceSubtypeUsages(subClassHandle, refactoringElements);
101: return null;
102: }
103:
104: protected JavaSource getJavaSource(Phase p) {
105: switch (p) {
106: default:
107: return JavaSource.forFileObject(refactoring
108: .getTypeElement().getFileObject());
109: }
110: }
111:
112: /**
113: *Checks whether the candidate element is a valid Type.
114: *@return Problem The problem instance indicating that an invalid element was selected.
115: */
116: @Override
117: public Problem preCheck() {
118: // Element subType = refactoring.getTypeElement();
119: // if(!(subType instanceof JavaClass)){
120: // String errMsg = NbBundle.getMessage(UseSuperTypeRefactoringPlugin.class,
121: // "ERR_UseSuperType_InvalidElement"); // NOI18N
122: // return new Problem(true, errMsg);
123: // }
124: return null;
125: }
126:
127: /**
128: * @return A problem indicating that no super type was selected.
129: */
130: @Override
131: public Problem fastCheckParameters() {
132: if (refactoring.getTargetSuperType() == null) {
133: return new Problem(true, NbBundle.getMessage(
134: UseSuperTypeRefactoringPlugin.class,
135: "ERR_UseSuperTypeNoSuperType"));
136: }
137: return null;
138: }
139:
140: /**
141: * A no op. Returns null
142: */
143: @Override
144: public Problem checkParameters() {
145: return null;
146: }
147:
148: //---------private methods follow--------
149:
150: private void replaceSubtypeUsages(
151: final TreePathHandle subClassHandle,
152: final RefactoringElementsBag elemsBag) {
153: JavaSource javaSrc = JavaSource.forFileObject(subClassHandle
154: .getFileObject());
155:
156: try {
157: javaSrc.runUserActionTask(
158: new CancellableTask<CompilationController>() {
159:
160: public void cancel() {
161: }
162:
163: public void run(
164: CompilationController complController)
165: throws IOException {
166: complController
167: .toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
168:
169: FileObject fo = subClassHandle
170: .getFileObject();
171: ClasspathInfo classpathInfo = JavaRefactoringUtils
172: .getClasspathInfoFor(fo);
173:
174: ClassIndex clsIndx = classpathInfo
175: .getClassIndex();
176: TypeElement javaClassElement = (TypeElement) subClassHandle
177: .resolveElement(complController);
178: EnumSet<ClassIndex.SearchKind> typeRefSearch = EnumSet
179: .of(ClassIndex.SearchKind.TYPE_REFERENCES);
180: Set<FileObject> refFileObjSet = clsIndx
181: .getResources(
182: ElementHandle
183: .create(javaClassElement),
184: typeRefSearch,
185: EnumSet
186: .of(ClassIndex.SearchScope.SOURCE));
187:
188: if (!refFileObjSet.isEmpty()) {
189: fireProgressListenerStart(
190: AbstractRefactoring.PREPARE,
191: refFileObjSet.size());
192: try {
193: Collection<ModificationResult> results = processFiles(
194: refFileObjSet,
195: new FindRefTask(
196: subClassHandle,
197: refactoring
198: .getTargetSuperType()));
199: elemsBag
200: .registerTransaction(new RetoucheCommit(
201: results));
202: for (ModificationResult result : results) {
203: for (FileObject fileObj : result
204: .getModifiedFileObjects()) {
205: for (Difference diff : result
206: .getDifferences(fileObj)) {
207: String old = diff
208: .getOldText();
209: if (old != null) {
210: elemsBag
211: .add(
212: refactoring,
213: DiffElement
214: .create(
215: diff,
216: fileObj,
217: result));
218: }
219: }
220: }
221: }
222: } finally {
223: fireProgressListenerStop();
224: }
225: }
226: }
227: }, false);
228: } catch (IOException ioex) {
229: ioex.printStackTrace();
230: }
231: return;
232: }
233:
234: private final class FindRefTask implements
235: CancellableTask<WorkingCopy> {
236:
237: private final TreePathHandle subClassHandle;
238: private final ElementHandle super ClassHandle;
239:
240: private FindRefTask(TreePathHandle subClassHandle,
241: ElementHandle super ClassHandle) {
242: this .subClassHandle = subClassHandle;
243: this .super ClassHandle = super ClassHandle;
244: }
245:
246: public void cancel() {
247: }
248:
249: public void run(WorkingCopy compiler) throws Exception {
250: try {
251: if (compiler.toPhase(JavaSource.Phase.RESOLVED) != JavaSource.Phase.RESOLVED) {
252: return;
253: }
254: CompilationUnitTree cu = compiler.getCompilationUnit();
255: if (cu == null) {
256: ErrorManager.getDefault().log(
257: ErrorManager.ERROR,
258: "compiler.getCompilationUnit() is null "
259: + compiler); // NOI18N
260: return;
261: }
262: Element subClassElement = subClassHandle
263: .resolveElement(compiler);
264: Element super ClassElement = super ClassHandle
265: .resolve(compiler);
266: assert subClassElement != null;
267: ReferencesVisitor findRefVisitor = new ReferencesVisitor(
268: compiler, subClassElement, super ClassElement);
269: findRefVisitor.scan(compiler.getCompilationUnit(),
270: subClassElement);
271: } finally {
272: fireProgressListenerStep();
273: }
274: }
275:
276: }
277:
278: private static class ReferencesVisitor extends RefactoringVisitor {
279:
280: private final TypeElement super TypeElement;
281: private final TypeElement subTypeElement;
282:
283: private ReferencesVisitor(WorkingCopy workingCopy,
284: Element subClassElement, Element super ClassElement) {
285: try {
286: setWorkingCopy(workingCopy);
287: } catch (ToPhaseException phase) {
288: //should never be thrown;
289: Exceptions.printStackTrace(phase);
290: }
291: this .super TypeElement = (TypeElement) super ClassElement;
292: this .subTypeElement = (TypeElement) subClassElement;
293: }
294:
295: @Override
296: public Tree visitMemberSelect(MemberSelectTree memSelTree,
297: Element elemToFind) {
298: Element elem = asElement(memSelTree);
299:
300: if ((elem != null) && isStatic(elem)) {
301: Element expreElem = asElement(memSelTree
302: .getExpression());
303: //If a static member was referenced using the object instead
304: //of the class, don't handle it here.
305: if (!(ElementKind.CLASS.equals(expreElem.getKind()) || ElementKind.INTERFACE
306: .equals(expreElem.getKind()))) {
307: return super .visitMemberSelect(memSelTree,
308: elemToFind);
309: }
310: TypeElement type = (TypeElement) expreElem;
311: if (!subTypeElement.equals(type)) {
312: return super .visitMemberSelect(memSelTree,
313: elemToFind);
314: }
315: if (hidesSupTypeMember(elem, super TypeElement)) {
316: replaceType(memSelTree, super TypeElement);
317: }
318: }
319: return super .visitMemberSelect(memSelTree, elemToFind);
320: }
321:
322: @Override
323: public Tree visitVariable(VariableTree varTree,
324: Element elementToMatch) {
325: TreePath treePath = getCurrentPath();
326: VariableElement varElement = (VariableElement) workingCopy
327: .getTrees().getElement(treePath);
328:
329: //This check shouldn't be needed (ideally).
330: if (varElement == null) {
331: return super .visitVariable(varTree, elementToMatch);
332: }
333:
334: Types types = workingCopy.getTypes();
335: TypeMirror varTypeErasure = erasureOf(varElement.asType());
336: TypeMirror elToMatchErasure = erasureOf(elementToMatch
337: .asType());
338:
339: if (types.isSameType(varTypeErasure, elToMatchErasure)) {
340: if (isReplaceCandidate(varElement)) {
341: replaceWithSuperType(varTree, super TypeElement);
342: }
343: }
344: return super .visitVariable(varTree, elementToMatch);
345: }
346:
347: private boolean hidesSupTypeMember(Element methElement,
348: TypeElement super TypeElement) {
349: Elements elements = workingCopy.getElements();
350: List<? extends Element> containedElements = elements
351: .getAllMembers(super TypeElement);
352: for (Element elem : containedElements) {
353: boolean isPresentInSuperType = methElement.equals(elem)
354: || elements.hides(methElement, elem);
355: if ((elem != null) && isStatic(elem)
356: && isPresentInSuperType) {
357: return true;
358: }
359: }
360: return false;
361: }
362:
363: private boolean isReplaceCandidate(VariableElement varElement) {
364: VarUsageVisitor varUsagesVisitor = new VarUsageVisitor(
365: subTypeElement, workingCopy, super TypeElement);
366: varUsagesVisitor.scan(workingCopy.getCompilationUnit(),
367: varElement);
368: return varUsagesVisitor.isReplaceCandidate();
369: }
370:
371: private boolean isStatic(Element element) {
372: Set<Modifier> modifiers = element.getModifiers();
373: return modifiers.contains(Modifier.STATIC);
374: }
375:
376: /* private boolean isWildCardType(Tree typeTree) {
377: Tree.Kind treeKind = typeTree.getKind();
378: return (Tree.Kind.EXTENDS_WILDCARD == treeKind ||
379: Tree.Kind.SUPER_WILDCARD == treeKind) ;
380: }
381: */
382: private void replaceType(MemberSelectTree memSelTree,
383: Element super TypeElement) {
384: MemberSelectTree newTree = make.MemberSelect(make
385: .Identifier(super TypeElement), memSelTree
386: .getIdentifier());
387: rewrite(memSelTree, newTree);
388: }
389:
390: private void replaceWithSuperType(VariableTree oldVarTree,
391: Element super TypeElement) {
392: TypeMirror supTypeErasure = erasureOf(super TypeElement
393: .asType());
394: Tree super TypeTree = make.Type(supTypeErasure);
395:
396: //TODO:The following code was an initial attempt at having intelligent
397: //substitution of the correct parameter in the super type reference.
398: //If the supertype is generic, the subtype's parameter (on the RHS of
399: //and expression) is used in the super type's reference as well (on the
400: //LHS of an expression. But this gets complex when the subtype extends
401: //an "instance" of a generic super type. Consider for example,
402: //"SubType <T> extends Supertype<Number>" where Supertype is a generic type.
403: //Now, if we have an expression of the form:
404: //Subtype<Serializable> obj = new Subtype<Serializable>();
405: //We cannot replace the LHS with Supertype<Serializable>
406: //More work is needed for that. Putting it off till later and using
407: //only the erasure of the super type for now. :(
408: //The commented section of code will be revived then.
409:
410: /* Tree typeTree = oldVarTree.getType();
411: if(Tree.Kind.PARAMETERIZED_TYPE == typeTree.getKind()){
412: ParameterizedTypeTree paramTypeTree = (ParameterizedTypeTree) typeTree;
413: List<? extends Tree> typeArgTreeList = paramTypeTree.getTypeArguments();
414: List<ExpressionTree> typeParamTrees = new ArrayList<ExpressionTree>(typeArgTreeList.size());
415: for (Tree tree : typeArgTreeList) {
416: if(isWildCardType(tree)){
417: //Clear the list of type params to be included in the
418: //super type's reference. We'll restrict the super type's
419: //occurence to its erasure in that case.
420: typeParamTrees.clear();
421: break;
422: }
423: typeParamTrees.add((ExpressionTree) tree);
424: }
425: if(! typeParamTrees.isEmpty()){
426: superTypeTree = make.ParameterizedType(
427: make.Identifier(superTypeElement.getSimpleName()),
428: typeParamTrees);
429: }
430: }
431: */
432: ExpressionTree oldInitTree = oldVarTree.getInitializer();
433: ModifiersTree oldModifiers = oldVarTree.getModifiers();
434: Tree newTree = make.Variable(oldModifiers, oldVarTree
435: .getName(), super TypeTree, oldInitTree);
436: rewrite(oldVarTree, newTree);
437: }
438:
439: private Element asElement(Tree tree) {
440: Trees treeUtil = workingCopy.getTrees();
441: TreePath treePath = treeUtil.getPath(workingCopy
442: .getCompilationUnit(), tree);
443: Element element = treeUtil.getElement(treePath);
444: return element;
445: }
446:
447: private TypeMirror erasureOf(TypeMirror type) {
448: Types types = workingCopy.getTypes();
449: return types.erasure(type);
450: }
451:
452: }
453: }
|