1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 package org.codehaus.groovy.ast;
36
37 import groovy.lang.GroovyObject;
38
39 import org.codehaus.groovy.GroovyBugError;
40 import org.codehaus.groovy.ast.expr.Expression;
41 import org.codehaus.groovy.ast.expr.TupleExpression;
42 import org.codehaus.groovy.ast.stmt.BlockStatement;
43 import org.codehaus.groovy.ast.stmt.EmptyStatement;
44 import org.codehaus.groovy.ast.stmt.Statement;
45 import org.objectweb.asm.Opcodes;
46
47 import java.lang.reflect.Array;
48 import java.lang.reflect.Constructor;
49 import java.lang.reflect.Field;
50 import java.lang.reflect.Method;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
57
58 /***
59 * Represents a class in the AST.<br/>
60 * A ClassNode should be created using the methods in ClassHelper.
61 * This ClassNode may be used to represent a class declaration or
62 * any other type. This class uses a proxy meschanism allowing to
63 * create a class for a plain name at ast creation time. In another
64 * phase of the compiler the real ClassNode for the plain name may be
65 * found. To avoid the need of exchanging this ClassNode with an
66 * instance of the correct ClassNode the correct ClassNode is set as
67 * redirect. All method calls are then redirected to that ClassNode.
68 * <br>
69 * Note: the proxy mechanism is only allowed for classes being marked
70 * as primary ClassNode which means they represent no actual class.
71 * The redirect itself can be any type of ClassNode
72 *
73 * @see org.codehaus.groovy.ast.ClassHelper
74 *
75 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
76 * @author Jochen Theodorou
77 * @version $Revision: 1.73 $
78 */
79 public class ClassNode extends AnnotatedNode implements Opcodes {
80
81 public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
82
83 public static ClassNode THIS = new ClassNode(Object.class);
84 public static ClassNode SUPER = new ClassNode(Object.class);
85
86 private String name;
87 private int modifiers;
88 private ClassNode[] interfaces;
89 private MixinNode[] mixins;
90 private List constructors = new ArrayList();
91 private List objectInitializers = new ArrayList();
92 private List methods = new ArrayList();
93 private List fields = new ArrayList();
94 private List properties = new ArrayList();
95 private Map fieldIndex = new HashMap();
96 private ModuleNode module;
97 private CompileUnit compileUnit;
98 private boolean staticClass = false;
99 private boolean scriptBody = false;
100 private boolean script;
101 private ClassNode superClass;
102 boolean isPrimaryNode;
103
104
105 protected Object lazyInitLock = new Object();
106
107
108 protected Class clazz;
109
110 private boolean lazyInitDone=true;
111
112 private ClassNode componentType = null;
113
114
115 private ClassNode redirect=null;
116
117 /***
118 * Returns the ClassNode this ClassNode is redirecting to.
119 */
120 protected ClassNode redirect(){
121 if (redirect==null) return this;
122 return redirect.redirect();
123 }
124
125 /***
126 * Sets this instance as proxy for the given ClassNode.
127 * @param cn the class to redirect to. If set to null the redirect will be removed
128 */
129 public void setRedirect(ClassNode cn) {
130 if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
131 if (cn!=null) cn = cn.redirect();
132 redirect = cn;
133 }
134
135 /***
136 * Returns a ClassNode representing an array of the class
137 * represented by this ClassNode
138 */
139 public ClassNode makeArray() {
140 if (redirect!=null) return redirect().makeArray();
141 ClassNode cn;
142 if (clazz!=null) {
143 Class ret = Array.newInstance(clazz,0).getClass();
144
145 cn = new ClassNode(ret,this);
146 } else {
147 cn = new ClassNode(this);
148 }
149 return cn;
150 }
151
152 /***
153 * Returns if this instance is a primary ClassNode
154 */
155 public boolean isPrimaryClassNode(){
156 return redirect().isPrimaryNode || (componentType!= null && componentType.isPrimaryClassNode());
157 }
158
159 /***
160 * Constructor used by makeArray() if no real class is available
161 */
162 private ClassNode(ClassNode componentType) {
163 this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
164 this.componentType = componentType.redirect();
165 isPrimaryNode=false;
166 }
167
168 /***
169 * Constructor used by makeArray() if a real class is available
170 */
171 private ClassNode(Class c, ClassNode componentType) {
172 this(c);
173 this.componentType = componentType;
174 isPrimaryNode=false;
175 }
176
177 /***
178 * Creates a ClassNode from a real class. The resulting
179 * ClassNode will be no primary ClassNode.
180 */
181 public ClassNode(Class c) {
182 this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
183 clazz=c;
184 lazyInitDone=false;
185 CompileUnit cu = getCompileUnit();
186 if (cu!=null) cu.addClass(this);
187 isPrimaryNode=false;
188 }
189
190 /***
191 * The complete class structure will be initialized only when really
192 * needed to avoid having too much objects during compilation
193 */
194 private void lazyClassInit() {
195 synchronized (lazyInitLock) {
196 if (lazyInitDone) return;
197
198 Field[] fields = clazz.getDeclaredFields();
199 for (int i=0;i<fields.length;i++){
200 addField(fields[i].getName(),fields[i].getModifiers(),this,null);
201 }
202 Method[] methods = clazz.getDeclaredMethods();
203 for (int i=0;i<methods.length;i++){
204 Method m = methods[i];
205 MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), ClassHelper.make(m.getExceptionTypes()), null);
206 addMethod(mn);
207 }
208 Constructor[] constructors = clazz.getConstructors();
209 for (int i=0;i<constructors.length;i++){
210 Constructor ctor = constructors[i];
211 addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),ClassHelper.make(ctor.getExceptionTypes()),null);
212 }
213 Class sc = clazz.getSuperclass();
214 if (sc!=null) superClass = ClassHelper.make(sc);
215 buildInterfaceTypes(clazz);
216 lazyInitDone=true;
217 }
218 }
219
220 private void buildInterfaceTypes(Class c) {
221 Class[] interfaces = c.getInterfaces();
222 ClassNode[] ret = new ClassNode[interfaces.length];
223 for (int i=0;i<interfaces.length;i++){
224 ret[i] = ClassHelper.make(interfaces[i]);
225 }
226 this.interfaces = ret;
227 }
228
229
230
231 private MethodNode enclosingMethod = null;
232
233 public MethodNode getEnclosingMethod() {
234 return redirect().enclosingMethod;
235 }
236
237 public void setEnclosingMethod(MethodNode enclosingMethod) {
238 redirect().enclosingMethod = enclosingMethod;
239 }
240
241
242 /***
243 * @param name is the full name of the class
244 * @param modifiers the modifiers,
245 * @param superClass the base class name - use "java.lang.Object" if no direct
246 * base class
247 * @see org.objectweb.asm.Opcodes
248 */
249 public ClassNode(String name, int modifiers, ClassNode superClass) {
250 this(name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
251 }
252
253 /***
254 * @param name is the full name of the class
255 * @param modifiers the modifiers,
256 * @param superClass the base class name - use "java.lang.Object" if no direct
257 * base class
258 * @see org.objectweb.asm.Opcodes
259 */
260 public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
261 this.name = name;
262 this.modifiers = modifiers;
263 this.superClass = superClass;
264 this.interfaces = interfaces;
265 this.mixins = mixins;
266 isPrimaryNode = true;
267 }
268
269
270 /***
271 * Sets the superclass of this ClassNode
272 */
273 public void setSuperClass(ClassNode superClass) {
274 redirect().superClass = superClass;
275 }
276
277 /***
278 * Returns a list containing FieldNode objects for
279 * each field in the class represented by this ClassNode
280 */
281 public List getFields() {
282 if (!lazyInitDone) {
283 lazyClassInit();
284 }
285 if (redirect!=null) return redirect().getFields();
286 return fields;
287 }
288
289 /***
290 * Returns an array of ClassNodes representing the
291 * interfaces the class implements
292 */
293 public ClassNode[] getInterfaces() {
294 if (!lazyInitDone) {
295 lazyClassInit();
296 }
297 if (redirect!=null) return redirect().getInterfaces();
298 return interfaces;
299 }
300
301 public MixinNode[] getMixins() {
302 return redirect().mixins;
303 }
304
305 /***
306 * Returns a list containing MethodNode objects for
307 * each method in the class represented by this ClassNode
308 */
309 public List getMethods() {
310 if (!lazyInitDone) {
311 lazyClassInit();
312 }
313 if (redirect!=null) return redirect().getMethods();
314 return methods;
315 }
316
317 /***
318 * Returns a list containing MethodNode objects for
319 * each abstract method in the class represented by
320 * this ClassNode
321 */
322 public List getAbstractMethods() {
323
324 HashSet abstractNodes = new HashSet();
325
326
327
328
329 ClassNode parent = this.redirect();
330 do {
331 abstractNodes.add(parent);
332 ClassNode[] interfaces = parent.getInterfaces();
333 for (int i = 0; i < interfaces.length; i++) {
334 abstractNodes.add(interfaces[i].redirect());
335 }
336 parent = parent.getSuperClass().redirect();
337 } while (parent!=null && ((parent.getModifiers() & Opcodes.ACC_ABSTRACT) != 0));
338
339 List result = new ArrayList();
340 for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) {
341 MethodNode method = (MethodNode) methIt.next();
342
343
344 if ( abstractNodes.contains(method.getDeclaringClass().redirect()) &&
345 (method.getModifiers() & Opcodes.ACC_ABSTRACT) != 0
346 ) {
347 result.add(method);
348 }
349 }
350 if (result.size() == 0) {
351 return null;
352 }
353 else {
354 return result;
355 }
356 }
357
358 public List getAllDeclaredMethods() {
359 return new ArrayList(getDeclaredMethodsMap().values());
360 }
361
362
363 protected Map getDeclaredMethodsMap() {
364
365 ClassNode parent = getSuperClass();
366 Map result = null;
367 if (parent != null) {
368 result = parent.getDeclaredMethodsMap();
369 }
370 else {
371 result = new HashMap();
372 }
373
374
375 ClassNode[] interfaces = getInterfaces();
376 for (int i = 0; i < interfaces.length; i++) {
377 ClassNode iface = interfaces[i];
378 Map ifaceMethodsMap = iface.getDeclaredMethodsMap();
379 for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) {
380 String methSig = (String) iter.next();
381 if (!result.containsKey(methSig)) {
382 MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig);
383 result.put(methSig, methNode);
384 }
385 }
386 }
387
388
389 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
390 MethodNode method = (MethodNode) iter.next();
391 String sig = method.getTypeDescriptor();
392 if (result.containsKey(sig)) {
393 MethodNode inheritedMethod = (MethodNode) result.get(sig);
394 if (inheritedMethod.isAbstract()) {
395 result.put(sig, method);
396 }
397 }
398 else {
399 result.put(sig, method);
400 }
401 }
402 return result;
403 }
404
405 public String getName() {
406 return redirect().name;
407 }
408
409 public String setName(String name) {
410 return redirect().name=name;
411 }
412
413 public int getModifiers() {
414 return redirect().modifiers;
415 }
416
417 public List getProperties() {
418 return redirect().properties;
419 }
420
421 public List getDeclaredConstructors() {
422 if (!lazyInitDone) {
423 lazyClassInit();
424 }
425 return redirect().constructors;
426 }
427
428 public ModuleNode getModule() {
429 return redirect().module;
430 }
431
432 public void setModule(ModuleNode module) {
433 redirect().module = module;
434 if (module != null) {
435 redirect().compileUnit = module.getUnit();
436 }
437 }
438
439 public void addField(FieldNode node) {
440 node.setDeclaringClass(redirect());
441 node.setOwner(redirect());
442 redirect().fields.add(node);
443 redirect().fieldIndex.put(node.getName(), node);
444 }
445
446 public void addProperty(PropertyNode node) {
447 node.setDeclaringClass(redirect());
448 FieldNode field = node.getField();
449 addField(field);
450
451 redirect().properties.add(node);
452 }
453
454 public PropertyNode addProperty(String name,
455 int modifiers,
456 ClassNode type,
457 Expression initialValueExpression,
458 Statement getterBlock,
459 Statement setterBlock) {
460 for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
461 PropertyNode pn = (PropertyNode) iter.next();
462 if (pn.getName().equals(name)) return pn;
463 }
464 PropertyNode node =
465 new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
466 addProperty(node);
467 return node;
468 }
469
470 public void addConstructor(ConstructorNode node) {
471 node.setDeclaringClass(this);
472 redirect().constructors.add(node);
473 }
474
475 public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
476 ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
477 addConstructor(node);
478 return node;
479 }
480
481 public void addMethod(MethodNode node) {
482 node.setDeclaringClass(this);
483 redirect().methods.add(node);
484 }
485
486 /***
487 * IF a method with the given name and parameters is already defined then it is returned
488 * otherwise the given method is added to this node. This method is useful for
489 * default method adding like getProperty() or invokeMethod() where there may already
490 * be a method defined in a class and so the default implementations should not be added
491 * if already present.
492 */
493 public MethodNode addMethod(String name,
494 int modifiers,
495 ClassNode returnType,
496 Parameter[] parameters,
497 ClassNode[] exceptions,
498 Statement code) {
499 MethodNode other = getDeclaredMethod(name, parameters);
500
501 if (other != null) {
502 return other;
503 }
504 MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
505 addMethod(node);
506 return node;
507 }
508
509 /***
510 * Adds a synthetic method as part of the compilation process
511 */
512 public MethodNode addSyntheticMethod(String name,
513 int modifiers,
514 ClassNode returnType,
515 Parameter[] parameters,
516 ClassNode[] exceptions,
517 Statement code) {
518 MethodNode answer = addMethod(name, modifiers, returnType, parameters, exceptions, code);
519 answer.setSynthetic(true);
520 return answer;
521 }
522
523 public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
524 FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
525 addField(node);
526 return node;
527 }
528
529 public void addInterface(ClassNode type) {
530
531 boolean skip = false;
532 ClassNode[] interfaces = redirect().interfaces;
533 for (int i = 0; i < interfaces.length; i++) {
534 if (type.equals(interfaces[i])) {
535 skip = true;
536 }
537 }
538 if (!skip) {
539 ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
540 System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
541 newInterfaces[interfaces.length] = type;
542 redirect().interfaces = newInterfaces;
543 }
544 }
545
546 public boolean equals(Object o) {
547 if (redirect!=null) return redirect().equals(o);
548 ClassNode cn = (ClassNode) o;
549 return (cn.getName().equals(getName()));
550 }
551
552 public void addMixin(MixinNode mixin) {
553
554 MixinNode[] mixins = redirect().mixins;
555 boolean skip = false;
556 for (int i = 0; i < mixins.length; i++) {
557 if (mixin.equals(mixins[i])) {
558 skip = true;
559 }
560 }
561 if (!skip) {
562 MixinNode[] newMixins = new MixinNode[mixins.length + 1];
563 System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
564 newMixins[mixins.length] = mixin;
565 redirect().mixins = newMixins;
566 }
567 }
568
569 public FieldNode getField(String name) {
570 return (FieldNode) redirect().fieldIndex.get(name);
571 }
572
573 /***
574 * @return the field node on the outer class or null if this is not an
575 * inner class
576 */
577 public FieldNode getOuterField(String name) {
578 return null;
579 }
580
581 /***
582 * Helper method to avoid casting to inner class
583 *
584 * @return
585 */
586 public ClassNode getOuterClass() {
587 return null;
588 }
589
590 public void addObjectInitializerStatements(Statement statements) {
591 objectInitializers.add(statements);
592 }
593
594 public List getObjectInitializerStatements() {
595 return objectInitializers;
596 }
597
598 public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) {
599 MethodNode method = null;
600 List declaredMethods = getDeclaredMethods("<clinit>");
601 if (declaredMethods.isEmpty()) {
602 method =
603 addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
604 method.setSynthetic(true);
605 }
606 else {
607 method = (MethodNode) declaredMethods.get(0);
608 }
609 BlockStatement block = null;
610 Statement statement = method.getCode();
611 if (statement == null) {
612 block = new BlockStatement();
613 }
614 else if (statement instanceof BlockStatement) {
615 block = (BlockStatement) statement;
616 }
617 else {
618 block = new BlockStatement();
619 block.addStatement(statement);
620 }
621
622
623
624
625
626 if (!fieldInit) {
627 block.addStatements(staticStatements);
628 } else {
629 List blockStatements = block.getStatements();
630 staticStatements.addAll(blockStatements);
631 blockStatements.clear();
632 blockStatements.addAll(staticStatements);
633 }
634 }
635
636 /***
637 * @return a list of methods which match the given name
638 */
639 public List getDeclaredMethods(String name) {
640 List answer = new ArrayList();
641 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
642 MethodNode method = (MethodNode) iter.next();
643 if (name.equals(method.getName())) {
644 answer.add(method);
645 }
646 }
647 return answer;
648 }
649
650 /***
651 * @return a list of methods which match the given name
652 */
653 public List getMethods(String name) {
654 List answer = new ArrayList();
655 ClassNode node = this;
656 do {
657 for (Iterator iter = node.getMethods().iterator(); iter.hasNext();) {
658 MethodNode method = (MethodNode) iter.next();
659 if (name.equals(method.getName())) {
660 answer.add(method);
661 }
662 }
663 node = node.getSuperClass();
664 }
665 while (node != null);
666 return answer;
667 }
668
669 /***
670 * @return the method matching the given name and parameters or null
671 */
672 public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
673 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
674 MethodNode method = (MethodNode) iter.next();
675 if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) {
676 return method;
677 }
678 }
679 return null;
680 }
681
682 /***
683 * @return true if this node is derived from the given class node
684 */
685 public boolean isDerivedFrom(ClassNode type) {
686 ClassNode node = getSuperClass();
687 while (node != null) {
688 if (type.equals(node)) {
689 return true;
690 }
691 node = node.getSuperClass();
692 }
693 return false;
694 }
695
696 /***
697 * @return true if this class is derived from a groovy object
698 * i.e. it implements GroovyObject
699 */
700 public boolean isDerivedFromGroovyObject() {
701 return implementsInteface(GroovyObject.class.getName());
702 }
703
704 /***
705 * @param name the fully qualified name of the interface
706 * @return true if this class or any base class implements the given interface
707 */
708 public boolean implementsInteface(String name) {
709 ClassNode node = redirect();
710 do {
711 if (node.declaresInterface(name)) {
712 return true;
713 }
714 node = node.getSuperClass();
715 }
716 while (node != null);
717 return false;
718 }
719
720 /***
721 * @param name the fully qualified name of the interface
722 * @return true if this class declares that it implements the given interface
723 */
724 public boolean declaresInterface(String name) {
725 ClassNode[] interfaces = redirect().getInterfaces();
726 int size = interfaces.length;
727 for (int i = 0; i < size; i++) {
728 if (interfaces[i].getName().equals(name)) {
729 return true;
730 }
731 }
732 return false;
733 }
734
735 /***
736 * @return the ClassNode of the super class of this type
737 */
738 public ClassNode getSuperClass() {
739 if (!lazyInitDone && !isResolved()) {
740 throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving");
741 }
742 return getUnresolvedSuperClass();
743 }
744
745 public ClassNode getUnresolvedSuperClass() {
746 if (!lazyInitDone) {
747 lazyClassInit();
748 }
749 return redirect().superClass;
750 }
751
752 /***
753 * Factory method to create a new MethodNode via reflection
754 */
755 protected MethodNode createMethodNode(Method method) {
756 Parameter[] parameters = createParameters(method.getParameterTypes());
757 return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, ClassHelper.make(method.getExceptionTypes()), EmptyStatement.INSTANCE);
758 }
759
760 /***
761 * @param types
762 * @return
763 */
764 protected Parameter[] createParameters(Class[] types) {
765 Parameter[] parameters = Parameter.EMPTY_ARRAY;
766 int size = types.length;
767 if (size > 0) {
768 parameters = new Parameter[size];
769 for (int i = 0; i < size; i++) {
770 parameters[i] = createParameter(types[i], i);
771 }
772 }
773 return parameters;
774 }
775
776 protected Parameter createParameter(Class parameterType, int idx) {
777 return new Parameter(ClassHelper.make(parameterType), "param" + idx);
778 }
779
780 public CompileUnit getCompileUnit() {
781 if (redirect!=null) return redirect().getCompileUnit();
782 if (compileUnit == null && module != null) {
783 compileUnit = module.getUnit();
784 }
785 return compileUnit;
786 }
787
788 protected void setCompileUnit(CompileUnit cu) {
789 if (redirect!=null) redirect().setCompileUnit(cu);
790 if (compileUnit!= null) compileUnit = cu;
791 }
792
793 /***
794 * @return true if the two arrays are of the same size and have the same contents
795 */
796 protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
797 if (a.length == b.length) {
798 boolean answer = true;
799 for (int i = 0; i < a.length; i++) {
800 if (!a[i].getType().equals(b[i].getType())) {
801 answer = false;
802 break;
803 }
804 }
805 return answer;
806 }
807 return false;
808 }
809
810 /***
811 * @return the package name of this class
812 */
813 public String getPackageName() {
814 int idx = getName().lastIndexOf('.');
815 if (idx > 0) {
816 return getName().substring(0, idx);
817 }
818 return null;
819 }
820
821 public String getNameWithoutPackage() {
822 int idx = getName().lastIndexOf('.');
823 if (idx > 0) {
824 return getName().substring(idx + 1);
825 }
826 return getName();
827 }
828
829 public void visitContents(GroovyClassVisitor visitor) {
830
831
832 for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
833 PropertyNode pn = (PropertyNode) iter.next();
834 visitor.visitProperty(pn);
835 }
836
837 for (Iterator iter = getFields().iterator(); iter.hasNext();) {
838 FieldNode fn = (FieldNode) iter.next();
839 visitor.visitField(fn);
840 }
841
842 for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
843 ConstructorNode cn = (ConstructorNode) iter.next();
844 visitor.visitConstructor(cn);
845 }
846
847 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
848 MethodNode mn = (MethodNode) iter.next();
849 visitor.visitMethod(mn);
850 }
851 }
852
853 public MethodNode getGetterMethod(String getterName) {
854 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
855 MethodNode method = (MethodNode) iter.next();
856 if (getterName.equals(method.getName())
857 && ClassHelper.VOID_TYPE!=method.getReturnType()
858 && method.getParameters().length == 0) {
859 return method;
860 }
861 }
862 return null;
863 }
864
865 public MethodNode getSetterMethod(String getterName) {
866 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
867 MethodNode method = (MethodNode) iter.next();
868 if (getterName.equals(method.getName())
869 && ClassHelper.VOID_TYPE==method.getReturnType()
870 && method.getParameters().length == 1) {
871 return method;
872 }
873 }
874 return null;
875 }
876
877 /***
878 * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
879 *
880 * @return
881 */
882 public boolean isStaticClass() {
883 return redirect().staticClass;
884 }
885
886 public void setStaticClass(boolean staticClass) {
887 redirect().staticClass = staticClass;
888 }
889
890 /***
891 * @return Returns true if this inner class or closure was declared inside a script body
892 */
893 public boolean isScriptBody() {
894 return redirect().scriptBody;
895 }
896
897 public void setScriptBody(boolean scriptBody) {
898 redirect().scriptBody = scriptBody;
899 }
900
901 public boolean isScript() {
902 return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
903 }
904
905 public void setScript(boolean script) {
906 redirect().script = script;
907 }
908
909 public String toString() {
910 return super.toString() + "[name: " + getName() + "]";
911 }
912
913 /***
914 * Returns true if the given method has a possibly matching method with the given name and arguments
915 */
916 public boolean hasPossibleMethod(String name, Expression arguments) {
917 int count = 0;
918
919 if (arguments instanceof TupleExpression) {
920 TupleExpression tuple = (TupleExpression) arguments;
921
922 count = tuple.getExpressions().size();
923 }
924 ClassNode node = this;
925 do {
926 for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
927 MethodNode method = (MethodNode) iter.next();
928 if (name.equals(method.getName()) && method.getParameters().length == count) {
929 return true;
930 }
931 }
932 node = node.getSuperClass();
933 }
934 while (node != null);
935 return false;
936 }
937
938 public boolean isInterface(){
939 return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
940 }
941
942 public boolean isResolved(){
943 return redirect().clazz!=null || (componentType != null && componentType.isResolved());
944 }
945
946 public boolean isArray(){
947 return componentType!=null;
948 }
949
950 public ClassNode getComponentType() {
951 return componentType;
952 }
953
954 public Class getTypeClass(){
955 Class c = redirect().clazz;
956 if (c!=null) return c;
957 ClassNode component = redirect().componentType;
958 if (component!=null && component.isResolved()){
959 ClassNode cn = component.makeArray();
960 setRedirect(cn);
961 return redirect().clazz;
962 }
963 throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
964 }
965
966 public boolean hasPackageName(){
967 return redirect().name.indexOf('.')>0;
968 }
969 }