001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.lowlevel.*;
007    import serp.bytecode.visitor.*;
008    import serp.util.*;
009    
010    /**
011     * Attribute declaring the checked exceptions a method can throw.
012     *
013     * @author Abe White
014     */
015    public class Exceptions extends Attribute {
016        private List _indexes = new LinkedList();
017    
018        Exceptions(int nameIndex, Attributes owner) {
019            super(nameIndex, owner);
020        }
021    
022        int getLength() {
023            return 2 + (2 * _indexes.size());
024        }
025    
026        /**
027         * Return the owning method.
028         */
029        public BCMethod getMethod() {
030            return (BCMethod) getOwner();
031        }
032    
033        /**
034         * Return the indexes in the class {@link ConstantPool} of the
035         * {@link ClassEntry}s for the exception types thrown by this method, or
036         * an empty array if none.
037         */
038        public int[] getExceptionIndexes() {
039            int[] indexes = new int[_indexes.size()];
040            Iterator itr = _indexes.iterator();
041            for (int i = 0; i < indexes.length; i++)
042                indexes[i] = ((Integer) itr.next()).intValue();
043            return indexes;
044        }
045    
046        /**
047         * Set the indexes in the class {@link ConstantPool} of the
048         * {@link ClassEntry}s for the exception types thrown by this method. Use
049         * null or an empty array for none.
050         */
051        public void setExceptionIndexes(int[] exceptionIndexes) {
052            _indexes.clear();
053            if (exceptionIndexes != null)
054                for (int i = 0; i < exceptionIndexes.length; i++)
055                    _indexes.add(Numbers.valueOf(exceptionIndexes[i]));
056        }
057    
058        /**
059         * Return the names of the exception types for this method, or an empty
060         * array if none. The names will be in a form suitable for a
061         * {@link Class#forName} call.
062         */
063        public String[] getExceptionNames() {
064            String[] names = new String[_indexes.size()];
065            Iterator itr = _indexes.iterator();
066            int index;
067            ClassEntry entry;
068            for (int i = 0; i < names.length; i++) {
069                index = ((Number) itr.next()).intValue();
070                entry = (ClassEntry) getPool().getEntry(index);
071                names[i] = getProject().getNameCache().getExternalForm(entry.
072                    getNameEntry().getValue(), false);
073            }
074            return names;
075        }
076    
077        /**
078         * Return the {@link Class} objects for the exception types for this
079         * method, or an empty array if none.
080         */
081        public Class[] getExceptionTypes() {
082            String[] names = getExceptionNames();
083            Class[] types = new Class[names.length];
084            for (int i = 0; i < names.length; i++)
085                types[i] = Strings.toClass(names[i], getClassLoader());
086            return types;
087        }
088    
089        /**
090         * Return bytecode for the exception types of this
091         * method, or an empty array if none.
092         */
093        public BCClass[] getExceptionBCs() {
094            String[] names = getExceptionNames();
095            BCClass[] types = new BCClass[names.length];
096            for (int i = 0; i < names.length; i++)
097                types[i] = getProject().loadClass(names[i], getClassLoader());
098            return types;
099        }
100    
101        /**
102         * Set the checked exceptions thrown by this method. Use null or an
103         * empty array for none.
104         */
105        public void setExceptions(String[] exceptions) {
106            if (exceptions != null) {
107                for (int i = 0; i < exceptions.length; i++)
108                    if (exceptions[i] == null)
109                        throw new NullPointerException("exceptions[" + i 
110                            + "] = null");
111            }
112    
113            clear();
114            if (exceptions != null)
115                for (int i = 0; i < exceptions.length; i++)
116                    addException(exceptions[i]);
117        }
118    
119        /**
120         * Set the checked exceptions thrown by this method. Use null or an
121         * empty array for none.
122         */
123        public void setExceptions(Class[] exceptions) {
124            String[] names = null;
125            if (exceptions != null) {
126                names = new String[exceptions.length];
127                for (int i = 0; i < exceptions.length; i++)
128                    names[i] = exceptions[i].getName();
129            }
130            setExceptions(names);
131        }
132    
133        /**
134         * Set the checked exceptions thrown by this method. Use null or an
135         * empty array for none.
136         */
137        public void setExceptions(BCClass[] exceptions) {
138            String[] names = null;
139            if (exceptions != null) {
140                names = new String[exceptions.length];
141                for (int i = 0; i < exceptions.length; i++)
142                    names[i] = exceptions[i].getName();
143            }
144            setExceptions(names);
145        }
146    
147        /**
148         * Clear this method of all exception declarations.
149         */
150        public void clear() {
151            _indexes.clear();
152        }
153    
154        /**
155         * Remove an exception type thrown by this method.
156         *
157         * @return true if the method had the exception type, false otherwise
158         */
159        public boolean removeException(String type) {
160            String internalForm = getProject().getNameCache().getInternalForm(type,
161                false);
162            ClassEntry entry;
163            for (Iterator itr = _indexes.iterator(); itr.hasNext();) {
164                entry = (ClassEntry) getPool().getEntry(((Integer) itr.next()).
165                    intValue());
166                if (entry.getNameEntry().getValue().equals(internalForm)) {
167                    itr.remove();
168                    return true;
169                }
170            }
171            return false;
172        }
173    
174        /**
175         * Remove an exception thrown by this method.
176         *
177         * @return true if the method had the exception type, false otherwise
178         */
179        public boolean removeException(Class type) {
180            if (type == null)
181                return false;
182            return removeException(type.getName());
183        }
184    
185        /**
186         * Remove an exception thrown by this method.
187         *
188         * @return true if the method had the exception type, false otherwise
189         */
190        public boolean removeException(BCClass type) {
191            if (type == null)
192                return false;
193            return removeException(type.getName());
194        }
195    
196        /**
197         * Add an exception type to those thrown by this method.
198         */
199        public void addException(String type) {
200            int index = getPool().findClassEntry(getProject().getNameCache().
201                getInternalForm(type, false), true);
202            _indexes.add(Numbers.valueOf(index));
203        }
204    
205        /**
206         * Add an exception to those thrown by this method.
207         */
208        public void addException(Class type) {
209            addException(type.getName());
210        }
211    
212        /**
213         * Add an exception to those thrown by this method.
214         */
215        public void addException(BCClass type) {
216            addException(type.getName());
217        }
218    
219        /**
220         * Return true if the method declares that it throws the given
221         * exception type.
222         */
223        public boolean throwsException(String type) {
224            String[] exceptions = getExceptionNames();
225            for (int i = 0; i < exceptions.length; i++)
226                if (exceptions[i].equals(type))
227                    return true;
228            return false;
229        }
230    
231        /**
232         * Return true if the method declares that it throws the given
233         * exception type.
234         */
235        public boolean throwsException(Class type) {
236            if (type == null)
237                return false;
238            return throwsException(type.getName());
239        }
240    
241        /**
242         * Return true if the method declares that it throws the given
243         * exception type.
244         */
245        public boolean throwsException(BCClass type) {
246            if (type == null)
247                return false;
248            return throwsException(type.getName());
249        }
250    
251        public void acceptVisit(BCVisitor visit) {
252            visit.enterExceptions(this);
253            visit.exitExceptions(this);
254        }
255    
256        void read(Attribute other) {
257            setExceptions(((Exceptions) other).getExceptionNames());
258        }
259    
260        void read(DataInput in, int length) throws IOException {
261            _indexes.clear();
262            int exceptionCount = in.readUnsignedShort();
263            for (int i = 0; i < exceptionCount; i++)
264                _indexes.add(Numbers.valueOf((int) in.readUnsignedShort()));
265        }
266    
267        void write(DataOutput out, int length) throws IOException {
268            out.writeShort(_indexes.size());
269            for (Iterator itr = _indexes.iterator(); itr.hasNext();)
270                out.writeShort(((Number) itr.next()).shortValue());
271        }
272    }