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 /** >= 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