View Javadoc
Minimize
Table

Bug Overview

linebug prioritybugbug descriptionexisting sinceauthor
858mediumIL_INFINITE_LOOPDiese Schleife terminiert nicht. Bitte Code überprüfen! Falls dies beabsichtigt ist und der Fehler soll ignoriert werden, dann //NOBUG verwenden. 18.06.2012-10:31Unbekannt
441mediumWMI_WRONG_MAP_ITERATORDer Zugriff auf den Wert eines Map-Eintrages ist per keySet-Iterator ineffizient, besser es wird entrySet-Iterator verwendet.  Details zum Fehler...18.06.2012-10:31Unbekannt
679mediumEQ_DOESNT_OVERRIDE_EQUALSDas Überschreiben der equals()-Methode der Superklasse ist nicht korrekt. Bitte Code überprüfen!  Details zum Fehler...18.06.2012-10:31Unbekannt
679mediumEQ_DOESNT_OVERRIDE_EQUALSDas Überschreiben der equals()-Methode der Superklasse ist nicht korrekt. Bitte Code überprüfen!  Details zum Fehler... Unbekannt
679mediumEQ_DOESNT_OVERRIDE_EQUALSDas Überschreiben der equals()-Methode der Superklasse ist nicht korrekt. Bitte Code überprüfen!  Details zum Fehler... Unbekannt
679mediumEQ_DOESNT_OVERRIDE_EQUALSDas Überschreiben der equals()-Methode der Superklasse ist nicht korrekt. Bitte Code überprüfen!  Details zum Fehler... Unbekannt
1181mediumTODODer Code ist an dieser Stelle eventuell unfertig oder fehlerhaft. Bitte überprüfen!18.06.2012-10:31Unbekannt
1202mediumTODODer Code ist an dieser Stelle eventuell unfertig oder fehlerhaft. Bitte überprüfen!18.06.2012-10:31Unbekannt
1260mediumTODODer Code ist an dieser Stelle eventuell unfertig oder fehlerhaft. Bitte überprüfen!18.06.2012-10:31Unbekannt

1   /*
2    * Copyright 2003-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.codehaus.groovy.ast;
17  
18  import org.codehaus.groovy.GroovyBugError;
19  import org.codehaus.groovy.ast.expr.BinaryExpression;
20  import org.codehaus.groovy.ast.expr.Expression;
21  import org.codehaus.groovy.ast.expr.FieldExpression;
22  import org.codehaus.groovy.ast.expr.MapExpression;
23  import org.codehaus.groovy.ast.expr.TupleExpression;
24  import org.codehaus.groovy.ast.stmt.ExpressionStatement;
25  import org.codehaus.groovy.ast.stmt.Statement;
26  import org.codehaus.groovy.ast.stmt.BlockStatement;
27  import org.codehaus.groovy.control.CompilePhase;
28  import org.codehaus.groovy.transform.ASTTransformation;
29  import org.codehaus.groovy.transform.GroovyASTTransformation;
30  import org.codehaus.groovy.vmplugin.VMPluginFactory;
31  import org.objectweb.asm.Opcodes;
32  
33  import java.lang.reflect.Array;
34  import java.util.*;
35  
36  import groovy.lang.GroovyObject;
37  
38  /**
39   * Represents a class in the AST.<br/>
40   * A ClassNode should be created using the methods in ClassHelper.
41   * This ClassNode may be used to represent a class declaration or
42   * any other type. This class uses a proxy mechanism allowing to
43   * create a class for a plain name at AST creation time. In another
44   * phase of the compiler the real ClassNode for the plain name may be
45   * found. To avoid the need of exchanging this ClassNode with an
46   * instance of the correct ClassNode the correct ClassNode is set as
47   * redirect. Most method calls are then redirected to that ClassNode.
48   * <br>
49   * There are three types of ClassNodes:
50   * <br>
51   * <ol>
52   * <li> Primary ClassNodes:<br>
53   * A primary ClassNode is one where we have a source representation
54   * which is to be compiled by Groovy and which we have an AST for. 
55   * The groovy compiler will output one class for each such ClassNode
56   * that passes through AsmBytecodeGenerator... not more, not less.
57   * That means for example Closures become such ClassNodes too at
58   * some point. 
59   * 
60   * <li> ClassNodes create through different sources (typically created
61   * from a java.lang.reflect.Class object):<br>
62   * The compiler will not output classes from these, the methods
63   * usually do not contain bodies. These kind of ClassNodes will be
64   * used in different checks, but not checks that work on the method 
65   * bodies. For example if such a ClassNode is a super class to a primary
66   * ClassNode, then the abstract method test and others will be done 
67   * with data based on these. Theoretically it is also possible to mix both 
68   * (1 and 2) kind of classes in a hierarchy, but this probably works only
69   *  in the newest Groovy versions. Such ClassNodes normally have to
70   *  isResolved() returning true without having a redirect.In the Groovy 
71   *  compiler the only version of this, that exists, is a ClassNode created 
72   *  through a Class instance
73   *
74   * <li> Labels:<br>
75   * ClassNodes created through ClassHelper.makeWithoutCaching. They 
76   * are place holders, its redirect points to the real structure, which can
77   * be a label too, but following all redirects it should end with a ClassNode
78   * from one of the other two categories. If ResolveVisitor finds such a 
79   * node, it tries to set the redirects. Any such label created after 
80   * ResolveVisitor has done its work needs to have a redirect pointing to 
81   * case 1 or 2. If not the compiler may react strange... this can be considered 
82   * as a kind of dangling pointer. 
83   * <br>
84   * <b>Note:</b> the redirect mechanism is only allowed for classes 
85   * that are not primary ClassNodes. Typically this is done for classes
86   * created by name only.  The redirect itself can be any type of ClassNode.
87   * <br>
88   * To describe generic type signature see {@link #getGenericsTypes()} and
89   * {@link #setGenericsTypes(GenericsType[])}. These methods are not proxied,
90   * they describe the type signature used at the point of declaration or the
91   * type signatures provided by the class. If the type signatures provided
92   * by the class are needed, then a call to {@link #redirect()} will help.
93   *
94   * @see org.codehaus.groovy.ast.ClassHelper
95   *
96   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
97   * @author Jochen Theodorou
98   * @version $Revision: 22588 $
99   */
100 public class ClassNode extends AnnotatedNode implements Opcodes {
101     private static class MapOfLists {
102         private Map<Object, List<MethodNode>> map = new HashMap<Object, List<MethodNode>>();
103         public List<MethodNode> get(Object key) {
104             return map.get(key);
105         }
106         public List<MethodNode> getNotNull(Object key) {
107             List<MethodNode> ret = get(key);
108             if (ret==null) ret = Collections.emptyList();
109             return ret;
110         }
111         public void put(Object key, MethodNode value) {
112             if (map.containsKey(key)) {
113                 get(key).add(value);
114             } else {
115                 ArrayList<MethodNode> list = new ArrayList<MethodNode>(2);
116                 list.add(value);
117                 map.put(key, list);
118             }
119         }
120     }
121 
122     public static final ClassNode[] EMPTY_ARRAY = new ClassNode[0];
123     public static final ClassNode THIS = new ClassNode(Object.class);
124     public static final ClassNode SUPER = new ClassNode(Object.class);
125 
126     private String name;
127     private int modifiers;
128     private boolean syntheticPublic;
129     private ClassNode[] interfaces;
130     private MixinNode[] mixins;
131     private List<ConstructorNode> constructors;
132     private List<Statement> objectInitializers;
133     private MapOfLists methods;
134     private List<MethodNode> methodsList;
135     private LinkedList<FieldNode> fields;
136     private List<PropertyNode> properties;
137     private Map<String, FieldNode> fieldIndex;
138     private ModuleNode module;
139     private CompileUnit compileUnit;
140     private boolean staticClass = false;
141     private boolean scriptBody = false;
142     private boolean script;
143     private ClassNode superClass;
144     protected boolean isPrimaryNode;
145     protected List<InnerClassNode> innerClasses;
146 
147     /**
148      * The ASTTransformations to be applied to the Class
149      */
150     private Map<CompilePhase, Map<Class<? extends ASTTransformation>, Set<ASTNode>>> transformInstances;
151 
152     // use this to synchronize access for the lazy init
153     protected Object lazyInitLock = new Object();
154 
155     // clazz!=null when resolved
156     protected Class clazz;
157     // only false when this classNode is constructed from a class
158     private boolean lazyInitDone=true;
159     // not null if if the ClassNode is an array
160     private ClassNode componentType = null;
161     // if not null this instance is handled as proxy
162     // for the redirect
163     private ClassNode redirect=null;
164     // flag if the classes or its members are annotated
165     private boolean annotated;
166 
167     // type spec for generics
168     private GenericsType[] genericsTypes=null;
169     private boolean usesGenerics=false;
170 
171     // if set to true the name getGenericsTypes consists
172     // of 1 element describing the name of the placeholder
173     private boolean placeholder;
174 
175     /**
176      * Returns the ClassNode this ClassNode is redirecting to.
177      */
178     public ClassNode redirect(){
179         if (redirect==null) return this;
180         return redirect.redirect();
181     }
182 
183     /**
184      * Sets this instance as proxy for the given ClassNode.
185      * @param cn the class to redirect to. If set to null the redirect will be removed
186      */
187     public void setRedirect(ClassNode cn) {
188         if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
189         if (cn!=null) cn = cn.redirect();
190         if (cn==this) return;
191         redirect = cn;
192     }
193 
194     /**
195      * Returns a ClassNode representing an array of the class
196      * represented by this ClassNode
197      */
198     public ClassNode makeArray() {
199         if (redirect!=null) {
200             ClassNode res = redirect().makeArray();
201             res.componentType = this;
202             return res;
203         }
204         ClassNode cn;
205         if (clazz!=null) {
206             Class ret = Array.newInstance(clazz,0).getClass();
207             // don't use the ClassHelper here!
208             cn = new ClassNode(ret,this);
209         } else {
210             cn = new ClassNode(this);
211         }
212         return cn;
213     }
214 
215     /**
216      * @return true if this instance is a primary ClassNode
217      */
218     public boolean isPrimaryClassNode() {
219         return redirect().isPrimaryNode || (componentType != null && componentType.isPrimaryClassNode());
220     }
221 
222     /*
223      * Constructor used by makeArray() if no real class is available
224      */
225     private ClassNode(ClassNode componentType) {
226         this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
227         this.componentType = componentType.redirect();
228         isPrimaryNode=false;
229     }
230 
231     /*
232      * Constructor used by makeArray() if a real class is available
233      */
234     private ClassNode(Class c, ClassNode componentType) {
235         this(c);
236         this.componentType = componentType;
237         isPrimaryNode=false;
238     }
239 
240     /**
241      * Creates a ClassNode from a real class. The resulting
242      * ClassNode will not be a primary ClassNode.
243      */
244     public ClassNode(Class c) {
245         this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
246         clazz=c;
247         lazyInitDone=false;
248         CompileUnit cu = getCompileUnit();
249         if (cu!=null) cu.addClass(this);
250         isPrimaryNode=false;
251     }
252 
253     /**
254      * The complete class structure will be initialized only when really
255      * needed to avoid having too many objects during compilation
256      */
257     private void lazyClassInit() {
258         synchronized (lazyInitLock) {
259             if (redirect!=null) {
260                 throw new GroovyBugError("lazyClassInit called on a proxy ClassNode, that must not happen."+
261                                          "A redirect() call is missing somewhere!");
262             }   
263             if (lazyInitDone) return;
264             VMPluginFactory.getPlugin().configureClassNode(compileUnit,this);
265             lazyInitDone = true;
266         }
267     }
268 
269     // added to track the enclosing method for local inner classes
270     private MethodNode enclosingMethod = null;
271 
272     public MethodNode getEnclosingMethod() {
273         return redirect().enclosingMethod;
274     }
275 
276     public void setEnclosingMethod(MethodNode enclosingMethod) {
277         redirect().enclosingMethod = enclosingMethod;
278     }
279 
280     /**
281      * Indicates that this class has been "promoted" to public by
282      * Groovy when in fact there was no public modifier explicitly
283      * in the source code. I.e. it remembers that it has applied
284      * Groovy's "public classes by default" rule.This property is
285      * typically only of interest to AST transform writers.
286      *
287      * @return true if this class is public but had no explicit public modifier
288      */
289     public boolean isSyntheticPublic() {
290         return syntheticPublic;
291     }
292 
293     public void setSyntheticPublic(boolean syntheticPublic) {
294         this.syntheticPublic = syntheticPublic;
295     }
296 
297     /**
298      * @param name       is the full name of the class
299      * @param modifiers  the modifiers,
300      * @param superClass the base class name - use "java.lang.Object" if no direct
301      *                   base class
302      * @see org.objectweb.asm.Opcodes
303      */
304     public ClassNode(String name, int modifiers, ClassNode superClass) {
305         this(name, modifiers, superClass, EMPTY_ARRAY, MixinNode.EMPTY_ARRAY);
306     }
307 
308     /**
309      * @param name       is the full name of the class
310      * @param modifiers  the modifiers,
311      * @param superClass the base class name - use "java.lang.Object" if no direct
312      *                   base class
313      * @param interfaces the interfaces for this class
314      * @param mixins     the mixins for this class
315      * @see org.objectweb.asm.Opcodes
316      */
317     public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
318         this.name = name;
319         this.modifiers = modifiers;
320         this.superClass = superClass;
321         this.interfaces = interfaces;
322         this.mixins = mixins;
323         isPrimaryNode = true;
324         if (superClass!=null) {
325             usesGenerics = superClass.isUsingGenerics();
326         }
327         if (!usesGenerics && interfaces!=null) {
328             for (ClassNode anInterface : interfaces) {
329                 usesGenerics = usesGenerics || anInterface.isUsingGenerics();
330             }
331         }
332         this.methods = new MapOfLists();
333         this.methodsList = new ArrayList<MethodNode>();
334     }
335 
336     /**
337      * Sets the superclass of this ClassNode
338      */
339     public void setSuperClass(ClassNode superClass) {
340         redirect().superClass = superClass;
341     }
342 
343     /**
344      * @return the list of FieldNode's associated with this ClassNode
345      */
346     public List<FieldNode> getFields() {
347         if (!redirect().lazyInitDone) redirect().lazyClassInit();
348         if (redirect!=null) return redirect().getFields();
349         if (fields == null)
350             fields = new LinkedList<FieldNode> ();
351         return fields;
352     }
353 
354     /**
355      * @return the array of interfaces which this ClassNode implements
356      */
357     public ClassNode[] getInterfaces() {
358         if (!redirect().lazyInitDone) redirect().lazyClassInit();
359         if (redirect!=null) return redirect().getInterfaces();
360         return interfaces;
361     }
362 
363     public void setInterfaces(ClassNode[] interfaces) {
364         if (redirect!=null) {
365             redirect().setInterfaces(interfaces);
366         } else {
367             this.interfaces = interfaces;
368         }
369     }
370 
371     /**
372      * @return the array of mixins associated with this ClassNode
373      */
374     public MixinNode[] getMixins() {
375         return redirect().mixins;
376     }
377 
378     /**
379      * @return the list of methods associated with this ClassNode
380      */
381     public List<MethodNode> getMethods() {
382         if (!redirect().lazyInitDone) redirect().lazyClassInit();
383         if (redirect!=null) return redirect().getMethods();
384         return methodsList;
385     }
386 
387     /**
388      * @return the list of abstract methods associated with this
389      * ClassNode or null if there are no such methods
390      */
391     public List<MethodNode> getAbstractMethods() {
392         List<MethodNode> result = new ArrayList<MethodNode>(3);
393         for (MethodNode method : getDeclaredMethodsMap().values()) {
394             if (method.isAbstract()) {
395                 result.add(method);
396             }
397         }
398         
399         if (result.isEmpty()) {
400             return null;
401         } else {
402             return result;
403         }
404     }
405 
406     public List<MethodNode> getAllDeclaredMethods() {
407         return new ArrayList<MethodNode>(getDeclaredMethodsMap().values());
408     }
409 
410     public Set<ClassNode> getAllInterfaces () {
411         Set<ClassNode> res = new HashSet<ClassNode>();
412         getAllInterfaces(res);
413         return res;
414     }
415 
416     private void getAllInterfaces(Set<ClassNode> res) {
417         if (isInterface())
418           res.add(this);
419 
420         for (ClassNode anInterface : getInterfaces()) {
421             res.add(anInterface);
422             anInterface.getAllInterfaces(res);
423         }
424     }
425 
426     public Map<String, MethodNode> getDeclaredMethodsMap() {
427         // Start off with the methods from the superclass.
428         ClassNode parent = getSuperClass();
429         Map<String, MethodNode> result = null;
430         if (parent != null) {
431             result = parent.getDeclaredMethodsMap();
432         } else {
433             result = new HashMap<String, MethodNode>();
434         }
435 
436         // add in unimplemented abstract methods from the interfaces
437         for (ClassNode iface : getInterfaces()) {
438             Map<String, MethodNode> ifaceMethodsMap = iface.getDeclaredMethodsMap();
439             for (String methSig : ifaceMethodsMap.keySet()) {
440                 if (!result.containsKey(methSig)) {
441 bug overview next bug                       MethodNode methNode = ifaceMethodsMap.get(methSig);  HEALTH4J >>  :  WMI_WRONG_MAP_ITERATOR 
442                     result.put(methSig, methNode);
443                 }
444             }
445         }
446 
447         // And add in the methods implemented in this class.
448         for (MethodNode method : getMethods()) {
449             String sig = method.getTypeDescriptor();
450             result.put(sig, method);
451         }
452         return result;
453     }
454 
455     public String getName() {
456         return redirect().name;
457     }
458 
459     public String getUnresolvedName() {
460         return name;
461     }
462 
463     public String setName(String name) {
464         return redirect().name=name;
465     }
466 
467     public int getModifiers() {
468         return redirect().modifiers;
469     }
470 
471     public void setModifiers(int modifiers) {
472         redirect().modifiers = modifiers;
473     }
474 
475     public List<PropertyNode> getProperties() {
476         final ClassNode r = redirect();
477         if (r.properties == null)
478             r.properties = new ArrayList<PropertyNode> ();
479         return r.properties;
480     }
481 
482     public List<ConstructorNode> getDeclaredConstructors() {
483         if (!redirect().lazyInitDone) redirect().lazyClassInit();
484         final ClassNode r = redirect();
485         if (r.constructors == null)
486             r.constructors = new ArrayList<ConstructorNode> ();
487         return r.constructors;
488     }
489 
490     public ModuleNode getModule() {
491         return redirect().module;
492     }
493 
494     public PackageNode getPackage() {
495         return getModule() == null ? null : getModule().getPackage();
496     }
497 
498     public void setModule(ModuleNode module) {
499         redirect().module = module;
500         if (module != null) {
501             redirect().compileUnit = module.getUnit();
502         }
503     }
504 
505     public void addField(FieldNode node) {
506         final ClassNode r = redirect();
507         node.setDeclaringClass(r);
508         node.setOwner(r);
509         if (r.fields == null)
510             r.fields = new LinkedList<FieldNode> ();
511         if (r.fieldIndex == null)
512             r.fieldIndex = new HashMap<String,FieldNode> ();
513         r.fields.add(node);
514         r.fieldIndex.put(node.getName(), node);
515     }
516 
517     public void addFieldFirst(FieldNode node) {
518         final ClassNode r = redirect();
519         node.setDeclaringClass(r);
520         node.setOwner(r);
521         if (r.fields == null)
522             r.fields = new LinkedList<FieldNode> ();
523         if (r.fieldIndex == null)
524             r.fieldIndex = new HashMap<String,FieldNode> ();
525         r.fields.addFirst(node);
526         r.fieldIndex.put(node.getName(), node);
527     }
528 
529     public void addProperty(PropertyNode node) {
530         node.setDeclaringClass(redirect());
531         FieldNode field = node.getField();
532         addField(field);
533         final ClassNode r = redirect();
534         if (r.properties == null)
535             r.properties = new ArrayList<PropertyNode> ();
536         r.properties.add(node);
537     }
538 
539     public PropertyNode addProperty(String name,
540                                     int modifiers,
541                                     ClassNode type,
542                                     Expression initialValueExpression,
543                                     Statement getterBlock,
544                                     Statement setterBlock) {
545         for (PropertyNode pn : getProperties()) {
546             if (pn.getName().equals(name)) {
547                 if (pn.getInitialExpression() == null && initialValueExpression != null)
548                     pn.getField().setInitialValueExpression(initialValueExpression);
549 
550                 if (pn.getGetterBlock() == null && getterBlock != null)
551                     pn.setGetterBlock(getterBlock);
552 
553                 if (pn.getSetterBlock() == null && setterBlock != null)
554                     pn.setSetterBlock(setterBlock);
555 
556                 return pn;
557             }
558         }
559         PropertyNode node =
560                 new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
561         addProperty(node);
562         return node;
563     }
564 
565     public boolean hasProperty(String name) {
566         return getProperty(name) != null;
567     }
568 
569     public PropertyNode getProperty(String name) {
570         for (PropertyNode pn : getProperties()) {
571             if (pn.getName().equals(name)) return pn;
572         }
573         return null;
574     }
575 
576     public void addConstructor(ConstructorNode node) {
577         node.setDeclaringClass(this);
578         final ClassNode r = redirect();
579         if (r.constructors == null)
580             r.constructors = new ArrayList<ConstructorNode> ();
581         r.constructors.add(node);
582     }
583 
584     public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
585         ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
586         addConstructor(node);
587         return node;
588     }
589 
590     public void addMethod(MethodNode node) {
591         node.setDeclaringClass(this);
592         redirect().methodsList.add(node);
593         redirect().methods.put(node.getName(), node);
594     }
595 
596     /**
597      * If a method with the given name and parameters is already defined then it is returned
598      * otherwise the given method is added to this node. This method is useful for
599      * default method adding like getProperty() or invokeMethod() where there may already
600      * be a method defined in a class and so the default implementations should not be added
601      * if already present.
602      */
603     public MethodNode addMethod(String name,
604                                 int modifiers,
605                                 ClassNode returnType,
606                                 Parameter[] parameters,
607                                 ClassNode[] exceptions,
608                                 Statement code) {
609         MethodNode other = getDeclaredMethod(name, parameters);
610         // let's not add duplicate methods
611         if (other != null) {
612             return other;
613         }
614         MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
615         addMethod(node);
616         return node;
617     }
618 
619     /**
620      * @see #getDeclaredMethod(String, Parameter[])
621      */
622     public boolean hasDeclaredMethod(String name, Parameter[] parameters) {
623         MethodNode other = getDeclaredMethod(name, parameters);
624         return other != null;
625     }
626 
627     /**
628      * @see #getMethod(String, Parameter[])
629      */
630     public boolean hasMethod(String name, Parameter[] parameters) {
631         MethodNode other = getMethod(name, parameters);
632         return other != null;
633     }
634 
635     /**
636      * Adds a synthetic method as part of the compilation process
637      */
638     public MethodNode addSyntheticMethod(String name,
639                                          int modifiers,
640                                          ClassNode returnType,
641                                          Parameter[] parameters,
642                                          ClassNode[] exceptions,
643                                          Statement code) {
644         MethodNode answer = addMethod(name, modifiers|ACC_SYNTHETIC, returnType, parameters, exceptions, code);
645         answer.setSynthetic(true);
646         return answer;
647     }
648 
649     public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
650         FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
651         addField(node);
652         return node;
653     }
654 
655     public FieldNode addFieldFirst(String name, int modifiers, ClassNode type, Expression initialValue) {
656         FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
657         addFieldFirst(node);
658         return node;
659     }
660 
661     public void addInterface(ClassNode type) {
662         // let's check if it already implements an interface
663         boolean skip = false;
664         ClassNode[] interfaces = redirect().interfaces;
665         for (ClassNode existing : interfaces) {
666             if (type.equals(existing)) {
667                 skip = true;
668             }
669         }
670         if (!skip) {
671             ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
672             System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
673             newInterfaces[interfaces.length] = type;
674             redirect().interfaces = newInterfaces;
675         }
676     }
677 
678     public boolean equals(Object o) {
679 previous bug bug overview next bug           if (redirect!=null) return redirect().equals(o);  HEALTH4J >>  :  EQ_DOESNT_OVERRIDE_EQUALS  EQ_DOESNT_OVERRIDE_EQUALS  EQ_DOESNT_OVERRIDE_EQUALS  EQ_DOESNT_OVERRIDE_EQUALS 
680         if (!(o instanceof ClassNode)) return false;
681         ClassNode cn = (ClassNode) o;
682         return (cn.getName().equals(getName()));
683     }
684 
685     public int hashCode() {
686         if (redirect!=null) return redirect().hashCode();
687         return getName().hashCode();
688     }
689 
690     public void addMixin(MixinNode mixin) {
691         // let's check if it already uses a mixin
692         MixinNode[] mixins = redirect().mixins;
693         boolean skip = false;
694         for (MixinNode existing : mixins) {
695             if (mixin.equals(existing)) {
696                 skip = true;
697             }
698         }
699         if (!skip) {
700             MixinNode[] newMixins = new MixinNode[mixins.length + 1];
701             System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
702             newMixins[mixins.length] = mixin;
703             redirect().mixins = newMixins;
704         }
705     }
706 
707     /**
708      * Finds a field matching the given name in this class.
709      *
710      * @param name the name of the field of interest
711      * @return the method matching the given name and parameters or null
712      */
713     public FieldNode getDeclaredField(String name) {
714         if (!redirect().lazyInitDone) redirect().lazyClassInit();
715         ClassNode r = redirect ();
716         if (r.fieldIndex == null)
717             r.fieldIndex = new HashMap<String,FieldNode> ();
718         return r.fieldIndex.get(name);
719     }
720 
721     /**
722      * Finds a field matching the given name in this class or a parent class.
723      *
724      * @param name the name of the field of interest
725      * @return the method matching the given name and parameters or null
726      */
727     public FieldNode getField(String name) {
728         ClassNode node = this;
729         while (node != null) {
730             FieldNode fn = node.getDeclaredField(name);
731             if (fn != null) return fn;
732             node = node.getSuperClass();
733         }
734         return null;
735     }
736 
737     /**
738      * @return the field node on the outer class or null if this is not an
739      *         inner class
740      */
741     public FieldNode getOuterField(String name) {
742         return null;
743     }
744 
745     /**
746      * Helper method to avoid casting to inner class
747      */
748     public ClassNode getOuterClass() {
749         return null;
750     }
751 
752     /**
753      * Adds a statement to the object initializer.
754      *
755      * @param statements the statement to be added
756      */
757     public void addObjectInitializerStatements(Statement statements) {
758         getObjectInitializerStatements().add(statements);
759     }
760 
761     public List<Statement> getObjectInitializerStatements() {
762         if (objectInitializers == null)
763             objectInitializers = new ArrayList<Statement> ();
764         return objectInitializers;
765     }
766 
767     private MethodNode getOrAddStaticConstructorNode() {
768         MethodNode method = null;
769         List declaredMethods = getDeclaredMethods("<clinit>");
770         if (declaredMethods.isEmpty()) {
771             method =
772                     addMethod("<clinit>", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
773             method.setSynthetic(true);
774         }
775         else {
776             method = (MethodNode) declaredMethods.get(0);
777         }
778         return method;
779     }
780     
781     public void addStaticInitializerStatements(List<Statement> staticStatements, boolean fieldInit) {
782         MethodNode method = getOrAddStaticConstructorNode();
783         BlockStatement block = null;
784         Statement statement = method.getCode();
785         if (statement == null) {
786             block = new BlockStatement();
787         }
788         else if (statement instanceof BlockStatement) {
789             block = (BlockStatement) statement;
790         }
791         else {
792             block = new BlockStatement();
793             block.addStatement(statement);
794         }
795 
796         // while anything inside a static initializer block is appended
797         // we don't want to append in the case we have a initialization
798         // expression of a static field. In that case we want to add
799         // before the other statements
800         if (!fieldInit) {
801             block.addStatements(staticStatements);
802         } else {
803             List<Statement> blockStatements = block.getStatements();
804             staticStatements.addAll(blockStatements);
805             blockStatements.clear();
806             blockStatements.addAll(staticStatements);
807         }
808     }
809 
810     public void positionStmtsAfterEnumInitStmts(List<Statement> staticFieldStatements) {
811         MethodNode method = getOrAddStaticConstructorNode();
812         Statement statement = method.getCode();
813         if (statement instanceof BlockStatement) {
814             BlockStatement block = (BlockStatement) statement;
815             // add given statements for explicitly declared static fields just after enum-special fields
816             // are found - the $VALUES binary expression marks the end of such fields.
817             List<Statement> blockStatements = block.getStatements();
818             ListIterator<Statement> litr = blockStatements.listIterator();
819             while (litr.hasNext()) {
820                 Statement stmt = litr.next();
821                 if (stmt instanceof ExpressionStatement &&
822                         ((ExpressionStatement) stmt).getExpression() instanceof BinaryExpression) {
823                     BinaryExpression bExp = (BinaryExpression) ((ExpressionStatement) stmt).getExpression();
824                     if (bExp.getLeftExpression() instanceof FieldExpression) {
825                         FieldExpression fExp = (FieldExpression) bExp.getLeftExpression();
826                         if (fExp.getFieldName().equals("$VALUES")) {
827                             for (Statement tmpStmt : staticFieldStatements) {
828                                 litr.add(tmpStmt);
829                             }
830                         }
831                     }
832                 }
833             }
834         }
835     }
836 
837     /**
838      * This methods returns a list of all methods of the given name
839      * defined in the current class
840      * @return the method list
841      * @see #getMethods(String)
842      */
843     public List<MethodNode> getDeclaredMethods(String name) {
844         if (!redirect().lazyInitDone) redirect().lazyClassInit();
845         if (redirect!=null) return redirect().getDeclaredMethods(name);
846         return methods.getNotNull(name);
847     }
848 
849     /**
850      * This methods creates a list of all methods with this name of the
851      * current class and of all super classes
852      * @return the methods list
853      * @see #getDeclaredMethods(String)
854      */
855     public List<MethodNode> getMethods(String name) {
856         List<MethodNode> answer = new ArrayList<MethodNode>();
857         ClassNode node = this;
858 previous bug bug overview next bug           while (node != null) {  HEALTH4J >>  :  IL_INFINITE_LOOP 
859             answer.addAll(node.getDeclaredMethods(name));
860             node = node.getSuperClass();
861         }
862         return answer;
863     }
864 
865     /**
866      * Finds a method matching the given name and parameters in this class.
867      *
868      * @return the method matching the given name and parameters or null
869      */
870     public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
871         for (MethodNode method :  getDeclaredMethods(name)) {
872             if (parametersEqual(method.getParameters(), parameters)) {
873                 return method;
874             }
875         }
876         return null;
877     }
878 
879     /**
880      * Finds a method matching the given name and parameters in this class
881      * or any parent class.
882      *
883      * @return the method matching the given name and parameters or null
884      */
885     public MethodNode getMethod(String name, Parameter[] parameters) {
886         for (MethodNode method : getMethods(name)) {
887             if (parametersEqual(method.getParameters(), parameters)) {
888                 return method;
889             }
890         }
891         return null;
892     }
893 
894     /**
895      * @param type the ClassNode of interest
896      * @return true if this node is derived from the given ClassNode
897      */
898     public boolean isDerivedFrom(ClassNode type) {
899         if (this.equals(ClassHelper.VOID_TYPE)) {
900             return type.equals(ClassHelper.VOID_TYPE);
901         }
902         if (type.equals(ClassHelper.OBJECT_TYPE)) return true;
903         ClassNode node = this;
904         while (node != null) {
905             if (type.equals(node)) {
906                 return true;
907             }
908             node = node.getSuperClass();
909         }
910         return false;
911     }
912 
913     /**
914      * @return true if this class is derived from a groovy object
915      *         i.e. it implements GroovyObject
916      */
917     public boolean isDerivedFromGroovyObject() {
918         return implementsInterface(ClassHelper.GROOVY_OBJECT_TYPE);
919     }
920 
921     /**
922      * @param classNode the class node for the interface
923      * @return true if this class or any base class implements the given interface
924      */
925     public boolean implementsInterface(ClassNode classNode) {
926         ClassNode node = redirect();
927         do {
928             if (node.declaresInterface(classNode)) {
929                 return true;
930             }
931             node = node.getSuperClass();
932         }
933         while (node != null);
934         return false;
935     }
936 
937     /**
938      * @param classNode the class node for the interface
939      * @return true if this class declares that it implements the given interface
940      * or if one of its interfaces extends directly or indirectly the interface
941      *
942      * NOTE: Doesn't consider an interface to implement itself.
943      * I think this is intended to be called on ClassNodes representing
944      * classes, not interfaces.
945      * 
946      */
947     public boolean declaresInterface(ClassNode classNode) {
948         ClassNode[] interfaces = redirect().getInterfaces();
949         for (ClassNode cn : interfaces) {
950             if (cn.equals(classNode)) return true;
951         }
952         for (ClassNode cn : interfaces) {
953             if (cn.declaresInterface(classNode)) return true;
954         }
955         return false;
956     }
957 
958     /**
959      * @return the ClassNode of the super class of this type
960      */
961     public ClassNode getSuperClass() {
962         if (!lazyInitDone && !isResolved()) {
963             throw new GroovyBugError("ClassNode#getSuperClass for "+getName()+" called before class resolving");
964         }
965         ClassNode sn = redirect().getUnresolvedSuperClass();
966         if (sn!=null) sn=sn.redirect();
967         return sn;
968     }
969 
970     public ClassNode getUnresolvedSuperClass() {
971         return getUnresolvedSuperClass(true);
972     }
973 
974     public ClassNode getUnresolvedSuperClass(boolean useRedirect) {
975         if (!useRedirect) return superClass;
976         if (!redirect().lazyInitDone) redirect().lazyClassInit();
977         return redirect().superClass;
978     }
979 
980     public void setUnresolvedSuperClass(ClassNode sn) {
981         superClass = sn;
982     }
983 
984     public ClassNode [] getUnresolvedInterfaces() {
985         return getUnresolvedInterfaces(true);
986     }
987 
988     public ClassNode [] getUnresolvedInterfaces(boolean useRedirect) {
989         if (!useRedirect) return interfaces;
990         if (!redirect().lazyInitDone) redirect().lazyClassInit();
991         return redirect().interfaces;
992     }
993 
994     public CompileUnit getCompileUnit() {
995         if (redirect!=null) return redirect().getCompileUnit();
996         if (compileUnit == null && module != null) {
997             compileUnit = module.getUnit();
998         }
999         return compileUnit;
1000     }
1001 
1002     protected void setCompileUnit(CompileUnit cu) {
1003         if (redirect!=null) redirect().setCompileUnit(cu);
1004         if (compileUnit!= null) compileUnit = cu;
1005     }
1006 
1007     /**
1008      * @return true if the two arrays are of the same size and have the same contents
1009      */
1010     protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
1011         if (a.length == b.length) {
1012             boolean answer = true;
1013             for (int i = 0; i < a.length; i++) {
1014                 if (!a[i].getType().equals(b[i].getType())) {
1015                     answer = false;
1016                     break;
1017                 }
1018             }
1019             return answer;
1020         }
1021         return false;
1022     }
1023 
1024     /**
1025      * @return the package name of this class
1026      */
1027     public String getPackageName() {
1028         int idx = getName().lastIndexOf('.');
1029         if (idx > 0) {
1030             return getName().substring(0, idx);
1031         }
1032         return null;
1033     }
1034 
1035     public String getNameWithoutPackage() {
1036         int idx = getName().lastIndexOf('.');
1037         if (idx > 0) {
1038             return getName().substring(idx + 1);
1039         }
1040         return getName();
1041     }
1042 
1043     public void visitContents(GroovyClassVisitor visitor) {
1044         // now let's visit the contents of the class
1045         for (PropertyNode pn : getProperties()) {
1046             visitor.visitProperty(pn);
1047         }
1048 
1049         for (FieldNode fn : getFields()) {
1050             visitor.visitField(fn);
1051         }
1052 
1053         for (ConstructorNode cn : getDeclaredConstructors()) {
1054             visitor.visitConstructor(cn);
1055         }
1056 
1057         for (MethodNode mn : getMethods()) {
1058             visitor.visitMethod(mn);
1059         }
1060     }
1061 
1062     public MethodNode getGetterMethod(String getterName) {
1063         for (MethodNode method : getDeclaredMethods(getterName)) {
1064             if (getterName.equals(method.getName())
1065                     && ClassHelper.VOID_TYPE!=method.getReturnType()
1066                     && method.getParameters().length == 0) {
1067                 return method;
1068             }
1069         }
1070         ClassNode parent = getSuperClass();
1071         if (parent!=null) return parent.getGetterMethod(getterName);
1072         return null;
1073     }
1074 
1075     public MethodNode getSetterMethod(String setterName) {
1076         return getSetterMethod(setterName, true);
1077     }
1078 
1079     public MethodNode getSetterMethod(String setterName, boolean voidOnly) {
1080         for (MethodNode method : getDeclaredMethods(setterName)) {
1081             if (setterName.equals(method.getName())
1082                     && (!voidOnly || ClassHelper.VOID_TYPE==method.getReturnType())
1083                     && method.getParameters().length == 1) {
1084                 return method;
1085             }
1086         }
1087         ClassNode parent = getSuperClass();
1088         if (parent!=null) return parent.getSetterMethod(setterName, voidOnly);
1089         return null;
1090     }
1091 
1092     /**
1093      * Is this class declared in a static method (such as a closure / inner class declared in a static method)
1094      */
1095     public boolean isStaticClass() {
1096         return redirect().staticClass;
1097     }
1098 
1099     public void setStaticClass(boolean staticClass) {
1100         redirect().staticClass = staticClass;
1101     }
1102 
1103     /**
1104      * @return Returns true if this inner class or closure was declared inside a script body
1105      */
1106     public boolean isScriptBody() {
1107         return redirect().scriptBody;
1108     }
1109 
1110     public void setScriptBody(boolean scriptBody) {
1111         redirect().scriptBody = scriptBody;
1112     }
1113 
1114     public boolean isScript() {
1115         return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
1116     }
1117 
1118     public void setScript(boolean script) {
1119         redirect().script = script;
1120     }
1121 
1122     public String toString() {
1123         String ret = getName();
1124         if (genericsTypes != null) {
1125             ret += " <";
1126             for (int i = 0; i < genericsTypes.length; i++) {
1127                 if (i != 0) ret += ", ";
1128                 GenericsType genericsType = genericsTypes[i];
1129                 ret += genericTypeAsString(genericsType);
1130             }
1131             ret += ">";
1132         }
1133         if (redirect != null) {
1134             ret += " -> " + redirect().toString();
1135         }
1136         return ret;
1137     }
1138 
1139     /**
1140      * This exists to avoid a recursive definition of toString. The default toString
1141      * in GenericsType calls ClassNode.toString(), which calls GenericsType.toString(), etc. 
1142      * @param genericsType
1143      * @return
1144      */
1145     private String genericTypeAsString(GenericsType genericsType) {
1146         String ret = genericsType.getName();
1147         if (genericsType.getUpperBounds() != null) {
1148             ret += " extends ";
1149             for (int i = 0; i < genericsType.getUpperBounds().length; i++) {
1150                 ClassNode classNode = genericsType.getUpperBounds()[i];
1151                 if (classNode.equals(this)) {
1152                     ret += classNode.getName();
1153                 } else {
1154                     ret += classNode.toString();
1155                 }
1156                 if (i + 1 < genericsType.getUpperBounds().length) ret += " & ";
1157             }
1158         } else if (genericsType.getLowerBound() !=null) {
1159             ClassNode classNode = genericsType.getLowerBound();
1160             if (classNode.equals(this)) {
1161                 ret += " super " + classNode.getName();
1162             } else {
1163                 ret += " super " + classNode;
1164             }
1165         }
1166         return ret;
1167     }
1168 
1169     /**
1170      * Returns true if the given method has a possibly matching instance method with the given name and arguments.
1171      *
1172      * @param name      the name of the method of interest
1173      * @param arguments the arguments to match against
1174      * @return true if a matching method was found
1175      */
1176     public boolean hasPossibleMethod(String name, Expression arguments) {
1177         int count = 0;
1178 
1179         if (arguments instanceof TupleExpression) {
1180             TupleExpression tuple = (TupleExpression) arguments;
1181 previous bug bug overview next bug               // TODO this won't strictly be true when using list expansion in argument calls 
1182             count = tuple.getExpressions().size();
1183         }
1184         ClassNode node = this;
1185         do {
1186             for (MethodNode method : getMethods(name)) {
1187                 if (method.getParameters().length == count && !method.isStatic()) {
1188                     return true;
1189                 }
1190             }
1191             node = node.getSuperClass();
1192         }
1193         while (node != null);
1194         return false;
1195     }
1196 
1197     public MethodNode tryFindPossibleMethod(String name, Expression arguments) {
1198         int count = 0;
1199 
1200         if (arguments instanceof TupleExpression) {
1201             TupleExpression tuple = (TupleExpression) arguments;
1202 previous bug bug overview next bug               // TODO this won't strictly be true when using list expansion in argument calls 
1203             count = tuple.getExpressions().size();
1204         } else
1205             return null;
1206 
1207         MethodNode res = null;
1208         ClassNode node = this;
1209         TupleExpression args = (TupleExpression) arguments;
1210         do {
1211             for (MethodNode method : node.getMethods(name)) {
1212                 if (method.getParameters().length == count) {
1213                     boolean match = true;
1214                     for (int i = 0; i != count; ++i)
1215                         if (!args.getType().isDerivedFrom(method.getParameters()[i].getType())) {
1216                             match = false;
1217                             break;
1218                         }
1219 
1220                     if (match) {
1221                         if (res == null)
1222                             res = method;
1223                         else {
1224                             if (res.getParameters().length != count)
1225                                 return null;
1226                             if (node.equals(this))
1227                                 return null;
1228 
1229                             match = true;
1230                             for (int i = 0; i != count; ++i)
1231                                 if (!res.getParameters()[i].getType().equals(method.getParameters()[i].getType())) {
1232                                     match = false;
1233                                     break;
1234                                 }
1235                             if (!match)
1236                                 return null;
1237                         }
1238                     }
1239                 }
1240             }
1241             node = node.getSuperClass();
1242         }
1243         while (node != null);
1244 
1245         return res;
1246     }
1247 
1248     /**
1249      * Returns true if the given method has a possibly matching static method with the given name and arguments.
1250      *
1251      * @param name      the name of the method of interest
1252      * @param arguments the arguments to match against
1253      * @return true if a matching method was found
1254      */
1255     public boolean hasPossibleStaticMethod(String name, Expression arguments) {
1256         int count = 0;
1257 
1258         if (arguments instanceof TupleExpression) {
1259             TupleExpression tuple = (TupleExpression) arguments;
1260 previous bug bug overview               // TODO this won't strictly be true when using list expansion in argument calls 
1261             count = tuple.getExpressions().size();
1262         } else if (arguments instanceof MapExpression) {
1263             count = 1;
1264         }
1265         
1266         for (MethodNode method : getMethods(name)) {
1267             if(method.isStatic()) {
1268                 Parameter[] parameters = method.getParameters(); 
1269                 if (parameters.length == count) return true;
1270 
1271                 // handle varargs case
1272                 if (parameters.length > 0 && parameters[parameters.length - 1].getType().isArray()) {
1273                     if (count >= parameters.length - 1) return true;
1274                 }
1275                 
1276                 // handle parameters with default values
1277                 int nonDefaultParameters = 0;
1278                 for (Parameter parameter : parameters) {
1279                     if (!parameter.hasInitialExpression()) {
1280                         nonDefaultParameters++;
1281                     }
1282                 }
1283 
1284                 if (count < parameters.length && nonDefaultParameters <= count) {
1285                     return true;
1286                 }
1287             }
1288         }
1289         return false;
1290     }
1291 
1292     public boolean isInterface(){
1293         return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
1294     }
1295 
1296     public boolean isResolved(){
1297         return redirect().clazz!=null || (componentType != null && componentType.isResolved());
1298     }
1299 
1300     public boolean isArray(){
1301         return componentType!=null;
1302     }
1303 
1304     public ClassNode getComponentType() {
1305         return componentType;
1306     }
1307 
1308     public Class getTypeClass(){
1309         Class c = redirect().clazz;
1310         if (c!=null) return c;
1311         ClassNode component = redirect().componentType;
1312         if (component!=null && component.isResolved()){
1313             ClassNode cn = component.makeArray();
1314             setRedirect(cn);
1315             return redirect().clazz;
1316         }
1317         throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
1318     }
1319 
1320     public boolean hasPackageName(){
1321         return redirect().name.indexOf('.')>0;
1322     }
1323 
1324     /**
1325      * Marks if the current class uses annotations or not
1326      * @param flag
1327      */
1328     public void setAnnotated(boolean flag) {
1329         this.annotated = flag;
1330     }
1331 
1332     public boolean isAnnotated() {
1333         return this.annotated;
1334     }
1335 
1336     public GenericsType[] getGenericsTypes() {
1337         return genericsTypes;
1338     }
1339 
1340     public void setGenericsTypes(GenericsType[] genericsTypes) {
1341         usesGenerics = usesGenerics || genericsTypes!=null;
1342         this.genericsTypes = genericsTypes;
1343     }
1344 
1345     public void setGenericsPlaceHolder(boolean b) {
1346         usesGenerics = usesGenerics || b;
1347         placeholder = b;
1348     }
1349 
1350     public boolean isGenericsPlaceHolder() {
1351         return placeholder;
1352     }
1353 
1354     public boolean isUsingGenerics() {
1355         return usesGenerics;
1356     }
1357 
1358     public void setUsingGenerics(boolean b) {
1359         usesGenerics = b;
1360     }
1361 
1362     public ClassNode getPlainNodeReference() {
1363         if (ClassHelper.isPrimitiveType(this)) return this;
1364         ClassNode n = new ClassNode(getName(),getModifiers(),getSuperClass(),null,null);
1365         n.isPrimaryNode = false;
1366         n.setRedirect(this.redirect);
1367         n.componentType = redirect().getComponentType();
1368         return n;
1369     }
1370 
1371     public boolean isAnnotationDefinition() {
1372         return redirect().isPrimaryNode &&
1373                isInterface() &&
1374                (getModifiers() & Opcodes.ACC_ANNOTATION)!=0;
1375     }
1376 
1377     public List<AnnotationNode> getAnnotations() {
1378         if (redirect!=null) return redirect.getAnnotations();
1379         lazyClassInit();
1380         return super.getAnnotations();
1381     }
1382 
1383     public List<AnnotationNode> getAnnotations(ClassNode type) {
1384         if (redirect!=null) return redirect.getAnnotations(type);
1385         lazyClassInit();
1386         return super.getAnnotations(type);
1387     }
1388 
1389     public void addTransform(Class<? extends ASTTransformation> transform, ASTNode node) {
1390         GroovyASTTransformation annotation = transform.getAnnotation(GroovyASTTransformation.class);
1391         Set<ASTNode> nodes = getTransformInstances().get(annotation.phase()).get(transform);
1392         if (nodes == null) {
1393             nodes = new LinkedHashSet<ASTNode>();
1394             getTransformInstances().get(annotation.phase()).put(transform, nodes);
1395         }
1396         nodes.add(node);
1397     }
1398 
1399     public Map<Class <? extends ASTTransformation>, Set<ASTNode>> getTransforms(CompilePhase phase) {
1400         return getTransformInstances().get(phase);
1401     }
1402 
1403     public void renameField(String oldName, String newName) {
1404         ClassNode r = redirect ();
1405         if (r.fieldIndex == null)
1406             r.fieldIndex = new HashMap<String,FieldNode> ();
1407         final Map<String,FieldNode> index = r.fieldIndex;
1408         index.put(newName, index.remove(oldName));
1409     }
1410     
1411     public void removeField(String oldName) {
1412         ClassNode r = redirect ();
1413         if (r.fieldIndex == null)
1414             r.fieldIndex = new HashMap<String,FieldNode> ();
1415         final Map<String,FieldNode> index = r.fieldIndex;
1416         r.fields.remove(index.get(oldName));
1417         index.remove(oldName);
1418     }
1419 
1420     public boolean isEnum() {
1421         return (getModifiers()&Opcodes.ACC_ENUM) != 0;
1422      }
1423 
1424     /**
1425      * @return iterator of inner classes defined inside this one
1426      */
1427     public Iterator<InnerClassNode> getInnerClasses() {
1428         return (innerClasses == null ? Collections.<InnerClassNode>emptyList() : innerClasses).iterator();
1429     }
1430 
1431     private Map<CompilePhase, Map<Class<? extends ASTTransformation>, Set<ASTNode>>> getTransformInstances() {
1432         if(transformInstances == null){
1433             transformInstances = new EnumMap<CompilePhase, Map<Class <? extends ASTTransformation>, Set<ASTNode>>>(CompilePhase.class);
1434             for (CompilePhase phase : CompilePhase.values()) {
1435                 transformInstances.put(phase, new HashMap<Class <? extends ASTTransformation>, Set<ASTNode>>());
1436             }
1437         }
1438         return transformInstances;
1439     }
1440 }