1package org.bouncycastle.asn1;
2
3import java.util.Enumeration;
4import java.util.ConcurrentModificationException;
5
6// BEGIN android-note
7/*
8 * This is a new class that was synthesized from the observed
9 * requirement for a lookup table that preserves order. Since in
10 * practice the element count is typically very low, we just use a
11 * flat list rather than doing any hashing / bucketing.
12 */
13// END android-note
14
15/**
16 * Ordered lookup table. Instances of this class will keep up to four
17 * key-value pairs directly, resorting to an external collection only
18 * if more elements than that need to be stored.
19 */
20public final class OrderedTable {
21    /** null-ok; key #0 */
22    private DERObjectIdentifier key0;
23
24    /** null-ok; key #1 */
25    private DERObjectIdentifier key1;
26
27    /** null-ok; key #2 */
28    private DERObjectIdentifier key2;
29
30    /** null-ok; key #3 */
31    private DERObjectIdentifier key3;
32
33    /** null-ok; value #0 */
34    private Object value0;
35
36    /** null-ok; value #1 */
37    private Object value1;
38
39    /** null-ok; value #2 */
40    private Object value2;
41
42    /** null-ok; value #3 */
43    private Object value3;
44
45    /**
46     * null-ok; array of additional keys and values, alternating
47     * key then value, etc.
48     */
49    private Object[] rest;
50
51    /** >= 0; number of elements in the list */
52    private int size;
53
54    // Note: Default public constructor.
55
56    /**
57     * Adds an element assuming no duplicate key.
58     *
59     * @see #put
60     *
61     * @param key non-null; the key
62     * @param value non-null; the value
63     */
64    public void add(DERObjectIdentifier key, Object value) {
65        if (key == null) {
66            throw new NullPointerException("key == null");
67        }
68
69        if (value == null) {
70            throw new NullPointerException("value == null");
71        }
72
73        int sz = size;
74
75        switch (sz) {
76            case 0: {
77                key0 = key;
78                value0 = value;
79                break;
80            }
81            case 1: {
82                key1 = key;
83                value1 = value;
84                break;
85            }
86            case 2: {
87                key2 = key;
88                value2 = value;
89                break;
90            }
91            case 3: {
92                key3 = key;
93                value3 = value;
94                break;
95            }
96            case 4: {
97                // Do initial allocation of rest.
98                rest = new Object[10];
99                rest[0] = key;
100                rest[1] = value;
101                break;
102            }
103            default: {
104                int index = (sz - 4) * 2;
105                int index1 = index + 1;
106                if (index1 >= rest.length) {
107                    // Grow rest.
108                    Object[] newRest = new Object[index1 * 2 + 10];
109                    System.arraycopy(rest, 0, newRest, 0, rest.length);
110                    rest = newRest;
111                }
112                rest[index] = key;
113                rest[index1] = value;
114                break;
115            }
116        }
117
118        size = sz + 1;
119    }
120
121    /**
122     * Gets the number of elements in this instance.
123     */
124    public int size() {
125        return size;
126    }
127
128    /**
129     * Look up the given key, returning the associated value if found.
130     *
131     * @param key non-null; the key to look up
132     * @return null-ok; the associated value
133     */
134    public Object get(DERObjectIdentifier key) {
135        int keyHash = key.hashCode();
136        int sz = size;
137
138        for (int i = 0; i < size; i++) {
139            DERObjectIdentifier probe = getKey(i);
140            if ((probe.hashCode() == keyHash) &&
141                    probe.equals(key)) {
142                return getValue(i);
143            }
144        }
145
146        return null;
147    }
148
149    /**
150     * Replace a key if present, otherwise add
151     *
152     * @see #add
153     *
154     * @param key non-null; the key
155     * @param value non-null; the value
156     */
157    public void put(DERObjectIdentifier key, Object value) {
158        if (key == null) {
159            throw new NullPointerException("key == null");
160        }
161
162        if (value == null) {
163            throw new NullPointerException("value == null");
164        }
165
166        int keyHash = key.hashCode();
167        int sz = size;
168
169        for (int i = 0; i < size; i++) {
170            DERObjectIdentifier probe = getKey(i);
171            if ((probe.hashCode() == keyHash) &&
172                    probe.equals(key)) {
173                setValue(i, value);
174                return;
175            }
176        }
177
178        add(key, value);
179    }
180
181    /**
182     * Gets the nth key.
183     *
184     * @param n index
185     * @return non-null; the nth key
186     */
187    public DERObjectIdentifier getKey(int n) {
188        if ((n < 0) || (n >= size)) {
189            throw new IndexOutOfBoundsException(Integer.toString(n));
190        }
191
192        switch (n) {
193            case 0: return key0;
194            case 1: return key1;
195            case 2: return key2;
196            case 3: return key3;
197            default: return (DERObjectIdentifier) rest[(n - 4) * 2];
198        }
199    }
200
201    /**
202     * Gets the nth value.
203     *
204     * @param n index
205     * @return non-null; the nth value
206     */
207    public Object getValue(int n) {
208        if ((n < 0) || (n >= size)) {
209            throw new IndexOutOfBoundsException(Integer.toString(n));
210        }
211
212        switch (n) {
213            case 0: return value0;
214            case 1: return value1;
215            case 2: return value2;
216            case 3: return value3;
217            default: return rest[((n - 4) * 2) + 1];
218        }
219    }
220
221    /**
222     * Sets the nth value.
223     *
224     * @param n index
225     * @param value non-null object
226     */
227    public void setValue(int n, Object value) {
228        if ((n < 0) || (n >= size)) {
229            throw new IndexOutOfBoundsException(Integer.toString(n));
230        }
231        if (value == null) {
232            throw new NullPointerException("value == null");
233        }
234
235        switch (n) {
236            case 0: value0 = value; return;
237            case 1: value1 = value; return;
238            case 2: value2 = value; return;
239            case 3: value3 = value; return;
240            default: rest[((n - 4) * 2) + 1] = value; return;
241        }
242    }
243
244    /**
245     * Gets an enumeration of the keys, in order.
246     *
247     * @return non-null; an enumeration of the keys
248     */
249    public Enumeration getKeys() {
250        return new KeyEnumeration();
251    }
252
253    /**
254     * Associated enumeration class.
255     */
256    private class KeyEnumeration implements Enumeration {
257        /** original size; used for modification detection */
258        private final int origSize = size;
259
260        /** &gt;= 0; current cursor */
261        private int at = 0;
262
263        /** {@inheritDoc} */
264        public boolean hasMoreElements() {
265            if (size != origSize) {
266                throw new ConcurrentModificationException();
267            }
268
269            return at < origSize;
270        }
271
272        /** {@inheritDoc} */
273        public Object nextElement() {
274            if (size != origSize) {
275                throw new ConcurrentModificationException();
276            }
277
278            return getKey(at++);
279        }
280    }
281}
282