This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 106678
Collapse All | Expand All

(-)src/org/netbeans/api/java/source/GeneratorUtilities.java (+593 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.api.java.source;
21
22
import com.sun.source.tree.AnnotationTree;
23
import com.sun.source.tree.BlockTree;
24
import com.sun.source.tree.ClassTree;
25
import com.sun.source.tree.CompilationUnitTree;
26
import com.sun.source.tree.ExpressionTree;
27
import com.sun.source.tree.IdentifierTree;
28
import com.sun.source.tree.ImportTree;
29
import com.sun.source.tree.MemberSelectTree;
30
import com.sun.source.tree.MethodInvocationTree;
31
import com.sun.source.tree.MethodTree;
32
import com.sun.source.tree.ModifiersTree;
33
import com.sun.source.tree.NewClassTree;
34
import com.sun.source.tree.PrimitiveTypeTree;
35
import com.sun.source.tree.Scope;
36
import com.sun.source.tree.StatementTree;
37
import com.sun.source.tree.Tree;
38
import com.sun.source.tree.TypeParameterTree;
39
import com.sun.source.tree.VariableTree;
40
import com.sun.source.util.TreePath;
41
import com.sun.source.util.Trees;
42
import com.sun.tools.javac.code.Symbol;
43
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
44
import java.io.IOException;
45
46
import java.util.ArrayList;
47
import java.util.Collections;
48
import java.util.EnumSet;
49
import java.util.Iterator;
50
import java.util.List;
51
import java.util.Set;
52
import javax.lang.model.element.Element;
53
54
import javax.lang.model.element.ElementKind;
55
import javax.lang.model.element.ExecutableElement;
56
import javax.lang.model.element.Modifier;
57
import javax.lang.model.element.TypeElement;
58
import javax.lang.model.element.TypeParameterElement;
59
import javax.lang.model.element.VariableElement;
60
import javax.lang.model.type.DeclaredType;
61
import javax.lang.model.type.ExecutableType;
62
import javax.lang.model.type.TypeKind;
63
import javax.lang.model.type.TypeMirror;
64
65
import org.netbeans.api.java.queries.SourceLevelQuery;
66
import org.netbeans.api.java.source.JavaSource.Phase;
67
import org.openide.filesystems.FileObject;
68
import org.openide.modules.SpecificationVersion;
69
import org.openide.util.Exceptions;
70
import org.openide.util.RequestProcessor;
71
72
/**
73
 *
74
 * @author Jan Lahoda, Dusan Balek
75
 */
76
public class GeneratorUtilities {
77
    
78
    private WorkingCopy copy;
79
    
80
    private  GeneratorUtilities(WorkingCopy copy) {
81
        this.copy = copy;
82
    }
83
    
84
    /**
85
     * Returns the instance of this class
86
     * 
87
     * @param copy 
88
     * @return the {@link GeneratorUtilities} instance
89
     */
90
    public static GeneratorUtilities get(WorkingCopy copy) {
91
        return new GeneratorUtilities(copy);
92
    }
93
    
94
    /**
95
     * Inserts a member to a class. Using the rules specified in the {@link CodeStyle}
96
     * it finds the proper place for the member and calls {@link TreeMaker.insertClassMember}
97
     * 
98
     * @param clazz the class to insert the member to
99
     * @param member the member to add
100
     * @return the modified class
101
     */
102
    public ClassTree insertClassMember(ClassTree clazz, Tree member) {
103
        assert clazz != null && member != null;
104
        int idx = 0;
105
        for (Tree tree : clazz.getMembers()) {
106
            if (ClassMemberComparator.compare(member, tree) < 0)
107
                break;
108
            idx++;
109
        }
110
        return copy.getTreeMaker().insertClassMember(clazz, idx, member);        
111
    }
112
    
113
    /**
114
     * Inserts members to a class. Using the rules specified in the {@link CodeStyle}
115
     * it finds the proper place for each of the members and calls {@link TreeMaker.insertClassMember}
116
     * 
117
     * @param clazz the class to insert the members to
118
     * @param members the members to insert
119
     * @return the modified class
120
     */
121
    public ClassTree insertClassMembers(ClassTree clazz, Iterable<? extends Tree> members) {
122
        assert members != null;
123
        for (Tree member : members)
124
            clazz = insertClassMember(clazz, member);
125
        return clazz;
126
    }
127
128
    /**
129
     * Creates implementations of the all abstract methods within a class.
130
     * 
131
     * @param clazz the class to create the implementations within
132
     * @return the abstract method implementations
133
     */
134
    public List<? extends MethodTree> createAllAbstractMethodImplementations(TypeElement clazz) {
135
        return createAbstractMethodImplementations(clazz, copy.getElementUtilities().findUnimplementedMethods(clazz));
136
    }
137
    
138
    /**
139
     * Creates implementations of abstract methods within a class.
140
     * 
141
     * @param clazz the class to create the implementations within
142
     * @param methods the abstract methods to implement
143
     * @return the abstract method implementations
144
     */
145
    public List<? extends MethodTree> createAbstractMethodImplementations(TypeElement clazz, Iterable<? extends ExecutableElement> methods) {
146
        assert methods != null;
147
        List<MethodTree> ret = new ArrayList<MethodTree>();
148
        for(ExecutableElement method : methods)
149
            ret.add(createAbstractMethodImplementation(clazz, method));
150
        return ret;
151
    }
152
    
153
    /**
154
     * Creates an implementation of an abstract method within a class.
155
     * 
156
     * @param clazz the class to create the implementation within
157
     * @param method the abstract method to implement
158
     * @return the abstract method implementation
159
     */
160
    public MethodTree createAbstractMethodImplementation(TypeElement clazz, ExecutableElement method) {
161
        assert clazz != null && method != null;
162
        return createMethod(method, (DeclaredType)clazz.asType());
163
    }
164
    
165
    /**
166
     * Creates overriding methods within a class.
167
     * 
168
     * @param clazz the class to create the methods within
169
     * @param methods the methods to override
170
     * @return the overriding methods
171
     */
172
    public List<? extends MethodTree> createOverridingMethods(TypeElement clazz, Iterable<? extends ExecutableElement> methods) {
173
        assert methods != null;
174
        List<MethodTree> ret = new ArrayList<MethodTree>();
175
        for(ExecutableElement method : methods)
176
            ret.add(createOverridingMethod(clazz, method));
177
        return ret;
178
    }
179
    
180
    /**
181
     * Creates an overriding method within a class.
182
     * 
183
     * @param clazz the class to create the method within
184
     * @param method the method to override
185
     * @return the overriding method
186
     */
187
    public MethodTree createOverridingMethod(TypeElement clazz, ExecutableElement method) {
188
        assert clazz != null && method != null;
189
        return createMethod(method, (DeclaredType)clazz.asType());
190
    }
191
192
    /**
193
     * Creates a class constructor.
194
     * 
195
     * @param clazz the class to create the constructor for
196
     * @param fields fields to be initialized by the constructor
197
     * @param constructor inherited constructor to be called
198
     * @return the constructor
199
     */
200
    public MethodTree createConstructor(TypeElement clazz, Iterable<? extends VariableElement> fields, ExecutableElement constructor) {
201
        assert clazz != null && fields != null;
202
        TreeMaker make = copy.getTreeMaker();
203
        Set<Modifier> mods = EnumSet.of(clazz.getKind() == ElementKind.ENUM ? Modifier.PRIVATE : Modifier.PUBLIC);
204
        List<VariableTree> parameters = new ArrayList<VariableTree>();
205
        List<StatementTree> statements = new ArrayList<StatementTree>();
206
        ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class));        
207
        if (constructor != null && !constructor.getParameters().isEmpty()) {
208
            List<ExpressionTree> arguments = new ArrayList<ExpressionTree>();
209
            for (VariableElement ve : constructor.getParameters()) {
210
                parameters.add(make.Variable(parameterModifiers, ve.getSimpleName(), make.Type(ve.asType()), null));
211
                arguments.add(make.Identifier(ve.getSimpleName())); //NOI18N
212
            }
213
            statements.add(make.ExpressionStatement(make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier("super"), arguments))); //NOI18N
214
        }
215
        for (VariableElement ve : fields) {
216
            TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), ve);
217
            parameters.add(make.Variable(parameterModifiers, ve.getSimpleName(), make.Type(type), null));
218
            statements.add(make.ExpressionStatement(make.Assignment(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier(ve.getSimpleName())))); //NOI18N
219
        }
220
        BlockTree body = make.Block(statements, false);
221
        return make.Method(make.Modifiers(mods), "<init>", null, Collections.<TypeParameterTree> emptyList(), parameters, Collections.<ExpressionTree>emptyList(), body, null); //NOI18N
222
    }
223
    
224
    /**
225
     * Creates a class constructor.
226
     * 
227
     * @param clazz the class to create the constructor for
228
     * @param fields fields to be initialized by the constructor
229
     * @return the constructor
230
     */
231
    public MethodTree createConstructor(ClassTree clazz, Iterable<? extends VariableTree> fields) {
232
        assert clazz != null && fields != null;
233
        TreeMaker make = copy.getTreeMaker();
234
        Set<Modifier> mods = EnumSet.of(copy.getTreeUtilities().isEnum(clazz) ? Modifier.PRIVATE : Modifier.PUBLIC);
235
        List<VariableTree> parameters = new ArrayList<VariableTree>();
236
        List<StatementTree> statements = new ArrayList<StatementTree>();
237
        ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class));        
238
        for (VariableTree vt : fields) {
239
            parameters.add(make.Variable(parameterModifiers, vt.getName(), vt.getType(), null));
240
            statements.add(make.ExpressionStatement(make.Assignment(make.MemberSelect(make.Identifier("this"), vt.getName()), make.Identifier(vt.getName())))); //NOI18N
241
        }
242
        BlockTree body = make.Block(statements, false);
243
        return make.Method(make.Modifiers(mods), "<init>", null, Collections.<TypeParameterTree> emptyList(), parameters, Collections.<ExpressionTree>emptyList(), body, null); //NOI18N
244
    }
245
    
246
    /**
247
     * Creates a getter method for a field.
248
     * 
249
     * @param clazz the class to create the getter within
250
     * @param field field to create getter for
251
     * @return the getter method
252
     */
253
    public MethodTree createGetter(TypeElement clazz, VariableElement field) {
254
        assert clazz != null && field != null;
255
        TreeMaker make = copy.getTreeMaker();
256
        Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
257
        if (field.getModifiers().contains(Modifier.STATIC))
258
            mods.add(Modifier.STATIC);
259
        CharSequence name = field.getSimpleName();
260
        assert name.length() > 0;
261
        TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), field);
262
        StringBuilder sb = new StringBuilder();
263
        sb.append(type.getKind() == TypeKind.BOOLEAN ? "is" : "get").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N
264
        BlockTree body = make.Block(Collections.singletonList(make.Return(make.Identifier(name))), false);
265
        return make.Method(make.Modifiers(mods), sb, make.Type(type), Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>emptyList(), body, null);
266
    }
267
    
268
    /**
269
     * Creates a getter method for a field.
270
     * 
271
     * @param field field to create getter for
272
     * @return the getter method
273
     */
274
    public MethodTree createGetter(VariableTree field) {
275
        assert field != null;
276
        TreeMaker make = copy.getTreeMaker();
277
        Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
278
        if (field.getModifiers().getFlags().contains(Modifier.STATIC))
279
            mods.add(Modifier.STATIC);
280
        CharSequence name = field.getName();
281
        assert name.length() > 0;
282
        Tree type = field.getType();
283
        StringBuilder sb = new StringBuilder();
284
        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
285
        BlockTree body = make.Block(Collections.singletonList(make.Return(make.Identifier(name))), false);
286
        return make.Method(make.Modifiers(mods), sb, type, Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>emptyList(), body, null);
287
    }
288
    
289
    /**
290
     * Creates a setter method for a field.
291
     * 
292
     * @param clazz the class to create the setter within
293
     * @param field field to create setter for
294
     * @return the setter method
295
     */
296
    public MethodTree createSetter(TypeElement clazz, VariableElement field) {
297
        assert clazz != null && field != null;
298
        TreeMaker make = copy.getTreeMaker();
299
        Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
300
        boolean isStatic = field.getModifiers().contains(Modifier.STATIC);
301
        if (isStatic)
302
            mods.add(Modifier.STATIC);
303
        CharSequence name = field.getSimpleName();
304
        assert name.length() > 0;
305
        TypeMirror type = copy.getTypes().asMemberOf((DeclaredType)clazz.asType(), field);
306
        StringBuilder sb = new StringBuilder();
307
        sb.append("set").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N
308
        List<VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(type), null));
309
        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
310
        return make.Method(make.Modifiers(mods), sb, make.Type(copy.getTypes().getNoType(TypeKind.VOID)), Collections.<TypeParameterTree>emptyList(), params, Collections.<ExpressionTree>emptyList(), body, null);
311
    }
312
313
    /**
314
     * Creates a setter method for a field.
315
     * 
316
     * @param clazz the class to create the setter within
317
     * @param field field to create setter for
318
     * @return the setter method
319
     */
320
    public MethodTree createSetter(ClassTree clazz, VariableTree field) {
321
        assert clazz != null && field != null;
322
        TreeMaker make = copy.getTreeMaker();
323
        Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
324
        boolean isStatic = field.getModifiers().getFlags().contains(Modifier.STATIC);
325
        if (isStatic)
326
            mods.add(Modifier.STATIC);
327
        CharSequence name = field.getName();
328
        assert name.length() > 0;
329
        StringBuilder sb = new StringBuilder();
330
        sb.append("set").append(Character.toUpperCase(name.charAt(0))).append(name.subSequence(1, name.length())); //NOI18N
331
        List<VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, field.getType(), null));
332
        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
333
        return make.Method(make.Modifiers(mods), sb, make.Type(copy.getTypes().getNoType(TypeKind.VOID)), Collections.<TypeParameterTree>emptyList(), params, Collections.<ExpressionTree>emptyList(), body, null);
334
    }
335
    
336
    /**Resolve full qualified name in the given context. Adds import statement as necessary.
337
     * Returns name that resolved to a given FQN in given context (either simple name
338
     * or full qualified name). Handles import conflicts.
339
     *
340
     * <br><b>Note:</b> if the <code>info</code> passed to this method is not an instance of {@link WorkingCopy},
341
     * missing import statement is added from a separate modification task executed asynchronously.
342
     * <br><b>Note:</b> after calling this method, it is not permitted to rewrite copy.getCompilationUnit().
343
     *
344
     * @param info CompilationInfo over which the method should work
345
     * @param context in which the fully qualified should be resolved
346
     * @param fqn the fully qualified name to resolve
347
     * @return either a simple name or a FQN that will resolve to given fqn in given context
348
     */
349
    public static String resolveImport(final CompilationInfo info, final TreePath context, final String fqn) throws NullPointerException, IOException {
350
        if (info == null)
351
            throw new NullPointerException();
352
        if (context == null)
353
            throw new NullPointerException();
354
        if (fqn == null)
355
            throw new NullPointerException();
356
        
357
        CompilationUnitTree cut = info.getCompilationUnit();
358
        final Trees trees = info.getTrees();
359
        final Scope scope = trees.getScope(context);
360
        String qName = fqn;
361
        StringBuilder sqName = new StringBuilder();
362
        String sName = null;
363
        boolean clashing = false;
364
        ElementUtilities eu = info.getElementUtilities();
365
        ElementUtilities.ElementAcceptor acceptor = new ElementUtilities.ElementAcceptor() {
366
            public boolean accept(Element e, TypeMirror type) {
367
                return (e.getKind().isClass() || e.getKind().isInterface()) && trees.isAccessible(scope, (TypeElement)e);
368
            }
369
        };
370
        while(qName != null && qName.length() > 0) {
371
            int lastDot = qName.lastIndexOf('.');
372
            String simple = qName.substring(lastDot < 0 ? 0 : lastDot + 1);
373
            if (sName == null)
374
                sName = simple;
375
            else
376
                sqName.insert(0, '.');
377
            sqName.insert(0, simple);
378
            if (info.getElements().getTypeElement(qName) != null) {
379
                boolean matchFound = false;
380
                for(Element e : eu.getLocalMembersAndVars(scope, acceptor)) {
381
                    if (simple.contentEquals(e.getSimpleName())) {
382
                        //either a clash or already imported:
383
                        if (qName.contentEquals(((TypeElement)e).getQualifiedName())) {
384
                            return sqName.toString();
385
                        } else if (fqn == qName) {
386
                            clashing = true;
387
                        }
388
                        matchFound = true;
389
                        break;
390
                    }
391
                }
392
                if (!matchFound) {
393
                    for(TypeElement e : eu.getGlobalTypes(acceptor)) {
394
                        if (simple.contentEquals(e.getSimpleName())) {
395
                            //either a clash or already imported:
396
                            if (qName.contentEquals(e.getQualifiedName())) {
397
                                return sqName.toString();
398
                            } else if (fqn == qName) {
399
                                clashing = true;
400
                            }
401
                            break;
402
                        }
403
                    }
404
                }
405
            }
406
            qName = lastDot < 0 ? null : qName.substring(0, lastDot);
407
        }
408
        if (clashing)
409
            return fqn;
410
        
411
        //not imported/visible so far by any means:
412
        if (info instanceof WorkingCopy) {
413
            CompilationUnitTree nue = (CompilationUnitTree) ((WorkingCopy)info).getChangeSet().getChange(cut);
414
            cut = nue != null ? nue : cut;
415
            ((WorkingCopy)info).rewrite(info.getCompilationUnit(), addImports(cut, Collections.singletonList(fqn), ((WorkingCopy)info).getTreeMaker()));
416
        } else {
417
            RequestProcessor.getDefault().post(new Runnable() {
418
                public void run() {
419
                    try {
420
                        info.getJavaSource().runModificationTask(new CancellableTask<WorkingCopy>() {
421
                            public void cancel() {
422
                            }
423
                            public void run(WorkingCopy copy) throws Exception {
424
                                copy.toPhase(Phase.ELEMENTS_RESOLVED);
425
                                copy.rewrite(copy.getCompilationUnit(), addImports(copy.getCompilationUnit(), Collections.singletonList(fqn), copy.getTreeMaker()));
426
                            }
427
                        }).commit();
428
                    } catch (IOException ioe) {
429
                        Exceptions.printStackTrace(ioe);
430
                    }
431
                }
432
            });
433
        }
434
        TypeElement te = info.getElements().getTypeElement(fqn);
435
        if (te != null) {
436
            ((JCCompilationUnit) info.getCompilationUnit()).namedImportScope.enterIfAbsent((Symbol) te);
437
        }
438
        
439
        return sName;
440
    }
441
    
442
    // private implementation --------------------------------------------------
443
    
444
    private MethodTree createMethod(ExecutableElement element, DeclaredType type) {
445
        TreeMaker make = copy.getTreeMaker();
446
        Set<Modifier> mods = element.getModifiers();
447
        Set<Modifier> flags = mods.isEmpty() ? EnumSet.noneOf(Modifier.class) : EnumSet.copyOf(mods);
448
        boolean isAbstract = flags.remove(Modifier.ABSTRACT);
449
        flags.remove(Modifier.NATIVE);
450
        
451
        ExecutableType et = (ExecutableType)copy.getTypes().asMemberOf(type, element);
452
        List<TypeParameterTree> typeParams = new ArrayList<TypeParameterTree>();
453
        for (TypeParameterElement tpe: element.getTypeParameters()) {
454
            List<ExpressionTree> bounds = new ArrayList<ExpressionTree>();
455
            for (TypeMirror bound : tpe.getBounds()) {
456
                if (bound.getKind() != TypeKind.NULL) {
457
                    //if the bound is java.lang.Object, do not generate the extends clause:
458
                    if (bound.getKind() != TypeKind.DECLARED || !"java.lang.Object".contentEquals(((TypeElement)((DeclaredType)bound).asElement()).getQualifiedName())) //NOI18N
459
                        bounds.add((ExpressionTree)make.Type(bound));
460
                }
461
            }            
462
            typeParams.add(make.TypeParameter(tpe.getSimpleName(), bounds));
463
        }
464
465
        Tree returnType = make.Type(et.getReturnType());
466
467
        List<VariableTree> params = new ArrayList<VariableTree>();        
468
        boolean isVarArgs = element.isVarArgs();
469
        Iterator<? extends VariableElement> formArgNames = element.getParameters().iterator();
470
        Iterator<? extends TypeMirror> formArgTypes = et.getParameterTypes().iterator();
471
        ModifiersTree parameterModifiers = make.Modifiers(EnumSet.noneOf(Modifier.class));
472
        while (formArgNames.hasNext() && formArgTypes.hasNext()) {
473
            VariableElement formArgName = formArgNames.next();
474
            TypeMirror formArgType = formArgTypes.next();
475
            if (isVarArgs && !formArgNames.hasNext())
476
                parameterModifiers = make.Modifiers(1L<<34, Collections.<AnnotationTree>emptyList());
477
            params.add(make.Variable(parameterModifiers, formArgName.getSimpleName(), make.Type(formArgType), null));
478
        }
479
480
        List<ExpressionTree> throwsList = new ArrayList<ExpressionTree>();
481
        for (TypeMirror tm : et.getThrownTypes()) {
482
            throwsList.add((ExpressionTree)make.Type(tm));
483
        }
484
        
485
        BlockTree body;
486
        List<AnnotationTree> annotations = new ArrayList<AnnotationTree>();
487
        if (isAbstract) {
488
            List<StatementTree> blockStatements = new ArrayList<StatementTree>();
489
            TypeElement uoe = copy.getElements().getTypeElement("java.lang.UnsupportedOperationException"); //NOI18N
490
            //TODO: if uoe == null: cannot resolve UnsupportedOperationException for some reason, create a different body in such a case
491
            if (uoe != null) {
492
                NewClassTree nue = make.NewClass(null, Collections.<ExpressionTree>emptyList(), make.QualIdent(uoe), Collections.singletonList(make.Literal("Not supported yet.")), null);
493
                blockStatements.add(make.Throw(nue));
494
            }
495
            body = make.Block(blockStatements, false);
496
        } else {
497
            List<ExpressionTree> arguments = new ArrayList<ExpressionTree>();
498
            for (VariableElement ve : element.getParameters()) {
499
                arguments.add(make.Identifier(ve.getSimpleName()));
500
            }            
501
            MethodInvocationTree inv = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("super"), element.getSimpleName()), arguments); //NOI18N
502
            StatementTree statement = copy.getTypes().getNoType(TypeKind.VOID) == element.getReturnType() ?
503
                make.ExpressionStatement(inv) : make.Return(inv);
504
            body = make.Block(Collections.singletonList(statement), false);
505
            
506
            //add @Override annotation if developing for 1.5:
507
            if (supportsOverride(copy.getFileObject())) {
508
                annotations.add(make.Annotation(make.Identifier("Override"), Collections.<ExpressionTree>emptyList())); //NOI18N
509
            }
510
        }
511
512
        return make.Method(make.Modifiers(flags, annotations), element.getSimpleName(), returnType, typeParams, params, throwsList, body, null);
513
    }
514
    
515
    private static boolean supportsOverride(FileObject fo) {
516
        SpecificationVersion myVersion = new SpecificationVersion(SourceLevelQuery.getSourceLevel(fo));
517
        SpecificationVersion version = new SpecificationVersion("1.5"); //NOI18N
518
        return myVersion.compareTo(version) >= 0;
519
    }
520
    
521
    private static class ClassMemberComparator {
522
        
523
        public static int compare(Tree tree1, Tree tree2) {
524
            if (tree1 == tree2)
525
                return 0;
526
            return getSortPriority(tree1) - getSortPriority(tree2);
527
        }
528
        
529
        private static int getSortPriority(Tree tree) {
530
            int ret = 0;
531
            ModifiersTree modifiers = null;
532
            switch (tree.getKind()) {
533
            case CLASS:
534
                ret = 4000;
535
                modifiers = ((ClassTree)tree).getModifiers();
536
                break;
537
            case METHOD:
538
                MethodTree mt = (MethodTree)tree;
539
                if (mt.getName().contentEquals("<init>"))
540
                    ret = 200;
541
                else
542
                    ret = 300;
543
                modifiers = mt.getModifiers();
544
                break;
545
            case VARIABLE:
546
                ret = 100;
547
                modifiers = ((VariableTree)tree).getModifiers();
548
                break;
549
            }
550
            if (modifiers != null) {
551
                if (!modifiers.getFlags().contains(Modifier.STATIC))
552
                    ret += 1000;
553
            }
554
            return ret;
555
        }
556
    }
557
    
558
    /**
559
     *
560
     *
561
     */
562
    private static CompilationUnitTree addImports(CompilationUnitTree cut, List<String> toImport, TreeMaker make)
563
            throws IOException {
564
        // do not modify the list given by the caller (may be reused or immutable).
565
        toImport = new ArrayList<String>(toImport);
566
        Collections.sort(toImport);
567
        
568
        List<ImportTree> imports = new ArrayList<ImportTree>(cut.getImports());
569
        int currentToImport = toImport.size() - 1;
570
        int currentExisting = imports.size() - 1;
571
        
572
        while (currentToImport >= 0 && currentExisting >= 0) {
573
            String currentToImportText = toImport.get(currentToImport);
574
            
575
            while (currentExisting >= 0 && (imports.get(currentExisting).isStatic() || imports.get(currentExisting).getQualifiedIdentifier().toString().compareTo(currentToImportText) > 0))
576
                currentExisting--;
577
            
578
            if (currentExisting >= 0) {
579
                imports.add(currentExisting+1, make.Import(make.Identifier(currentToImportText), false));
580
                currentToImport--;
581
            }
582
        }
583
        // we are at the head of import section and we still have some imports
584
        // to add, put them to the very beginning
585
        while (currentToImport >= 0) {
586
            String importText = toImport.get(currentToImport);
587
            imports.add(0, make.Import(make.Identifier(importText), false));
588
            currentToImport--;
589
        }
590
        // return a copy of the unit with changed imports section
591
        return make.CompilationUnit(cut.getPackageName(), imports, cut.getTypeDecls(), cut.getSourceFile());
592
    }
593
}
(-)src/org/netbeans/api/java/source/ElementUtilities.java (-1 / +51 lines)
Lines 13-19 Link Here
13
 * "Portions Copyrighted [year] [name of copyright owner]"
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
18
 */
19
package org.netbeans.api.java.source;
19
package org.netbeans.api.java.source;
Lines 45-53 Link Here
45
import java.util.ArrayList;
45
import java.util.ArrayList;
46
import java.util.HashMap;
46
import java.util.HashMap;
47
import java.util.HashSet;
47
import java.util.HashSet;
48
import java.util.List;
48
import javax.lang.model.element.Element;
49
import javax.lang.model.element.Element;
49
import javax.lang.model.element.ElementKind;
50
import javax.lang.model.element.ElementKind;
50
import javax.lang.model.element.ExecutableElement;
51
import javax.lang.model.element.ExecutableElement;
52
import javax.lang.model.element.Modifier;
51
import javax.lang.model.element.PackageElement;
53
import javax.lang.model.element.PackageElement;
52
import javax.lang.model.element.TypeElement;
54
import javax.lang.model.element.TypeElement;
53
import javax.lang.model.type.DeclaredType;
55
import javax.lang.model.type.DeclaredType;
Lines 401-404 Link Here
401
    public boolean implementsMethod(ExecutableElement element) {
403
    public boolean implementsMethod(ExecutableElement element) {
402
        return delegate.implementsMethod(element);
404
        return delegate.implementsMethod(element);
403
    }
405
    }
406
    
407
    /**Find all methods in given type and its supertypes, which are not implemented.
408
     * 
409
     * @param type to inspect
410
     * @return list of all unimplemented methods
411
     */
412
    public List<? extends ExecutableElement> findUnimplementedMethods(TypeElement impl) {
413
        return findUnimplementedMethods(impl, impl);
414
    }
415
416
    // private implementation --------------------------------------------------
417
418
419
    private List<? extends ExecutableElement> findUnimplementedMethods(TypeElement impl, TypeElement element) {
420
        List<ExecutableElement> undef = new ArrayList<ExecutableElement>();
421
        if (element.getModifiers().contains(Modifier.ABSTRACT)) {
422
            for (Element e : element.getEnclosedElements()) {
423
                if (e.getKind() == ElementKind.METHOD && e.getModifiers().contains(Modifier.ABSTRACT)) {
424
                    ExecutableElement ee = (ExecutableElement)e;
425
                    Element eeImpl = getImplementationOf(ee, impl);
426
                    if (eeImpl == null || eeImpl == ee)
427
                        undef.add(ee);
428
                }
429
            }
430
        }
431
        Types types = JavacTypes.instance(ctx);
432
        DeclaredType implType = (DeclaredType)impl.asType();
433
        for (TypeMirror t : types.directSupertypes(element.asType())) {
434
            for (ExecutableElement ee : findUnimplementedMethods(impl, (TypeElement)((DeclaredType)t).asElement())) {
435
                //check if "the same" method has already been added:
436
                boolean exists = false;
437
                TypeMirror eeType = types.asMemberOf(implType, ee);
438
                for (ExecutableElement existing : undef) {
439
                    if (existing.getSimpleName().contentEquals(ee.getSimpleName())) {
440
                        TypeMirror existingType = types.asMemberOf(implType, existing);
441
                        if (types.isSameType(eeType, existingType)) {
442
                            exists = true;
443
                            break;
444
                        }
445
                    }
446
                }
447
                if (!exists)
448
                    undef.add(ee);
449
            }
450
        }
451
        return undef;
452
    }
453
404
}
454
}
(-)test/unit/src/org/netbeans/api/java/source/GeneratorUtilitiesTest.java (+706 lines)
Added Link Here
1
/*
2
 * The contents of this file are subject to the terms of the Common Development
3
 * and Distribution License (the License). You may not use this file except in
4
 * compliance with the License.
5
 *
6
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7
 * or http://www.netbeans.org/cddl.txt.
8
 *
9
 * When distributing Covered Code, include this CDDL Header Notice in each file
10
 * and include the License file at http://www.netbeans.org/cddl.txt.
11
 * If applicable, add the following below the CDDL Header, with the fields
12
 * enclosed by brackets [] replaced by your own identifying information:
13
 * "Portions Copyrighted [year] [name of copyright owner]"
14
 *
15
 * The Original Software is NetBeans. The Initial Developer of the Original
16
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17
 * Microsystems, Inc. All Rights Reserved.
18
 */
19
20
package org.netbeans.api.java.source;
21
22
import com.sun.source.tree.ClassTree;
23
import com.sun.source.tree.ExpressionTree;
24
import com.sun.source.tree.MethodTree;
25
import com.sun.source.tree.StatementTree;
26
import com.sun.source.tree.Tree;
27
import com.sun.source.tree.TypeParameterTree;
28
import com.sun.source.tree.VariableTree;
29
import com.sun.source.util.TreePath;
30
import java.beans.PropertyVetoException;
31
import java.io.File;
32
import java.io.IOException;
33
import java.io.OutputStream;
34
import java.util.ArrayList;
35
import java.util.Collections;
36
import java.util.EnumSet;
37
import java.util.List;
38
import java.util.Set;
39
import javax.lang.model.element.ElementKind;
40
import javax.lang.model.element.ExecutableElement;
41
import javax.lang.model.element.Modifier;
42
import javax.lang.model.element.Name;
43
import javax.lang.model.element.TypeElement;
44
import javax.lang.model.element.VariableElement;
45
import javax.lang.model.type.ArrayType;
46
import javax.lang.model.type.DeclaredType;
47
import javax.lang.model.type.TypeKind;
48
import javax.lang.model.type.TypeMirror;
49
import javax.lang.model.util.ElementFilter;
50
import org.netbeans.junit.NbTestCase;
51
import org.openide.filesystems.FileLock;
52
import org.openide.filesystems.FileObject;
53
import org.openide.filesystems.FileUtil;
54
import org.openide.filesystems.LocalFileSystem;
55
import org.openide.filesystems.Repository;
56
57
/**
58
 *
59
 * @author Jan Lahoda, Dusan Balek
60
 */
61
public class GeneratorUtilitiesTest extends NbTestCase {
62
    
63
    public GeneratorUtilitiesTest(String testName) {
64
        super(testName);
65
    }
66
67
    protected void setUp() throws Exception {
68
        SourceUtilsTestUtil.prepareTest(new String[0], new Object[0]);
69
    }
70
71
    private void writeIntoFile(FileObject file, String what) throws Exception {
72
        FileLock lock = file.lock();
73
        OutputStream out = file.getOutputStream(lock);
74
        
75
        try {
76
            out.write(what.getBytes());
77
        } finally {
78
            out.close();
79
            lock.releaseLock();
80
        }
81
    }
82
    
83
    public void testInsertFields() throws Exception {
84
        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",
85
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.FIELD), new InsertMemberValidator(2));
86
    }
87
88
    public void testInsertStaticFields() throws Exception {
89
        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",
90
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.FIELD), new InsertMemberValidator(1));
91
    }
92
93
    public void testInsertConstructors() throws Exception {
94
        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",
95
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.CONSTRUCTOR), new InsertMemberValidator(3));
96
    }
97
98
    public void testInsertMethods() throws Exception {
99
        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",
100
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.METHOD), new InsertMemberValidator(4));
101
    }
102
103
    public void testInsertStaticMethods() throws Exception {
104
        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",
105
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.METHOD), new InsertMemberValidator(1));
106
    }
107
108
    public void testInsertNestedClasses() throws Exception {
109
        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",
110
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC), ElementKind.CLASS), new InsertMemberValidator(5));
111
    }
112
113
    public void testInsertStaticNestedClasses() throws Exception {
114
        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",
115
                new InsertMembersTask(34, EnumSet.of(Modifier.PUBLIC, Modifier.STATIC), ElementKind.CLASS), new InsertMemberValidator(4));
116
    }
117
118
    public void testImplementAllAbstractMethods1() throws Exception {
119
        performTest("package test;\npublic class Test implements Runnable {\npublic Test(){\n}\n }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator());
120
    }
121
    
122
    public void testImplementAllAbstractMethods2() throws Exception {
123
        performTest("package test;\npublic class Test implements Runnable {\n }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator());
124
    }
125
    
126
    public void testImplementAllAbstractMethods3() throws Exception {
127
        performTest("package test;\npublic class Test implements Runnable {\npublic void testMethod() {\n} }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator());
128
    }
129
    
130
    public void testImplementAllAbstractMethods4() throws Exception {
131
        performTest("package test;\npublic class Test implements Runnable {\npublic Test(){\n}\npublic void testMethod() {\n} }\n", new AddAllAbstractMethodsTask(54), new RunnableValidator());
132
    }
133
    
134
    public void testImplementAllAbstractMethods5() throws Exception {
135
        performTest("package test;import java.util.concurrent.*;\npublic class Test implements Future<String>{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(89), new SimpleFutureValidator("java.lang.String"));
136
    }
137
    
138
    public void testImplementAllAbstractMethods6() throws Exception {
139
        performTest("package test;import java.util.concurrent.*;\npublic class Test implements Future<java.util.List<? extends java.util.List>>{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(123), new FutureValidator() {
140
            protected TypeMirror returnType(CompilationInfo info) {
141
                return info.getTreeUtilities().parseType("java.util.List<? extends java.util.List>", info.getElements().getTypeElement("test.Test"));
142
            }
143
        });
144
    }
145
    
146
    public void testImplementAllAbstractMethods7() throws Exception {
147
        performTest("package test;\npublic class Test extends java.util.AbstractList{\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(64), null);
148
    }
149
    
150
    /** issue #85966
151
     */
152
    public void testImplementAllAbstractMethods8() throws Exception {
153
        performTest("package test;\npublic class Test implements XX {\npublic Test(){\n} }\ninterface XX {\npublic void test(String ... a);}", new AddAllAbstractMethodsTask(42), new Validator() {
154
            public void validate(CompilationInfo info) {
155
                TypeElement clazz = info.getElements().getTypeElement("test.Test");
156
                ExecutableElement method = ElementFilter.methodsIn(clazz.getEnclosedElements()).get(0);                
157
                assertTrue(method.isVarArgs());
158
            }
159
        });
160
    }
161
    
162
    public void testImplementAllAbstractMethods9() throws Exception {
163
        performTest("package test;\npublic class Test implements java.util.concurrent.ExecutorService {\npublic Test(){\n} }\n", new AddAllAbstractMethodsTask(30), null);
164
    }
165
    
166
    public void testImplementAllAbstractMethodsa() throws Exception {
167
        performTest("package test;\npublic class Test implements XX {\npublic Test(){\n} }\ninterface XX {public <T extends java.util.List> void test(T t);}", new AddAllAbstractMethodsTask(30), null);
168
    }
169
    
170
    public void testOverrideMethods1() throws Exception {
171
        performTest("package test;\npublic class Test {\npublic Test(){\n}\n }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator());
172
    }
173
    
174
    public void testOverrideMethods2() throws Exception {
175
        performTest("package test;\npublic class Test {\n }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator());
176
    }
177
    
178
    public void testOverrideMethods3() throws Exception {
179
        performTest("package test;\npublic class Test {\npublic void testMethod() {\n} }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator());
180
    }
181
    
182
    public void testOverrideMethods4() throws Exception {
183
        performTest("package test;\npublic class Test {\npublic Test(){\n}\npublic void testMethod() {\n} }\n", new SimpleOverrideMethodsTask(34), new CloneAndToStringValidator());
184
    }
185
    
186
    public void testOverrideMethods5() throws Exception {
187
        performTest("package test;\npublic class Test extends XX<Number> {\npublic Test(){\n} }\nclass XX<T> {\npublic void test(T ... a) {}}", new OverrideMethodsTask(30), new Validator() {
188
            public void validate(CompilationInfo info) {
189
                TypeElement clazz = info.getElements().getTypeElement("test.Test");
190
                ExecutableElement method = ElementFilter.methodsIn(clazz.getEnclosedElements()).get(0);
191
                assertTrue(method.getSimpleName().contentEquals("test"));
192
                TypeElement te = info.getElements().getTypeElement("java.lang.Number");
193
                assertEquals(1, method.getParameters().size());
194
                TypeMirror paramType = method.getParameters().get(0).asType();
195
                assertNotNull(paramType);
196
                assertTrue(paramType.getKind() == TypeKind.ARRAY);
197
                assertTrue(info.getTypes().isSameType(te.asType(), ((ArrayType)paramType).getComponentType()));
198
                assertTrue(method.isVarArgs());
199
            }
200
        });
201
    }
202
    
203
    public void testConstructor1() throws Exception {
204
        performTest("package test;\npublic class Test {\nprivate int test;\n}\n", new ConstructorTask(34), new ConstructorValidator());
205
    }
206
    
207
    public void testConstructor2() throws Exception {
208
        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());
209
    }
210
    
211
    public void testGetter() throws Exception {
212
        performTest("package test;\npublic class Test {\nprivate int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true));
213
    }
214
    
215
    public void testBooleanGetter() throws Exception {
216
        performTest("package test;\npublic class Test {\nprivate boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true));
217
    }
218
    
219
    public void testStaticGetter() throws Exception {
220
        performTest("package test;\npublic class Test {\nprivate static int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true));
221
    }
222
223
    public void testStaticBooleanGetter() throws Exception {
224
        performTest("package test;\npublic class Test {\nprivate static boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, true), new GetterSetterValidator(true));
225
    }
226
    
227
    public void testSetter() throws Exception {
228
        performTest("package test;\npublic class Test {\nprivate int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false));
229
    }
230
    
231
    public void testBooleanSetter() throws Exception {
232
        performTest("package test;\npublic class Test {\nprivate boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false));
233
    }
234
    
235
    public void testStaticSetter() throws Exception {
236
        performTest("package test;\npublic class Test {\nprivate static int test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false));
237
    }
238
239
    public void testStaticBooleanSetter() throws Exception {
240
        performTest("package test;\npublic class Test {\nprivate static boolean test;\npublic Test(){\n}\n }\n", new GetterSetterTask(34, false), new GetterSetterValidator(false));
241
    }
242
    
243
    public static interface Validator {
244
        
245
        public void validate(CompilationInfo info);
246
        
247
    }
248
    
249
    private static class InsertMembersTask implements CancellableTask<WorkingCopy> {        
250
    
251
        private int offset;
252
        private Set<Modifier> modifiers;
253
        private ElementKind kind;
254
        
255
        public InsertMembersTask(int offset, Set<Modifier> modifiers, ElementKind kind) {
256
            this.offset = offset;
257
            this.modifiers = modifiers;
258
            this.kind = kind;
259
        }
260
261
        public void cancel() {
262
        }
263
    
264
        public void run(WorkingCopy copy) throws Exception {
265
            copy.toPhase(JavaSource.Phase.RESOLVED);
266
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
267
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
268
            ClassTree ct = (ClassTree)tp.getLeaf();
269
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
270
            assertNotNull(utilities);
271
            TreeMaker maker = copy.getTreeMaker();
272
            ArrayList<Tree> members = new ArrayList<Tree>(1);
273
            switch(kind) {
274
            case FIELD:
275
                members.add(maker.Variable(maker.Modifiers(modifiers), "test", maker.PrimitiveType(TypeKind.INT), null));
276
                break;
277
            case METHOD:
278
                members.add(maker.Method(maker.Modifiers(modifiers), "test", maker.PrimitiveType(TypeKind.VOID),
279
                        Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(),
280
                        Collections.<ExpressionTree>emptyList(), maker.Block(Collections.<StatementTree>emptyList(), false),
281
                        null));
282
                break;
283
            case CONSTRUCTOR:
284
                members.add(maker.Method(maker.Modifiers(modifiers), "<init>", null,
285
                        Collections.<TypeParameterTree>emptyList(), Collections.singletonList(maker.Variable(
286
                        maker.Modifiers(EnumSet.noneOf(Modifier.class)), "s", maker.Identifier("String"), null)),
287
                        Collections.<ExpressionTree>emptyList(), maker.Block(Collections.<StatementTree>emptyList(), false),
288
                        null));
289
                break;
290
            case CLASS:
291
                members.add(maker.Class(maker.Modifiers(modifiers), "test", Collections.<TypeParameterTree>emptyList(),
292
                        null, Collections.<ExpressionTree>emptyList(), Collections.<Tree>emptyList()));
293
                break;
294
            }
295
            ClassTree newCt = utilities.insertClassMembers(ct, members);
296
            copy.rewrite(ct, newCt);
297
        }
298
    }
299
    
300
    private final class InsertMemberValidator implements Validator {
301
        
302
        private int testIdx;
303
        
304
        public InsertMemberValidator(int testIdx) {
305
            this.testIdx = testIdx;
306
        }
307
308
        public void validate(CompilationInfo info) {
309
            TypeElement test = info.getElements().getTypeElement("test.Test");
310
            ClassTree ct = info.getTrees().getTree(test);
311
            assertNotNull(ct);
312
            
313
            int foundTestIdx = -1;
314
            
315
            int idx = 0;
316
            for (Tree t : ct.getMembers()) {
317
                Name name = null;
318
                switch(t.getKind()) {
319
                case VARIABLE:
320
                    name = ((VariableTree)t).getName();
321
                    break;
322
                case METHOD:
323
                    name = ((MethodTree)t).getName();
324
                    break;
325
                case CLASS:
326
                    name = ((ClassTree)t).getSimpleName();
327
                    break;
328
                }
329
                if (name != null) {
330
                    if (name.contentEquals("test")) {
331
                        assertEquals(-1, foundTestIdx);
332
                        foundTestIdx = idx;
333
                    } else if (name.contentEquals("<init>") && ((MethodTree)t).getParameters().size() > 0) {
334
                        assertEquals(-1, foundTestIdx);
335
                        foundTestIdx = idx;
336
                    }
337
                }
338
                idx++;
339
            }
340
            
341
            assertEquals(testIdx, foundTestIdx);
342
        }
343
        
344
    }
345
    
346
    private static class AddAllAbstractMethodsTask implements CancellableTask<WorkingCopy> {        
347
    
348
        private int offset;
349
        
350
        public AddAllAbstractMethodsTask(int offset) {
351
            this.offset = offset;
352
        }
353
        
354
        public void cancel() {
355
        }
356
    
357
        public void run(WorkingCopy copy) throws Exception {
358
            copy.toPhase(JavaSource.Phase.RESOLVED);
359
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
360
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
361
            ClassTree ct = (ClassTree)tp.getLeaf();
362
            TypeElement te = (TypeElement)copy.getTrees().getElement(tp);
363
            assertNotNull(te);
364
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
365
            assertNotNull(utilities);
366
            ClassTree newCt = utilities.insertClassMembers(ct, utilities.createAllAbstractMethodImplementations(te));
367
            copy.rewrite(ct, newCt);
368
        }
369
    }
370
    
371
    private final class RunnableValidator implements Validator {
372
        
373
        public void validate(CompilationInfo info) {
374
            TypeElement test = info.getElements().getTypeElement("test.Test");
375
            
376
            boolean foundRunMethod = false;
377
            
378
            for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) {
379
                if ("run".equals(ee.getSimpleName().toString())) {
380
                    if (ee.getParameters().isEmpty()) {
381
                        assertFalse(foundRunMethod);
382
                        foundRunMethod = true;
383
                    }
384
                }
385
            }
386
            
387
            assertTrue(foundRunMethod);
388
        }
389
        
390
    }
391
    
392
    private final class SimpleFutureValidator extends FutureValidator {
393
        
394
        private String returnTypeName;
395
        
396
        public SimpleFutureValidator(String returnTypeName) {
397
            this.returnTypeName = returnTypeName;
398
        }
399
        
400
        protected TypeMirror returnType(CompilationInfo info) {
401
            TypeElement returnTypeElement = info.getElements().getTypeElement(returnTypeName);
402
            
403
            return returnTypeElement.asType();
404
        }
405
    }
406
    
407
    private abstract class FutureValidator implements Validator {
408
        
409
        protected abstract TypeMirror returnType(CompilationInfo info);
410
411
        public void validate(CompilationInfo info) {
412
            TypeElement test = info.getElements().getTypeElement("test.Test");
413
            TypeMirror returnType = returnType(info);
414
            
415
            boolean hasShortGet = false;
416
            boolean hasLongGet = false;
417
            
418
            for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) {
419
                if ("get".equals(ee.getSimpleName().toString())) {
420
                    if (ee.getParameters().isEmpty()) {
421
                        assertFalse(hasShortGet);
422
                        assertTrue(info.getTypes().isSameType(returnType, ee.getReturnType()));
423
                        hasShortGet = true;
424
                    }
425
                    if (ee.getParameters().size() == 2) {
426
                        assertFalse(hasLongGet);
427
                        assertTrue(info.getTypes().isSameType(returnType, ee.getReturnType()));
428
                        hasLongGet = true;
429
                    }
430
                }
431
            }
432
            
433
            assertTrue(hasShortGet);
434
            assertTrue(hasLongGet);
435
        }
436
        
437
    }
438
    
439
    private static class SimpleOverrideMethodsTask implements CancellableTask<WorkingCopy> {        
440
    
441
        private int offset;
442
        
443
        public SimpleOverrideMethodsTask(int offset) {
444
            this.offset = offset;
445
        }
446
447
        public void cancel() {
448
        }
449
    
450
        public void run(WorkingCopy copy) throws Exception {
451
            copy.toPhase(JavaSource.Phase.RESOLVED);
452
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
453
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
454
            ClassTree ct = (ClassTree)tp.getLeaf();
455
            TypeElement te = (TypeElement)copy.getTrees().getElement(tp);
456
            assertNotNull(te);
457
            ArrayList<ExecutableElement> methods = new ArrayList<ExecutableElement>(2);
458
            TypeElement object = copy.getElements().getTypeElement("java.lang.Object");
459
            assertNotNull(object);
460
            for (ExecutableElement method : ElementFilter.methodsIn(object.getEnclosedElements())) {
461
                if (method.getSimpleName().contentEquals("clone"))
462
                    methods.add(method);
463
                else if (method.getSimpleName().contentEquals("toString"))
464
                    methods.add(method);
465
            }
466
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
467
            assertNotNull(utilities);
468
            ClassTree newCt = utilities.insertClassMembers(ct, utilities.createOverridingMethods(te, methods));
469
            copy.rewrite(ct, newCt);
470
        }
471
    }
472
    
473
    private static class OverrideMethodsTask implements CancellableTask<WorkingCopy> {        
474
    
475
        private int offset;
476
        
477
        public OverrideMethodsTask(int offset) {
478
            this.offset = offset;
479
        }
480
481
        public void cancel() {
482
        }
483
    
484
        public void run(WorkingCopy copy) throws Exception {
485
            copy.toPhase(JavaSource.Phase.RESOLVED);
486
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
487
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
488
            ClassTree ct = (ClassTree)tp.getLeaf();
489
            TypeElement te = (TypeElement)copy.getTrees().getElement(tp);
490
            assertNotNull(te);
491
            ArrayList<ExecutableElement> methods = new ArrayList<ExecutableElement>(1);
492
            TypeElement sup = (TypeElement)((DeclaredType)te.getSuperclass()).asElement();
493
            assertNotNull(sup);
494
            for (ExecutableElement method : ElementFilter.methodsIn(sup.getEnclosedElements())) {
495
                if (method.getSimpleName().contentEquals("test"))
496
                    methods.add(method);
497
            }
498
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
499
            assertNotNull(utilities);
500
            ClassTree newCt = utilities.insertClassMembers(ct, utilities.createOverridingMethods(te, methods));
501
            copy.rewrite(ct, newCt);
502
        }
503
    }
504
505
    private final class CloneAndToStringValidator implements Validator {
506
        
507
        public void validate(CompilationInfo info) {
508
            TypeElement test = info.getElements().getTypeElement("test.Test");
509
            
510
            boolean foundCloneMethod = false;
511
            boolean foundToStringMethod = false;
512
            
513
            for (ExecutableElement ee : ElementFilter.methodsIn(test.getEnclosedElements())) {
514
                if (ee.getSimpleName().contentEquals("clone")) {
515
                    if (ee.getParameters().isEmpty()) {
516
                        assertFalse(foundCloneMethod);
517
                        foundCloneMethod = true;
518
                    }
519
                } else if (ee.getSimpleName().contentEquals("toString")) {
520
                    if (ee.getParameters().isEmpty()) {
521
                        assertFalse(foundToStringMethod);
522
                        foundToStringMethod = true;
523
                    }
524
                }
525
            }
526
            
527
            assertTrue(foundCloneMethod);
528
            assertTrue(foundToStringMethod);
529
        }
530
        
531
    }
532
533
    private static class ConstructorTask implements CancellableTask<WorkingCopy> {        
534
    
535
        private int offset;
536
        
537
        public ConstructorTask(int offset) {
538
            this.offset = offset;
539
        }
540
        
541
        public void cancel() {
542
        }
543
    
544
        public void run(WorkingCopy copy) throws Exception {
545
            copy.toPhase(JavaSource.Phase.RESOLVED);
546
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
547
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
548
            ClassTree ct = (ClassTree)tp.getLeaf();
549
            TypeElement te = (TypeElement)copy.getTrees().getElement(tp);
550
            assertNotNull(te);
551
            List<? extends VariableElement> vars = ElementFilter.fieldsIn(te.getEnclosedElements());
552
            TypeElement sup = (TypeElement)((DeclaredType)te.getSuperclass()).asElement();
553
            assertNotNull(sup);
554
            List<? extends ExecutableElement> ctors = sup.getQualifiedName().contentEquals("java.lang.Object")
555
                    ? null : ElementFilter.constructorsIn(sup.getEnclosedElements());
556
            if (ctors != null)
557
                assertEquals(1, ctors.size());
558
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
559
            assertNotNull(utilities);
560
            ClassTree newCt = utilities.insertClassMember(ct, utilities.createConstructor(te, vars, ctors != null ? ctors.get(0) : null));
561
            copy.rewrite(ct, newCt);
562
        }
563
    }
564
    
565
    private final class ConstructorValidator implements Validator {
566
        
567
        public void validate(CompilationInfo info) {
568
            TypeElement test = info.getElements().getTypeElement("test.Test");
569
            VariableElement var = ElementFilter.fieldsIn(test.getEnclosedElements()).get(0);
570
            TypeElement sup = (TypeElement)((DeclaredType)test.getSuperclass()).asElement();
571
            ExecutableElement supCtor = sup.getQualifiedName().contentEquals("java.lang.Object")
572
                    ? null : ElementFilter.constructorsIn(sup.getEnclosedElements()).get(0);
573
574
            List<? extends ExecutableElement> ctors = ElementFilter.constructorsIn(test.getEnclosedElements());
575
            assertEquals(1, ctors.size());
576
            ExecutableElement ctor = ctors.get(0);
577
578
            assertEquals(supCtor == null ? 1 : 2, ctor.getParameters().size());
579
        }
580
        
581
    }
582
    
583
    private static class GetterSetterTask implements CancellableTask<WorkingCopy> {        
584
    
585
        private int offset;
586
        private boolean getter;
587
        
588
        public GetterSetterTask(int offset, boolean getter) {
589
            this.offset = offset;
590
            this.getter = getter;
591
        }
592
        
593
        public void cancel() {
594
        }
595
    
596
        public void run(WorkingCopy copy) throws Exception {
597
            copy.toPhase(JavaSource.Phase.RESOLVED);
598
            TreePath tp = copy.getTreeUtilities().pathFor(offset);
599
            assertTrue(tp.getLeaf().getKind() == Tree.Kind.CLASS);
600
            ClassTree ct = (ClassTree)tp.getLeaf();
601
            TypeElement te = (TypeElement)copy.getTrees().getElement(tp);
602
            assertNotNull(te);
603
            List<? extends VariableElement> vars = ElementFilter.fieldsIn(te.getEnclosedElements());
604
            assertEquals(1, vars.size());
605
            GeneratorUtilities utilities = GeneratorUtilities.get(copy);
606
            assertNotNull(utilities);
607
            ClassTree newCt = utilities.insertClassMember(ct, getter
608
                    ? utilities.createGetter(te, vars.get(0))
609
                    : utilities.createSetter(te, vars.get(0)));
610
            copy.rewrite(ct, newCt);
611
        }
612
    }
613
    
614
    private final class GetterSetterValidator implements Validator {
615
        
616
        private boolean getter;
617
        
618
        public GetterSetterValidator(boolean getter) {
619
            this.getter = getter;
620
        }
621
622
        public void validate(CompilationInfo info) {
623
            TypeElement test = info.getElements().getTypeElement("test.Test");
624
            VariableElement var = ElementFilter.fieldsIn(test.getEnclosedElements()).get(0);
625
626
            List<? extends ExecutableElement> methods = ElementFilter.methodsIn(test.getEnclosedElements());
627
            assertEquals(1, methods.size());
628
            ExecutableElement method = methods.get(0);
629
630
            TypeMirror type = info.getTypes().asMemberOf((DeclaredType)test.asType(), var);            
631
            if (getter) {
632
                assertTrue(info.getTypes().isSameType(type, method.getReturnType()));
633
                assertEquals(type.getKind() == TypeKind.BOOLEAN ? "isTest" : "getTest", method.getSimpleName().toString());
634
                assertEquals(0, method.getParameters().size());
635
            } else {
636
                assertTrue(info.getTypes().isSameType(info.getTypes().getNoType(TypeKind.VOID), method.getReturnType()));
637
                assertEquals("setTest", method.getSimpleName().toString());
638
                assertEquals(1, method.getParameters().size());
639
                assertTrue(info.getTypes().isSameType(type, method.getParameters().get(0).asType()));
640
            }
641
        }
642
        
643
    }
644
    
645
    private void performTest(String sourceCode, final CancellableTask<WorkingCopy> task, final Validator validator) throws Exception {
646
        FileObject root = makeScratchDir(this);
647
        
648
        FileObject sourceDir = root.createFolder("src");
649
        FileObject buildDir = root.createFolder("build");
650
        FileObject cacheDir = root.createFolder("cache");
651
        
652
        FileObject source = sourceDir.createFolder("test").createData("Test.java");
653
        
654
        writeIntoFile(source, sourceCode);
655
        
656
        SourceUtilsTestUtil.prepareTest(sourceDir, buildDir, cacheDir, new FileObject[0]);
657
        
658
        JavaSource js = JavaSource.forFileObject(source);
659
        
660
        ModificationResult result = js.runModificationTask(task);
661
        
662
        result.commit();
663
        
664
        js.runUserActionTask(new CancellableTask<CompilationController>() {
665
            public void cancel() {
666
            }
667
            public void run(CompilationController controller) throws Exception {
668
                System.err.println("text:");
669
                System.err.println(controller.getText());
670
                controller.toPhase(JavaSource.Phase.RESOLVED);
671
                
672
                assertEquals(controller.getDiagnostics().toString(), 0, controller.getDiagnostics().size());
673
                
674
                if (validator != null)
675
                    validator.validate(controller);
676
            }
677
        }, true);
678
    }
679
    
680
    /**Copied from org.netbeans.api.project.
681
     * Create a scratch directory for tests.
682
     * Will be in /tmp or whatever, and will be empty.
683
     * If you just need a java.io.File use clearWorkDir + getWorkDir.
684
     */
685
    public static FileObject makeScratchDir(NbTestCase test) throws IOException {
686
        test.clearWorkDir();
687
        File root = test.getWorkDir();
688
        assert root.isDirectory() && root.list().length == 0;
689
        FileObject fo = FileUtil.toFileObject(root);
690
        if (fo != null) {
691
            // Presumably using masterfs.
692
            return fo;
693
        } else {
694
            // For the benefit of those not using masterfs.
695
            LocalFileSystem lfs = new LocalFileSystem();
696
            try {
697
                lfs.setRootDirectory(root);
698
            } catch (PropertyVetoException e) {
699
                assert false : e;
700
            }
701
            Repository.getDefault().addFileSystem(lfs);
702
            return lfs.getRoot();
703
        }
704
    }
705
    
706
}

Return to bug 106678