001package serp.bytecode;
002
003import serp.bytecode.visitor.*;
004import serp.util.*;
005
006/**
007 * A method of a class.
008 *
009 * @author Abe White
010 */
011public class BCMethod extends BCMember implements VisitAcceptor {
012    BCMethod(BCClass owner) {
013        super(owner);
014    }
015
016    /////////////////////
017    // Access operations
018    /////////////////////
019
020    /**
021     * Manipulate the method access flags.
022     */
023    public boolean isSynchronized() {
024        return (getAccessFlags() & Constants.ACCESS_SYNCHRONIZED) > 0;
025    }
026
027    /**
028     * Manipulate the method access flags.
029     */
030    public void setSynchronized(boolean on) {
031        if (on)
032            setAccessFlags(getAccessFlags() | Constants.ACCESS_SYNCHRONIZED);
033        else
034            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_SYNCHRONIZED);
035    }
036
037    /**
038     * Manipulate the method access flags.
039     */
040    public boolean isNative() {
041        return (getAccessFlags() & Constants.ACCESS_NATIVE) > 0;
042    }
043
044    /**
045     * Manipulate the method access flags.
046     */
047    public void setNative(boolean on) {
048        if (on)
049            setAccessFlags(getAccessFlags() | Constants.ACCESS_NATIVE);
050        else
051            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_NATIVE);
052    }
053
054    /**
055     * Manipulate the method access flags.
056     */
057    public boolean isAbstract() {
058        return (getAccessFlags() & Constants.ACCESS_ABSTRACT) > 0;
059    }
060
061    /**
062     * Manipulate the method access flags.
063     */
064    public void setAbstract(boolean on) {
065        if (on)
066            setAccessFlags(getAccessFlags() | Constants.ACCESS_ABSTRACT);
067        else
068            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_ABSTRACT);
069    }
070
071    /**
072     * Manipulate the method access flags.
073     */
074    public boolean isStrict() {
075        return (getAccessFlags() & Constants.ACCESS_STRICT) > 0;
076    }
077
078    /**
079     * Manipulate the method access flags.
080     */
081    public void setStrict(boolean on) {
082        if (on)
083            setAccessFlags(getAccessFlags() | Constants.ACCESS_STRICT);
084        else
085            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_STRICT);
086    }
087
088    /**
089     * Manipulate the method access flags.
090     */
091    public boolean isVarArgs() {
092        return (getAccessFlags() & Constants.ACCESS_VARARGS) > 0;
093    }
094
095    /**
096     * Manipulate the method access flags.
097     */
098    public void setVarArgs(boolean on) {
099        if (on)
100            setAccessFlags(getAccessFlags() | Constants.ACCESS_VARARGS);
101        else
102            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_VARARGS);
103    }
104
105    /**
106     * Manipulate the method access flags.
107     */
108    public boolean isBridge() {
109        return (getAccessFlags() & Constants.ACCESS_BRIDGE) > 0;
110    }
111
112    /**
113     * Manipulate the method access flags.
114     */
115    public void setBridge(boolean on) {
116        if (on)
117            setAccessFlags(getAccessFlags() | Constants.ACCESS_BRIDGE);
118        else
119            setAccessFlags(getAccessFlags() & ~Constants.ACCESS_BRIDGE);
120    }
121
122    /////////////////////
123    // Return operations
124    /////////////////////
125
126    /**
127     * Return the name of the type returned by this method. The name
128     * will be given in a form suitable for a {@link Class#forName} call.
129     *
130     * @see BCMember#getDescriptor
131     */
132    public String getReturnName() {
133        return getProject().getNameCache().getExternalForm(getProject().
134            getNameCache().getDescriptorReturnName(getDescriptor()), false);
135    }
136
137    /**
138     * Return the {@link Class} object for the return type of this method.
139     *
140     * @see BCMember#getDescriptor
141     */
142    public Class getReturnType() {
143        return Strings.toClass(getReturnName(), getClassLoader());
144    }
145
146    /**
147     * Return the bytecode for the return type of this method.
148     *
149     * @see BCMember#getDescriptor
150     */
151    public BCClass getReturnBC() {
152        return getProject().loadClass(getReturnName(), getClassLoader());
153    }
154
155    /**
156     * Set the return type of this method.
157     */
158    public void setReturn(String name) {
159        setDescriptor(getProject().getNameCache().getDescriptor(name, 
160            getParamNames()));
161    }
162
163    /**
164     * Set the return type of this method.
165     */
166    public void setReturn(Class type) {
167        setReturn(type.getName());
168    }
169
170    /**
171     * Set the return type of this method.
172     */
173    public void setReturn(BCClass type) {
174        setReturn(type.getName());
175    }
176
177    ////////////////////////
178    // Parameter operations
179    ////////////////////////
180
181    /**
182     * Return the names of all the parameter types for this method. The names
183     * will be returned in a form suitable for a {@link Class#forName} call.
184     *
185     * @see BCMember#getDescriptor
186     */
187    public String[] getParamNames() {
188        // get the parameter types from the descriptor
189        String[] params = getProject().getNameCache().getDescriptorParamNames
190            (getDescriptor());
191
192        // convert them to external form
193        for (int i = 0; i < params.length; i++)
194            params[i] = getProject().getNameCache().getExternalForm(params[i], 
195                false);
196        return params;
197    }
198
199    /**
200     * Return the {@link Class} objects for all the parameter types for this
201     * method.
202     *
203     * @see BCMember#getDescriptor
204     */
205    public Class[] getParamTypes() {
206        String[] paramNames = getParamNames();
207        Class[] params = new Class[paramNames.length];
208        for (int i = 0; i < paramNames.length; i++)
209            params[i] = Strings.toClass(paramNames[i], getClassLoader());
210        return params;
211    }
212
213    /**
214     * Return the bytecode for all the parameter types for this method.
215     *
216     * @see BCMember#getDescriptor
217     */
218    public BCClass[] getParamBCs() {
219        String[] paramNames = getParamNames();
220        BCClass[] params = new BCClass[paramNames.length];
221        for (int i = 0; i < paramNames.length; i++)
222            params[i] = getProject().loadClass(paramNames[i], getClassLoader());
223        return params;
224    }
225
226    /**
227     * Set the parameter types of this method.
228     *
229     * @see BCMember#setDescriptor
230     */
231    public void setParams(String[] names) {
232        if (names == null)
233            names = new String[0];
234        setDescriptor(getProject().getNameCache().getDescriptor(getReturnName(),
235            names));
236    }
237
238    /**
239     * Set the parameter type of this method.
240     *
241     * @see BCMember#setDescriptor
242     */
243    public void setParams(Class[] types) {
244        if (types == null)
245            setParams((String[]) null);
246        else {
247            String[] names = new String[types.length];
248            for (int i = 0; i < types.length; i++)
249                names[i] = types[i].getName();
250            setParams(names);
251        }
252    }
253
254    /**
255     * Set the parameter type of this method.
256     *
257     * @see BCMember#setDescriptor
258     */
259    public void setParams(BCClass[] types) {
260        if (types == null)
261            setParams((String[]) null);
262        else {
263            String[] names = new String[types.length];
264            for (int i = 0; i < types.length; i++)
265                names[i] = types[i].getName();
266            setParams(names);
267        }
268    }
269
270    /**
271     * Add a parameter type to this method.
272     */
273    public void addParam(String type) {
274        String[] origParams = getParamNames();
275        String[] params = new String[origParams.length + 1];
276        for (int i = 0; i < origParams.length; i++)
277            params[i] = origParams[i];
278        params[origParams.length] = type;
279        setParams(params);
280    }
281
282    /**
283     * Add a parameter type to this method.
284     */
285    public void addParam(Class type) {
286        addParam(type.getName());
287    }
288
289    /**
290     * Add a parameter type to this method.
291     */
292    public void addParam(BCClass type) {
293        addParam(type.getName());
294    }
295
296    /**
297     * Add a parameter type to this method.
298     *
299     * @see java.util.List#add(int,Object)
300     */
301    public void addParam(int pos, String type) {
302        String[] origParams = getParamNames();
303        if ((pos < 0) || (pos >= origParams.length))
304            throw new IndexOutOfBoundsException("pos = " + pos);
305
306        String[] params = new String[origParams.length + 1];
307        for (int i = 0, index = 0; i < params.length; i++) {
308            if (i == pos)
309                params[i] = type;
310            else
311                params[i] = origParams[index++];
312        }
313        setParams(params);
314    }
315
316    /**
317     * Add a parameter type to this method.
318     *
319     * @see java.util.List#add(int,Object)
320     */
321    public void addParam(int pos, Class type) {
322        addParam(pos, type.getName());
323    }
324
325    /**
326     * Add a parameter type to this method.
327     *
328     * @see java.util.List#add(int,Object)
329     */
330    public void addParam(int pos, BCClass type) {
331        addParam(pos, type.getName());
332    }
333
334    /**
335     * Change a parameter type of this method.
336     *
337     * @see java.util.List#set(int,Object)
338     */
339    public void setParam(int pos, String type) {
340        String[] origParams = getParamNames();
341        if ((pos < 0) || (pos >= origParams.length))
342            throw new IndexOutOfBoundsException("pos = " + pos);
343
344        String[] params = new String[origParams.length];
345        for (int i = 0; i < params.length; i++) {
346            if (i == pos)
347                params[i] = type;
348            else
349                params[i] = origParams[i];
350        }
351        setParams(params);
352    }
353
354    /**
355     * Change a parameter type of this method.
356     *
357     * @see java.util.List#set(int,Object)
358     */
359    public void setParam(int pos, Class type) {
360        setParam(pos, type.getName());
361    }
362
363    /**
364     * Change a parameter type of this method.
365     *
366     * @see java.util.List#set(int,Object)
367     */
368    public void setParam(int pos, BCClass type) {
369        setParam(pos, type.getName());
370    }
371
372    /**
373     * Clear all parameters from this method.
374     */
375    public void clearParams() {
376        setParams((String[]) null);
377    }
378
379    /**
380     * Remove a parameter from this method.
381     */
382    public void removeParam(int pos) {
383        String[] origParams = getParamNames();
384        if ((pos < 0) || (pos >= origParams.length))
385            throw new IndexOutOfBoundsException("pos = " + pos);
386
387        String[] params = new String[origParams.length - 1];
388
389        for (int i = 0, index = 0; i < origParams.length; i++)
390            if (i != pos)
391                params[index++] = origParams[i];
392        setParams(params);
393    }
394
395    ///////////////////////
396    // Convenience methods
397    ///////////////////////
398
399    /**
400     * Return the checked exceptions information for the method.
401     * Acts internally through the {@link Attributes} interface.
402     *
403     * @param add if true, a new exceptions attribute will be added
404     * if not already present
405     * @return the exceptions information, or null if none and the
406     * <code>add</code> param is set to false
407     */
408    public Exceptions getExceptions(boolean add) {
409        Exceptions exceptions = (Exceptions) getAttribute
410            (Constants.ATTR_EXCEPTIONS);
411        if (!add || (exceptions != null))
412            return exceptions;
413
414        if (exceptions == null)
415            exceptions = (Exceptions) addAttribute(Constants.ATTR_EXCEPTIONS);
416        return exceptions;
417    }
418
419    /**
420     * Remove the exceptions attribute for the method.
421     * Acts internally through the {@link Attributes} interface.
422     *
423     * @return true if there was a value to remove
424     */
425    public boolean removeExceptions() {
426        return removeAttribute(Constants.ATTR_EXCEPTIONS);
427    }
428
429    /**
430     * Return the code for the method. If the code already exists, its
431     * iterator will be reset to the first instruction.
432     * Acts internally through the {@link Attributes} interface.
433     *
434     * @param add if true, a new code attribute will be added
435     * if not already present
436     * @return the code for the metohd, or null if none and the
437     * <code>add</code> param is set to false
438     */
439    public Code getCode(boolean add) {
440        Code code = (Code) getAttribute(Constants.ATTR_CODE);
441        if (code != null) {
442            code.beforeFirst();
443            return code;
444        }
445        if (!add)
446            return null;
447        return (Code) addAttribute(Constants.ATTR_CODE);
448    }
449
450    /**
451     * Remove the code attribute from the method.
452     * Acts internally through the {@link Attributes} interface.
453     *
454     * @return true if there was a value to remove
455     */
456    public boolean removeCode() {
457        return removeAttribute(Constants.ATTR_CODE);
458    }
459
460    ////////////////////////////////
461    // VisitAcceptor implementation
462    ////////////////////////////////
463
464    public void acceptVisit(BCVisitor visit) {
465        visit.enterBCMethod(this);
466        visitAttributes(visit);
467        visit.exitBCMethod(this);
468    }
469
470    void initialize(String name, String descriptor) {
471        super.initialize(name, descriptor);
472        makePublic();
473    }
474}