
io.jsync.http.CaseInsensitiveMultiMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsync.io Show documentation
Show all versions of jsync.io Show documentation
jsync.io is a non-blocking, event-driven networking framework for Java
/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.jsync.http;
import io.jsync.MultiMap;
import java.util.*;
/**
* This multi-map implementation has case insensitive keys, and can be used to hold some HTTP headers
* prior to making an HTTP request.
*
* @author Norman Maurer
*/
public final class CaseInsensitiveMultiMap implements MultiMap {
private static final int BUCKET_SIZE = 17;
private final MapEntry[] entries = new MapEntry[BUCKET_SIZE];
private final MapEntry head = new MapEntry(-1, null, null);
public CaseInsensitiveMultiMap() {
head.before = head.after = head;
}
private static int hash(String name) {
int h = 0;
for (int i = name.length() - 1; i >= 0; i--) {
char c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 32;
}
h = 31 * h + c;
}
if (h > 0) {
return h;
} else if (h == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
} else {
return -h;
}
}
private static boolean eq(String name1, String name2) {
int nameLen = name1.length();
if (nameLen != name2.length()) {
return false;
}
for (int i = nameLen - 1; i >= 0; i--) {
char c1 = name1.charAt(i);
char c2 = name2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
private MultiMap set0(Iterable> map) {
clear();
for (Map.Entry entry : map) {
add(entry.getKey(), entry.getValue());
}
return this;
}
@Override
public MultiMap set(MultiMap headers) {
return set0(headers);
}
@Override
public MultiMap set(Map headers) {
return set0(headers.entrySet());
}
@Override
public int size() {
return names().size();
}
@Override
public MultiMap add(final String name, final String strVal) {
int h = hash(name);
int i = index(h);
add0(h, i, name, strVal);
return this;
}
@Override
public MultiMap add(String name, Iterable values) {
int h = hash(name);
int i = index(h);
for (String vstr : values) {
add0(h, i, name, vstr);
}
return this;
}
@Override
public MultiMap add(MultiMap headers) {
for (Map.Entry entry : headers.entries()) {
add(entry.getKey(), entry.getValue());
}
return this;
}
@Override
public MultiMap add(Map map) {
for (Map.Entry entry : map.entrySet()) {
add(entry.getKey(), entry.getValue());
}
return this;
}
private void add0(int h, int i, final String name, final String value) {
// Update the hash table.
MapEntry e = entries[i];
MapEntry newEntry;
entries[i] = newEntry = new MapEntry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
@Override
public MultiMap remove(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
remove0(h, i, name);
return this;
}
private void remove0(int h, int i, String name) {
MapEntry e = entries[i];
if (e == null) {
return;
}
for (; ; ) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
MapEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (; ; ) {
MapEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
}
}
@Override
public MultiMap set(final String name, final String strVal) {
int h = hash(name);
int i = index(h);
remove0(h, i, name);
add0(h, i, name, strVal);
return this;
}
@Override
public MultiMap set(final String name, final Iterable values) {
if (values == null) {
throw new NullPointerException("values");
}
int h = hash(name);
int i = index(h);
remove0(h, i, name);
for (String v : values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
@Override
public MultiMap clear() {
for (int i = 0; i < entries.length; i++) {
entries[i] = null;
}
head.before = head.after = head;
return this;
}
@Override
public String get(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
MapEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
return e.getValue();
}
e = e.next;
}
return null;
}
@Override
public List getAll(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
LinkedList values = new LinkedList<>();
int h = hash(name);
int i = index(h);
MapEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.getValue());
}
e = e.next;
}
return values;
}
@Override
public List> entries() {
List> all =
new LinkedList<>();
MapEntry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
@Override
public Iterator> iterator() {
return entries().iterator();
}
@Override
public boolean contains(String name) {
return get(name) != null;
}
@Override
public boolean isEmpty() {
return head == head.after;
}
@Override
public Set names() {
Set names = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
MapEntry e = head.after;
while (e != head) {
names.add(e.getKey());
e = e.after;
}
return names;
}
@Override
public String get(CharSequence name) {
return get(name.toString());
}
@Override
public List getAll(CharSequence name) {
return getAll(name.toString());
}
@Override
public boolean contains(CharSequence name) {
return contains(name.toString());
}
@Override
public MultiMap add(CharSequence name, CharSequence value) {
return add(name.toString(), value.toString());
}
@Override
public MultiMap add(CharSequence name, Iterable values) {
String n = name.toString();
for (CharSequence seq : values) {
add(n, seq.toString());
}
return this;
}
@Override
public MultiMap set(CharSequence name, CharSequence value) {
return set(name.toString(), value.toString());
}
@Override
public MultiMap set(CharSequence name, Iterable values) {
remove(name);
String n = name.toString();
for (CharSequence seq : values) {
add(n, seq.toString());
}
return this;
}
@Override
public MultiMap remove(CharSequence name) {
return remove(name.toString());
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry entry : this) {
sb.append(entry).append('\n');
}
return sb.toString();
}
private static final class MapEntry implements Map.Entry {
final int hash;
final String key;
String value;
MapEntry next;
MapEntry before, after;
MapEntry(int hash, String key, String value) {
this.hash = hash;
this.key = key;
this.value = value;
}
void remove() {
before.after = after;
after.before = before;
}
void addBefore(MapEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
public String setValue(String value) {
if (value == null) {
throw new NullPointerException("value");
}
String oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return getKey() + ": " + getValue();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy