1/*
2 * Copyright (C) 2008 The Android Open Source Project
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
17package com.android.dexgen.dex.code;
18
19import com.android.dexgen.rop.cst.CstType;
20import com.android.dexgen.util.FixedSizeList;
21import com.android.dexgen.util.Hex;
22
23/**
24 * Ordered list of (exception type, handler address) entries.
25 */
26public final class CatchHandlerList extends FixedSizeList
27        implements Comparable<CatchHandlerList> {
28    /** {@code non-null;} empty instance */
29    public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
30
31    /**
32     * Constructs an instance. All indices initially contain {@code null}.
33     *
34     * @param size {@code >= 0;} the size of the list
35     */
36    public CatchHandlerList(int size) {
37        super(size);
38    }
39
40    /**
41     * Gets the element at the given index. It is an error to call
42     * this with the index for an element which was never set; if you
43     * do that, this will throw {@code NullPointerException}.
44     *
45     * @param n {@code >= 0, < size();} which index
46     * @return {@code non-null;} element at that index
47     */
48    public Entry get(int n) {
49        return (Entry) get0(n);
50    }
51
52    /** {@inheritDoc} */
53    public String toHuman() {
54        return toHuman("", "");
55    }
56
57    /**
58     * Get the human form of this instance, prefixed on each line
59     * with the string.
60     *
61     * @param prefix {@code non-null;} the prefix for every line
62     * @param header {@code non-null;} the header for the first line (after the
63     * first prefix)
64     * @return {@code non-null;} the human form
65     */
66    public String toHuman(String prefix, String header) {
67        StringBuilder sb = new StringBuilder(100);
68        int size = size();
69
70        sb.append(prefix);
71        sb.append(header);
72        sb.append("catch ");
73
74        for (int i = 0; i < size; i++) {
75            Entry entry = get(i);
76
77            if (i != 0) {
78                sb.append(",\n");
79                sb.append(prefix);
80                sb.append("  ");
81            }
82
83            if ((i == (size - 1)) && catchesAll()) {
84                sb.append("<any>");
85            } else {
86                sb.append(entry.getExceptionType().toHuman());
87            }
88
89            sb.append(" -> ");
90            sb.append(Hex.u2or4(entry.getHandler()));
91        }
92
93        return sb.toString();
94    }
95
96    /**
97     * Returns whether or not this instance ends with a "catch-all"
98     * handler.
99     *
100     * @return {@code true} if this instance ends with a "catch-all"
101     * handler or {@code false} if not
102     */
103    public boolean catchesAll() {
104        int size = size();
105
106        if (size == 0) {
107            return false;
108        }
109
110        Entry last = get(size - 1);
111        return last.getExceptionType().equals(CstType.OBJECT);
112    }
113
114    /**
115     * Sets the entry at the given index.
116     *
117     * @param n {@code >= 0, < size();} which index
118     * @param exceptionType {@code non-null;} type of exception handled
119     * @param handler {@code >= 0;} exception handler address
120     */
121    public void set(int n, CstType exceptionType, int handler) {
122        set0(n, new Entry(exceptionType, handler));
123    }
124
125    /**
126     * Sets the entry at the given index.
127     *
128     * @param n {@code >= 0, < size();} which index
129     * @param entry {@code non-null;} the entry to set at {@code n}
130     */
131    public void set(int n, Entry entry) {
132        set0(n, entry);
133    }
134
135    /** {@inheritDoc} */
136    public int compareTo(CatchHandlerList other) {
137        if (this == other) {
138            // Easy out.
139            return 0;
140        }
141
142        int thisSize = size();
143        int otherSize = other.size();
144        int checkSize = Math.min(thisSize, otherSize);
145
146        for (int i = 0; i < checkSize; i++) {
147            Entry thisEntry = get(i);
148            Entry otherEntry = other.get(i);
149            int compare = thisEntry.compareTo(otherEntry);
150            if (compare != 0) {
151                return compare;
152            }
153        }
154
155        if (thisSize < otherSize) {
156            return -1;
157        } else if (thisSize > otherSize) {
158            return 1;
159        }
160
161        return 0;
162    }
163
164    /**
165     * Entry in the list.
166     */
167    public static class Entry implements Comparable<Entry> {
168        /** {@code non-null;} type of exception handled */
169        private final CstType exceptionType;
170
171        /** {@code >= 0;} exception handler address */
172        private final int handler;
173
174        /**
175         * Constructs an instance.
176         *
177         * @param exceptionType {@code non-null;} type of exception handled
178         * @param handler {@code >= 0;} exception handler address
179         */
180        public Entry(CstType exceptionType, int handler) {
181            if (handler < 0) {
182                throw new IllegalArgumentException("handler < 0");
183            }
184
185            if (exceptionType == null) {
186                throw new NullPointerException("exceptionType == null");
187            }
188
189            this.handler = handler;
190            this.exceptionType = exceptionType;
191        }
192
193        /** {@inheritDoc} */
194        @Override
195        public int hashCode() {
196            return (handler * 31) + exceptionType.hashCode();
197        }
198
199        /** {@inheritDoc} */
200        @Override
201        public boolean equals(Object other) {
202            if (other instanceof Entry) {
203                return (compareTo((Entry) other) == 0);
204            }
205
206            return false;
207        }
208
209        /** {@inheritDoc} */
210        public int compareTo(Entry other) {
211            if (handler < other.handler) {
212                return -1;
213            } else if (handler > other.handler) {
214                return 1;
215            }
216
217            return exceptionType.compareTo(other.exceptionType);
218        }
219
220        /**
221         * Gets the exception type handled.
222         *
223         * @return {@code non-null;} the exception type
224         */
225        public CstType getExceptionType() {
226            return exceptionType;
227        }
228
229        /**
230         * Gets the handler address.
231         *
232         * @return {@code >= 0;} the handler address
233         */
234        public int getHandler() {
235            return handler;
236        }
237    }
238}
239