Index: src/org/netbeans/api/java/source/GeneratorUtilities.java =================================================================== RCS file: src/org/netbeans/api/java/source/GeneratorUtilities.java diff -N src/org/netbeans/api/java/source/GeneratorUtilities.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/netbeans/api/java/source/GeneratorUtilities.java 14 Jun 2007 14:40:50 -0000 @@ -0,0 +1,593 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.java.source; + +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.Scope; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.lang.model.element.Element; + +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; + +import org.netbeans.api.java.queries.SourceLevelQuery; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.openide.filesystems.FileObject; +import org.openide.modules.SpecificationVersion; +import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; + +/** + * + * @author Jan Lahoda, Dusan Balek + */ +public class GeneratorUtilities { + + private WorkingCopy copy; + + private GeneratorUtilities(WorkingCopy copy) { + this.copy = copy; + } + + /** + * Returns the instance of this class + * + * @param copy + * @return the {@link GeneratorUtilities} instance + */ + public static GeneratorUtilities get(WorkingCopy copy) { + return new GeneratorUtilities(copy); + } + + /** + * Inserts a member to a class. Using the rules specified in the {@link CodeStyle} + * it finds the proper place for the member and calls {@link TreeMaker.insertClassMember} + * + * @param clazz the class to insert the member to + * @param member the member to add + * @return the modified class + */ + public ClassTree insertClassMember(ClassTree clazz, Tree member) { + assert clazz != null && member != null; + int idx = 0; + for (Tree tree : clazz.getMembers()) { + if (ClassMemberComparator.compare(member, tree) < 0) + break; + idx++; + } + return copy.getTreeMaker().insertClassMember(clazz, idx, member); + } + + /** + * Inserts members to a class. Using the rules specified in the {@link CodeStyle} + * it finds the proper place for each of the members and calls {@link TreeMaker.insertClassMember} + * + * @param clazz the class to insert the members to + * @param members the members to insert + * @return the modified class + */ + public ClassTree insertClassMembers(ClassTree clazz, Iterable members) { + assert members != null; + for (Tree member : members) + clazz = insertClassMember(clazz, member); + return clazz; + } + + /** + * Creates implementations of the all abstract methods within a class. + * + * @param clazz the class to create the implementations within + * @return the abstract method implementations + */ + public List createAllAbstractMethodImplementations(TypeElement clazz) { + return createAbstractMethodImplementations(clazz, copy.getElementUtilities().findUnimplementedMethods(clazz)); + } + + /** + * Creates implementations of abstract methods within a class. + * + * @param clazz the class to create the implementations within + * @param methods the abstract methods to implement + * @return the abstract method implementations + */ + public List createAbstractMethodImplementations(TypeElement clazz, Iterable methods) { + assert methods != null; + List ret = new ArrayList(); + for(ExecutableElement method : methods) + ret.add(createAbstractMethodImplementation(clazz, method)); + return ret; + } + + /** + * Creates an implementation of an abstract method within a class. + * + * @param clazz the class to create the implementation within + * @param method the abstract method to implement + * @return the abstract method implementation + */ + public MethodTree createAbstractMethodImplementation(TypeElement clazz, ExecutableElement method) { + assert clazz != null && method != null; + return createMethod(method, (DeclaredType)clazz.asType()); + } + + /** + * Creates overriding methods within a class. + * + * @param clazz the class to create the methods within + * @param methods the methods to override + * @return the overriding methods + */ + public List createOverridingMethods(TypeElement clazz, Iterable methods) { + assert methods != null; + List ret = new ArrayList(); + for(ExecutableElement method : methods) + ret.add(createOverridingMethod(clazz, method)); + return ret; + } + + /** + * Creates an overriding method within a class. + * + * @param clazz the class to create the method within + * @param method the method to override + * @return the overriding method + */ + public MethodTree createOverridingMethod(TypeElement clazz, ExecutableElement method) { + assert clazz != null && method != null; + return createMethod(method, (DeclaredType)clazz.asType()); + } + + /** + * Creates a class constructor. + * + * @param clazz the class to create the constructor for + * @param fields fields to be initialized by the constructor + * @param constructor inherited constructor to be called + * @return the constructor + */ + public MethodTree createConstructor(TypeElement clazz, Iterable fields, ExecutableElement constructor) { + assert clazz != null && fields != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(clazz.getKind() == ElementKind.ENUM ? Modifier.PRIVATE : Modifier.PUBLIC); + List parameters = new ArrayList(); + List statements = new ArrayList(); + ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class)); + if (constructor != null && !constructor.getParameters().isEmpty()) { + List arguments = new ArrayList(); + for (VariableElement ve : constructor.getParameters()) { + parameters.add(make.Variable(parameterModifiers, ve.getSimpleName(), make.Type(ve.asType()), null)); + arguments.add(make.Identifier(ve.getSimpleName())); //NOI18N + } + statements.add(make.ExpressionStatement(make.MethodInvocation(Collections.emptyList(), make.Identifier("super"), arguments))); //NOI18N + } + for (VariableElement ve : fields) { + TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), ve); + parameters.add(make.Variable(parameterModifiers, ve.getSimpleName(), make.Type(type), null)); + statements.add(make.ExpressionStatement(make.Assignment(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier(ve.getSimpleName())))); //NOI18N + } + BlockTree body = make.Block(statements, false); + return make.Method(make.Modifiers(mods), "", null, Collections. emptyList(), parameters, Collections.emptyList(), body, null); //NOI18N + } + + /** + * Creates a class constructor. + * + * @param clazz the class to create the constructor for + * @param fields fields to be initialized by the constructor + * @return the constructor + */ + public MethodTree createConstructor(ClassTree clazz, Iterable fields) { + assert clazz != null && fields != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(copy.getTreeUtilities().isEnum(clazz) ? Modifier.PRIVATE : Modifier.PUBLIC); + List parameters = new ArrayList(); + List statements = new ArrayList(); + ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class)); + for (VariableTree vt : fields) { + parameters.add(make.Variable(parameterModifiers, vt.getName(), vt.getType(), null)); + statements.add(make.ExpressionStatement(make.Assignment(make.MemberSelect(make.Identifier("this"), vt.getName()), make.Identifier(vt.getName())))); //NOI18N + } + BlockTree body = make.Block(statements, false); + return make.Method(make.Modifiers(mods), "", null, Collections. emptyList(), parameters, Collections.emptyList(), body, null); //NOI18N + } + + /** + * Creates a getter method for a field. + * + * @param clazz the class to create the getter within + * @param field field to create getter for + * @return the getter method + */ + public MethodTree createGetter(TypeElement clazz, VariableElement field) { + assert clazz != null && field != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(Modifier.PUBLIC); + if (field.getModifiers().contains(Modifier.STATIC)) + mods.add(Modifier.STATIC); + CharSequence name = field.getSimpleName(); + assert name.length() > 0; + TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), field); + StringBuilder sb = new StringBuilder(); + sb.append(type.getKind() == TypeKind.BOOLEAN ? "is" : "get").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N + BlockTree body = make.Block(Collections.singletonList(make.Return(make.Identifier(name))), false); + return make.Method(make.Modifiers(mods), sb, make.Type(type), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), body, null); + } + + /** + * Creates a getter method for a field. + * + * @param field field to create getter for + * @return the getter method + */ + public MethodTree createGetter(VariableTree field) { + assert field != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(Modifier.PUBLIC); + if (field.getModifiers().getFlags().contains(Modifier.STATIC)) + mods.add(Modifier.STATIC); + CharSequence name = field.getName(); + assert name.length() > 0; + Tree type = field.getType(); + StringBuilder sb = new StringBuilder(); + sb.append(type.getKind() == Tree.Kind.PRIMITIVE_TYPE && ((PrimitiveTypeTree)type).getPrimitiveTypeKind() == TypeKind.BOOLEAN ? "is" : "get").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N + BlockTree body = make.Block(Collections.singletonList(make.Return(make.Identifier(name))), false); + return make.Method(make.Modifiers(mods), sb, type, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), body, null); + } + + /** + * Creates a setter method for a field. + * + * @param clazz the class to create the setter within + * @param field field to create setter for + * @return the setter method + */ + public MethodTree createSetter(TypeElement clazz, VariableElement field) { + assert clazz != null && field != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(Modifier.PUBLIC); + boolean isStatic = field.getModifiers().contains(Modifier.STATIC); + if (isStatic) + mods.add(Modifier.STATIC); + CharSequence name = field.getSimpleName(); + assert name.length() > 0; + TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), field); + StringBuilder sb = new StringBuilder(); + sb.append("set").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N + List params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(type), null)); + BlockTree body = make.Block(Collections.singletonList(make.ExpressionStatement(make.Assignment(make.MemberSelect(isStatic? make.Identifier(field.getEnclosingElement().getSimpleName()) : make.Identifier("this"), name), make.Identifier(name)))), false); //NOI18N + return make.Method(make.Modifiers(mods), sb, make.Type(copy.getTypes().getNoType(TypeKind.VOID)), Collections.emptyList(), params, Collections.emptyList(), body, null); + } + + /** + * Creates a setter method for a field. + * + * @param clazz the class to create the setter within + * @param field field to create setter for + * @return the setter method + */ + public MethodTree createSetter(ClassTree clazz, VariableTree field) { + assert clazz != null && field != null; + TreeMaker make = copy.getTreeMaker(); + Set mods = EnumSet.of(Modifier.PUBLIC); + boolean isStatic = field.getModifiers().getFlags().contains(Modifier.STATIC); + if (isStatic) + mods.add(Modifier.STATIC); + CharSequence name = field.getName(); + assert name.length() > 0; + StringBuilder sb = new StringBuilder(); + sb.append("set").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N + List params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, field.getType(), null)); + BlockTree body = make.Block(Collections.singletonList(make.ExpressionStatement(make.Assignment(make.MemberSelect(isStatic? make.Identifier(clazz.getSimpleName()) : make.Identifier("this"), name), make.Identifier(name)))), false); //NOI18N + return make.Method(make.Modifiers(mods), sb, make.Type(copy.getTypes().getNoType(TypeKind.VOID)), Collections.emptyList(), params, Collections.emptyList(), body, null); + } + + /**Resolve full qualified name in the given context. Adds import statement as necessary. + * Returns name that resolved to a given FQN in given context (either simple name + * or full qualified name). Handles import conflicts. + * + *
Note: if the info passed to this method is not an instance of {@link WorkingCopy}, + * missing import statement is added from a separate modification task executed asynchronously. + *
Note: after calling this method, it is not permitted to rewrite copy.getCompilationUnit(). + * + * @param info CompilationInfo over which the method should work + * @param context in which the fully qualified should be resolved + * @param fqn the fully qualified name to resolve + * @return either a simple name or a FQN that will resolve to given fqn in given context + */ + public static String resolveImport(final CompilationInfo info, final TreePath context, final String fqn) throws NullPointerException, IOException { + if (info == null) + throw new NullPointerException(); + if (context == null) + throw new NullPointerException(); + if (fqn == null) + throw new NullPointerException(); + + CompilationUnitTree cut = info.getCompilationUnit(); + final Trees trees = info.getTrees(); + final Scope scope = trees.getScope(context); + String qName = fqn; + StringBuilder sqName = new StringBuilder(); + String sName = null; + boolean clashing = false; + ElementUtilities eu = info.getElementUtilities(); + ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() { + public boolean accept(Element e, TypeMirror type) { + return (e.getKind().isClass() || e.getKind().isInterface()) && trees.isAccessible(scope, (TypeElement)e); + } + }; + while(qName != null && qName.length() > 0) { + int lastDot = qName.lastIndexOf('.'); + String simple = qName.substring(lastDot < 0 ? 0 : lastDot + 1); + if (sName == null) + sName = simple; + else + sqName.insert(0, '.'); + sqName.insert(0, simple); + if (info.getElements().getTypeElement(qName) != null) { + boolean matchFound = false; + for(Element e : eu.getLocalMembersAndVars(scope, acceptor)) { + if (simple.contentEquals(e.getSimpleName())) { + //either a clash or already imported: + if (qName.contentEquals(((TypeElement)e).getQualifiedName())) { + return sqName.toString(); + } else if (fqn == qName) { + clashing = true; + } + matchFound = true; + break; + } + } + if (!matchFound) { + for(TypeElement e : eu.getGlobalTypes(acceptor)) { + if (simple.contentEquals(e.getSimpleName())) { + //either a clash or already imported: + if (qName.contentEquals(e.getQualifiedName())) { + return sqName.toString(); + } else if (fqn == qName) { + clashing = true; + } + break; + } + } + } + } + qName = lastDot < 0 ? null : qName.substring(0, lastDot); + } + if (clashing) + return fqn; + + //not imported/visible so far by any means: + if (info instanceof WorkingCopy) { + CompilationUnitTree nue = (CompilationUnitTree) ((WorkingCopy)info).getChangeSet().getChange(cut); + cut = nue != null ? nue : cut; + ((WorkingCopy)info).rewrite(info.getCompilationUnit(), addImports(cut, Collections.singletonList(fqn), ((WorkingCopy)info).getTreeMaker())); + } else { + RequestProcessor.getDefault().post(new Runnable() { + public void run() { + try { + info.getJavaSource().runModificationTask(new CancellableTask() { + public void cancel() { + } + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(Phase.ELEMENTS_RESOLVED); + copy.rewrite(copy.getCompilationUnit(), addImports(copy.getCompilationUnit(), Collections.singletonList(fqn), copy.getTreeMaker())); + } + }).commit(); + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } + } + }); + } + TypeElement te = info.getElements().getTypeElement(fqn); + if (te != null) { + ((JCCompilationUnit) info.getCompilationUnit()).namedImportScope.enterIfAbsent((Symbol) te); + } + + return sName; + } + + // private implementation -------------------------------------------------- + + private MethodTree createMethod(ExecutableElement element, DeclaredType type) { + TreeMaker make = copy.getTreeMaker(); + Set mods = element.getModifiers(); + Set flags = mods.isEmpty() ? EnumSet.noneOf(Modifier.class) : EnumSet.copyOf(mods); + boolean isAbstract = flags.remove(Modifier.ABSTRACT); + flags.remove(Modifier.NATIVE); + + ExecutableType et = (ExecutableType)copy.getTypes().asMemberOf(type, element); + List typeParams = new ArrayList(); + for (TypeParameterElement tpe: element.getTypeParameters()) { + List bounds = new ArrayList(); + for (TypeMirror bound : tpe.getBounds()) { + if (bound.getKind() != TypeKind.NULL) { + //if the bound is java.lang.Object, do not generate the extends clause: + if (bound.getKind() != TypeKind.DECLARED || !"java.lang.Object".contentEquals(((TypeElement)((DeclaredType)bound).asElement()).getQualifiedName())) //NOI18N + bounds.add((ExpressionTree)make.Type(bound)); + } + } + typeParams.add(make.TypeParameter(tpe.getSimpleName(), bounds)); + } + + Tree returnType = make.Type(et.getReturnType()); + + List params = new ArrayList(); + boolean isVarArgs = element.isVarArgs(); + Iterator formArgNames = element.getParameters().iterator(); + Iterator formArgTypes = et.getParameterTypes().iterator(); + ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class)); + while (formArgNames.hasNext() && formArgTypes.hasNext()) { + VariableElement formArgName = formArgNames.next(); + TypeMirror formArgType = formArgTypes.next(); + if (isVarArgs && !formArgNames.hasNext()) + parameterModifiers = make.Modifiers(1L<<34, Collections.emptyList()); + params.add(make.Variable(parameterModifiers, formArgName.getSimpleName(), make.Type(formArgType), null)); + } + + List throwsList = new ArrayList(); + for (TypeMirror tm : et.getThrownTypes()) { + throwsList.add((ExpressionTree)make.Type(tm)); + } + + BlockTree body; + List annotations = new ArrayList(); + if (isAbstract) { + List blockStatements = new ArrayList(); + TypeElement uoe = copy.getElements().getTypeElement("java.lang.UnsupportedOperationException"); //NOI18N + //TODO: if uoe == null: cannot resolve UnsupportedOperationException for some reason, create a different body in such a case + if (uoe != null) { + NewClassTree nue = make.NewClass(null, Collections.emptyList(), make.QualIdent(uoe), Collections.singletonList(make.Literal("Not supported yet.")), null); + blockStatements.add(make.Throw(nue)); + } + body = make.Block(blockStatements, false); + } else { + List arguments = new ArrayList(); + for (VariableElement ve : element.getParameters()) { + arguments.add(make.Identifier(ve.getSimpleName())); + } + MethodInvocationTree inv = make.MethodInvocation(Collections.emptyList(), make.MemberSelect(make.Identifier("super"), element.getSimpleName()), arguments); //NOI18N + StatementTree statement = copy.getTypes().getNoType(TypeKind.VOID) == element.getReturnType() ? + make.ExpressionStatement(inv) : make.Return(inv); + body = make.Block(Collections.singletonList(statement), false); + + //add @Override annotation if developing for 1.5: + if (supportsOverride(copy.getFileObject())) { + annotations.add(make.Annotation(make.Identifier("Override"), Collections.emptyList())); //NOI18N + } + } + + return make.Method(make.Modifiers(flags, annotations), element.getSimpleName(), returnType, typeParams, params, throwsList, body, null); + } + + private static boolean supportsOverride(FileObject fo) { + SpecificationVersion myVersion = new SpecificationVersion(SourceLevelQuery.getSourceLevel(fo)); + SpecificationVersion version = new SpecificationVersion("1.5"); //NOI18N + return myVersion.compareTo(version) >= 0; + } + + private static class ClassMemberComparator { + + public static int compare(Tree tree1, Tree tree2) { + if (tree1 == tree2) + return 0; + return getSortPriority(tree1) - getSortPriority(tree2); + } + + private static int getSortPriority(Tree tree) { + int ret = 0; + ModifiersTree modifiers = null; + switch (tree.getKind()) { + case CLASS: + ret = 4000; + modifiers = ((ClassTree)tree).getModifiers(); + break; + case METHOD: + MethodTree mt = (MethodTree)tree; + if (mt.getName().contentEquals("")) + ret = 200; + else + ret = 300; + modifiers = mt.getModifiers(); + break; + case VARIABLE: + ret = 100; + modifiers = ((VariableTree)tree).getModifiers(); + break; + } + if (modifiers != null) { + if (!modifiers.getFlags().contains(Modifier.STATIC)) + ret += 1000; + } + return ret; + } + } + + /** + * + * + */ + private static CompilationUnitTree addImports(CompilationUnitTree cut, List toImport, TreeMaker make) + throws IOException { + // do not modify the list given by the caller (may be reused or immutable). + toImport = new ArrayList(toImport); + Collections.sort(toImport); + + List imports = new ArrayList(cut.getImports()); + int currentToImport = toImport.size() - 1; + int currentExisting = imports.size() - 1; + + while (currentToImport >= 0 && currentExisting >= 0) { + String currentToImportText = toImport.get(currentToImport); + + while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0)) + currentExisting--; + + if (currentExisting >= 0) { + imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false)); + currentToImport--; + } + } + // we are at the head of import section and we still have some imports + // to add, put them to the very beginning + while (currentToImport >= 0) { + String importText = toImport.get(currentToImport); + imports.add(0, make.Import(make.Identifier(importText), false)); + currentToImport--; + } + // return a copy of the unit with changed imports section + return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile()); + } +} Index: src/org/netbeans/api/java/source/ElementUtilities.java =================================================================== RCS file: /cvs/java/source/src/org/netbeans/api/java/source/ElementUtilities.java,v retrieving revision 1.11 diff -u -r1.11 ElementUtilities.java --- src/org/netbeans/api/java/source/ElementUtilities.java 11 Jan 2007 12:24:03 -0000 1.11 +++ src/org/netbeans/api/java/source/ElementUtilities.java 14 Jun 2007 14:40:51 -0000 @@ -13,7 +13,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.api.java.source; @@ -45,9 +45,11 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; @@ -401,4 +403,52 @@ public boolean implementsMethod(ExecutableElement element) { return delegate.implementsMethod(element); } + + /**Find all methods in given type and its supertypes, which are not implemented. + * + * @param type to inspect + * @return list of all unimplemented methods + */ + public List findUnimplementedMethods(TypeElement impl) { + return findUnimplementedMethods(impl, impl); + } + + // private implementation -------------------------------------------------- + + + private List findUnimplementedMethods(TypeElement impl, TypeElement element) { + List undef = new ArrayList(); + if (element.getModifiers().contains(Modifier.ABSTRACT)) { + for (Element e : element.getEnclosedElements()) { + if (e.getKind() == ElementKind.METHOD && e.getModifiers().contains(Modifier.ABSTRACT)) { + ExecutableElement ee = (ExecutableElement)e; + Element eeImpl = getImplementationOf(ee, impl); + if (eeImpl == null || eeImpl == ee) + undef.add(ee); + } + } + } + Types types = JavacTypes.instance(ctx); + DeclaredType implType = (DeclaredType)impl.asType(); + for (TypeMirror t : types.directSupertypes(element.asType())) { + for (ExecutableElement ee : findUnimplementedMethods(impl, (TypeElement)((DeclaredType)t).asElement())) { + //check if "the same" method has already been added: + boolean exists = false; + TypeMirror eeType = types.asMemberOf(implType, ee); + for (ExecutableElement existing : undef) { + if (existing.getSimpleName().contentEquals(ee.getSimpleName())) { + TypeMirror existingType = types.asMemberOf(implType, existing); + if (types.isSameType(eeType, existingType)) { + exists = true; + break; + } + } + } + if (!exists) + undef.add(ee); + } + } + return undef; + } + } Index: test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java =================================================================== RCS file: test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java diff -N test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java 14 Jun 2007 14:40:51 -0000 @@ -0,0 +1,706 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.java.source; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import java.beans.PropertyVetoException; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileLock; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.LocalFileSystem; +import org.openide.filesystems.Repository; + +/** + * + * @author Jan Lahoda, Dusan Balek + */ +public class GeneratorUtilitiesTest extends NbTestCase { + + public GeneratorUtilitiesTest(String testName) { + super(testName); + } + + protected void setUp() throws Exception { + SourceUtilsTestUtil.prepareTest(new String[0], new Object[0]); + } + + private void writeIntoFile(FileObject file, String what) throws Exception { + FileLock lock = file.lock(); + OutputStream out = file.getOutputStream(lock); + + try { + out.write(what.getBytes()); + } finally { + out.close(); + lock.releaseLock(); + } + } + + public void testInsertFields() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.FIELD), new InsertMemberValidator(2)); + } + + public void testInsertStaticFields() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.FIELD), new InsertMemberValidator(1)); + } + + public void testInsertConstructors() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.CONSTRUCTOR), new InsertMemberValidator(3)); + } + + public void testInsertMethods() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.METHOD), new InsertMemberValidator(4)); + } + + public void testInsertStaticMethods() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.METHOD), new InsertMemberValidator(1)); + } + + public void testInsertNestedClasses() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.CLASS), new InsertMemberValidator(5)); + } + + public void testInsertStaticNestedClasses() throws Exception { + performTest("package test;\npublic class Test {\npublic static int i;\nprivate String s;\npublic Test(){\n}\npublic void op(){\n}\nprivate class Nested{\n}\n}\n", + new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.CLASS), new InsertMemberValidator(4)); + } + + public void testImplementAllAbstractMethods1() throws Exception { + performTest("package test;\npublic class Test implements Runnable {\npublic Test(){\n}\n }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator()); + } + + public void testImplementAllAbstractMethods2() throws Exception { + performTest("package test;\npublic class Test implements Runnable {\n }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator()); + } + + public void testImplementAllAbstractMethods3() throws Exception { + performTest("package test;\npublic class Test implements Runnable {\npublic void testMethod() {\n} }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator()); + } + + public void testImplementAllAbstractMethods4() throws Exception { + performTest("package test;\npublic class Test implements Runnable {\npublic Test(){\n}\npublic void testMethod() {\n} }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator()); + } + + public void testImplementAllAbstractMethods5() throws Exception { + performTest("package test;import java.util.concurrent.*;\npublic class Test implements Future{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(89), new SimpleFutureValidator("java.lang.String")); + } + + public void testImplementAllAbstractMethods6() throws Exception { + performTest("package test;import java.util.concurrent.*;\npublic class Test implements Future>{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(123), new FutureValidator() { + protected TypeMirror returnType(CompilationInfo info) { + return info.getTreeUtilities().parseType("java.util.List", info.getElements().getTypeElement("test.Test")); + } + }); + } + + public void testImplementAllAbstractMethods7() throws Exception { + performTest("package test;\npublic class Test extends java.util.AbstractList{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(64), null); + } + + /** issue #85966 + */ + public void testImplementAllAbstractMethods8() throws Exception { + performTest("package test;\npublic class Test implements XX {\npublic Test(){\n} }\ninterface XX {\npublic void test(String ... a);}", new AddAllAbstractMethodsTask(42), new Validator() { + public void validate(CompilationInfo info) { + TypeElement clazz = info.getElements().getTypeElement("test.Test"); + ExecutableElement method = ElementFilter.methodsIn(clazz.getEnclosedElements()).get(0); + assertTrue(method.isVarArgs()); + } + }); + } + + public void testImplementAllAbstractMethods9() throws Exception { + performTest("package test;\npublic class Test implements java.util.concurrent.ExecutorService {\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(30), null); + } + + public void testImplementAllAbstractMethodsa() throws Exception { + performTest("package test;\npublic class Test implements XX {\npublic Test(){\n} }\ninterface XX {public void test(T t);}", new AddAllAbstractMethodsTask(30), null); + } + + public void testOverrideMethods1() throws Exception { + performTest("package test;\npublic class Test {\npublic Test(){\n}\n }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator()); + } + + public void testOverrideMethods2() throws Exception { + performTest("package test;\npublic class Test {\n }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator()); + } + + public void testOverrideMethods3() throws Exception { + performTest("package test;\npublic class Test {\npublic void testMethod() {\n} }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator()); + } + + public void testOverrideMethods4() throws Exception { + performTest("package test;\npublic class Test {\npublic Test(){\n}\npublic void testMethod() {\n} }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator()); + } + + public void testOverrideMethods5() throws Exception { + performTest("package test;\npublic class Test extends XX {\npublic Test(){\n} }\nclass XX {\npublic void test(T ... a) {}}", new OverrideMethodsTask(30), new Validator() { + public void validate(CompilationInfo info) { + TypeElement clazz = info.getElements().getTypeElement("test.Test"); + ExecutableElement method = ElementFilter.methodsIn(clazz.getEnclosedElements()).get(0); + assertTrue(method.getSimpleName().contentEquals("test")); + TypeElement te = info.getElements().getTypeElement("java.lang.Number"); + assertEquals(1, method.getParameters().size()); + TypeMirror paramType = method.getParameters().get(0).asType(); + assertNotNull(paramType); + assertTrue(paramType.getKind() == TypeKind.ARRAY); + assertTrue(info.getTypes().isSameType(te.asType(), ((ArrayType)paramType).getComponentType())); + assertTrue(method.isVarArgs()); + } + }); + } + + public void testConstructor1() throws Exception { + performTest("package test;\npublic class Test {\nprivate int test;\n}\n", new ConstructorTask(34), new ConstructorValidator()); + } + + public void testConstructor2() throws Exception { + performTest("package test;\npublic class Test extends XX {\nprivate int test;\n}\nclass XX {\npublic XX(boolean b){\n}\n}\n", new ConstructorTask(30), new ConstructorValidator()); + } + + public void testGetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true)); + } + + public void testBooleanGetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true)); + } + + public void testStaticGetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate static int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true)); + } + + public void testStaticBooleanGetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate static boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true)); + } + + public void testSetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false)); + } + + public void testBooleanSetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false)); + } + + public void testStaticSetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate static int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false)); + } + + public void testStaticBooleanSetter() throws Exception { + performTest("package test;\npublic class Test {\nprivate static boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false)); + } + + public static interface Validator { + + public void validate(CompilationInfo info); + + } + + private static class InsertMembersTask implements CancellableTask { + + private int offset; + private Set modifiers; + private ElementKind kind; + + public InsertMembersTask(int offset, Set modifiers, ElementKind kind) { + this.offset = offset; + this.modifiers = modifiers; + this.kind = kind; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + TreeMaker maker = copy.getTreeMaker(); + ArrayList members = new ArrayList(1); + switch(kind) { + case FIELD: + members.add(maker.Variable(maker.Modifiers(modifiers), "test", maker.PrimitiveType(TypeKind.INT), null)); + break; + case METHOD: + members.add(maker.Method(maker.Modifiers(modifiers), "test", maker.PrimitiveType(TypeKind.VOID), + Collections.emptyList(), Collections.emptyList(), + Collections.emptyList(), maker.Block(Collections.emptyList(), false), + null)); + break; + case CONSTRUCTOR: + members.add(maker.Method(maker.Modifiers(modifiers), "", null, + Collections.emptyList(), Collections.singletonList(maker.Variable( + maker.Modifiers(EnumSet.noneOf(Modifier.class)), "s", maker.Identifier("String"), null)), + Collections.emptyList(), maker.Block(Collections.emptyList(), false), + null)); + break; + case CLASS: + members.add(maker.Class(maker.Modifiers(modifiers), "test", Collections.emptyList(), + null, Collections.emptyList(), Collections.emptyList())); + break; + } + ClassTree newCt = utilities.insertClassMembers(ct, members); + copy.rewrite(ct, newCt); + } + } + + private final class InsertMemberValidator implements Validator { + + private int testIdx; + + public InsertMemberValidator(int testIdx) { + this.testIdx = testIdx; + } + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + ClassTree ct = info.getTrees().getTree(test); + assertNotNull(ct); + + int foundTestIdx = -1; + + int idx = 0; + for (Tree t : ct.getMembers()) { + Name name = null; + switch(t.getKind()) { + case VARIABLE: + name = ((VariableTree)t).getName(); + break; + case METHOD: + name = ((MethodTree)t).getName(); + break; + case CLASS: + name = ((ClassTree)t).getSimpleName(); + break; + } + if (name != null) { + if (name.contentEquals("test")) { + assertEquals(-1, foundTestIdx); + foundTestIdx = idx; + } else if (name.contentEquals("") && ((MethodTree)t).getParameters().size() > 0) { + assertEquals(-1, foundTestIdx); + foundTestIdx = idx; + } + } + idx++; + } + + assertEquals(testIdx, foundTestIdx); + } + + } + + private static class AddAllAbstractMethodsTask implements CancellableTask { + + private int offset; + + public AddAllAbstractMethodsTask(int offset) { + this.offset = offset; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + TypeElement te = (TypeElement)copy.getTrees().getElement(tp); + assertNotNull(te); + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + ClassTree newCt = utilities.insertClassMembers(ct, utilities.createAllAbstractMethodImplementations(te)); + copy.rewrite(ct, newCt); + } + } + + private final class RunnableValidator implements Validator { + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + + boolean foundRunMethod = false; + + for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) { + if ("run".equals(ee.getSimpleName().toString())) { + if (ee.getParameters().isEmpty()) { + assertFalse(foundRunMethod); + foundRunMethod = true; + } + } + } + + assertTrue(foundRunMethod); + } + + } + + private final class SimpleFutureValidator extends FutureValidator { + + private String returnTypeName; + + public SimpleFutureValidator(String returnTypeName) { + this.returnTypeName = returnTypeName; + } + + protected TypeMirror returnType(CompilationInfo info) { + TypeElement returnTypeElement = info.getElements().getTypeElement(returnTypeName); + + return returnTypeElement.asType(); + } + } + + private abstract class FutureValidator implements Validator { + + protected abstract TypeMirror returnType(CompilationInfo info); + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + TypeMirror returnType = returnType(info); + + boolean hasShortGet = false; + boolean hasLongGet = false; + + for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) { + if ("get".equals(ee.getSimpleName().toString())) { + if (ee.getParameters().isEmpty()) { + assertFalse(hasShortGet); + assertTrue(info.getTypes().isSameType(returnType, ee.getReturnType())); + hasShortGet = true; + } + if (ee.getParameters().size() == 2) { + assertFalse(hasLongGet); + assertTrue(info.getTypes().isSameType(returnType, ee.getReturnType())); + hasLongGet = true; + } + } + } + + assertTrue(hasShortGet); + assertTrue(hasLongGet); + } + + } + + private static class SimpleOverrideMethodsTask implements CancellableTask { + + private int offset; + + public SimpleOverrideMethodsTask(int offset) { + this.offset = offset; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + TypeElement te = (TypeElement)copy.getTrees().getElement(tp); + assertNotNull(te); + ArrayList methods = new ArrayList(2); + TypeElement object = copy.getElements().getTypeElement("java.lang.Object"); + assertNotNull(object); + for (ExecutableElement method : ElementFilter.methodsIn(object.getEnclosedElements())) { + if (method.getSimpleName().contentEquals("clone")) + methods.add(method); + else if (method.getSimpleName().contentEquals("toString")) + methods.add(method); + } + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + ClassTree newCt = utilities.insertClassMembers(ct, utilities.createOverridingMethods(te, methods)); + copy.rewrite(ct, newCt); + } + } + + private static class OverrideMethodsTask implements CancellableTask { + + private int offset; + + public OverrideMethodsTask(int offset) { + this.offset = offset; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + TypeElement te = (TypeElement)copy.getTrees().getElement(tp); + assertNotNull(te); + ArrayList methods = new ArrayList(1); + TypeElement sup = (TypeElement)((DeclaredType)te.getSuperclass()).asElement(); + assertNotNull(sup); + for (ExecutableElement method : ElementFilter.methodsIn(sup.getEnclosedElements())) { + if (method.getSimpleName().contentEquals("test")) + methods.add(method); + } + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + ClassTree newCt = utilities.insertClassMembers(ct, utilities.createOverridingMethods(te, methods)); + copy.rewrite(ct, newCt); + } + } + + private final class CloneAndToStringValidator implements Validator { + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + + boolean foundCloneMethod = false; + boolean foundToStringMethod = false; + + for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) { + if (ee.getSimpleName().contentEquals("clone")) { + if (ee.getParameters().isEmpty()) { + assertFalse(foundCloneMethod); + foundCloneMethod = true; + } + } else if (ee.getSimpleName().contentEquals("toString")) { + if (ee.getParameters().isEmpty()) { + assertFalse(foundToStringMethod); + foundToStringMethod = true; + } + } + } + + assertTrue(foundCloneMethod); + assertTrue(foundToStringMethod); + } + + } + + private static class ConstructorTask implements CancellableTask { + + private int offset; + + public ConstructorTask(int offset) { + this.offset = offset; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + TypeElement te = (TypeElement)copy.getTrees().getElement(tp); + assertNotNull(te); + List vars = ElementFilter.fieldsIn(te.getEnclosedElements()); + TypeElement sup = (TypeElement)((DeclaredType)te.getSuperclass()).asElement(); + assertNotNull(sup); + List ctors = sup.getQualifiedName().contentEquals("java.lang.Object") + ? null : ElementFilter.constructorsIn(sup.getEnclosedElements()); + if (ctors != null) + assertEquals(1, ctors.size()); + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + ClassTree newCt = utilities.insertClassMember(ct, utilities.createConstructor(te, vars, ctors != null ? ctors.get(0) : null)); + copy.rewrite(ct, newCt); + } + } + + private final class ConstructorValidator implements Validator { + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + VariableElement var = ElementFilter.fieldsIn(test.getEnclosedElements()).get(0); + TypeElement sup = (TypeElement)((DeclaredType)test.getSuperclass()).asElement(); + ExecutableElement supCtor = sup.getQualifiedName().contentEquals("java.lang.Object") + ? null : ElementFilter.constructorsIn(sup.getEnclosedElements()).get(0); + + List ctors = ElementFilter.constructorsIn(test.getEnclosedElements()); + assertEquals(1, ctors.size()); + ExecutableElement ctor = ctors.get(0); + + assertEquals(supCtor == null ? 1 : 2, ctor.getParameters().size()); + } + + } + + private static class GetterSetterTask implements CancellableTask { + + private int offset; + private boolean getter; + + public GetterSetterTask(int offset, boolean getter) { + this.offset = offset; + this.getter = getter; + } + + public void cancel() { + } + + public void run(WorkingCopy copy) throws Exception { + copy.toPhase(JavaSource.Phase.RESOLVED); + TreePath tp = copy.getTreeUtilities().pathFor(offset); + assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS); + ClassTree ct = (ClassTree)tp.getLeaf(); + TypeElement te = (TypeElement)copy.getTrees().getElement(tp); + assertNotNull(te); + List vars = ElementFilter.fieldsIn(te.getEnclosedElements()); + assertEquals(1, vars.size()); + GeneratorUtilities utilities = GeneratorUtilities.get(copy); + assertNotNull(utilities); + ClassTree newCt = utilities.insertClassMember(ct, getter + ? utilities.createGetter(te, vars.get(0)) + : utilities.createSetter(te, vars.get(0))); + copy.rewrite(ct, newCt); + } + } + + private final class GetterSetterValidator implements Validator { + + private boolean getter; + + public GetterSetterValidator(boolean getter) { + this.getter = getter; + } + + public void validate(CompilationInfo info) { + TypeElement test = info.getElements().getTypeElement("test.Test"); + VariableElement var = ElementFilter.fieldsIn(test.getEnclosedElements()).get(0); + + List methods = ElementFilter.methodsIn(test.getEnclosedElements()); + assertEquals(1, methods.size()); + ExecutableElement method = methods.get(0); + + TypeMirror type = info.getTypes().asMemberOf((DeclaredType)test.asType(), var); + if (getter) { + assertTrue(info.getTypes().isSameType(type, method.getReturnType())); + assertEquals(type.getKind() == TypeKind.BOOLEAN ? "isTest" : "getTest", method.getSimpleName().toString()); + assertEquals(0, method.getParameters().size()); + } else { + assertTrue(info.getTypes().isSameType(info.getTypes().getNoType(TypeKind.VOID), method.getReturnType())); + assertEquals("setTest", method.getSimpleName().toString()); + assertEquals(1, method.getParameters().size()); + assertTrue(info.getTypes().isSameType(type, method.getParameters().get(0).asType())); + } + } + + } + + private void performTest(String sourceCode, final CancellableTask task, final Validator validator) throws Exception { + FileObject root = makeScratchDir(this); + + FileObject sourceDir = root.createFolder("src"); + FileObject buildDir = root.createFolder("build"); + FileObject cacheDir = root.createFolder("cache"); + + FileObject source = sourceDir.createFolder("test").createData("Test.java"); + + writeIntoFile(source, sourceCode); + + SourceUtilsTestUtil.prepareTest(sourceDir, buildDir, cacheDir, new FileObject[0]); + + JavaSource js = JavaSource.forFileObject(source); + + ModificationResult result = js.runModificationTask(task); + + result.commit(); + + js.runUserActionTask(new CancellableTask() { + public void cancel() { + } + public void run(CompilationController controller) throws Exception { + System.err.println("text:"); + System.err.println(controller.getText()); + controller.toPhase(JavaSource.Phase.RESOLVED); + + assertEquals(controller.getDiagnostics().toString(), 0, controller.getDiagnostics().size()); + + if (validator != null) + validator.validate(controller); + } + }, true); + } + + /**Copied from org.netbeans.api.project. + * Create a scratch directory for tests. + * Will be in /tmp or whatever, and will be empty. + * If you just need a java.io.File use clearWorkDir + getWorkDir. + */ + public static FileObject makeScratchDir(NbTestCase test) throws IOException { + test.clearWorkDir(); + File root = test.getWorkDir(); + assert root.isDirectory() && root.list().length == 0; + FileObject fo = FileUtil.toFileObject(root); + if (fo != null) { + // Presumably using masterfs. + return fo; + } else { + // For the benefit of those not using masterfs. + LocalFileSystem lfs = new LocalFileSystem(); + try { + lfs.setRootDirectory(root); + } catch (PropertyVetoException e) { + assert false : e; + } + Repository.getDefault().addFileSystem(lfs); + return lfs.getRoot(); + } + } + +}