blob: 1f3385d48403b075dd6301ec8da38e833a984b5d [file] [log] [blame]
/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.ast;
import groovy.lang.GroovyObject;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Represents a class in the AST.<br/>
* A ClassNode should be created using the methods in ClassHelper.
* This ClassNode may be used to represent a class declaration or
* any other type. This class uses a proxy meschanism allowing to
* create a class for a plain name at ast creation time. In another
* phase of the compiler the real ClassNode for the plain name may be
* found. To avoid the need of exchanging this ClassNode with an
* instance of the correct ClassNode the correct ClassNode is set as
* redirect. Most method calls are then redirected to that ClassNode.
* <br>
* <b>Note:</b> the proxy mechanism is only allowed for classes being marked
* as primary ClassNode which means they represent no actual class.
* The redirect itself can be any type of ClassNode
* <br>
* To descirbe generic type signature see {@link #getGenericsTypes()} and
* {@link #setGenericsTypes(GenericsType[])}. These emthods are not proxied,
* they describe the type signature used at the point of declaration or the
* type signatures provided by the class. If the type signatures provided
* by the class are needed, then a call to {@link #redirect()} will help.
*
* @see org.codehaus.groovy.ast.ClassHelper
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @author Jochen Theodorou
* @version $Revision$
*/
public class ClassNode extends AnnotatedNode implements Opcodes {
public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
public static ClassNode THIS = new ClassNode(Object.class);
public static ClassNode SUPER = new ClassNode(Object.class);
private String name;
private final int modifiers;
private ClassNode[] interfaces;
private MixinNode[] mixins;
private List constructors = new ArrayList();
private List objectInitializers = new ArrayList();
private List methods = new ArrayList();
private List fields = new ArrayList();
private List properties = new ArrayList();
private Map fieldIndex = new HashMap();
private ModuleNode module;
private CompileUnit compileUnit;
private boolean staticClass = false;
private boolean scriptBody = false;
private boolean script;
private ClassNode superClass;
boolean isPrimaryNode;
// use this to synchronize access for the lazy intit
protected Object lazyInitLock = new Object();
// clazz!=null when resolved
protected Class clazz;
// only false when this classNode is constructed from a class
private boolean lazyInitDone=true;
// not null if if the ClassNode is an array
private ClassNode componentType = null;
// if not null this instance is handled as proxy
// for the redirect
private ClassNode redirect=null;
// flag if the classes or its members are annotated
private boolean annotated;
// type spec for generics
private GenericsType[] genericsTypes=null;
private boolean usesGenerics=false;
// if set tot true the name getGenericsTypes consists
// of 1 element describing the name of the placeholder
private boolean placeholder;
/**
* Returns the ClassNode this ClassNode is redirecting to.
*/
protected ClassNode redirect(){
if (redirect==null) return this;
return redirect.redirect();
}
/**
* Sets this instance as proxy for the given ClassNode.
* @param cn the class to redirect to. If set to null the redirect will be removed
*/
public void setRedirect(ClassNode cn) {
if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
if (cn!=null) cn = cn.redirect();
if (cn==this) return;
redirect = cn;
}
/**
* Returns a ClassNode representing an array of the class
* represented by this ClassNode
*/
public ClassNode makeArray() {
if (redirect!=null) return redirect().makeArray();
ClassNode cn;
if (clazz!=null) {
Class ret = Array.newInstance(clazz,0).getClass();
// don't use the ClassHelper here!
cn = new ClassNode(ret,this);
} else {
cn = new ClassNode(this);
}
return cn;
}
/**
* Returns if this instance is a primary ClassNode
*/
public boolean isPrimaryClassNode(){
return redirect().isPrimaryNode || (componentType!= null && componentType.isPrimaryClassNode());
}
/**
* Constructor used by makeArray() if no real class is available
*/
private ClassNode(ClassNode componentType) {
this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
this.componentType = componentType.redirect();
isPrimaryNode=false;
}
/**
* Constructor used by makeArray() if a real class is available
*/
private ClassNode(Class c, ClassNode componentType) {
this(c);
this.componentType = componentType;
isPrimaryNode=false;
}
/**
* Creates a ClassNode from a real class. The resulting
* ClassNode will be no primary ClassNode.
*/
public ClassNode(Class c) {
this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
clazz=c;
lazyInitDone=false;
CompileUnit cu = getCompileUnit();
if (cu!=null) cu.addClass(this);
isPrimaryNode=false;
}
/**
* The complete class structure will be initialized only when really
* needed to avoid having too much objects during compilation
*/
private void lazyClassInit() {
synchronized (lazyInitLock) {
if (lazyInitDone) return;
Field[] fields = clazz.getDeclaredFields();
for (int i=0;i<fields.length;i++){
addField(fields[i].getName(),fields[i].getModifiers(),this,null);
}
Method[] methods = clazz.getDeclaredMethods();
for (int i=0;i<methods.length;i++){
Method m = methods[i];
MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), ClassHelper.make(m.getExceptionTypes()), null);
addMethod(mn);
}
Constructor[] constructors = clazz.getDeclaredConstructors();
for (int i=0;i<constructors.length;i++){
Constructor ctor = constructors[i];
addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),ClassHelper.make(ctor.getExceptionTypes()),null);
}
Class sc = clazz.getSuperclass();
if (sc!=null) superClass = ClassHelper.make(sc);
buildInterfaceTypes(clazz);
lazyInitDone=true;
}
}
private void buildInterfaceTypes(Class c) {
Class[] interfaces = c.getInterfaces();
ClassNode[] ret = new ClassNode[interfaces.length];
for (int i=0;i<interfaces.length;i++){
ret[i] = ClassHelper.make(interfaces[i]);
}
this.interfaces = ret;
}
// added to track the enclosing method for local inner classes
private MethodNode enclosingMethod = null;
public MethodNode getEnclosingMethod() {
return redirect().enclosingMethod;
}
public void setEnclosingMethod(MethodNode enclosingMethod) {
redirect().enclosingMethod = enclosingMethod;
}
/**
* @param name is the full name of the class
* @param modifiers the modifiers,
* @param superClass the base class name - use "java.lang.Object" if no direct
* base class
* @see org.objectweb.asm.Opcodes
*/
public ClassNode(String name, int modifiers, ClassNode superClass) {
this(name, modifiers, superClass, EMPTY_ARRAY, MixinNode.EMPTY_ARRAY);
}
/**
* @param name is the full name of the class
* @param modifiers the modifiers,
* @param superClass the base class name - use "java.lang.Object" if no direct
* base class
* @see org.objectweb.asm.Opcodes
*/
public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
this.name = name;
this.modifiers = modifiers;
this.superClass = superClass;
this.interfaces = interfaces;
this.mixins = mixins;
isPrimaryNode = true;
if (superClass!=null) {
usesGenerics = superClass.isUsingGenerics();
}
if (!usesGenerics && interfaces!=null) {
for (int i = 0; i < interfaces.length; i++) {
usesGenerics = usesGenerics || interfaces[i].isUsingGenerics();
}
}
}
/**
* Sets the superclass of this ClassNode
*/
public void setSuperClass(ClassNode superClass) {
redirect().superClass = superClass;
}
/**
* Returns a list containing FieldNode objects for
* each field in the class represented by this ClassNode
*/
public List getFields() {
if (!lazyInitDone) {
lazyClassInit();
}
if (redirect!=null) return redirect().getFields();
return fields;
}
/**
* Returns an array of ClassNodes representing the
* interfaces the class implements
*/
public ClassNode[] getInterfaces() {
if (!lazyInitDone) {
lazyClassInit();
}
if (redirect!=null) return redirect().getInterfaces();
return interfaces;
}
public MixinNode[] getMixins() {
return redirect().mixins;
}
/**
* Returns a list containing MethodNode objects for
* each method in the class represented by this ClassNode
*/
public List getMethods() {
if (!lazyInitDone) {
lazyClassInit();
}
if (redirect!=null) return redirect().getMethods();
return methods;
}
/**
* Returns a list containing MethodNode objects for
* each abstract method in the class represented by
* this ClassNode
*/
public List getAbstractMethods() {
HashSet abstractNodes = new HashSet();
// let us collect the abstract super classes and stop at the
// first non abstract super class. If such a class still
// contains abstract methods, then loading that class will fail.
// No need to be extra carefull here for that.
ClassNode parent = this.redirect();
do {
abstractNodes.add(parent);
ClassNode[] interfaces = parent.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
abstractNodes.add(interfaces[i].redirect());
}
parent = parent.getSuperClass().redirect();
} while (parent!=null && ((parent.getModifiers() & Opcodes.ACC_ABSTRACT) != 0));
List result = new ArrayList();
for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) {
MethodNode method = (MethodNode) methIt.next();
// add only abstract methods from abtract classes that
// are not overwritten
if ( abstractNodes.contains(method.getDeclaringClass().redirect()) &&
(method.getModifiers() & Opcodes.ACC_ABSTRACT) != 0
) {
result.add(method);
}
}
if (result.isEmpty()) {
return null;
}
else {
return result;
}
}
public List getAllDeclaredMethods() {
return new ArrayList(getDeclaredMethodsMap().values());
}
protected Map getDeclaredMethodsMap() {
// Start off with the methods from the superclass.
ClassNode parent = getSuperClass();
Map result = null;
if (parent != null) {
result = parent.getDeclaredMethodsMap();
}
else {
result = new HashMap();
}
// add in unimplemented abstract methods from the interfaces
ClassNode[] interfaces = getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
ClassNode iface = interfaces[i];
Map ifaceMethodsMap = iface.getDeclaredMethodsMap();
for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) {
String methSig = (String) iter.next();
if (!result.containsKey(methSig)) {
MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig);
result.put(methSig, methNode);
}
}
}
// And add in the methods implemented in this class.
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
String sig = method.getTypeDescriptor();
result.put(sig, method);
}
return result;
}
public String getName() {
return redirect().name;
}
public String setName(String name) {
return redirect().name=name;
}
public int getModifiers() {
return redirect().modifiers;
}
public List getProperties() {
return redirect().properties;
}
public List getDeclaredConstructors() {
if (!lazyInitDone) {
lazyClassInit();
}
return redirect().constructors;
}
public ModuleNode getModule() {
return redirect().module;
}
public void setModule(ModuleNode module) {
redirect().module = module;
if (module != null) {
redirect().compileUnit = module.getUnit();
}
}
public void addField(FieldNode node) {
node.setDeclaringClass(redirect());
node.setOwner(redirect());
redirect().fields.add(node);
redirect().fieldIndex.put(node.getName(), node);
}
public void addProperty(PropertyNode node) {
node.setDeclaringClass(redirect());
FieldNode field = node.getField();
addField(field);
redirect().properties.add(node);
}
public PropertyNode addProperty(String name,
int modifiers,
ClassNode type,
Expression initialValueExpression,
Statement getterBlock,
Statement setterBlock) {
for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
PropertyNode pn = (PropertyNode) iter.next();
if (pn.getName().equals(name)) return pn;
}
PropertyNode node =
new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
addProperty(node);
return node;
}
public void addConstructor(ConstructorNode node) {
node.setDeclaringClass(this);
redirect().constructors.add(node);
}
public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
addConstructor(node);
return node;
}
public void addMethod(MethodNode node) {
node.setDeclaringClass(this);
redirect().methods.add(node);
}
/**
* IF a method with the given name and parameters is already defined then it is returned
* otherwise the given method is added to this node. This method is useful for
* default method adding like getProperty() or invokeMethod() where there may already
* be a method defined in a class and so the default implementations should not be added
* if already present.
*/
public MethodNode addMethod(String name,
int modifiers,
ClassNode returnType,
Parameter[] parameters,
ClassNode[] exceptions,
Statement code) {
MethodNode other = getDeclaredMethod(name, parameters);
// lets not add duplicate methods
if (other != null) {
return other;
}
MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
addMethod(node);
return node;
}
/**
* Adds a synthetic method as part of the compilation process
*/
public MethodNode addSyntheticMethod(String name,
int modifiers,
ClassNode returnType,
Parameter[] parameters,
ClassNode[] exceptions,
Statement code) {
MethodNode answer = addMethod(name, modifiers, returnType, parameters, exceptions, code);
answer.setSynthetic(true);
return answer;
}
public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
addField(node);
return node;
}
public void addInterface(ClassNode type) {
// lets check if it already implements an interface
boolean skip = false;
ClassNode[] interfaces = redirect().interfaces;
for (int i = 0; i < interfaces.length; i++) {
if (type.equals(interfaces[i])) {
skip = true;
}
}
if (!skip) {
ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
newInterfaces[interfaces.length] = type;
redirect().interfaces = newInterfaces;
}
}
public boolean equals(Object o) {
if (redirect!=null) return redirect().equals(o);
ClassNode cn = (ClassNode) o;
return (cn.getName().equals(getName()));
}
public void addMixin(MixinNode mixin) {
// lets check if it already uses a mixin
MixinNode[] mixins = redirect().mixins;
boolean skip = false;
for (int i = 0; i < mixins.length; i++) {
if (mixin.equals(mixins[i])) {
skip = true;
}
}
if (!skip) {
MixinNode[] newMixins = new MixinNode[mixins.length + 1];
System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
newMixins[mixins.length] = mixin;
redirect().mixins = newMixins;
}
}
public FieldNode getField(String name) {
return (FieldNode) redirect().fieldIndex.get(name);
}
/**
* @return the field node on the outer class or null if this is not an
* inner class
*/
public FieldNode getOuterField(String name) {
return null;
}
/**
* Helper method to avoid casting to inner class
*/
public ClassNode getOuterClass() {
return null;
}
public void addObjectInitializerStatements(Statement statements) {
objectInitializers.add(statements);
}
public List getObjectInitializerStatements() {
return objectInitializers;
}
public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) {
MethodNode method = null;
List declaredMethods = getDeclaredMethods("<clinit>");
if (declaredMethods.isEmpty()) {
method =
addMethod("<clinit>", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
method.setSynthetic(true);
}
else {
method = (MethodNode) declaredMethods.get(0);
}
BlockStatement block = null;
Statement statement = method.getCode();
if (statement == null) {
block = new BlockStatement();
}
else if (statement instanceof BlockStatement) {
block = (BlockStatement) statement;
}
else {
block = new BlockStatement();
block.addStatement(statement);
}
// while anything inside a static initializer block is appended
// we don't want to append in the case we have a initialization
// expression of a static field. In that case we want to add
// before the other statements
if (!fieldInit) {
block.addStatements(staticStatements);
} else {
List blockStatements = block.getStatements();
staticStatements.addAll(blockStatements);
blockStatements.clear();
blockStatements.addAll(staticStatements);
}
}
/**
* @return a list of methods which match the given name
*/
public List getDeclaredMethods(String name) {
List answer = new ArrayList();
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (name.equals(method.getName())) {
answer.add(method);
}
}
return answer;
}
/**
* @return a list of methods which match the given name
*/
public List getMethods(String name) {
List answer = new ArrayList();
ClassNode node = this;
do {
for (Iterator iter = node.getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (name.equals(method.getName())) {
answer.add(method);
}
}
node = node.getSuperClass();
}
while (node != null);
return answer;
}
/**
* @return the method matching the given name and parameters or null
*/
public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (name.equals(method.getName()) && parametersEqual(method.getParameters(), parameters)) {
return method;
}
}
return null;
}
/**
* @return true if this node is derived from the given class node
*/
public boolean isDerivedFrom(ClassNode type) {
ClassNode node = this;
while (node != null) {
if (type.equals(node)) {
return true;
}
node = node.getSuperClass();
}
return false;
}
/**
* @return true if this class is derived from a groovy object
* i.e. it implements GroovyObject
*/
public boolean isDerivedFromGroovyObject() {
return implementsInterface(GroovyObject.class.getName());
}
/**
* @param name the fully qualified name of the interface
* @return true if this class or any base class implements the given interface
*/
public boolean implementsInterface(String name) {
ClassNode node = redirect();
do {
if (node.declaresInterface(name)) {
return true;
}
node = node.getSuperClass();
}
while (node != null);
return false;
}
/**
* @param name the fully qualified name of the interface
* @return true if this class declares that it implements the given interface
*/
public boolean declaresInterface(String name) {
ClassNode[] interfaces = redirect().getInterfaces();
int size = interfaces.length;
for (int i = 0; i < size; i++) {
if (interfaces[i].getName().equals(name)) {
return true;
}
}
return false;
}
/**
* @return the ClassNode of the super class of this type
*/
public ClassNode getSuperClass() {
if (!lazyInitDone && !isResolved()) {
throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving");
}
return redirect().getUnresolvedSuperClass();
}
public ClassNode getUnresolvedSuperClass() {
return getUnresolvedSuperClass(true);
}
public ClassNode getUnresolvedSuperClass(boolean useRedirect) {
if (!useRedirect) return superClass;
if (!lazyInitDone) {
lazyClassInit();
}
return redirect().superClass;
}
/**
* Factory method to create a new MethodNode via reflection
*/
protected MethodNode createMethodNode(Method method) {
Parameter[] parameters = createParameters(method.getParameterTypes());
return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, ClassHelper.make(method.getExceptionTypes()), EmptyStatement.INSTANCE);
}
/**
* @param types
*/
protected Parameter[] createParameters(Class[] types) {
Parameter[] parameters = Parameter.EMPTY_ARRAY;
int size = types.length;
if (size > 0) {
parameters = new Parameter[size];
for (int i = 0; i < size; i++) {
parameters[i] = createParameter(types[i], i);
}
}
return parameters;
}
protected Parameter createParameter(Class parameterType, int idx) {
return new Parameter(ClassHelper.make(parameterType), "param" + idx);
}
public CompileUnit getCompileUnit() {
if (redirect!=null) return redirect().getCompileUnit();
if (compileUnit == null && module != null) {
compileUnit = module.getUnit();
}
return compileUnit;
}
protected void setCompileUnit(CompileUnit cu) {
if (redirect!=null) redirect().setCompileUnit(cu);
if (compileUnit!= null) compileUnit = cu;
}
/**
* @return true if the two arrays are of the same size and have the same contents
*/
protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
if (a.length == b.length) {
boolean answer = true;
for (int i = 0; i < a.length; i++) {
if (!a[i].getType().equals(b[i].getType())) {
answer = false;
break;
}
}
return answer;
}
return false;
}
/**
* @return the package name of this class
*/
public String getPackageName() {
int idx = getName().lastIndexOf('.');
if (idx > 0) {
return getName().substring(0, idx);
}
return null;
}
public String getNameWithoutPackage() {
int idx = getName().lastIndexOf('.');
if (idx > 0) {
return getName().substring(idx + 1);
}
return getName();
}
public void visitContents(GroovyClassVisitor visitor) {
// now lets visit the contents of the class
for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
PropertyNode pn = (PropertyNode) iter.next();
visitor.visitProperty(pn);
}
for (Iterator iter = getFields().iterator(); iter.hasNext();) {
FieldNode fn = (FieldNode) iter.next();
visitor.visitField(fn);
}
for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
ConstructorNode cn = (ConstructorNode) iter.next();
visitor.visitConstructor(cn);
}
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode mn = (MethodNode) iter.next();
visitor.visitMethod(mn);
}
}
public MethodNode getGetterMethod(String getterName) {
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (getterName.equals(method.getName())
&& ClassHelper.VOID_TYPE!=method.getReturnType()
&& method.getParameters().length == 0) {
return method;
}
}
return null;
}
public MethodNode getSetterMethod(String getterName) {
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (getterName.equals(method.getName())
&& ClassHelper.VOID_TYPE==method.getReturnType()
&& method.getParameters().length == 1) {
return method;
}
}
return null;
}
/**
* Is this class delcared in a static method (such as a closure / inner class declared in a static method)
*/
public boolean isStaticClass() {
return redirect().staticClass;
}
public void setStaticClass(boolean staticClass) {
redirect().staticClass = staticClass;
}
/**
* @return Returns true if this inner class or closure was declared inside a script body
*/
public boolean isScriptBody() {
return redirect().scriptBody;
}
public void setScriptBody(boolean scriptBody) {
redirect().scriptBody = scriptBody;
}
public boolean isScript() {
return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
}
public void setScript(boolean script) {
redirect().script = script;
}
public String toString() {
String ret = getName();
if (genericsTypes!=null) {
ret += " <";
for (int i = 0; i < genericsTypes.length; i++) {
if (i!=0) ret+=", ";
ret += genericsTypes[i];
}
ret += ">";
}
if (redirect!=null) {
ret += " -> "+redirect().toString();
}
return ret;
}
/**
* Returns true if the given method has a possibly matching method with the given name and arguments
*/
public boolean hasPossibleMethod(String name, Expression arguments) {
int count = 0;
if (arguments instanceof TupleExpression) {
TupleExpression tuple = (TupleExpression) arguments;
// TODO this won't strictly be true when using list expansion in argument calls
count = tuple.getExpressions().size();
}
ClassNode node = this;
do {
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (name.equals(method.getName()) && method.getParameters().length == count) {
return true;
}
}
node = node.getSuperClass();
}
while (node != null);
return false;
}
/**
* Returns true if the given method has a possibly matching static method with the given name and arguments
*/
public boolean hasPossibleStaticMethod(String name, Expression arguments) {
int count = 0;
if (arguments instanceof TupleExpression) {
TupleExpression tuple = (TupleExpression) arguments;
// TODO this won't strictly be true when using list expansion in argument calls
count = tuple.getExpressions().size();
}
for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
MethodNode method = (MethodNode) iter.next();
if (name.equals(method.getName()) && method.getParameters().length == count && method.isStatic()) {
return true;
}
// handle varargs case
if (name.equals(method.getName()) && method.isStatic() && method.getParameters().length > 0 &&
method.getParameters()[method.getParameters().length - 1].getType().getTypeClass() == Object[].class) {
if (count >= method.getParameters().length - 1) return true;
}
}
return false;
}
public boolean isInterface(){
return (getModifiers() & Opcodes.ACC_INTERFACE) > 0;
}
public boolean isResolved(){
return redirect().clazz!=null || (componentType != null && componentType.isResolved());
}
public boolean isArray(){
return componentType!=null;
}
public ClassNode getComponentType() {
return componentType;
}
public Class getTypeClass(){
Class c = redirect().clazz;
if (c!=null) return c;
ClassNode component = redirect().componentType;
if (component!=null && component.isResolved()){
ClassNode cn = component.makeArray();
setRedirect(cn);
return redirect().clazz;
}
throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
}
public boolean hasPackageName(){
return redirect().name.indexOf('.')>0;
}
/**
* Marks if the current class uses annotations or not
* @param flag
*/
public void setAnnotated(boolean flag) {
this.annotated = flag;
}
public boolean isAnnotated() {
return this.annotated;
}
public GenericsType[] getGenericsTypes() {
return genericsTypes;
}
public void setGenericsTypes(GenericsType[] genericsTypes) {
usesGenerics = usesGenerics || genericsTypes!=null;
this.genericsTypes = genericsTypes;
}
public void setGenericsPlaceHolder(boolean b) {
usesGenerics = usesGenerics || b;
placeholder = b;
}
public boolean isGenericsPlaceHolder() {
return placeholder;
}
public boolean isUsingGenerics() {
return usesGenerics;
}
public void setUsingGenerics(boolean b) {
usesGenerics = b;
}
}