io.undertow.util.HeaderValues Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.undertow.util;
import static org.wildfly.common.Assert.checkNotNullParam;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;
/**
* An array-backed list/deque for header string values.
*
* @author David M. Lloyd
*/
public final class HeaderValues extends AbstractCollection implements Deque, List, RandomAccess {
private static final String[] NO_STRINGS = new String[0];
final HttpString key;
byte size;
Object value;
HeaderValues(final HttpString key) {
this.key = key;
}
public HttpString getHeaderName() {
return key;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
final byte size = this.size;
if (size == 0) return;
clearInternal();
}
private void clearInternal() {
final Object value = this.value;
if (value instanceof String[]) {
final String[] strings = (String[]) value;
final int len = strings.length;
Arrays.fill(strings, 0, len, null);
} else {
this.value = null;
}
this.size = 0;
}
private int index(int idx) {
assert idx >= 0;
assert idx < size;
final int len = ((String[]) value).length;
if (idx > len) {
idx -= len;
}
return idx;
}
public ListIterator listIterator() {
return iterator(0, true);
}
public ListIterator listIterator(final int index) {
return iterator(index, true);
}
public Iterator iterator() {
return iterator(0, true);
}
public Iterator descendingIterator() {
return iterator(0, false);
}
private ListIterator iterator(final int start, final boolean forwards) {
return new ListIterator() {
int idx = start;
int returned = -1;
public boolean hasNext() {
return idx < size;
}
public boolean hasPrevious() {
return idx > 0;
}
public String next() {
try {
final String next;
if (forwards) {
int idx = this.idx;
next = get(idx);
returned = idx;
this.idx = idx + 1;
return next;
} else {
int idx = this.idx - 1;
next = get(idx);
this.idx = returned = idx;
}
return next;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
public int nextIndex() {
return idx;
}
public String previous() {
try {
final String prev;
if (forwards) {
int idx = this.idx - 1;
prev = get(idx);
this.idx = returned = idx;
} else {
int idx = this.idx;
prev = get(idx);
returned = idx;
this.idx = idx + 1;
return prev;
}
return prev;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
public int previousIndex() {
return idx - 1;
}
public void remove() {
if (returned == -1) {
throw new IllegalStateException();
}
HeaderValues.this.remove(returned);
returned = -1;
}
public void set(final String headerValue) {
if (returned == -1) {
throw new IllegalStateException();
}
HeaderValues.this.set(returned, headerValue);
}
public void add(final String headerValue) {
if (returned == -1) {
throw new IllegalStateException();
}
final int idx = this.idx;
HeaderValues.this.add(idx, headerValue);
this.idx = idx + 1;
returned = -1;
}
};
}
public boolean offerFirst(final String headerValue) {
int size = this.size;
if (headerValue == null || size == Byte.MAX_VALUE) return false;
final Object value = this.value;
if (value instanceof String[]) {
final String[] strings = (String[]) value;
final int len = strings.length;
if (size == len) {
final String[] newStrings = new String[len + 2];
System.arraycopy(strings, 0, newStrings, 1, len);
newStrings[0] = headerValue;
this.value = newStrings;
} else {
System.arraycopy(strings, 0, strings, 1, strings.length - 1);
strings[0] = headerValue;
}
this.size = (byte) (size + 1);
} else {
if (size == 0) {
this.value = headerValue;
this.size = (byte) 1;
} else {
this.value = new String[] { headerValue, (String) value, null, null };
this.size = (byte) 2;
}
}
return true;
}
public boolean offerLast(final String headerValue) {
int size = this.size;
if (headerValue == null || size == Byte.MAX_VALUE) return false;
final Object value = this.value;
if (value instanceof String[]) {
offerLastMultiValue(headerValue, size, (String[]) value);
} else {
if (size == 0) {
this.value = headerValue;
this.size = (byte) 1;
} else {
this.value = new String[] { (String) value, headerValue, null, null };
this.size = (byte) 2;
}
}
return true;
}
private void offerLastMultiValue(String headerValue, int size, String[] value) {
final String[] strings = value;
final int len = strings.length;
if (size == len) {
final String[] newStrings = new String[len + 2];
System.arraycopy(strings, 0, newStrings, 0, len);
newStrings[len] = headerValue;
this.value = newStrings;
} else {
strings[size] = headerValue;
}
this.size = (byte) (size + 1);
}
private boolean offer(int idx, final String headerValue) {
int size = this.size;
if (idx < 0 || idx > size || size == Byte.MAX_VALUE || headerValue == null) return false;
if (idx == 0) return offerFirst(headerValue);
if (idx == size) return offerLast(headerValue);
assert size >= 2; // must be >= 2 to pass the last two checks
final Object value = this.value;
assert value instanceof String[];
final String[] strings = (String[]) value;
final int len = strings.length;
// This stuff is all algebraically derived.
if (size == len) {
// Grow the list, copy each segment into new spots so that head = 0
final int newLen = len + 2;
final String[] newStrings = new String[newLen];
System.arraycopy(value, 0, newStrings, 0, idx);
System.arraycopy(value, idx, newStrings, idx + 1, len - idx);
// finally fill in the new value
newStrings[idx] = headerValue;
this.value = newStrings;
} else{
System.arraycopy(value, idx, value, idx + 1, len - idx);
// finally fill in the new value
strings[idx] = headerValue;
}
this.size = (byte) (size + 1);
return true;
}
public String pollFirst() {
final byte size = this.size;
if (size == 0) return null;
final Object value = this.value;
if (value instanceof String) {
this.size = 0;
this.value = null;
return (String) value;
} else {
final String[] strings = (String[]) value;
String ret = strings[0];
System.arraycopy(strings, 1, strings, 0, strings.length - 1);
this.size = (byte) (size - 1);
return ret;
}
}
public String pollLast() {
final byte size = this.size;
if (size == 0) return null;
final Object value = this.value;
if (value instanceof String) {
this.size = 0;
this.value = null;
return (String) value;
} else {
final String[] strings = (String[]) value;
int idx = (this.size = (byte) (size - 1));
final int len = strings.length;
if (idx > len) idx -= len;
try {
return strings[idx];
} finally {
strings[idx] = null;
}
}
}
public String remove(int idx) {
final int size = this.size;
if (idx < 0 || idx >= size) throw new IndexOutOfBoundsException();
if (idx == 0) return removeFirst();
if (idx == size - 1) return removeLast();
assert size > 2; // must be > 2 to pass the last two checks
// value must be an array since size > 2
final String[] value = (String[]) this.value;
final int len = value.length;
String ret = value[idx];
System.arraycopy(value, idx + 1, value, idx, len - idx - 1);
value[len - 1] = null;
this.size = (byte) (size - 1);
return ret;
}
public String get(int idx) {
if (idx > size) {
throw new IndexOutOfBoundsException();
}
Object value = this.value;
assert value != null;
if (value instanceof String) {
assert size == 1;
return (String) value;
}
final String[] a = (String[]) value;
return a[index(idx)];
}
public int indexOf(final Object o) {
if (o == null || size == 0) return -1;
if (value instanceof String[]) {
final String[] list = (String[]) value;
final int len = list.length;
for (int i = 0; i < size; i ++) {
if ((i > len ? list[i - len] : list[i]).equals(o)) {
return i;
}
}
} else if (o.equals(value)) {
return 0;
}
return -1;
}
public int lastIndexOf(final Object o) {
if (o == null || size == 0) return -1;
if (value instanceof String[]) {
final String[] list = (String[]) value;
final int len = list.length;
int idx;
for (int i = size - 1; i >= 0; i --) {
idx = i;
if ((idx > len ? list[idx - len] : list[idx]).equals(o)) {
return i;
}
}
} else if (o.equals(value)) {
return 0;
}
return -1;
}
public String set(final int index, final String element) {
checkNotNullParam("element", element);
final byte size = this.size;
if (index < 0 || index >= size) throw new IndexOutOfBoundsException();
final Object value = this.value;
if (size == 1 && value instanceof String) try {
return (String) value;
} finally {
this.value = element;
} else {
final String[] list = (String[]) value;
final int i = index(index);
try {
return list[i];
} finally {
list[i] = element;
}
}
}
public boolean addAll(int index, final Collection extends String> c) {
final int size = this.size;
if (index < 0 || index > size) throw new IndexOutOfBoundsException();
final Iterator extends String> iterator = c.iterator();
boolean result = false;
while (iterator.hasNext()) {
result |= offer(index, iterator.next());
}
return result;
}
public List subList(final int fromIndex, final int toIndex) {
// todo - this is about 75% correct, by spec...
if (fromIndex < 0 || toIndex > size || fromIndex > toIndex) throw new IndexOutOfBoundsException();
final int len = toIndex - fromIndex;
final String[] strings = new String[len];
for (int i = 0; i < len; i ++) {
strings[i] = get(i + fromIndex);
}
return Arrays.asList(strings);
}
public String[] toArray() {
int size = this.size;
if (size == 0) {
return NO_STRINGS;
}
final Object v = this.value;
if (v instanceof String) return new String[] { (String) v };
final String[] list = (String[]) v;
final int len = list.length;
final int copyEnd = size;
if (copyEnd < len) {
return Arrays.copyOfRange(list, 0, copyEnd);
} else {
String[] ret = Arrays.copyOfRange(list, 0, copyEnd);
System.arraycopy(list, 0, ret, len, copyEnd - len);
return ret;
}
}
public T[] toArray(final T[] a) {
int size = this.size;
if (size == 0) return a;
final int inLen = a.length;
final Object[] target = inLen < size ? Arrays.copyOfRange(a, inLen, inLen + size) : a;
final Object v = this.value;
if (v instanceof String) {
target[0] = v;
} else {
System.arraycopy(v, 0, target, 0, size);
}
return (T[]) target;
}
//======================================
//
// Derived methods
//
//======================================
public void addFirst(final String s) {
if (s == null) return;
if (! offerFirst(s)) throw new IllegalStateException();
}
public void addLast(final String s) {
if (s == null) return;
if (! offerLast(s)) throw new IllegalStateException();
}
public void add(final int index, final String s) {
if (s == null) return;
if (! offer(index, s)) throw new IllegalStateException();
}
public boolean contains(final Object o) {
return indexOf(o) != -1;
}
public String peekFirst() {
return size == 0 ? null : get(0);
}
public String peekLast() {
return size == 0 ? null : get(size - 1);
}
public boolean removeFirstOccurrence(final Object o) {
int i = indexOf(o);
return i != -1 && remove(i) != null;
}
public boolean removeLastOccurrence(final Object o) {
int i = lastIndexOf(o);
return i != -1 && remove(i) != null;
}
public boolean add(final String s) {
addLast(s);
return true;
}
public void push(final String s) {
addFirst(s);
}
public String pop() {
return removeFirst();
}
public boolean offer(final String s) {
return offerLast(s);
}
public String poll() {
return pollFirst();
}
public String peek() {
return peekFirst();
}
public String remove() {
return removeFirst();
}
public String removeFirst() {
final String s = pollFirst();
if (s == null) {
throw new NoSuchElementException();
}
return s;
}
public String removeLast() {
final String s = pollLast();
if (s == null) {
throw new NoSuchElementException();
}
return s;
}
public String getFirst() {
final String s = peekFirst();
if (s == null) {
throw new NoSuchElementException();
}
return s;
}
public String getLast() {
final String s = peekLast();
if (s == null) {
throw new NoSuchElementException();
}
return s;
}
public String element() {
return getFirst();
}
public boolean remove(Object obj) {
return removeFirstOccurrence(obj);
}
public boolean addAll(final Collection extends String> c) {
for (String s : c) {
add(s);
}
return !c.isEmpty();
}
}