0001: /*
0002: * $Id: ClassNode.java 4216 2006-11-13 16:04:23Z blackdrag $
0003: *
0004: * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
0005: *
0006: * Redistribution and use of this software and associated documentation
0007: * ("Software"), with or without modification, are permitted provided that the
0008: * following conditions are met:
0009: * 1. Redistributions of source code must retain copyright statements and
0010: * notices. Redistributions must also contain a copy of this document.
0011: * 2. Redistributions in binary form must reproduce the above copyright
0012: * notice, this list of conditions and the following disclaimer in the
0013: * documentation and/or other materials provided with the distribution.
0014: * 3. The name "groovy" must not be used to endorse or promote products
0015: * derived from this Software without prior written permission of The Codehaus.
0016: * For written permission, please contact info@codehaus.org.
0017: * 4. Products derived from this Software may not be called "groovy" nor may
0018: * "groovy" appear in their names without prior written permission of The
0019: * Codehaus. "groovy" is a registered trademark of The Codehaus.
0020: * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
0021: *
0022: * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
0023: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0024: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0025: * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
0026: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0027: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0028: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
0029: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0030: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0031: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
0032: * DAMAGE.
0033: *
0034: */
0035: package org.codehaus.groovy.ast;
0036:
0037: import groovy.lang.GroovyObject;
0038:
0039: import org.codehaus.groovy.GroovyBugError;
0040: import org.codehaus.groovy.ast.expr.Expression;
0041: import org.codehaus.groovy.ast.expr.TupleExpression;
0042: import org.codehaus.groovy.ast.stmt.BlockStatement;
0043: import org.codehaus.groovy.ast.stmt.EmptyStatement;
0044: import org.codehaus.groovy.ast.stmt.Statement;
0045: import org.objectweb.asm.Opcodes;
0046:
0047: import java.lang.reflect.Array;
0048: import java.lang.reflect.Constructor;
0049: import java.lang.reflect.Field;
0050: import java.lang.reflect.Method;
0051: import java.util.ArrayList;
0052: import java.util.HashMap;
0053: import java.util.HashSet;
0054: import java.util.Iterator;
0055: import java.util.List;
0056: import java.util.Map;
0057:
0058: /**
0059: * Represents a class in the AST.<br/>
0060: * A ClassNode should be created using the methods in ClassHelper.
0061: * This ClassNode may be used to represent a class declaration or
0062: * any other type. This class uses a proxy meschanism allowing to
0063: * create a class for a plain name at ast creation time. In another
0064: * phase of the compiler the real ClassNode for the plain name may be
0065: * found. To avoid the need of exchanging this ClassNode with an
0066: * instance of the correct ClassNode the correct ClassNode is set as
0067: * redirect. All method calls are then redirected to that ClassNode.
0068: * <br>
0069: * Note: the proxy mechanism is only allowed for classes being marked
0070: * as primary ClassNode which means they represent no actual class.
0071: * The redirect itself can be any type of ClassNode
0072: *
0073: * @see org.codehaus.groovy.ast.ClassHelper
0074: *
0075: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
0076: * @author Jochen Theodorou
0077: * @version $Revision: 4216 $
0078: */
0079: public class ClassNode extends AnnotatedNode implements Opcodes {
0080:
0081: public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
0082:
0083: public static ClassNode THIS = new ClassNode(Object.class);
0084: public static ClassNode SUPER = new ClassNode(Object.class);
0085:
0086: private String name;
0087: private int modifiers;
0088: private ClassNode[] interfaces;
0089: private MixinNode[] mixins;
0090: private List constructors = new ArrayList();
0091: private List objectInitializers = new ArrayList();
0092: private List methods = new ArrayList();
0093: private List fields = new ArrayList();
0094: private List properties = new ArrayList();
0095: private Map fieldIndex = new HashMap();
0096: private ModuleNode module;
0097: private CompileUnit compileUnit;
0098: private boolean staticClass = false;
0099: private boolean scriptBody = false;
0100: private boolean script;
0101: private ClassNode super Class;
0102: boolean isPrimaryNode;
0103:
0104: // use this to synchronize access for the lazy intit
0105: protected Object lazyInitLock = new Object();
0106:
0107: // clazz!=null when resolved
0108: protected Class clazz;
0109: // only false when this classNode is constructed from a class
0110: private boolean lazyInitDone = true;
0111: // not null if if the ClassNode is an array
0112: private ClassNode componentType = null;
0113: // if not null this instance is handled as proxy
0114: // for the redirect
0115: private ClassNode redirect = null;
0116:
0117: /**
0118: * Returns the ClassNode this ClassNode is redirecting to.
0119: */
0120: protected ClassNode redirect() {
0121: if (redirect == null)
0122: return this ;
0123: return redirect.redirect();
0124: }
0125:
0126: /**
0127: * Sets this instance as proxy for the given ClassNode.
0128: * @param cn the class to redirect to. If set to null the redirect will be removed
0129: */
0130: public void setRedirect(ClassNode cn) {
0131: if (isPrimaryNode)
0132: throw new GroovyBugError(
0133: "tried to set a redirect for a primary ClassNode ("
0134: + getName() + "->" + cn.getName() + ").");
0135: if (cn != null)
0136: cn = cn.redirect();
0137: redirect = cn;
0138: }
0139:
0140: /**
0141: * Returns a ClassNode representing an array of the class
0142: * represented by this ClassNode
0143: */
0144: public ClassNode makeArray() {
0145: if (redirect != null)
0146: return redirect().makeArray();
0147: ClassNode cn;
0148: if (clazz != null) {
0149: Class ret = Array.newInstance(clazz, 0).getClass();
0150: // don't use the ClassHelper here!
0151: cn = new ClassNode(ret, this );
0152: } else {
0153: cn = new ClassNode(this );
0154: }
0155: return cn;
0156: }
0157:
0158: /**
0159: * Returns if this instance is a primary ClassNode
0160: */
0161: public boolean isPrimaryClassNode() {
0162: return redirect().isPrimaryNode
0163: || (componentType != null && componentType
0164: .isPrimaryClassNode());
0165: }
0166:
0167: /**
0168: * Constructor used by makeArray() if no real class is available
0169: */
0170: private ClassNode(ClassNode componentType) {
0171: this (componentType.getName() + "[]", ACC_PUBLIC,
0172: ClassHelper.OBJECT_TYPE);
0173: this .componentType = componentType.redirect();
0174: isPrimaryNode = false;
0175: }
0176:
0177: /**
0178: * Constructor used by makeArray() if a real class is available
0179: */
0180: private ClassNode(Class c, ClassNode componentType) {
0181: this (c);
0182: this .componentType = componentType;
0183: isPrimaryNode = false;
0184: }
0185:
0186: /**
0187: * Creates a ClassNode from a real class. The resulting
0188: * ClassNode will be no primary ClassNode.
0189: */
0190: public ClassNode(Class c) {
0191: this (c.getName(), c.getModifiers(), null, null,
0192: MixinNode.EMPTY_ARRAY);
0193: clazz = c;
0194: lazyInitDone = false;
0195: CompileUnit cu = getCompileUnit();
0196: if (cu != null)
0197: cu.addClass(this );
0198: isPrimaryNode = false;
0199: }
0200:
0201: /**
0202: * The complete class structure will be initialized only when really
0203: * needed to avoid having too much objects during compilation
0204: */
0205: private void lazyClassInit() {
0206: synchronized (lazyInitLock) {
0207: if (lazyInitDone)
0208: return;
0209:
0210: Field[] fields = clazz.getDeclaredFields();
0211: for (int i = 0; i < fields.length; i++) {
0212: addField(fields[i].getName(), fields[i].getModifiers(),
0213: this , null);
0214: }
0215: Method[] methods = clazz.getDeclaredMethods();
0216: for (int i = 0; i < methods.length; i++) {
0217: Method m = methods[i];
0218: MethodNode mn = new MethodNode(m.getName(), m
0219: .getModifiers(), ClassHelper.make(m
0220: .getReturnType()), createParameters(m
0221: .getParameterTypes()), ClassHelper.make(m
0222: .getExceptionTypes()), null);
0223: addMethod(mn);
0224: }
0225: Constructor[] constructors = clazz
0226: .getDeclaredConstructors();
0227: for (int i = 0; i < constructors.length; i++) {
0228: Constructor ctor = constructors[i];
0229: addConstructor(ctor.getModifiers(),
0230: createParameters(ctor.getParameterTypes()),
0231: ClassHelper.make(ctor.getExceptionTypes()),
0232: null);
0233: }
0234: Class sc = clazz.getSuperclass();
0235: if (sc != null)
0236: super Class = ClassHelper.make(sc);
0237: buildInterfaceTypes(clazz);
0238: lazyInitDone = true;
0239: }
0240: }
0241:
0242: private void buildInterfaceTypes(Class c) {
0243: Class[] interfaces = c.getInterfaces();
0244: ClassNode[] ret = new ClassNode[interfaces.length];
0245: for (int i = 0; i < interfaces.length; i++) {
0246: ret[i] = ClassHelper.make(interfaces[i]);
0247: }
0248: this .interfaces = ret;
0249: }
0250:
0251: // added to track the enclosing method for local inner classes
0252: private MethodNode enclosingMethod = null;
0253:
0254: public MethodNode getEnclosingMethod() {
0255: return redirect().enclosingMethod;
0256: }
0257:
0258: public void setEnclosingMethod(MethodNode enclosingMethod) {
0259: redirect().enclosingMethod = enclosingMethod;
0260: }
0261:
0262: /**
0263: * @param name is the full name of the class
0264: * @param modifiers the modifiers,
0265: * @param superClass the base class name - use "java.lang.Object" if no direct
0266: * base class
0267: * @see org.objectweb.asm.Opcodes
0268: */
0269: public ClassNode(String name, int modifiers, ClassNode super Class) {
0270: this (name, modifiers, super Class, ClassHelper.EMPTY_TYPE_ARRAY,
0271: MixinNode.EMPTY_ARRAY);
0272: }
0273:
0274: /**
0275: * @param name is the full name of the class
0276: * @param modifiers the modifiers,
0277: * @param superClass the base class name - use "java.lang.Object" if no direct
0278: * base class
0279: * @see org.objectweb.asm.Opcodes
0280: */
0281: public ClassNode(String name, int modifiers, ClassNode super Class,
0282: ClassNode[] interfaces, MixinNode[] mixins) {
0283: this .name = name;
0284: this .modifiers = modifiers;
0285: this .super Class = super Class;
0286: this .interfaces = interfaces;
0287: this .mixins = mixins;
0288: isPrimaryNode = true;
0289: }
0290:
0291: /**
0292: * Sets the superclass of this ClassNode
0293: */
0294: public void setSuperClass(ClassNode super Class) {
0295: redirect().super Class = super Class;
0296: }
0297:
0298: /**
0299: * Returns a list containing FieldNode objects for
0300: * each field in the class represented by this ClassNode
0301: */
0302: public List getFields() {
0303: if (!lazyInitDone) {
0304: lazyClassInit();
0305: }
0306: if (redirect != null)
0307: return redirect().getFields();
0308: return fields;
0309: }
0310:
0311: /**
0312: * Returns an array of ClassNodes representing the
0313: * interfaces the class implements
0314: */
0315: public ClassNode[] getInterfaces() {
0316: if (!lazyInitDone) {
0317: lazyClassInit();
0318: }
0319: if (redirect != null)
0320: return redirect().getInterfaces();
0321: return interfaces;
0322: }
0323:
0324: public MixinNode[] getMixins() {
0325: return redirect().mixins;
0326: }
0327:
0328: /**
0329: * Returns a list containing MethodNode objects for
0330: * each method in the class represented by this ClassNode
0331: */
0332: public List getMethods() {
0333: if (!lazyInitDone) {
0334: lazyClassInit();
0335: }
0336: if (redirect != null)
0337: return redirect().getMethods();
0338: return methods;
0339: }
0340:
0341: /**
0342: * Returns a list containing MethodNode objects for
0343: * each abstract method in the class represented by
0344: * this ClassNode
0345: */
0346: public List getAbstractMethods() {
0347:
0348: HashSet abstractNodes = new HashSet();
0349: // let us collect the abstract super classes and stop at the
0350: // first non abstract super class. If such a class still
0351: // contains abstract methods, then loading that class will fail.
0352: // No need to be extra carefull here for that.
0353: ClassNode parent = this .redirect();
0354: do {
0355: abstractNodes.add(parent);
0356: ClassNode[] interfaces = parent.getInterfaces();
0357: for (int i = 0; i < interfaces.length; i++) {
0358: abstractNodes.add(interfaces[i].redirect());
0359: }
0360: parent = parent.getSuperClass().redirect();
0361: } while (parent != null
0362: && ((parent.getModifiers() & Opcodes.ACC_ABSTRACT) != 0));
0363:
0364: List result = new ArrayList();
0365: for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt
0366: .hasNext();) {
0367: MethodNode method = (MethodNode) methIt.next();
0368: // add only abstract methods from abtract classes that
0369: // are not overwritten
0370: if (abstractNodes.contains(method.getDeclaringClass()
0371: .redirect())
0372: && (method.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {
0373: result.add(method);
0374: }
0375: }
0376: if (result.size() == 0) {
0377: return null;
0378: } else {
0379: return result;
0380: }
0381: }
0382:
0383: public List getAllDeclaredMethods() {
0384: return new ArrayList(getDeclaredMethodsMap().values());
0385: }
0386:
0387: protected Map getDeclaredMethodsMap() {
0388: // Start off with the methods from the superclass.
0389: ClassNode parent = getSuperClass();
0390: Map result = null;
0391: if (parent != null) {
0392: result = parent.getDeclaredMethodsMap();
0393: } else {
0394: result = new HashMap();
0395: }
0396:
0397: // add in unimplemented abstract methods from the interfaces
0398: ClassNode[] interfaces = getInterfaces();
0399: for (int i = 0; i < interfaces.length; i++) {
0400: ClassNode iface = interfaces[i];
0401: Map ifaceMethodsMap = iface.getDeclaredMethodsMap();
0402: for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter
0403: .hasNext();) {
0404: String methSig = (String) iter.next();
0405: if (!result.containsKey(methSig)) {
0406: MethodNode methNode = (MethodNode) ifaceMethodsMap
0407: .get(methSig);
0408: result.put(methSig, methNode);
0409: }
0410: }
0411: }
0412:
0413: // And add in the methods implemented in this class.
0414: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0415: MethodNode method = (MethodNode) iter.next();
0416: String sig = method.getTypeDescriptor();
0417: result.put(sig, method);
0418: }
0419: return result;
0420: }
0421:
0422: public String getName() {
0423: return redirect().name;
0424: }
0425:
0426: public String setName(String name) {
0427: return redirect().name = name;
0428: }
0429:
0430: public int getModifiers() {
0431: return redirect().modifiers;
0432: }
0433:
0434: public List getProperties() {
0435: return redirect().properties;
0436: }
0437:
0438: public List getDeclaredConstructors() {
0439: if (!lazyInitDone) {
0440: lazyClassInit();
0441: }
0442: return redirect().constructors;
0443: }
0444:
0445: public ModuleNode getModule() {
0446: return redirect().module;
0447: }
0448:
0449: public void setModule(ModuleNode module) {
0450: redirect().module = module;
0451: if (module != null) {
0452: redirect().compileUnit = module.getUnit();
0453: }
0454: }
0455:
0456: public void addField(FieldNode node) {
0457: node.setDeclaringClass(redirect());
0458: node.setOwner(redirect());
0459: redirect().fields.add(node);
0460: redirect().fieldIndex.put(node.getName(), node);
0461: }
0462:
0463: public void addProperty(PropertyNode node) {
0464: node.setDeclaringClass(redirect());
0465: FieldNode field = node.getField();
0466: addField(field);
0467:
0468: redirect().properties.add(node);
0469: }
0470:
0471: public PropertyNode addProperty(String name, int modifiers,
0472: ClassNode type, Expression initialValueExpression,
0473: Statement getterBlock, Statement setterBlock) {
0474: for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
0475: PropertyNode pn = (PropertyNode) iter.next();
0476: if (pn.getName().equals(name))
0477: return pn;
0478: }
0479: PropertyNode node = new PropertyNode(name, modifiers, type,
0480: redirect(), initialValueExpression, getterBlock,
0481: setterBlock);
0482: addProperty(node);
0483: return node;
0484: }
0485:
0486: public void addConstructor(ConstructorNode node) {
0487: node.setDeclaringClass(this );
0488: redirect().constructors.add(node);
0489: }
0490:
0491: public ConstructorNode addConstructor(int modifiers,
0492: Parameter[] parameters, ClassNode[] exceptions,
0493: Statement code) {
0494: ConstructorNode node = new ConstructorNode(modifiers,
0495: parameters, exceptions, code);
0496: addConstructor(node);
0497: return node;
0498: }
0499:
0500: public void addMethod(MethodNode node) {
0501: node.setDeclaringClass(this );
0502: redirect().methods.add(node);
0503: }
0504:
0505: /**
0506: * IF a method with the given name and parameters is already defined then it is returned
0507: * otherwise the given method is added to this node. This method is useful for
0508: * default method adding like getProperty() or invokeMethod() where there may already
0509: * be a method defined in a class and so the default implementations should not be added
0510: * if already present.
0511: */
0512: public MethodNode addMethod(String name, int modifiers,
0513: ClassNode returnType, Parameter[] parameters,
0514: ClassNode[] exceptions, Statement code) {
0515: MethodNode other = getDeclaredMethod(name, parameters);
0516: // lets not add duplicate methods
0517: if (other != null) {
0518: return other;
0519: }
0520: MethodNode node = new MethodNode(name, modifiers, returnType,
0521: parameters, exceptions, code);
0522: addMethod(node);
0523: return node;
0524: }
0525:
0526: /**
0527: * Adds a synthetic method as part of the compilation process
0528: */
0529: public MethodNode addSyntheticMethod(String name, int modifiers,
0530: ClassNode returnType, Parameter[] parameters,
0531: ClassNode[] exceptions, Statement code) {
0532: MethodNode answer = addMethod(name, modifiers, returnType,
0533: parameters, exceptions, code);
0534: answer.setSynthetic(true);
0535: return answer;
0536: }
0537:
0538: public FieldNode addField(String name, int modifiers,
0539: ClassNode type, Expression initialValue) {
0540: FieldNode node = new FieldNode(name, modifiers, type,
0541: redirect(), initialValue);
0542: addField(node);
0543: return node;
0544: }
0545:
0546: public void addInterface(ClassNode type) {
0547: // lets check if it already implements an interface
0548: boolean skip = false;
0549: ClassNode[] interfaces = redirect().interfaces;
0550: for (int i = 0; i < interfaces.length; i++) {
0551: if (type.equals(interfaces[i])) {
0552: skip = true;
0553: }
0554: }
0555: if (!skip) {
0556: ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
0557: System.arraycopy(interfaces, 0, newInterfaces, 0,
0558: interfaces.length);
0559: newInterfaces[interfaces.length] = type;
0560: redirect().interfaces = newInterfaces;
0561: }
0562: }
0563:
0564: public boolean equals(Object o) {
0565: if (redirect != null)
0566: return redirect().equals(o);
0567: ClassNode cn = (ClassNode) o;
0568: return (cn.getName().equals(getName()));
0569: }
0570:
0571: public void addMixin(MixinNode mixin) {
0572: // lets check if it already uses a mixin
0573: MixinNode[] mixins = redirect().mixins;
0574: boolean skip = false;
0575: for (int i = 0; i < mixins.length; i++) {
0576: if (mixin.equals(mixins[i])) {
0577: skip = true;
0578: }
0579: }
0580: if (!skip) {
0581: MixinNode[] newMixins = new MixinNode[mixins.length + 1];
0582: System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
0583: newMixins[mixins.length] = mixin;
0584: redirect().mixins = newMixins;
0585: }
0586: }
0587:
0588: public FieldNode getField(String name) {
0589: return (FieldNode) redirect().fieldIndex.get(name);
0590: }
0591:
0592: /**
0593: * @return the field node on the outer class or null if this is not an
0594: * inner class
0595: */
0596: public FieldNode getOuterField(String name) {
0597: return null;
0598: }
0599:
0600: /**
0601: * Helper method to avoid casting to inner class
0602: */
0603: public ClassNode getOuterClass() {
0604: return null;
0605: }
0606:
0607: public void addObjectInitializerStatements(Statement statements) {
0608: objectInitializers.add(statements);
0609: }
0610:
0611: public List getObjectInitializerStatements() {
0612: return objectInitializers;
0613: }
0614:
0615: public void addStaticInitializerStatements(List staticStatements,
0616: boolean fieldInit) {
0617: MethodNode method = null;
0618: List declaredMethods = getDeclaredMethods("<clinit>");
0619: if (declaredMethods.isEmpty()) {
0620: method = addMethod("<clinit>", ACC_PUBLIC | ACC_STATIC,
0621: ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY,
0622: ClassNode.EMPTY_ARRAY, new BlockStatement());
0623: method.setSynthetic(true);
0624: } else {
0625: method = (MethodNode) declaredMethods.get(0);
0626: }
0627: BlockStatement block = null;
0628: Statement statement = method.getCode();
0629: if (statement == null) {
0630: block = new BlockStatement();
0631: } else if (statement instanceof BlockStatement) {
0632: block = (BlockStatement) statement;
0633: } else {
0634: block = new BlockStatement();
0635: block.addStatement(statement);
0636: }
0637:
0638: // while anything inside a static initializer block is appended
0639: // we don't want to append in the case we have a initialization
0640: // expression of a static field. In that case we want to add
0641: // before the other statements
0642: if (!fieldInit) {
0643: block.addStatements(staticStatements);
0644: } else {
0645: List blockStatements = block.getStatements();
0646: staticStatements.addAll(blockStatements);
0647: blockStatements.clear();
0648: blockStatements.addAll(staticStatements);
0649: }
0650: }
0651:
0652: /**
0653: * @return a list of methods which match the given name
0654: */
0655: public List getDeclaredMethods(String name) {
0656: List answer = new ArrayList();
0657: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0658: MethodNode method = (MethodNode) iter.next();
0659: if (name.equals(method.getName())) {
0660: answer.add(method);
0661: }
0662: }
0663: return answer;
0664: }
0665:
0666: /**
0667: * @return a list of methods which match the given name
0668: */
0669: public List getMethods(String name) {
0670: List answer = new ArrayList();
0671: ClassNode node = this ;
0672: do {
0673: for (Iterator iter = node.getMethods().iterator(); iter
0674: .hasNext();) {
0675: MethodNode method = (MethodNode) iter.next();
0676: if (name.equals(method.getName())) {
0677: answer.add(method);
0678: }
0679: }
0680: node = node.getSuperClass();
0681: } while (node != null);
0682: return answer;
0683: }
0684:
0685: /**
0686: * @return the method matching the given name and parameters or null
0687: */
0688: public MethodNode getDeclaredMethod(String name,
0689: Parameter[] parameters) {
0690: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0691: MethodNode method = (MethodNode) iter.next();
0692: if (name.equals(method.getName())
0693: && parametersEqual(method.getParameters(),
0694: parameters)) {
0695: return method;
0696: }
0697: }
0698: return null;
0699: }
0700:
0701: /**
0702: * @return true if this node is derived from the given class node
0703: */
0704: public boolean isDerivedFrom(ClassNode type) {
0705: ClassNode node = this ;
0706: while (node != null) {
0707: if (type.equals(node)) {
0708: return true;
0709: }
0710: node = node.getSuperClass();
0711: }
0712: return false;
0713: }
0714:
0715: /**
0716: * @return true if this class is derived from a groovy object
0717: * i.e. it implements GroovyObject
0718: */
0719: public boolean isDerivedFromGroovyObject() {
0720: return implements Interface(GroovyObject.class.getName());
0721: }
0722:
0723: /**
0724: * @param name the fully qualified name of the interface
0725: * @return true if this class or any base class implements the given interface
0726: */
0727: public boolean implements Interface(String name) {
0728: ClassNode node = redirect();
0729: do {
0730: if (node.declaresInterface(name)) {
0731: return true;
0732: }
0733: node = node.getSuperClass();
0734: } while (node != null);
0735: return false;
0736: }
0737:
0738: /**
0739: * @param name the fully qualified name of the interface
0740: * @return true if this class declares that it implements the given interface
0741: */
0742: public boolean declaresInterface(String name) {
0743: ClassNode[] interfaces = redirect().getInterfaces();
0744: int size = interfaces.length;
0745: for (int i = 0; i < size; i++) {
0746: if (interfaces[i].getName().equals(name)) {
0747: return true;
0748: }
0749: }
0750: return false;
0751: }
0752:
0753: /**
0754: * @return the ClassNode of the super class of this type
0755: */
0756: public ClassNode getSuperClass() {
0757: if (!lazyInitDone && !isResolved()) {
0758: throw new GroovyBugError("Classnode#getSuperClass for "
0759: + getName() + " called before class resolving");
0760: }
0761: return redirect().getUnresolvedSuperClass();
0762: }
0763:
0764: public ClassNode getUnresolvedSuperClass() {
0765: if (!lazyInitDone) {
0766: lazyClassInit();
0767: }
0768: return redirect().super Class;
0769: }
0770:
0771: /**
0772: * Factory method to create a new MethodNode via reflection
0773: */
0774: protected MethodNode createMethodNode(Method method) {
0775: Parameter[] parameters = createParameters(method
0776: .getParameterTypes());
0777: return new MethodNode(method.getName(), method.getModifiers(),
0778: ClassHelper.make(method.getReturnType()), parameters,
0779: ClassHelper.make(method.getExceptionTypes()),
0780: EmptyStatement.INSTANCE);
0781: }
0782:
0783: /**
0784: * @param types
0785: */
0786: protected Parameter[] createParameters(Class[] types) {
0787: Parameter[] parameters = Parameter.EMPTY_ARRAY;
0788: int size = types.length;
0789: if (size > 0) {
0790: parameters = new Parameter[size];
0791: for (int i = 0; i < size; i++) {
0792: parameters[i] = createParameter(types[i], i);
0793: }
0794: }
0795: return parameters;
0796: }
0797:
0798: protected Parameter createParameter(Class parameterType, int idx) {
0799: return new Parameter(ClassHelper.make(parameterType), "param"
0800: + idx);
0801: }
0802:
0803: public CompileUnit getCompileUnit() {
0804: if (redirect != null)
0805: return redirect().getCompileUnit();
0806: if (compileUnit == null && module != null) {
0807: compileUnit = module.getUnit();
0808: }
0809: return compileUnit;
0810: }
0811:
0812: protected void setCompileUnit(CompileUnit cu) {
0813: if (redirect != null)
0814: redirect().setCompileUnit(cu);
0815: if (compileUnit != null)
0816: compileUnit = cu;
0817: }
0818:
0819: /**
0820: * @return true if the two arrays are of the same size and have the same contents
0821: */
0822: protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
0823: if (a.length == b.length) {
0824: boolean answer = true;
0825: for (int i = 0; i < a.length; i++) {
0826: if (!a[i].getType().equals(b[i].getType())) {
0827: answer = false;
0828: break;
0829: }
0830: }
0831: return answer;
0832: }
0833: return false;
0834: }
0835:
0836: /**
0837: * @return the package name of this class
0838: */
0839: public String getPackageName() {
0840: int idx = getName().lastIndexOf('.');
0841: if (idx > 0) {
0842: return getName().substring(0, idx);
0843: }
0844: return null;
0845: }
0846:
0847: public String getNameWithoutPackage() {
0848: int idx = getName().lastIndexOf('.');
0849: if (idx > 0) {
0850: return getName().substring(idx + 1);
0851: }
0852: return getName();
0853: }
0854:
0855: public void visitContents(GroovyClassVisitor visitor) {
0856:
0857: // now lets visit the contents of the class
0858: for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
0859: PropertyNode pn = (PropertyNode) iter.next();
0860: visitor.visitProperty(pn);
0861: }
0862:
0863: for (Iterator iter = getFields().iterator(); iter.hasNext();) {
0864: FieldNode fn = (FieldNode) iter.next();
0865: visitor.visitField(fn);
0866: }
0867:
0868: for (Iterator iter = getDeclaredConstructors().iterator(); iter
0869: .hasNext();) {
0870: ConstructorNode cn = (ConstructorNode) iter.next();
0871: visitor.visitConstructor(cn);
0872: }
0873:
0874: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0875: MethodNode mn = (MethodNode) iter.next();
0876: visitor.visitMethod(mn);
0877: }
0878: }
0879:
0880: public MethodNode getGetterMethod(String getterName) {
0881: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0882: MethodNode method = (MethodNode) iter.next();
0883: if (getterName.equals(method.getName())
0884: && ClassHelper.VOID_TYPE != method.getReturnType()
0885: && method.getParameters().length == 0) {
0886: return method;
0887: }
0888: }
0889: return null;
0890: }
0891:
0892: public MethodNode getSetterMethod(String getterName) {
0893: for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
0894: MethodNode method = (MethodNode) iter.next();
0895: if (getterName.equals(method.getName())
0896: && ClassHelper.VOID_TYPE == method.getReturnType()
0897: && method.getParameters().length == 1) {
0898: return method;
0899: }
0900: }
0901: return null;
0902: }
0903:
0904: /**
0905: * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
0906: */
0907: public boolean isStaticClass() {
0908: return redirect().staticClass;
0909: }
0910:
0911: public void setStaticClass(boolean staticClass) {
0912: redirect().staticClass = staticClass;
0913: }
0914:
0915: /**
0916: * @return Returns true if this inner class or closure was declared inside a script body
0917: */
0918: public boolean isScriptBody() {
0919: return redirect().scriptBody;
0920: }
0921:
0922: public void setScriptBody(boolean scriptBody) {
0923: redirect().scriptBody = scriptBody;
0924: }
0925:
0926: public boolean isScript() {
0927: return redirect().script
0928: || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
0929: }
0930:
0931: public void setScript(boolean script) {
0932: redirect().script = script;
0933: }
0934:
0935: public String toString() {
0936: return super .toString() + "[name: " + getName() + "]";
0937: }
0938:
0939: /**
0940: * Returns true if the given method has a possibly matching method with the given name and arguments
0941: */
0942: public boolean hasPossibleMethod(String name, Expression arguments) {
0943: int count = 0;
0944:
0945: if (arguments instanceof TupleExpression) {
0946: TupleExpression tuple = (TupleExpression) arguments;
0947: // TODO this won't strictly be true when using list expension in argument calls
0948: count = tuple.getExpressions().size();
0949: }
0950: ClassNode node = this ;
0951: do {
0952: for (Iterator iter = getMethods().iterator(); iter
0953: .hasNext();) {
0954: MethodNode method = (MethodNode) iter.next();
0955: if (name.equals(method.getName())
0956: && method.getParameters().length == count) {
0957: return true;
0958: }
0959: }
0960: node = node.getSuperClass();
0961: } while (node != null);
0962: return false;
0963: }
0964:
0965: public boolean isInterface() {
0966: return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
0967: }
0968:
0969: public boolean isResolved() {
0970: return redirect().clazz != null
0971: || (componentType != null && componentType.isResolved());
0972: }
0973:
0974: public boolean isArray() {
0975: return componentType != null;
0976: }
0977:
0978: public ClassNode getComponentType() {
0979: return componentType;
0980: }
0981:
0982: public Class getTypeClass() {
0983: Class c = redirect().clazz;
0984: if (c != null)
0985: return c;
0986: ClassNode component = redirect().componentType;
0987: if (component != null && component.isResolved()) {
0988: ClassNode cn = component.makeArray();
0989: setRedirect(cn);
0990: return redirect().clazz;
0991: }
0992: throw new GroovyBugError("ClassNode#getTypeClass for "
0993: + getName()
0994: + " is called before the type class is set ");
0995: }
0996:
0997: public boolean hasPackageName() {
0998: return redirect().name.indexOf('.') > 0;
0999: }
1000: }
|