View Javadoc
Minimize
Table

1   /*
2    * Copyright 2003-2010 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.ast.stmt.BlockStatement;
19  import org.codehaus.groovy.ast.stmt.Statement;
20  import org.objectweb.asm.Opcodes;
21  
22  import java.lang.reflect.Modifier;
23  import java.util.List;
24  
25  /**
26   * Represents a method declaration
27   *
28   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
29   * @author Hamlet D'Arcy
30   * @version $Revision: 22561 $
31   */
32  public class MethodNode extends AnnotatedNode implements Opcodes {
33  
34      private final String name;
35      private int modifiers;
36      private boolean syntheticPublic;
37      private ClassNode returnType;
38      private Parameter[] parameters;
39      private boolean hasDefaultValue = false;
40      private Statement code;
41      private boolean dynamicReturnType;
42      private VariableScope variableScope;
43      private final ClassNode[] exceptions;
44      private final boolean staticConstructor;
45  
46      // type spec for generics
47      private GenericsType[] genericsTypes = null;
48      private boolean hasDefault;
49  
50      // cached data
51      String typeDescriptor;
52  
53      public MethodNode(String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
54          this.name = name;
55          this.modifiers = modifiers;
56          this.code = code;
57          setReturnType(returnType);
58          VariableScope scope = new VariableScope();
59          setVariableScope(scope);
60          setParameters(parameters);
61          this.hasDefault = false;
62          this.exceptions = exceptions;
63          this.staticConstructor = (name != null && name.equals("<clinit>"));
64      }
65  
66      /**
67       * The type descriptor for a method node is a string containing the name of the method, its return type,
68       * and its parameter types in a canonical form. For simplicity, I'm using the format of a Java declaration
69       * without parameter names.
70       *
71       * @return the type descriptor
72       */
73      public String getTypeDescriptor() {
74          if (typeDescriptor == null) {
75              StringBuffer buf = new StringBuffer(name.length() + parameters.length * 10);
76              buf.append(returnType.getName());
77              buf.append(' ');
78              buf.append(name);
79              buf.append('(');
80              for (int i = 0; i < parameters.length; i++) {
81                  if (i > 0) {
82                      buf.append(", ");
83                  }
84                  Parameter param = parameters[i];
85                  buf.append(param.getType().getName());
86              }
87              buf.append(')');
88              typeDescriptor = buf.toString();
89          }
90          return typeDescriptor;
91      }
92  
93      private void invalidateCachedData() {
94          typeDescriptor = null;
95      }
96  
97      public boolean isVoidMethod() {
98          return returnType == ClassHelper.VOID_TYPE;
99      }
100 
101     public Statement getCode() {
102         return code;
103     }
104 
105     public void setCode(Statement code) {
106         this.code = code;
107     }
108 
109     public int getModifiers() {
110         return modifiers;
111     }
112 
113     public void setModifiers(int modifiers) {
114         invalidateCachedData();
115         this.modifiers = modifiers;
116     }
117 
118     public String getName() {
119         return name;
120     }
121 
122     public Parameter[] getParameters() {
123         return parameters;
124     }
125 
126     public void setParameters(Parameter[] parameters) {
127         invalidateCachedData();
128         VariableScope scope = new VariableScope();
129         this.parameters = parameters;
130         if (parameters != null && parameters.length > 0) {
131             for (Parameter para : parameters) {
132                 if (para.hasInitialExpression()) {
133                     this.hasDefaultValue = true;
134                 }
135                 para.setInStaticContext(isStatic());
136                 scope.putDeclaredVariable(para);
137             }
138         }
139         setVariableScope(scope);
140     }
141 
142     public ClassNode getReturnType() {
143         return returnType;
144     }
145 
146     public VariableScope getVariableScope() {
147         return variableScope;
148     }
149 
150     public void setVariableScope(VariableScope variableScope) {
151         this.variableScope = variableScope;
152         variableScope.setInStaticContext(isStatic());
153     }
154 
155     public boolean isDynamicReturnType() {
156         return dynamicReturnType;
157     }
158 
159     public boolean isAbstract() {
160         return (modifiers & ACC_ABSTRACT) != 0;
161     }
162 
163     public boolean isStatic() {
164         return (modifiers & ACC_STATIC) != 0;
165     }
166 
167     public boolean isPublic() {
168         return (modifiers & ACC_PUBLIC) != 0;
169     }
170 
171     public boolean isPrivate() {
172         return (modifiers & ACC_PRIVATE) != 0;
173     }
174 
175     public boolean isFinal() {
176         return (modifiers & ACC_FINAL) != 0;
177     }
178 
179     public boolean isProtected() {
180         return (modifiers & ACC_PROTECTED) != 0;
181     }
182 
183     public boolean hasDefaultValue() {
184         return this.hasDefaultValue;
185     }
186 
187     /**
188      * @return true if this method is the run method from a script
189      */
190     public boolean isScriptBody() {
191         return getDeclaringClass() != null &&
192                 getDeclaringClass().isScript() &&
193                 getName().equals("run") &&
194                 getColumnNumber() == -1;
195     }
196 
197     public String toString() {
198         return "MethodNode@" + hashCode() + "[" + getTypeDescriptor() + "]";
199     }
200 
201     public void setReturnType(ClassNode returnType) {
202         invalidateCachedData();
203         dynamicReturnType |= ClassHelper.DYNAMIC_TYPE == returnType;
204         this.returnType = returnType;
205         if (returnType == null) this.returnType = ClassHelper.OBJECT_TYPE;
206     }
207 
208     public ClassNode[] getExceptions() {
209         return exceptions;
210     }
211 
212     public Statement getFirstStatement() {
213         if (code == null) return null;
214         Statement first = code;
215         while (first instanceof BlockStatement) {
216             List<Statement> list = ((BlockStatement) first).getStatements();
217             if (list.isEmpty()) {
218                 first = null;
219             } else {
220                 first = list.get(0);
221             }
222         }
223         return first;
224     }
225 
226     public GenericsType[] getGenericsTypes() {
227         return genericsTypes;
228     }
229 
230     public void setGenericsTypes(GenericsType[] genericsTypes) {
231         invalidateCachedData();
232         this.genericsTypes = genericsTypes;
233     }
234 
235     public void setAnnotationDefault(boolean b) {
236         this.hasDefault = b;
237     }
238 
239     public boolean hasAnnotationDefault() {
240         return hasDefault;
241     }
242 
243     public boolean isStaticConstructor() {
244         return staticConstructor;
245     }
246 
247     /**
248      * Indicates that this method has been "promoted" to public by
249      * Groovy when in fact there was no public modifier explicitly
250      * in the source code. I.e. it remembers that it has applied
251      * Groovy's "public methods by default" rule. This property is
252      * typically only of interest to AST transform writers.
253      *
254      * @return true if this class is public but had no explicit public modifier
255      */
256     public boolean isSyntheticPublic() {
257         return syntheticPublic;
258     }
259 
260     public void setSyntheticPublic(boolean syntheticPublic) {
261         this.syntheticPublic = syntheticPublic;
262     }
263 
264     /**
265      * Provides a nicely formatted string of the method definition. For simplicity, generic types on some of the elements
266      * are not displayed. 
267      * @return
268      *      string form of node with some generic elements suppressed
269      */
270     @Override
271     public String getText() {
272         String retType = AstToTextHelper.getClassText(returnType);
273         String exceptionTypes = AstToTextHelper.getThrowsClauseText(exceptions);
274         String parms = AstToTextHelper.getParametersText(parameters);
275         return AstToTextHelper.getModifiersText(modifiers) + " " + retType + " " + name + "(" + parms + ") " + exceptionTypes + " { ... }";
276     }
277 }