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

Save This Page
Home » groovy-src-1.6.3 » org.codehaus » groovy » ast » [javadoc | source]