Add TextHeaders.getAndRemove(...) and its variants

Related issue: #2649 and #2745

Motivation:

At the moment there is no way to get and remove a header with one call.
This means you need to search the headers two times. We should add
getAndRemove(...) to allow doing so with one call.

Modifications:

Add getAndRemove(...) and getUnconvertedAndRemove(...) and their
variants

Result:

More efficient API
This commit is contained in:
jxu 2014-08-06 17:06:58 +08:00 committed by Trustin Lee
parent 286b89933c
commit 2d36caa9f6
3 changed files with 418 additions and 0 deletions

View File

@ -459,6 +459,158 @@ public class DefaultTextHeaders implements TextHeaders {
return HttpHeaderDateFormat.get().parse(v.toString(), defaultValue);
}
@Override
public CharSequence getUnconvertedAndRemove(CharSequence name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
CharSequence value = null;
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
value = e.value;
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return value;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
value = next.value;
e.next = next.next;
next.remove();
} else {
e = next;
}
}
if (value != null) {
return value;
}
return null;
}
@Override
public String getAndRemove(CharSequence name) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
return null;
}
return v.toString();
}
@Override
public String getAndRemove(CharSequence name, String defaultValue) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
return defaultValue;
}
return v.toString();
}
@Override
public int getIntAndRemove(CharSequence name) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
throw new NoSuchElementException(String.valueOf(name));
}
if (v instanceof AsciiString) {
return ((AsciiString) v).parseInt();
} else {
return Integer.parseInt(v.toString());
}
}
@Override
public int getIntAndRemove(CharSequence name, int defaultValue) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
return defaultValue;
}
try {
if (v instanceof AsciiString) {
return ((AsciiString) v).parseInt();
} else {
return Integer.parseInt(v.toString());
}
} catch (NumberFormatException ignored) {
return defaultValue;
}
}
@Override
public long getLongAndRemove(CharSequence name) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
throw new NoSuchElementException(String.valueOf(name));
}
if (v instanceof AsciiString) {
return ((AsciiString) v).parseLong();
} else {
return Long.parseLong(v.toString());
}
}
@Override
public long getLongAndRemove(CharSequence name, long defaultValue) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
return defaultValue;
}
try {
if (v instanceof AsciiString) {
return ((AsciiString) v).parseLong();
} else {
return Long.parseLong(v.toString());
}
} catch (NumberFormatException ignored) {
return defaultValue;
}
}
@Override
public long getTimeMillisAndRemove(CharSequence name) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
throw new NoSuchElementException(String.valueOf(name));
}
return HttpHeaderDateFormat.get().parse(v.toString());
}
@Override
public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
CharSequence v = getUnconvertedAndRemove(name);
if (v == null) {
return defaultValue;
}
return HttpHeaderDateFormat.get().parse(v.toString(), defaultValue);
}
@Override
public List<CharSequence> getAllUnconverted(CharSequence name) {
if (name == null) {
@ -501,6 +653,104 @@ public class DefaultTextHeaders implements TextHeaders {
return values;
}
@Override
public List<String> getAllAndRemove(CharSequence name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
List<String> values = new ArrayList<String>(4);
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(e.getValue().toString());
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
Collections.reverse(values);
return values;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
values.add(next.getValue().toString());
e.next = next.next;
next.remove();
} else {
e = next;
}
}
Collections.reverse(values);
return values;
}
@Override
public List<CharSequence> getAllUnconvertedAndRemove(CharSequence name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hashCode(name);
int i = index(h);
HeaderEntry e = entries[i];
if (e == null) {
return null;
}
List<CharSequence> values = new ArrayList<CharSequence>(4);
for (;;) {
if (e.hash == h && nameEquals(e.name, name)) {
values.add(e.getValue());
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
Collections.reverse(values);
return values;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && nameEquals(next.name, name)) {
values.add(next.getValue());
e.next = next.next;
next.remove();
} else {
e = next;
}
}
Collections.reverse(values);
return values;
}
@Override
public List<Map.Entry<String, String>> entries() {
int cnt = 0;

View File

@ -67,11 +67,56 @@ public class EmptyTextHeaders implements TextHeaders {
return defaultValue;
}
@Override
public String getAndRemove(CharSequence name) {
return null;
}
@Override
public String getAndRemove(CharSequence name, String defaultValue) {
return defaultValue;
}
@Override
public int getIntAndRemove(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override
public int getIntAndRemove(CharSequence name, int defaultValue) {
return defaultValue;
}
@Override
public long getLongAndRemove(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override
public long getLongAndRemove(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public long getTimeMillisAndRemove(CharSequence name) {
throw new NoSuchElementException(String.valueOf(name));
}
@Override
public long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
return defaultValue;
}
@Override
public CharSequence getUnconverted(CharSequence name) {
return null;
}
@Override
public CharSequence getUnconvertedAndRemove(CharSequence name) {
return null;
}
@Override
public List<String> getAll(CharSequence name) {
return Collections.emptyList();
@ -82,6 +127,16 @@ public class EmptyTextHeaders implements TextHeaders {
return Collections.emptyList();
}
@Override
public List<String> getAllAndRemove(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<CharSequence> getAllUnconvertedAndRemove(CharSequence name) {
return Collections.emptyList();
}
@Override
public List<Entry<String, String>> entries() {
return Collections.emptyList();

View File

@ -48,6 +48,94 @@ public interface TextHeaders extends Iterable<Map.Entry<String, String>> {
long getTimeMillis(CharSequence name);
long getTimeMillis(CharSequence name, long defaultValue);
/**
* Returns and Removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
String getAndRemove(CharSequence name);
/**
* Returns and Removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return The first header value or {@code defaultValue} if there is no such header
*/
String getAndRemove(CharSequence name, String defaultValue);
/**
* Returns and Removes the integer value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value
* @throws java.util.NoSuchElementException
* if no such header
* @throws NumberFormatException
* if the value of header is not number
*/
int getIntAndRemove(CharSequence name);
/**
* Returns and Removes the integer value of a header with the specified name. If there are more than one values for
* the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return The first header value or {@code defaultValue} if there is no such header or the value of header is not
* number
*/
int getIntAndRemove(CharSequence name, int defaultValue);
/**
* Returns and Removes the long value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value
* @throws java.util.NoSuchElementException
* if no such header
* @throws NumberFormatException
* if the value of header is not number
*/
long getLongAndRemove(CharSequence name);
/**
* Returns and Removes the long value of a header with the specified name. If there are more than one values for
* the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return The first header value or {@code defaultValue} if there is no such header or the value of header is not
* number
*/
long getLongAndRemove(CharSequence name, long defaultValue);
/**
* Returns and Removes the millisecond value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value
* @throws java.util.NoSuchElementException
* if no such header
*/
long getTimeMillisAndRemove(CharSequence name);
/**
* Returns and Removes the millisecond value of a header with the specified name. If there are more than one values
* for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue default value
* @return The first header value or {@code defaultValue} if there is no such header
*/
long getTimeMillisAndRemove(CharSequence name, long defaultValue);
/**
* Returns the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
@ -57,6 +145,15 @@ public interface TextHeaders extends Iterable<Map.Entry<String, String>> {
*/
CharSequence getUnconverted(CharSequence name);
/**
* Returns and Removes the value of a header with the specified name. If there are
* more than one values for the specified name, the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
CharSequence getUnconvertedAndRemove(CharSequence name);
/**
* Returns the values of headers with the specified name
*
@ -73,6 +170,22 @@ public interface TextHeaders extends Iterable<Map.Entry<String, String>> {
*/
List<CharSequence> getAllUnconverted(CharSequence name);
/**
* Returns and Removes the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<String> getAllAndRemove(CharSequence name);
/**
* Returns and Removes the values of headers with the specified name
*
* @param name The name of the headers to search
* @return A {@link List} of header values which will be empty if no values are found
*/
List<CharSequence> getAllUnconvertedAndRemove(CharSequence name);
/**
* Returns a new {@link List} that contains all headers in this object. Note that modifying the
* returned {@link List} will not affect the state of this object. If you intend to enumerate over the header