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.
58     *
59     * @param key non-null; the key
60     * @param value non-null; the value
61     */
62    public void add(DERObjectIdentifier key, Object value) {
63        if (key == null) {
64            throw new NullPointerException("key == null");
65        }
66
67        if (value == null) {
68            throw new NullPointerException("value == null");
69        }
70
71        int sz = size;
72
73        switch (sz) {
74            case 0: {
75                key0 = key;
76                value0 = value;
77                break;
78            }
79            case 1: {
80                key1 = key;
81                value1 = value;
82                break;
83            }
84            case 2: {
85                key2 = key;
86                value2 = value;
87                break;
88            }
89            case 3: {
90                key3 = key;
91                value3 = value;
92                break;
93            }
94            case 4: {
95                // Do initial allocation of rest.
96                rest = new Object[10];
97                rest[0] = key;
98                rest[1] = value;
99                break;
100            }
101            default: {
102                int index = (sz - 4) * 2;
103                int index1 = index + 1;
104                if (index1 >= rest.length) {
105                    // Grow rest.
106                    Object[] newRest = new Object[index1 * 2 + 10];
107                    System.arraycopy(rest, 0, newRest, 0, rest.length);
108                    rest = newRest;
109                }
110                rest[index] = key;
111                rest[index1] = value;
112                break;
113            }
114        }
115
116        size = sz + 1;
117    }
118
119    /**
120     * Gets the number of elements in this instance.
121     */
122    public int size() {
123        return size;
124    }
125
126    /**
127     * Look up the given key, returning the associated value if found.
128     *
129     * @param key non-null; the key to look up
130     * @return null-ok; the associated value
131     */
132    public Object get(DERObjectIdentifier key) {
133        int keyHash = key.hashCode();
134        int sz = size;
135
136        for (int i = 0; i < size; i++) {
137            DERObjectIdentifier probe = getKey(i);
138            if ((probe.hashCode() == keyHash) &&
139                    probe.equals(key)) {
140                return getValue(i);
141            }
142        }
143
144        return null;
145    }
146
147    /**
148     * Gets the nth key.
149     *
150     * @param n index
151     * @return non-null; the nth key
152     */
153    public DERObjectIdentifier getKey(int n) {
154        if ((n < 0) || (n >= size)) {
155            throw new IndexOutOfBoundsException(Integer.toString(n));
156        }
157
158        switch (n) {
159            case 0: return key0;
160            case 1: return key1;
161            case 2: return key2;
162            case 3: return key3;
163            default: return (DERObjectIdentifier) rest[(n - 4) * 2];
164        }
165    }
166
167    /**
168     * Gets the nth value.
169     *
170     * @param n index
171     * @return non-null; the nth value
172     */
173    public Object getValue(int n) {
174        if ((n < 0) || (n >= size)) {
175            throw new IndexOutOfBoundsException(Integer.toString(n));
176        }
177
178        switch (n) {
179            case 0: return value0;
180            case 1: return value1;
181            case 2: return value2;
182            case 3: return value3;
183            default: return rest[((n - 4) * 2) + 1];
184        }
185    }
186
187    /**
188     * Gets an enumeration of the keys, in order.
189     *
190     * @return non-null; an enumeration of the keys
191     */
192    public Enumeration getKeys() {
193        return new KeyEnumeration();
194    }
195
196    /**
197     * Associated enumeration class.
198     */
199    private class KeyEnumeration implements Enumeration {
200        /** original size; used for modification detection */
201        private final int origSize = size;
202
203        /** &gt;= 0; current cursor */
204        private int at = 0;
205
206        /** {@inheritDoc} */
207        public boolean hasMoreElements() {
208            if (size != origSize) {
209                throw new ConcurrentModificationException();
210            }
211
212            return at < origSize;
213        }
214
215        /** {@inheritDoc} */
216        public Object nextElement() {
217            if (size != origSize) {
218                throw new ConcurrentModificationException();
219            }
220
221            return getKey(at++);
222        }
223    }
224}
225