All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.ast.ClassNode Maven / Gradle / Ivy

There is a newer version: 1.5.8
Show newest version
/*
 * 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.Collections;
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.
* 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. *
* Note: 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 *
* 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 James Strachan * @author Jochen Theodorou * @version $Revision: 9652 $ */ public class ClassNode extends AnnotatedNode implements Opcodes { private static class MapOfLists { private Map map = new HashMap(); public List get(Object key) { return (List) map.get(key); } public List getNotNull(Object key) { List ret = get(key); if (ret==null) ret = Collections.EMPTY_LIST; return ret; } public void put(Object key, Object value) { if (map.containsKey(key)) { get(key).add(value); } else { ArrayList list = new ArrayList(2); list.add(value); map.put(key, list); } } } 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 MapOfLists methods; private List methodsList; 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 to 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. */ public 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"); if (declaredMethods.isEmpty()) { method = addMethod("", 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); } } /** * This methods returns a list of all methods of the given name * defined in the current class * @return the method list * @see #getMethods(String) */ public List getDeclaredMethods(String name) { if (!lazyInitDone) lazyClassInit(); if (redirect!=null) return redirect().getDeclaredMethods(name); return methods.getNotNull(name); } /** * This methods creates a list of all methods with this name of the * current class and of all super classes * @return the methods list * @see #getDeclaredMethods(String) */ public List getMethods(String name) { List answer = new ArrayList(getDeclaredMethods(name)); ClassNode parent = getSuperClass(); if (parent!=null) answer.addAll(parent.getMethods(name)); return answer; } /** * @return the method matching the given name and parameters or null */ public MethodNode getDeclaredMethod(String name, Parameter[] parameters) { List list = getDeclaredMethods(name); for (Iterator iter = list.iterator(); iter.hasNext();) { MethodNode method = (MethodNode) iter.next(); if (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) { if (type.equals(ClassHelper.OBJECT_TYPE)) return true; 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"); } ClassNode sn = redirect().getUnresolvedSuperClass(); if (sn!=null) sn=sn.redirect(); return sn; } 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 = getDeclaredMethods(getterName).iterator(); iter.hasNext();) { MethodNode method = (MethodNode) iter.next(); if (getterName.equals(method.getName()) && ClassHelper.VOID_TYPE!=method.getReturnType() && method.getParameters().length == 0) { return method; } } ClassNode parent = getSuperClass(); if (parent!=null) return parent.getGetterMethod(getterName); return null; } public MethodNode getSetterMethod(String setterName) { for (Iterator iter = getDeclaredMethods(setterName).iterator(); iter.hasNext();) { MethodNode method = (MethodNode) iter.next(); if (setterName.equals(method.getName()) && ClassHelper.VOID_TYPE==method.getReturnType() && method.getParameters().length == 1) { return method; } } ClassNode parent = getSuperClass(); if (parent!=null) return parent.getSetterMethod(setterName); 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 = getDeclaredMethods(name).iterator(); iter.hasNext();) { MethodNode method = (MethodNode) iter.next(); if (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 = getDeclaredMethods(name).iterator(); iter.hasNext();) { MethodNode method = (MethodNode) iter.next(); if (method.getParameters().length == count && method.isStatic()) { return true; } // handle varargs case if (method.isStatic() && method.getParameters().length > 0 && method.getParameters()[method.getParameters().length - 1].getType().isArray()) { 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; } public ClassNode getPlainNodeReference() { ClassNode n = new ClassNode(getName(),getModifiers(),getSuperClass(),null,null); n.isPrimaryNode = false; n.setRedirect(this.redirect); return n; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy