1
18
19 package io.undertow.util;
20
21 import java.util.AbstractCollection;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Deque;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.NoSuchElementException;
29 import java.util.RandomAccess;
30
31
36 public final class HeaderValues extends AbstractCollection<String> implements Deque<String>, List<String>, RandomAccess {
37
38 private static final String[] NO_STRINGS = new String[0];
39 final HttpString key;
40 byte size;
41 Object value;
42
43 HeaderValues(final HttpString key) {
44 this.key = key;
45 }
46
47 public HttpString getHeaderName() {
48 return key;
49 }
50
51 public int size() {
52 return size;
53 }
54
55 public boolean isEmpty() {
56 return size == 0;
57 }
58
59 public void clear() {
60 final byte size = this.size;
61 if (size == 0) return;
62 clearInternal();
63 }
64
65 private void clearInternal() {
66 final Object value = this.value;
67 if (value instanceof String[]) {
68 final String[] strings = (String[]) value;
69 final int len = strings.length;
70 Arrays.fill(strings, 0, len, null);
71 } else {
72 this.value = null;
73 }
74 this.size = 0;
75 }
76
77 private int index(int idx) {
78 assert idx >= 0;
79 assert idx < size;
80 final int len = ((String[]) value).length;
81 if (idx > len) {
82 idx -= len;
83 }
84 return idx;
85 }
86
87 public ListIterator<String> listIterator() {
88 return iterator(0, true);
89 }
90
91 public ListIterator<String> listIterator(final int index) {
92 return iterator(index, true);
93 }
94
95 public Iterator<String> iterator() {
96 return iterator(0, true);
97 }
98
99 public Iterator<String> descendingIterator() {
100 return iterator(0, false);
101 }
102
103 private ListIterator<String> iterator(final int start, final boolean forwards) {
104 return new ListIterator<String>() {
105 int idx = start;
106 int returned = -1;
107
108 public boolean hasNext() {
109 return idx < size;
110 }
111
112 public boolean hasPrevious() {
113 return idx > 0;
114 }
115
116 public String next() {
117 try {
118 final String next;
119 if (forwards) {
120 int idx = this.idx;
121 next = get(idx);
122 returned = idx;
123 this.idx = idx + 1;
124 return next;
125 } else {
126 int idx = this.idx - 1;
127 next = get(idx);
128 this.idx = returned = idx;
129 }
130 return next;
131 } catch (IndexOutOfBoundsException e) {
132 throw new NoSuchElementException();
133 }
134 }
135
136 public int nextIndex() {
137 return idx;
138 }
139
140 public String previous() {
141 try {
142 final String prev;
143 if (forwards) {
144 int idx = this.idx - 1;
145 prev = get(idx);
146 this.idx = returned = idx;
147 } else {
148 int idx = this.idx;
149 prev = get(idx);
150 returned = idx;
151 this.idx = idx + 1;
152 return prev;
153 }
154 return prev;
155 } catch (IndexOutOfBoundsException e) {
156 throw new NoSuchElementException();
157 }
158 }
159
160 public int previousIndex() {
161 return idx - 1;
162 }
163
164 public void remove() {
165 if (returned == -1) {
166 throw new IllegalStateException();
167 }
168 HeaderValues.this.remove(returned);
169 returned = -1;
170 }
171
172 public void set(final String headerValue) {
173 if (returned == -1) {
174 throw new IllegalStateException();
175 }
176 HeaderValues.this.set(returned, headerValue);
177 }
178
179 public void add(final String headerValue) {
180 if (returned == -1) {
181 throw new IllegalStateException();
182 }
183 final int idx = this.idx;
184 HeaderValues.this.add(idx, headerValue);
185 this.idx = idx + 1;
186 returned = -1;
187 }
188 };
189 }
190
191 public boolean offerFirst(final String headerValue) {
192 int size = this.size;
193 if (headerValue == null || size == Byte.MAX_VALUE) return false;
194 final Object value = this.value;
195 if (value instanceof String[]) {
196 final String[] strings = (String[]) value;
197 final int len = strings.length;
198 if (size == len) {
199 final String[] newStrings = new String[len + 2];
200 System.arraycopy(strings, 0, newStrings, 1, len);
201 newStrings[0] = headerValue;
202 this.value = newStrings;
203 } else {
204 System.arraycopy(strings, 0, strings, 1, strings.length - 1);
205 strings[0] = headerValue;
206 }
207 this.size = (byte) (size + 1);
208 } else {
209 if (size == 0) {
210 this.value = headerValue;
211 this.size = (byte) 1;
212 } else {
213 this.value = new String[] { headerValue, (String) value, null, null };
214 this.size = (byte) 2;
215 }
216 }
217 return true;
218 }
219
220 public boolean offerLast(final String headerValue) {
221 int size = this.size;
222 if (headerValue == null || size == Byte.MAX_VALUE) return false;
223 final Object value = this.value;
224 if (value instanceof String[]) {
225 offerLastMultiValue(headerValue, size, (String[]) value);
226 } else {
227 if (size == 0) {
228 this.value = headerValue;
229 this.size = (byte) 1;
230 } else {
231 this.value = new String[] { (String) value, headerValue, null, null };
232 this.size = (byte) 2;
233 }
234 }
235 return true;
236 }
237
238 private void offerLastMultiValue(String headerValue, int size, String[] value) {
239 final String[] strings = value;
240 final int len = strings.length;
241 if (size == len) {
242 final String[] newStrings = new String[len + 2];
243 System.arraycopy(strings, 0, newStrings, 0, len);
244 newStrings[len] = headerValue;
245 this.value = newStrings;
246 } else {
247 strings[size] = headerValue;
248 }
249 this.size = (byte) (size + 1);
250 }
251
252 private boolean offer(int idx, final String headerValue) {
253 int size = this.size;
254 if (idx < 0 || idx > size || size == Byte.MAX_VALUE || headerValue == null) return false;
255 if (idx == 0) return offerFirst(headerValue);
256 if (idx == size) return offerLast(headerValue);
257 assert size >= 2;
258 final Object value = this.value;
259 assert value instanceof String[];
260 final String[] strings = (String[]) value;
261 final int len = strings.length;
262
263 if (size == len) {
264
265 final int newLen = len + 2;
266 final String[] newStrings = new String[newLen];
267 System.arraycopy(value, 0, newStrings, 0, idx);
268 System.arraycopy(value, idx, newStrings, idx + 1, len - idx);
269
270
271 newStrings[idx] = headerValue;
272 this.value = newStrings;
273 } else{
274 System.arraycopy(value, idx, value, idx + 1, len - idx);
275
276
277 strings[idx] = headerValue;
278 }
279 this.size = (byte) (size + 1);
280 return true;
281 }
282
283 public String pollFirst() {
284 final byte size = this.size;
285 if (size == 0) return null;
286
287 final Object value = this.value;
288 if (value instanceof String) {
289 this.size = 0;
290 this.value = null;
291 return (String) value;
292 } else {
293 final String[] strings = (String[]) value;
294 String ret = strings[0];
295 System.arraycopy(strings, 1, strings, 0, strings.length - 1);
296 this.size = (byte) (size - 1);
297 return ret;
298 }
299 }
300
301 public String pollLast() {
302 final byte size = this.size;
303 if (size == 0) return null;
304
305 final Object value = this.value;
306 if (value instanceof String) {
307 this.size = 0;
308 this.value = null;
309 return (String) value;
310 } else {
311 final String[] strings = (String[]) value;
312 int idx = (this.size = (byte) (size - 1));
313 final int len = strings.length;
314 if (idx > len) idx -= len;
315 try {
316 return strings[idx];
317 } finally {
318 strings[idx] = null;
319 }
320 }
321 }
322
323 public String remove(int idx) {
324 final int size = this.size;
325 if (idx < 0 || idx >= size) throw new IndexOutOfBoundsException();
326 if (idx == 0) return removeFirst();
327 if (idx == size - 1) return removeLast();
328 assert size > 2;
329
330 final String[] value = (String[]) this.value;
331 final int len = value.length;
332 String ret = value[idx];
333 System.arraycopy(value, idx + 1, value, idx, len - idx - 1);
334 value[len - 1] = null;
335 this.size = (byte) (size - 1);
336 return ret;
337 }
338
339 public String get(int idx) {
340 if (idx > size) {
341 throw new IndexOutOfBoundsException();
342 }
343 Object value = this.value;
344 assert value != null;
345 if (value instanceof String) {
346 assert size == 1;
347 return (String) value;
348 }
349 final String[] a = (String[]) value;
350 return a[index(idx)];
351 }
352
353 public int indexOf(final Object o) {
354 if (o == null || size == 0) return -1;
355 if (value instanceof String[]) {
356 final String[] list = (String[]) value;
357 final int len = list.length;
358 for (int i = 0; i < size; i ++) {
359 if ((i > len ? list[i - len] : list[i]).equals(o)) {
360 return i;
361 }
362 }
363 } else if (o.equals(value)) {
364 return 0;
365 }
366 return -1;
367 }
368
369 public int lastIndexOf(final Object o) {
370 if (o == null || size == 0) return -1;
371 if (value instanceof String[]) {
372 final String[] list = (String[]) value;
373 final int len = list.length;
374 int idx;
375 for (int i = size - 1; i >= 0; i --) {
376 idx = i;
377 if ((idx > len ? list[idx - len] : list[idx]).equals(o)) {
378 return i;
379 }
380 }
381 } else if (o.equals(value)) {
382 return 0;
383 }
384 return -1;
385 }
386
387 public String set(final int index, final String element) {
388 if (element == null) throw new IllegalArgumentException();
389
390 final byte size = this.size;
391 if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
392
393 final Object value = this.value;
394 if (size == 1 && value instanceof String) try {
395 return (String) value;
396 } finally {
397 this.value = element;
398 } else {
399 final String[] list = (String[]) value;
400 final int i = index(index);
401 try {
402 return list[i];
403 } finally {
404 list[i] = element;
405 }
406 }
407 }
408
409 public boolean addAll(int index, final Collection<? extends String> c) {
410 final int size = this.size;
411 if (index < 0 || index > size) throw new IndexOutOfBoundsException();
412 final Iterator<? extends String> iterator = c.iterator();
413 boolean result = false;
414 while (iterator.hasNext()) {
415 result |= offer(index, iterator.next());
416 }
417 return result;
418 }
419
420 public List<String> subList(final int fromIndex, final int toIndex) {
421
422 if (fromIndex < 0 || toIndex > size || fromIndex > toIndex) throw new IndexOutOfBoundsException();
423 final int len = toIndex - fromIndex;
424 final String[] strings = new String[len];
425 for (int i = 0; i < len; i ++) {
426 strings[i] = get(i + fromIndex);
427 }
428 return Arrays.asList(strings);
429 }
430
431 public String[] toArray() {
432 int size = this.size;
433 if (size == 0) {
434 return NO_STRINGS;
435 }
436 final Object v = this.value;
437 if (v instanceof String) return new String[] { (String) v };
438 final String[] list = (String[]) v;
439 final int len = list.length;
440 final int copyEnd = size;
441 if (copyEnd < len) {
442 return Arrays.copyOfRange(list, 0, copyEnd);
443 } else {
444 String[] ret = Arrays.copyOfRange(list, 0, copyEnd);
445 System.arraycopy(list, 0, ret, len, copyEnd - len);
446 return ret;
447 }
448 }
449
450 public <T> T[] toArray(final T[] a) {
451 int size = this.size;
452 if (size == 0) return a;
453 final int inLen = a.length;
454 final Object[] target = inLen < size ? Arrays.copyOfRange(a, inLen, inLen + size) : a;
455 final Object v = this.value;
456 if (v instanceof String) {
457 target[0] = v;
458 } else {
459 System.arraycopy(v, 0, target, 0, size);
460 }
461 return (T[]) target;
462 }
463
464
465
466
467
468
469
470 public void addFirst(final String s) {
471 if (s == null) return;
472 if (! offerFirst(s)) throw new IllegalStateException();
473 }
474
475 public void addLast(final String s) {
476 if (s == null) return;
477 if (! offerLast(s)) throw new IllegalStateException();
478 }
479
480 public void add(final int index, final String s) {
481 if (s == null) return;
482 if (! offer(index, s)) throw new IllegalStateException();
483 }
484
485 public boolean contains(final Object o) {
486 return indexOf(o) != -1;
487 }
488
489 public String peekFirst() {
490 return size == 0 ? null : get(0);
491 }
492
493 public String peekLast() {
494 return size == 0 ? null : get(size - 1);
495 }
496
497 public boolean removeFirstOccurrence(final Object o) {
498 int i = indexOf(o);
499 return i != -1 && remove(i) != null;
500 }
501
502 public boolean removeLastOccurrence(final Object o) {
503 int i = lastIndexOf(o);
504 return i != -1 && remove(i) != null;
505 }
506
507 public boolean add(final String s) {
508 addLast(s);
509 return true;
510 }
511
512 public void push(final String s) {
513 addFirst(s);
514 }
515
516 public String pop() {
517 return removeFirst();
518 }
519
520 public boolean offer(final String s) {
521 return offerLast(s);
522 }
523
524 public String poll() {
525 return pollFirst();
526 }
527
528 public String peek() {
529 return peekFirst();
530 }
531
532 public String remove() {
533 return removeFirst();
534 }
535
536 public String removeFirst() {
537 final String s = pollFirst();
538 if (s == null) {
539 throw new NoSuchElementException();
540 }
541 return s;
542 }
543
544 public String removeLast() {
545 final String s = pollLast();
546 if (s == null) {
547 throw new NoSuchElementException();
548 }
549 return s;
550 }
551
552 public String getFirst() {
553 final String s = peekFirst();
554 if (s == null) {
555 throw new NoSuchElementException();
556 }
557 return s;
558 }
559
560 public String getLast() {
561 final String s = peekLast();
562 if (s == null) {
563 throw new NoSuchElementException();
564 }
565 return s;
566 }
567
568 public String element() {
569 return getFirst();
570 }
571
572 public boolean remove(Object obj) {
573 return removeFirstOccurrence(obj);
574 }
575
576 public boolean addAll(final Collection<? extends String> c) {
577 for (String s : c) {
578 add(s);
579 }
580 return !c.isEmpty();
581 }
582 }
583