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:
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 java.util.ArrayList;
048: import java.util.Collections;
049: import java.util.List;
050: import javax.lang.model.element.*;
051: import javax.lang.model.element.Modifier;
052: import org.netbeans.api.java.classpath.ClassPath;
053: import org.netbeans.api.java.source.GeneratorUtilities;
054: import org.netbeans.api.java.source.SourceUtils;
055: import org.netbeans.api.java.source.WorkingCopy;
056: import org.netbeans.modules.refactoring.api.Problem;
057: import org.netbeans.modules.refactoring.java.RetoucheUtils;
058: import org.netbeans.modules.refactoring.java.api.InnerToOuterRefactoring;
059: import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
060: import org.netbeans.modules.refactoring.java.spi.ToPhaseException;
061: import org.openide.filesystems.FileObject;
062: import org.openide.util.NbBundle;
063:
064: /**
065: *
066: * @author Jan Becicka
067: */
068: public class InnerToOuterTransformer extends RefactoringVisitor {
069:
070: private Element inner;
071: private Element outer;
072: private InnerToOuterRefactoring refactoring;
073:
074: private Element getCurrentElement() {
075: return workingCopy.getTrees().getElement(getCurrentPath());
076: }
077:
078: public InnerToOuterTransformer(InnerToOuterRefactoring re) {
079: this .refactoring = re;
080: }
081:
082: @Override
083: public void setWorkingCopy(WorkingCopy wc) throws ToPhaseException {
084: super .setWorkingCopy(wc);
085: this .inner = refactoring.getSourceType().resolveElement(wc);
086: outer = SourceUtils.getEnclosingTypeElement(inner);
087: }
088:
089: @Override
090: public Tree visitIdentifier(IdentifierTree node, Element p) {
091: if (inner.equals(getCurrentElement())) {
092: Tree newTree = make.setLabel(node, refactoring
093: .getClassName());
094: rewrite(node, newTree);
095: } else if (isThisReferenceToOuter()) {
096: IdentifierTree m;
097: if (refactoring.getReferenceName() == null) {
098: m = make.Identifier(outer.getSimpleName().toString()
099: + "." + node.getName().toString()); // NOI18N
100: } else {
101: m = make.Identifier(refactoring.getReferenceName()
102: + "." + node.getName().toString()); // NOI18N
103: }
104:
105: rewrite(node, m);
106: }
107: return super .visitIdentifier(node, p);
108: }
109:
110: @Override
111: public Tree visitNewClass(NewClassTree arg0, Element arg1) {
112: Element currentElement = workingCopy.getTrees().getElement(
113: getCurrentPath());
114: if (refactoring.getReferenceName() != null
115: && currentElement != null
116: && workingCopy.getTypes().isSubtype(
117: getCurrentElement().getEnclosingElement()
118: .asType(), inner.asType())) {
119: String this String;
120: if (getCurrentClass() == inner) {
121: this String = refactoring.getReferenceName();
122: } else if (workingCopy.getTypes().isSubtype(
123: getCurrentClass().asType(), outer.asType())) {
124: this String = "this"; // NOI18N
125: } else {
126: TypeElement this Outer = getOuter(getCurrentClass());
127: if (this Outer != null)
128: this String = getOuter(getCurrentClass())
129: .getQualifiedName().toString()
130: + ".this"; // NOI18N
131: else
132: this String = "this"; // NOI18N
133:
134: }
135: if (this String != null) {
136: ExecutableElement constr = (ExecutableElement) currentElement;
137: if (constr.isVarArgs()) {
138: int index = constr.getParameters().size() - 1;
139: rewrite(arg0, make.insertNewClassArgument(arg0,
140: index, make.Identifier(this String)));
141: } else {
142: rewrite(arg0, make.addNewClassArgument(arg0, make
143: .Identifier(this String)));
144: }
145: }
146: }
147: return super .visitNewClass(arg0, arg1);
148: }
149:
150: private TypeElement getOuter(TypeElement element) {
151: while (element != null
152: && !workingCopy.getTypes().isSubtype(element.asType(),
153: outer.asType())) {
154: element = SourceUtils.getEnclosingTypeElement(element);
155: }
156: return element;
157: }
158:
159: @Override
160: public Tree visitMethod(MethodTree constructor, Element element) {
161: if (constructor.getReturnType() == null) {
162: //constructor
163: if (refactoring.getReferenceName() != null
164: && !inner.equals(getCurrentClass())
165: && workingCopy.getTypes().isSubtype(
166: getCurrentElement().getEnclosingElement()
167: .asType(), inner.asType())) {
168: MemberSelectTree arg = make
169: .MemberSelect(
170: make.Identifier(getCurrentClass()
171: .getEnclosingElement()
172: .getSimpleName()), "this"); // NOI18N
173: MethodInvocationTree super Call = (MethodInvocationTree) ((ExpressionStatementTree) constructor
174: .getBody().getStatements().get(0))
175: .getExpression();
176: int index = hasVarArgs(constructor) ? constructor
177: .getParameters().size() - 1 : 0;
178: MethodInvocationTree newSuperCall = make
179: .insertMethodInvocationArgument(super Call,
180: index, arg);
181: rewrite(super Call, newSuperCall);
182: }
183:
184: }
185: return super .visitMethod(constructor, element);
186: }
187:
188: @Override
189: public Tree visitClass(ClassTree classTree, Element element) {
190: Element currentElement = workingCopy.getTrees().getElement(
191: getCurrentPath());
192: GeneratorUtilities genUtils = GeneratorUtilities
193: .get(workingCopy); // helper
194: if (currentElement != null && currentElement == outer) {
195: Element outerouter = outer.getEnclosingElement();
196:
197: TreePath tp = workingCopy.getTrees().getPath(inner);
198: ClassTree innerClass = (ClassTree) tp.getLeaf();
199:
200: ClassTree newInnerClass = innerClass;
201: newInnerClass = genUtils.importComments(newInnerClass,
202: workingCopy.getCompilationUnit());
203: newInnerClass = genUtils.importFQNs(newInnerClass);
204:
205: newInnerClass = make.setLabel(newInnerClass, refactoring
206: .getClassName());
207:
208: newInnerClass = refactorInnerClass(newInnerClass);
209: RetoucheUtils
210: .copyJavadoc(inner, newInnerClass, workingCopy);
211:
212: if (outerouter.getKind() == ElementKind.PACKAGE) {
213: FileObject sourceRoot = ClassPath.getClassPath(
214: workingCopy.getFileObject(), ClassPath.SOURCE)
215: .findOwnerRoot(workingCopy.getFileObject());
216: ClassTree outerTree = (ClassTree) workingCopy
217: .getTrees().getTree(outer);
218: ClassTree newOuter = make.removeClassMember(outerTree,
219: innerClass);
220: workingCopy.rewrite(outerTree, newOuter);
221: JavaRefactoringUtils.cacheTreePathInfo(workingCopy
222: .getTrees().getPath(outer), workingCopy);
223: CompilationUnitTree compilationUnit = tp
224: .getCompilationUnit();
225: String relativePath = RetoucheUtils.getPackageName(
226: compilationUnit).replace('.', '/')
227: + '/' + refactoring.getClassName() + ".java"; // NOI18N
228: CompilationUnitTree newCompilation = make
229: .CompilationUnit(sourceRoot, relativePath,
230: null, Collections
231: .singletonList(newInnerClass));
232: workingCopy.rewrite(null, newCompilation);
233: } else {
234: ClassTree outerTree = (ClassTree) workingCopy
235: .getTrees().getTree(outer);
236: ClassTree outerouterTree = (ClassTree) workingCopy
237: .getTrees().getTree(outerouter);
238: ClassTree newOuter = make.removeClassMember(outerTree,
239: innerClass);
240: ClassTree newOuterOuter = GeneratorUtilities.get(
241: workingCopy).insertClassMember(outerouterTree,
242: newInnerClass);
243: workingCopy.rewrite(outerTree, newOuter);
244: JavaRefactoringUtils.cacheTreePathInfo(workingCopy
245: .getTrees().getPath(outer), workingCopy);
246: workingCopy.rewrite(outerouterTree, newOuterOuter);
247: }
248:
249: for (Element super Type : RetoucheUtils.getSuperTypes(
250: (TypeElement) inner, workingCopy, true)) {
251: ClassTree tree = (ClassTree) workingCopy.getTrees()
252: .getTree(super Type);
253: }
254: } else if (refactoring.getReferenceName() != null
255: && currentElement != null
256: && workingCopy.getTypes().isSubtype(
257: currentElement.asType(), inner.asType())
258: && currentElement != inner) {
259: VariableTree variable = make.Variable(make
260: .Modifiers(Collections.<Modifier> emptySet()),
261: refactoring.getReferenceName(), make.Type(outer
262: .asType()), null);
263: for (Tree member : classTree.getMembers()) {
264: if (member.getKind() == Tree.Kind.METHOD) {
265: MethodTree m = (MethodTree) member;
266: if (m.getReturnType() == null) {
267: MethodInvocationTree super Call = (MethodInvocationTree) ((ExpressionStatementTree) m
268: .getBody().getStatements().get(0))
269: .getExpression();
270: List<ExpressionTree> newArgs = new ArrayList(
271: super Call.getArguments());
272:
273: MethodTree newConstructor = null;
274: ExpressionTree exprTree = (ExpressionTree) make
275: .Identifier(variable.getName()
276: .toString());
277: if (hasVarArgs(m)) {
278: int index = m.getParameters().size() - 1;
279: newArgs.add(index, exprTree);
280: newConstructor = make
281: .insertMethodParameter(m, index,
282: variable);
283: } else {
284: newArgs.add(exprTree);
285: newConstructor = make.addMethodParameter(m,
286: variable);
287: }
288: MethodInvocationTree method = make
289: .MethodInvocation(Collections
290: .<ExpressionTree> emptyList(),
291: make.Identifier("super"), // NOI18N
292: newArgs);
293:
294: BlockTree block = make.insertBlockStatement(m
295: .getBody(), 0, make
296: .ExpressionStatement(method));
297: block = make.removeBlockStatement(block, 1);
298:
299: newConstructor = make.Constructor(make
300: .Modifiers(newConstructor
301: .getModifiers().getFlags(),
302: newConstructor.getModifiers()
303: .getAnnotations()),
304: newConstructor.getTypeParameters(),
305: newConstructor.getParameters(),
306: newConstructor.getThrows(), block);
307:
308: rewrite(m, newConstructor);
309: }
310: }
311: }
312: }
313: return super .visitClass(classTree, element);
314: }
315:
316: private Problem problem;
317:
318: public Problem getProblem() {
319: return problem;
320: }
321:
322: @Override
323: public Tree visitMemberSelect(MemberSelectTree memberSelect,
324: Element element) {
325: Element current = getCurrentElement();
326: if (inner.equals(current)) {
327: ExpressionTree ex = memberSelect.getExpression();
328: Tree newTree;
329: if (ex.getKind() == Tree.Kind.IDENTIFIER) {
330: newTree = make.Identifier(refactoring.getClassName());
331: rewrite(memberSelect, newTree);
332: } else if (ex.getKind() == Tree.Kind.MEMBER_SELECT) {
333: MemberSelectTree m = make.MemberSelect(
334: ((MemberSelectTree) ex).getExpression(),
335: refactoring.getClassName());
336: rewrite(memberSelect, m);
337: }
338: } else if (isThisReferenceToOuter()
339: && !"class".equals(memberSelect.getIdentifier()
340: .toString())) { //NOI18N
341: if (refactoring.getReferenceName() != null) {
342: MemberSelectTree m = make.MemberSelect(make
343: .Identifier(refactoring.getReferenceName()),
344: memberSelect.getIdentifier());
345: rewrite(memberSelect, m);
346: } else {
347: problem = MoveTransformer.createProblem(problem, true,
348: NbBundle.getMessage(PushDownTransformer.class,
349: "ERR_InnerToOuter_UseDeclareField",
350: memberSelect));
351: }
352: }
353:
354: return super .visitMemberSelect(memberSelect, element);
355: }
356:
357: private boolean isThisReferenceToOuter() {
358: Element cur = getCurrentElement();
359: if (cur == null || cur.getKind() == ElementKind.PACKAGE)
360: return false;
361: TypeElement encl = SourceUtils.getEnclosingTypeElement(cur);
362: if (outer.equals(encl)
363: && workingCopy.getTypes().isSubtype(
364: getCurrentClass().asType(), inner.asType())) {
365: return true;
366: }
367: return false;
368: }
369:
370: private TypeElement getCurrentClass() {
371: TreePath tp = getCurrentPath().getParentPath();
372: while (tp != null) {
373: if (tp.getLeaf().getKind() == Tree.Kind.CLASS) {
374: return (TypeElement) workingCopy.getTrees().getElement(
375: tp);
376: }
377: tp = tp.getParentPath();
378: }
379: throw new IllegalStateException();
380: }
381:
382: private boolean isIn(Element el) {
383: if (el == null)
384: return false;
385: Element current = el;
386: while (current.getKind() != ElementKind.PACKAGE) {
387: if (current.equals(inner)) {
388: return true;
389: }
390: current = current.getEnclosingElement();
391: }
392: return false;
393: }
394:
395: private boolean hasVarArgs(MethodTree mt) {
396: List list = mt.getParameters();
397: if (list.isEmpty()) {
398: return false;
399: }
400: VariableTree vt = (VariableTree) list.get(list.size() - 1);
401: return vt.toString().indexOf("...") != -1; // [NOI18N] [TODO] temporal hack, will be rewritten
402: }
403:
404: private ClassTree refactorInnerClass(ClassTree newInnerClass) {
405: String referenceName = refactoring.getReferenceName();
406: VariableTree variable = null;
407: if (referenceName != null) {
408: variable = make.Variable(make.Modifiers(Collections
409: .<Modifier> emptySet()), refactoring
410: .getReferenceName(), make.Type(outer.asType()),
411: null);
412: newInnerClass = GeneratorUtilities.get(workingCopy)
413: .insertClassMember(newInnerClass, variable);
414: }
415:
416: ModifiersTree modifiersTree = newInnerClass.getModifiers();
417: ModifiersTree newModifiersTree = make.removeModifiersModifier(
418: modifiersTree, Modifier.PRIVATE);
419: newModifiersTree = make.removeModifiersModifier(
420: newModifiersTree, Modifier.STATIC);
421: newModifiersTree = make.removeModifiersModifier(
422: newModifiersTree, Modifier.PROTECTED);
423: rewrite(modifiersTree, newModifiersTree);
424:
425: if (referenceName != null) {
426: for (Tree member : newInnerClass.getMembers()) {
427: if (member.getKind() == Tree.Kind.METHOD) {
428: MethodTree m = (MethodTree) member;
429: if (m.getReturnType() == null) {
430: MethodTree newConstructor = hasVarArgs(m) ? make
431: .insertMethodParameter(m, m
432: .getParameters().size() - 1,
433: variable)
434: : make.addMethodParameter(m, variable);
435:
436: AssignmentTree assign = make.Assignment(make
437: .Identifier("this." + referenceName),
438: make.Identifier(referenceName)); // NOI18N
439: BlockTree block = make.insertBlockStatement(
440: newConstructor.getBody(), 1, make
441: .ExpressionStatement(assign));
442: newConstructor = make.Constructor(make
443: .Modifiers(newConstructor
444: .getModifiers().getFlags(),
445: newConstructor.getModifiers()
446: .getAnnotations()),
447: newConstructor.getTypeParameters(),
448: newConstructor.getParameters(),
449: newConstructor.getThrows(), block);
450:
451: newInnerClass = make.removeClassMember(
452: newInnerClass, m);
453: newInnerClass = GeneratorUtilities.get(
454: workingCopy).insertClassMember(
455: newInnerClass, newConstructor);
456: }
457: }
458: }
459: }
460:
461: if (inner.getKind() == ElementKind.ENUM) {
462: for (Tree member : newInnerClass.getMembers()) {
463: if (member.getKind() == Tree.Kind.METHOD) {
464: MethodTree m = (MethodTree) member;
465: if (m.getReturnType() == null) {
466: rewrite(m.getBody(), make.removeBlockStatement(
467: m.getBody(), 0));
468: }
469: }
470: }
471: }
472: return newInnerClass;
473: }
474: }
|