001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.lowlevel.*;
007import serp.bytecode.visitor.*;
008import serp.util.*;
009
010/**
011 * Attribute declaring the checked exceptions a method can throw.
012 *
013 * @author Abe White
014 */
015public 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}