001    package serp.bytecode;
002    
003    import serp.bytecode.visitor.*;
004    import serp.util.*;
005    
006    /**
007     * A method of a class.
008     *
009     * @author Abe White
010     */
011    public 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    }