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.BlockTree;
045: import com.sun.source.tree.ClassTree;
046: import com.sun.source.tree.ExpressionStatementTree;
047: import com.sun.source.tree.ExpressionTree;
048: import com.sun.source.tree.MethodInvocationTree;
049: import com.sun.source.tree.MethodTree;
050: import com.sun.source.tree.ModifiersTree;
051: import com.sun.source.tree.StatementTree;
052: import com.sun.source.tree.Tree;
053: import com.sun.source.tree.TypeParameterTree;
054: import com.sun.source.tree.VariableTree;
055: import com.sun.source.util.TreePath;
056: import java.io.IOException;
057: import java.net.URL;
058: import java.util.ArrayList;
059: import java.util.Collections;
060: import java.util.HashSet;
061: import java.util.Iterator;
062: import java.util.List;
063: import java.util.Set;
064: import javax.lang.model.element.Element;
065: import javax.lang.model.element.ElementKind;
066: import javax.lang.model.element.ExecutableElement;
067: import javax.lang.model.element.Modifier;
068: import javax.lang.model.element.PackageElement;
069: import javax.lang.model.element.TypeElement;
070: import javax.lang.model.element.TypeParameterElement;
071: import javax.lang.model.element.VariableElement;
072: import javax.lang.model.type.DeclaredType;
073: import javax.lang.model.type.TypeKind;
074: import javax.lang.model.type.TypeMirror;
075: import javax.lang.model.util.ElementFilter;
076: import javax.lang.model.util.Types;
077: import org.netbeans.api.java.source.CancellableTask;
078: import org.netbeans.api.java.source.CompilationController;
079: import org.netbeans.api.java.source.CompilationInfo;
080: import org.netbeans.api.java.source.ElementHandle;
081: import org.netbeans.api.java.source.GeneratorUtilities;
082: import org.netbeans.api.java.source.JavaSource;
083: import org.netbeans.api.java.source.ModificationResult;
084: import org.netbeans.api.java.source.TreeMaker;
085: import org.netbeans.api.java.source.TreePathHandle;
086: import org.netbeans.api.java.source.TypeMirrorHandle;
087: import org.netbeans.api.java.source.WorkingCopy;
088: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
089: import org.netbeans.modules.refactoring.api.Problem;
090: import org.netbeans.modules.refactoring.java.spi.DiffElement;
091: import org.netbeans.modules.refactoring.java.RetoucheUtils;
092: import org.netbeans.modules.refactoring.java.api.ExtractSuperclassRefactoring;
093: import org.netbeans.modules.refactoring.java.api.MemberInfo;
094: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
095: import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation;
096: import org.openide.filesystems.FileObject;
097: import org.openide.filesystems.Repository;
098: import org.openide.filesystems.URLMapper;
099: import org.openide.loaders.DataFolder;
100: import org.openide.loaders.DataObject;
101: import org.openide.loaders.DataObjectNotFoundException;
102: import org.openide.text.PositionBounds;
103: import org.openide.util.Exceptions;
104: import org.openide.util.Lookup;
105: import org.openide.util.NbBundle;
106: import org.openide.util.Utilities;
107: import org.openide.util.lookup.Lookups;
108:
109: /** Plugin that implements the core functionality of Extract Super Class refactoring.
110: *
111: * @author Martin Matula, Jan Pokorsky
112: */
113: public final class ExtractSuperclassRefactoringPlugin extends
114: JavaRefactoringPlugin {
115: /** Reference to the parent refactoring instance */
116: private final ExtractSuperclassRefactoring refactoring;
117:
118: /** source class */
119: private ElementHandle<TypeElement> classHandle;
120:
121: private String pkgName;
122:
123: /** Creates a new instance of ExtractSuperClassRefactoringPlugin
124: * @param refactoring Parent refactoring instance.
125: */
126: ExtractSuperclassRefactoringPlugin(
127: ExtractSuperclassRefactoring refactoring) {
128: this .refactoring = refactoring;
129: }
130:
131: protected JavaSource getJavaSource(Phase p) {
132: return JavaSource.forFileObject(refactoring.getSourceType()
133: .getFileObject());
134: }
135:
136: @Override
137: protected Problem preCheck(CompilationController javac)
138: throws IOException {
139: // fire operation start on the registered progress listeners (2 step)
140: fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 2);
141: javac.toPhase(JavaSource.Phase.RESOLVED);
142: try {
143: TreePathHandle sourceType = refactoring.getSourceType();
144:
145: // check whether the element is valid
146: Problem result = isElementAvail(sourceType, javac);
147: if (result != null) {
148: // fatal error -> don't continue with further checks
149: return result;
150: }
151: if (!RetoucheUtils.isElementInOpenProject(sourceType
152: .getFileObject())) {
153: return new Problem(true, NbBundle.getMessage(
154: ExtractSuperclassRefactoringPlugin.class,
155: "ERR_ProjectNotOpened")); // NOI18N
156: }
157:
158: // check whether the element is an unresolved class
159: Element sourceElm = sourceType.resolveElement(javac);
160: if (sourceElm == null
161: || (sourceElm.getKind() != ElementKind.CLASS)) {
162: // fatal error -> return
163: return new Problem(true, NbBundle.getMessage(
164: ExtractSuperclassRefactoringPlugin.class,
165: "ERR_ElementNotAvailable")); // NOI18N
166: }
167:
168: classHandle = ElementHandle
169: .<TypeElement> create((TypeElement) sourceElm);
170:
171: PackageElement pkgElm = (PackageElement) javac
172: .getElementUtilities().outermostTypeElement(
173: sourceElm).getEnclosingElement();
174: pkgName = pkgElm.getQualifiedName().toString();
175:
176: // increase progress (step 1)
177: fireProgressListenerStep();
178:
179: // all checks passed -> return null
180: return null;
181: } finally {
182: // fire operation end on the registered progress listeners
183: fireProgressListenerStop();
184: }
185: }
186:
187: @Override
188: public Problem fastCheckParameters() {
189: Problem result = null;
190:
191: String newName = refactoring.getSuperClassName();
192:
193: if (!Utilities.isJavaIdentifier(newName)) {
194: result = createProblem(result, true, NbBundle.getMessage(
195: ExtractSuperclassRefactoringPlugin.class,
196: "ERR_InvalidIdentifier", newName)); // NOI18N
197: return result;
198: }
199:
200: FileObject primFile = refactoring.getSourceType()
201: .getFileObject();
202: FileObject folder = primFile.getParent();
203: FileObject[] children = folder.getChildren();
204: for (FileObject child : children) {
205: if (!child.isVirtual() && child.getName().equals(newName)
206: && "java".equals(child.getExt())) { // NOI18N
207: result = createProblem(
208: result,
209: true,
210: NbBundle
211: .getMessage(
212: ExtractSuperclassRefactoringPlugin.class,
213: "ERR_ClassClash", newName,
214: pkgName)); // NOI18N
215: return result;
216: }
217: }
218:
219: return null;
220: }
221:
222: @Override
223: public Problem checkParameters() {
224: MemberInfo[] members = refactoring.getMembers();
225: if (members.length == 0) {
226: return new Problem(true, NbBundle.getMessage(
227: ExtractSuperclassRefactoringPlugin.class,
228: "ERR_ExtractSuperClass_MembersNotAvailable")); // NOI18N);
229: }
230: return super .checkParameters();
231:
232: }
233:
234: @Override
235: protected Problem checkParameters(CompilationController javac)
236: throws IOException {
237: javac.toPhase(JavaSource.Phase.RESOLVED);
238:
239: TypeElement sourceType = (TypeElement) refactoring
240: .getSourceType().resolveElement(javac);
241: assert sourceType != null;
242:
243: Set<? extends Element> members = new HashSet<Element>(
244: sourceType.getEnclosedElements());
245:
246: fireProgressListenerStart(AbstractRefactoring.PARAMETERS_CHECK,
247: refactoring.getMembers().length);
248: try {
249: for (MemberInfo info : refactoring.getMembers()) {
250: Problem p = null;
251: switch (info.getGroup()) {
252: case FIELD:
253: @SuppressWarnings("unchecked")
254: ElementHandle<VariableElement> vehandle = (ElementHandle<VariableElement>) info
255: .getElementHandle();
256: VariableElement field = vehandle.resolve(javac);
257: p = checkFieldParameter(javac, field, members);
258: break;
259: case METHOD:
260: @SuppressWarnings("unchecked")
261: ElementHandle<ExecutableElement> eehandle = (ElementHandle<ExecutableElement>) info
262: .getElementHandle();
263: ExecutableElement method = eehandle.resolve(javac);
264: p = checkMethodParameter(javac, method, members);
265: break;
266: }
267:
268: if (p != null) {
269: return p;
270: }
271:
272: fireProgressListenerStep();
273: }
274: } finally {
275: fireProgressListenerStop();
276: }
277:
278: // XXX check refactoring.getImplements()
279:
280: return null;
281: }
282:
283: private Problem checkFieldParameter(CompilationController javac,
284: VariableElement elm, Set<? extends Element> members)
285: throws IOException {
286: if (elm == null) {
287: return new Problem(true, NbBundle.getMessage(
288: ExtractSuperclassRefactoringPlugin.class,
289: "ERR_ElementNotAvailable")); // NOI18N
290: }
291: if (javac.getElementUtilities().isSynthetic(elm)
292: || elm.getKind() != ElementKind.FIELD) {
293: return new Problem(true, NbBundle.getMessage(
294: ExtractInterfaceRefactoringPlugin.class,
295: "ERR_ExtractSuperClass_UnknownMember", // NOI18N
296: elm.toString()));
297: }
298: if (!members.contains(elm)) {
299: return new Problem(true, NbBundle.getMessage(
300: ExtractInterfaceRefactoringPlugin.class,
301: "ERR_ExtractSuperClass_UnknownMember", // NOI18N
302: elm.toString()));
303: }
304: // Set<Modifier> mods = elm.getModifiers();
305: // if (mods.contains(Modifier.PUBLIC) && mods.contains(Modifier.STATIC) && mods.contains(Modifier.FINAL)) {
306: // VariableTree tree = (VariableTree) javac.getTrees().getTree(elm);
307: // if (tree.getInitializer() != null) {
308: // continue;
309: // }
310: // }
311: // return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractInterface_WrongModifiers", elm.getSimpleName().toString())); // NOI18N
312: return null;
313: }
314:
315: private Problem checkMethodParameter(CompilationController javac,
316: ExecutableElement elm, Set<? extends Element> members)
317: throws IOException {
318: if (elm == null) {
319: return new Problem(true, NbBundle.getMessage(
320: ExtractSuperclassRefactoringPlugin.class,
321: "ERR_ElementNotAvailable")); // NOI18N
322: }
323: if (javac.getElementUtilities().isSynthetic(elm)
324: || elm.getKind() != ElementKind.METHOD) {
325: return new Problem(true, NbBundle.getMessage(
326: ExtractInterfaceRefactoringPlugin.class,
327: "ERR_ExtractSuperClass_UnknownMember", // NOI18N
328: elm.toString()));
329: }
330: if (!members.contains(elm)) {
331: return new Problem(true, NbBundle.getMessage(
332: ExtractInterfaceRefactoringPlugin.class,
333: "ERR_ExtractSuperClass_UnknownMember", // NOI18N
334: elm.toString()));
335: }
336: // Set<Modifier> mods = elm.getModifiers();
337: // if (!mods.contains(Modifier.PUBLIC) || mods.contains(Modifier.STATIC)) {
338: // return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractInterface_WrongModifiers", elm.getSimpleName().toString())); // NOI18N
339: // }
340: return null;
341:
342: }
343:
344: public Problem prepare(RefactoringElementsBag bag) {
345: FileObject primFile = refactoring.getSourceType()
346: .getFileObject();
347: try {
348: bag.add(refactoring, new CreateSuperclassElement(
349: refactoring, primFile.getParent(), classHandle));
350: UpdateClassTask.create(bag, primFile, refactoring,
351: classHandle);
352: } catch (IOException ex) {
353: throw (RuntimeException) new RuntimeException()
354: .initCause(ex);
355: }
356: return null;
357: }
358:
359: private static List<TypeMirror> findUsedGenericTypes(
360: CompilationInfo javac, TypeElement javaClass,
361: ExtractSuperclassRefactoring refactoring) {
362: List<TypeMirror> typeArgs = RetoucheUtils
363: .resolveTypeParamsAsTypes(javaClass.getTypeParameters());
364: if (typeArgs.isEmpty())
365: return typeArgs;
366:
367: Types typeUtils = javac.getTypes();
368: List<TypeMirror> result = new ArrayList<TypeMirror>(typeArgs
369: .size());
370:
371: // check super class
372: TypeMirror super Class = javaClass.getSuperclass();
373: RetoucheUtils.findUsedGenericTypes(typeUtils, typeArgs, result,
374: super Class);
375:
376: MemberInfo[] members = refactoring.getMembers();
377: for (int i = 0; i < members.length && !typeArgs.isEmpty(); i++) {
378: if (members[i].getGroup() == MemberInfo.Group.METHOD) {
379: // check methods
380: @SuppressWarnings("unchecked")
381: ElementHandle<ExecutableElement> handle = (ElementHandle<ExecutableElement>) members[i]
382: .getElementHandle();
383: ExecutableElement elm = handle.resolve(javac);
384:
385: RetoucheUtils.findUsedGenericTypes(typeUtils, typeArgs,
386: result, elm.getReturnType());
387:
388: for (Iterator<? extends VariableElement> paramIter = elm
389: .getParameters().iterator(); paramIter
390: .hasNext()
391: && !typeArgs.isEmpty();) {
392: VariableElement param = paramIter.next();
393: RetoucheUtils.findUsedGenericTypes(typeUtils,
394: typeArgs, result, param.asType());
395: }
396: } else if (members[i].getGroup() == MemberInfo.Group.IMPLEMENTS) {
397: // check implements
398: TypeMirrorHandle handle = (TypeMirrorHandle) members[i]
399: .getElementHandle();
400: TypeMirror implemetz = handle.resolve(javac);
401: RetoucheUtils.findUsedGenericTypes(typeUtils, typeArgs,
402: result, implemetz);
403: }
404: // do not check fields since static fields cannot use type parameter of the enclosing class
405: }
406:
407: return result;
408: }
409:
410: // --- REFACTORING ELEMENTS ------------------------------------------------
411:
412: private static final class CreateSuperclassElement extends
413: SimpleRefactoringElementImplementation implements
414: CancellableTask<WorkingCopy> {
415: private final URL folderURL;
416: private URL super ClassURL;
417: private final String super ClassName;
418: private final ExtractSuperclassRefactoring refactoring;
419: private final ElementHandle<TypeElement> sourceType;
420:
421: private CreateSuperclassElement(
422: ExtractSuperclassRefactoring refactoring,
423: FileObject folder, ElementHandle<TypeElement> sourceType) {
424: this .refactoring = refactoring;
425: this .folderURL = URLMapper.findURL(folder,
426: URLMapper.INTERNAL);
427: this .super ClassName = refactoring.getSuperClassName();
428: this .sourceType = sourceType;
429: }
430:
431: // --- SimpleRefactoringElementImpl methods ----------------------------------
432:
433: public void performChange() {
434: try {
435: FileObject folderFO = URLMapper
436: .findFileObject(folderURL);
437: if (folderFO == null)
438: return;
439:
440: // create new file
441:
442: // XXX not nice; user might modify the template to something entirely different from the standard template.
443: FileObject tempFO = Repository.getDefault()
444: .getDefaultFileSystem().findResource(
445: "Templates/Classes/Class.java"); // NOI18N
446:
447: DataFolder folder = (DataFolder) DataObject
448: .find(folderFO);
449: DataObject template = DataObject.find(tempFO);
450: DataObject newSuperClassDO = template
451: .createFromTemplate(folder, super ClassName);
452: this .super ClassURL = URLMapper.findURL(newSuperClassDO
453: .getPrimaryFile(), URLMapper.INTERNAL);
454: refactoring.getContext().add(
455: newSuperClassDO.getPrimaryFile());
456:
457: // add type params and members
458: JavaSource js = JavaSource
459: .forFileObject(newSuperClassDO.getPrimaryFile());
460: js.runModificationTask(this ).commit();
461: } catch (DataObjectNotFoundException ex) {
462: Exceptions.printStackTrace(ex);
463: } catch (IOException ex) {
464: Exceptions.printStackTrace(ex);
465: }
466: }
467:
468: @Override
469: public void undoChange() {
470: FileObject ifcFO = null;
471: if (super ClassURL != null) {
472: ifcFO = URLMapper.findFileObject(super ClassURL);
473: }
474: if (ifcFO != null) {
475: try {
476: ifcFO.delete();
477: } catch (IOException ex) {
478: Exceptions.printStackTrace(ex);
479: }
480: }
481: }
482:
483: public String getText() {
484: return NbBundle.getMessage(
485: ExtractInterfaceRefactoringPlugin.class,
486: "TXT_ExtractSC_CreateSC", super ClassName); // NOI18N
487: }
488:
489: public String getDisplayText() {
490: return getText();
491: }
492:
493: public FileObject getParentFile() {
494: return URLMapper.findFileObject(folderURL);
495: }
496:
497: public PositionBounds getPosition() {
498: return null;
499: }
500:
501: public Lookup getLookup() {
502: FileObject fo = super ClassURL == null ? null : URLMapper
503: .findFileObject(super ClassURL);
504: return fo != null ? Lookups.singleton(fo) : Lookup.EMPTY;
505: }
506:
507: // --- CancellableTask methods ----------------------------------
508:
509: public void cancel() {
510:
511: }
512:
513: public void run(WorkingCopy wc) throws Exception {
514: wc.toPhase(JavaSource.Phase.RESOLVED);
515: ClassTree classTree = findClass(wc, super ClassName);
516: boolean makeAbstract = false;
517: TreeMaker make = wc.getTreeMaker();
518: GeneratorUtilities genUtils = GeneratorUtilities.get(wc);
519:
520: // add type parameters
521: List<TypeMirror> typeParams = findUsedGenericTypes(wc,
522: sourceType.resolve(wc), refactoring);
523: List<TypeParameterTree> newTypeParams = new ArrayList<TypeParameterTree>(
524: typeParams.size());
525: // lets retrieve param type trees from origin class since it is
526: // almost impossible to create them via TreeMaker
527: TypeElement sourceTypeElm = sourceType.resolve(wc);
528: for (TypeParameterElement typeParam : sourceTypeElm
529: .getTypeParameters()) {
530: TypeMirror origParam = typeParam.asType();
531: for (TypeMirror newParam : typeParams) {
532: if (wc.getTypes().isSameType(origParam, newParam)) {
533: Tree t = wc.getTrees().getTree(typeParam);
534: if (t.getKind() == Tree.Kind.TYPE_PARAMETER) {
535: newTypeParams.add((TypeParameterTree) t);
536: }
537: }
538: }
539: }
540:
541: // add fields, methods and implements
542: List<Tree> members = new ArrayList<Tree>();
543: List<Tree> implements List = new ArrayList<Tree>();
544:
545: addConstructors(wc, sourceTypeElm, members);
546:
547: for (MemberInfo member : refactoring.getMembers()) {
548: if (member.getGroup() == MemberInfo.Group.FIELD) {
549: @SuppressWarnings("unchecked")
550: ElementHandle<VariableElement> handle = (ElementHandle<VariableElement>) member
551: .getElementHandle();
552: VariableElement elm = handle.resolve(wc);
553: VariableTree tree = (VariableTree) wc.getTrees()
554: .getTree(elm);
555: VariableTree copy = genUtils.importComments(tree,
556: wc.getTrees().getPath(elm)
557: .getCompilationUnit());
558: copy = genUtils.importFQNs(copy);
559: members.add(copy);
560: } else if (member.getGroup() == MemberInfo.Group.METHOD) {
561: @SuppressWarnings("unchecked")
562: ElementHandle<ExecutableElement> handle = (ElementHandle<ExecutableElement>) member
563: .getElementHandle();
564: ExecutableElement elm = handle.resolve(wc);
565: MethodTree methodTree = wc.getTrees().getTree(elm);
566: if (member.isMakeAbstract()
567: && !elm.getModifiers().contains(
568: Modifier.ABSTRACT)) {
569: methodTree = make.Method(makeAbstract(make,
570: methodTree.getModifiers()), methodTree
571: .getName(), methodTree.getReturnType(),
572: methodTree.getTypeParameters(),
573: methodTree.getParameters(), methodTree
574: .getThrows(), (BlockTree) null,
575: null);
576: methodTree = genUtils.importFQNs(methodTree);
577: RetoucheUtils.copyJavadoc(elm, methodTree, wc);
578: } else {
579: methodTree = genUtils.importComments(
580: methodTree, wc.getTrees().getPath(elm)
581: .getCompilationUnit());
582: methodTree = genUtils.importFQNs(methodTree);
583: }
584: makeAbstract |= methodTree.getModifiers()
585: .getFlags().contains(Modifier.ABSTRACT);
586: members.add(methodTree);
587: } else if (member.getGroup() == MemberInfo.Group.IMPLEMENTS) {
588: TypeMirrorHandle handle = (TypeMirrorHandle) member
589: .getElementHandle();
590: // XXX check if interface is not aready there; the templates might be changed by user :-(
591: TypeMirror implMirror = handle.resolve(wc);
592: implements List.add(make.Type(implMirror));
593: // XXX needs more granular check
594: makeAbstract |= true;
595: }
596: }
597:
598: // create superclass
599: Tree super Class = makeSuperclass(make, sourceTypeElm);
600:
601: ModifiersTree classModifiersTree = makeAbstract ? makeAbstract(
602: make, classTree.getModifiers())
603: : classTree.getModifiers();
604: classModifiersTree = genUtils
605: .importFQNs(classModifiersTree);
606:
607: // create new class
608: ClassTree newClassTree = make.Class(classModifiersTree,
609: classTree.getSimpleName(), newTypeParams,
610: super Class, implements List, Collections
611: .<Tree> emptyList());
612:
613: newClassTree = GeneratorUtilities.get(wc)
614: .insertClassMembers(newClassTree, members);
615: wc.rewrite(classTree, newClassTree);
616: }
617:
618: // --- helper methods ----------------------------------
619:
620: private static ClassTree findClass(CompilationInfo javac,
621: String name) {
622: for (Tree tree : javac.getCompilationUnit().getTypeDecls()) {
623: if (Tree.Kind.CLASS == tree.getKind()
624: && !javac.getTreeUtilities().isInterface(
625: (ClassTree) tree)
626: && !javac.getTreeUtilities().isAnnotation(
627: (ClassTree) tree)
628: && !javac.getTreeUtilities().isEnum(
629: (ClassTree) tree)
630: && name.contentEquals(((ClassTree) tree)
631: .getSimpleName())) {
632: return (ClassTree) tree;
633: }
634: }
635: throw new IllegalStateException(
636: "wrong template, cannot find the class in "
637: + javac.getFileObject()); // NOI18N
638: }
639:
640: private static ModifiersTree makeAbstract(TreeMaker make,
641: ModifiersTree oldMods) {
642: if (oldMods.getFlags().contains(Modifier.ABSTRACT)) {
643: return oldMods;
644: }
645: Set<Modifier> flags = new HashSet<Modifier>(oldMods
646: .getFlags());
647: flags.add(Modifier.ABSTRACT);
648: flags.remove(Modifier.FINAL);
649: return make.Modifiers(flags, oldMods.getAnnotations());
650: }
651:
652: private static Tree makeSuperclass(TreeMaker make,
653: TypeElement clazz) {
654: DeclaredType supType = (DeclaredType) clazz.getSuperclass();
655: TypeElement supEl = (TypeElement) supType.asElement();
656: return supEl.getSuperclass().getKind() == TypeKind.NONE ? null
657: : make.Type(supType);
658: }
659:
660: /* in case there are constructors delegating to old superclass it is necessery to create delegates in new superclass */
661: private static void addConstructors(final WorkingCopy javac,
662: final TypeElement origClass, final List<Tree> members) {
663: final TreeMaker make = javac.getTreeMaker();
664: final GeneratorUtilities genUtils = GeneratorUtilities
665: .get(javac);
666:
667: // cache of already resolved constructors
668: final Set<Element> added = new HashSet<Element>();
669: for (ExecutableElement constr : ElementFilter
670: .constructorsIn(origClass.getEnclosedElements())) {
671: if (javac.getElementUtilities().isSynthetic(constr)) {
672: continue;
673: }
674:
675: TreePath path = javac.getTrees().getPath(constr);
676: MethodTree mc = (MethodTree) (path != null ? path
677: .getLeaf() : null);
678: if (mc != null) {
679: for (StatementTree stmt : mc.getBody()
680: .getStatements()) {
681: // search super(...); statement
682: if (stmt.getKind() == Tree.Kind.EXPRESSION_STATEMENT) {
683: ExpressionStatementTree estmt = (ExpressionStatementTree) stmt;
684: boolean isSyntheticSuper = javac
685: .getTreeUtilities()
686: .isSynthetic(
687: javac
688: .getTrees()
689: .getPath(
690: path
691: .getCompilationUnit(),
692: estmt));
693: ExpressionTree expr = estmt.getExpression();
694: TreePath expath = javac.getTrees().getPath(
695: path.getCompilationUnit(), expr);
696: Element el = javac.getTrees().getElement(
697: expath);
698: if (el != null
699: && el.getKind() == ElementKind.CONSTRUCTOR
700: && added.add(el)) {
701: MethodTree template = (MethodTree) javac
702: .getTrees().getTree(el);
703: MethodInvocationTree invk = (MethodInvocationTree) expr;
704: // create constructor block with super call
705: BlockTree block = isSyntheticSuper ? make
706: .Block(
707: Collections
708: .<StatementTree> emptyList(),
709: false)
710: : make
711: .Block(
712: Collections
713: .<StatementTree> singletonList(make
714: .ExpressionStatement(make
715: .MethodInvocation(
716: Collections
717: .<ExpressionTree> emptyList(),
718: invk
719: .getMethodSelect(),
720: params2Arguments(
721: make,
722: template
723: .getParameters())))),
724: false);
725: // create constructor
726: MethodTree newConstr = make
727: .Constructor(
728: template.getModifiers(),
729: template
730: .getTypeParameters(),
731: template
732: .getParameters(),
733: template.getThrows(),
734: block);
735: newConstr = genUtils
736: .importFQNs(newConstr);
737: members.add(newConstr);
738: }
739:
740: }
741: // take just first statement super(...)
742: break;
743: }
744: }
745: }
746: }
747:
748: private static List<? extends ExpressionTree> params2Arguments(
749: TreeMaker make, List<? extends VariableTree> params) {
750: if (params.isEmpty()) {
751: return Collections.<ExpressionTree> emptyList();
752: }
753: List<ExpressionTree> args = new ArrayList<ExpressionTree>(
754: params.size());
755: for (VariableTree param : params) {
756: args.add(make.Identifier(param.getName()));
757: }
758: return args;
759: }
760:
761: }
762:
763: private final static class UpdateClassTask implements
764: CancellableTask<WorkingCopy> {
765: private final ExtractSuperclassRefactoring refactoring;
766: private final ElementHandle<TypeElement> sourceType;
767:
768: private UpdateClassTask(
769: ExtractSuperclassRefactoring refactoring,
770: ElementHandle<TypeElement> sourceType) {
771: this .sourceType = sourceType;
772: this .refactoring = refactoring;
773: }
774:
775: public static void create(RefactoringElementsBag bag,
776: FileObject fo,
777: ExtractSuperclassRefactoring refactoring,
778: ElementHandle<TypeElement> sourceType)
779: throws IOException {
780: JavaSource js = JavaSource.forFileObject(fo);
781: ModificationResult modification = js
782: .runModificationTask(new UpdateClassTask(
783: refactoring, sourceType));
784: List<? extends ModificationResult.Difference> diffs = modification
785: .getDifferences(fo);
786: for (ModificationResult.Difference diff : diffs) {
787: bag.add(refactoring, DiffElement.create(diff, fo,
788: modification));
789: }
790: bag.registerTransaction(new RetoucheCommit(Collections
791: .singletonList(modification)));
792: }
793:
794: public void cancel() {
795: }
796:
797: public void run(WorkingCopy wc) throws Exception {
798: wc.toPhase(JavaSource.Phase.RESOLVED);
799: TypeElement clazz = this .sourceType.resolve(wc);
800: assert clazz != null;
801: ClassTree classTree = wc.getTrees().getTree(clazz);
802: TreeMaker make = wc.getTreeMaker();
803: // fake interface since interface file does not exist yet
804: Tree super ClassTree;
805: List<TypeMirror> typeParams = findUsedGenericTypes(wc,
806: clazz, refactoring);
807: if (typeParams.isEmpty()) {
808: super ClassTree = make.Identifier(refactoring
809: .getSuperClassName());
810: } else {
811: List<ExpressionTree> typeParamTrees = new ArrayList<ExpressionTree>(
812: typeParams.size());
813: for (TypeMirror typeParam : typeParams) {
814: Tree t = make.Type(typeParam);
815: typeParamTrees.add((ExpressionTree) t);
816: }
817: super ClassTree = make.ParameterizedType(make
818: .Identifier(refactoring.getSuperClassName()),
819: typeParamTrees);
820: }
821:
822: Set<Tree> members2Remove = new HashSet<Tree>();
823: Set<Tree> interfaces2Remove = new HashSet<Tree>();
824:
825: members2Remove.addAll(getMembers2Remove(wc, refactoring
826: .getMembers()));
827: interfaces2Remove.addAll(getImplements2Remove(wc,
828: refactoring.getMembers(), clazz));
829:
830: // filter out obsolete members
831: List<Tree> members2Add = new ArrayList<Tree>();
832: for (Tree tree : classTree.getMembers()) {
833: if (!members2Remove.contains(tree)) {
834: members2Add.add(tree);
835: }
836: }
837: // filter out obsolete implements trees
838: List<Tree> impls2Add = resolveImplements(classTree
839: .getImplementsClause(), interfaces2Remove);
840:
841: ClassTree nc;
842: nc = make.Class(classTree.getModifiers(), classTree
843: .getSimpleName(), classTree.getTypeParameters(),
844: super ClassTree, impls2Add, members2Add);
845:
846: wc.rewrite(classTree, nc);
847: }
848:
849: private List<Tree> getMembers2Remove(CompilationInfo javac,
850: MemberInfo[] members) {
851: if (members == null || members.length == 0) {
852: return Collections.<Tree> emptyList();
853: }
854: List<Tree> result = new ArrayList<Tree>(members.length);
855: for (MemberInfo member : members) {
856: if (member.getGroup() == MemberInfo.Group.FIELD) {
857: @SuppressWarnings("unchecked")
858: ElementHandle<VariableElement> handle = (ElementHandle<VariableElement>) member
859: .getElementHandle();
860: VariableElement elm = handle.resolve(javac);
861: assert elm != null;
862: Tree t = javac.getTrees().getTree(elm);
863: assert t != null;
864: result.add(t);
865: } else if (member.getGroup() == MemberInfo.Group.METHOD
866: && !member.isMakeAbstract()) {
867: @SuppressWarnings("unchecked")
868: ElementHandle<ExecutableElement> handle = (ElementHandle<ExecutableElement>) member
869: .getElementHandle();
870: ExecutableElement elm = handle.resolve(javac);
871: assert elm != null;
872: Tree t = javac.getTrees().getTree(elm);
873: assert t != null;
874: result.add(t);
875: }
876:
877: }
878:
879: return result;
880: }
881:
882: private List<Tree> getImplements2Remove(CompilationInfo javac,
883: MemberInfo[] members, TypeElement clazz) {
884: if (members == null || members.length == 0) {
885: return Collections.<Tree> emptyList();
886: }
887:
888: // resolve members to remove
889: List<TypeMirror> memberTypes = new ArrayList<TypeMirror>(
890: members.length);
891: for (MemberInfo member : members) {
892: if (member.getGroup() == MemberInfo.Group.IMPLEMENTS) {
893: TypeMirrorHandle handle = (TypeMirrorHandle) member
894: .getElementHandle();
895: TypeMirror tm = handle.resolve(javac);
896: memberTypes.add(tm);
897: }
898: }
899:
900: ClassTree classTree = javac.getTrees().getTree(clazz);
901: List<Tree> result = new ArrayList<Tree>();
902: Types types = javac.getTypes();
903:
904: // map TypeMirror to Tree
905: for (Tree tree : classTree.getImplementsClause()) {
906: TreePath path = javac.getTrees().getPath(
907: javac.getCompilationUnit(), tree);
908: TypeMirror existingTM = javac.getTrees().getTypeMirror(
909: path);
910:
911: for (TypeMirror tm : memberTypes) {
912: if (types.isSameType(tm, existingTM)) {
913: result.add(tree);
914: break;
915: }
916: }
917: }
918:
919: return result;
920: }
921:
922: private static List<Tree> resolveImplements(
923: List<? extends Tree> allImpls, Set<Tree> impls2Remove) {
924: List<Tree> ret;
925: if (allImpls == null) {
926: ret = new ArrayList<Tree>(1);
927: } else {
928: ret = new ArrayList<Tree>(allImpls.size() + 1);
929: ret.addAll(allImpls);
930: }
931:
932: if (impls2Remove != null && !impls2Remove.isEmpty()) {
933: ret.removeAll(impls2Remove);
934: }
935: return ret;
936: }
937: }
938: }
|