/**
* Copyright 2005-2012 Restlet S.A.S.
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL
* 1.0 (the "Licenses"). You can select the license that you prefer but you may
* not use this file except in compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the LGPL 3.0 license at
* http://www.opensource.org/licenses/lgpl-3.0
*
* You can obtain a copy of the LGPL 2.1 license at
* http://www.opensource.org/licenses/lgpl-2.1
*
* You can obtain a copy of the CDDL 1.0 license at
* http://www.opensource.org/licenses/cddl1
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://www.restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.client.engine.header;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.restlet.client.Context;
import org.restlet.client.Message;
import org.restlet.client.Request;
import org.restlet.client.Response;
import org.restlet.client.data.AuthenticationInfo;
import org.restlet.client.data.ChallengeRequest;
import org.restlet.client.data.ChallengeResponse;
import org.restlet.client.data.ClientInfo;
import org.restlet.client.data.Conditions;
import org.restlet.client.data.CookieSetting;
import org.restlet.client.data.Digest;
import org.restlet.client.data.Disposition;
import org.restlet.client.data.MediaType;
import org.restlet.client.data.Method;
import org.restlet.client.data.Reference;
import org.restlet.client.data.Status;
import org.restlet.client.data.Tag;
import org.restlet.client.engine.Engine;
import org.restlet.client.engine.util.CaseInsensitiveHashSet;
import org.restlet.client.engine.util.DateUtils;
import org.restlet.client.engine.util.StringUtils;
import org.restlet.client.representation.EmptyRepresentation;
import org.restlet.client.representation.Representation;
import org.restlet.client.util.Series;
/**
* HTTP-style header utilities.
*
* @author Jerome Louvel
*/
public class HeaderUtils {
/**
* Standard set of headers which cannot be modified.
*/
private static final Set<String> STANDARD_HEADERS = Collections
.unmodifiableSet(new CaseInsensitiveHashSet(Arrays.asList(
HeaderConstants.HEADER_ACCEPT,
HeaderConstants.HEADER_ACCEPT_CHARSET,
HeaderConstants.HEADER_ACCEPT_ENCODING,
HeaderConstants.HEADER_ACCEPT_LANGUAGE,
HeaderConstants.HEADER_ACCEPT_RANGES,
HeaderConstants.HEADER_AGE, HeaderConstants.HEADER_ALLOW,
HeaderConstants.HEADER_AUTHENTICATION_INFO,
HeaderConstants.HEADER_AUTHORIZATION,
HeaderConstants.HEADER_CACHE_CONTROL,
HeaderConstants.HEADER_CONNECTION,
HeaderConstants.HEADER_CONTENT_DISPOSITION,
HeaderConstants.HEADER_CONTENT_ENCODING,
HeaderConstants.HEADER_CONTENT_LANGUAGE,
HeaderConstants.HEADER_CONTENT_LENGTH,
HeaderConstants.HEADER_CONTENT_LOCATION,
HeaderConstants.HEADER_CONTENT_MD5,
HeaderConstants.HEADER_CONTENT_RANGE,
HeaderConstants.HEADER_CONTENT_TYPE,
HeaderConstants.HEADER_COOKIE, HeaderConstants.HEADER_DATE,
HeaderConstants.HEADER_ETAG, HeaderConstants.HEADER_EXPECT,
HeaderConstants.HEADER_EXPIRES,
HeaderConstants.HEADER_FROM, HeaderConstants.HEADER_HOST,
HeaderConstants.HEADER_IF_MATCH,
HeaderConstants.HEADER_IF_MODIFIED_SINCE,
HeaderConstants.HEADER_IF_NONE_MATCH,
HeaderConstants.HEADER_IF_RANGE,
HeaderConstants.HEADER_IF_UNMODIFIED_SINCE,
HeaderConstants.HEADER_LAST_MODIFIED,
HeaderConstants.HEADER_LOCATION,
HeaderConstants.HEADER_MAX_FORWARDS,
HeaderConstants.HEADER_PROXY_AUTHENTICATE,
HeaderConstants.HEADER_PROXY_AUTHORIZATION,
HeaderConstants.HEADER_RANGE,
HeaderConstants.HEADER_REFERRER,
HeaderConstants.HEADER_RETRY_AFTER,
HeaderConstants.HEADER_SERVER,
HeaderConstants.HEADER_SET_COOKIE,
HeaderConstants.HEADER_SET_COOKIE2,
HeaderConstants.HEADER_USER_AGENT,
HeaderConstants.HEADER_VARY, HeaderConstants.HEADER_VIA,
HeaderConstants.HEADER_WARNING,
HeaderConstants.HEADER_WWW_AUTHENTICATE)));
/**
* Set of unsupported headers that will be covered in future versions.
*/
private static final Set<String> UNSUPPORTED_STANDARD_HEADERS = Collections
.unmodifiableSet(new CaseInsensitiveHashSet(Arrays.asList(
HeaderConstants.HEADER_PRAGMA,
HeaderConstants.HEADER_TRAILER,
HeaderConstants.HEADER_TRANSFER_ENCODING,
HeaderConstants.HEADER_TRANSFER_EXTENSION,
HeaderConstants.HEADER_UPGRADE)));
/**
* Adds the entity headers based on the {@link Representation} to the
* {@link Series}.
*
* @param entity
* The source entity {@link Representation}.
* @param headers
* The target headers {@link Series}.
*/
public static void addEntityHeaders(Representation entity,
Series<Header> headers) {
if (entity == null || !entity.isAvailable()) {
addHeader(HeaderConstants.HEADER_CONTENT_LENGTH, "0", headers);
} else if (entity.getAvailableSize() != Representation.UNKNOWN_SIZE) {
addHeader(HeaderConstants.HEADER_CONTENT_LENGTH,
Long.toString(entity.getAvailableSize()), headers);
}
if (entity != null) {
addHeader(HeaderConstants.HEADER_CONTENT_ENCODING,
EncodingWriter.write(entity.getEncodings()), headers);
addHeader(HeaderConstants.HEADER_CONTENT_LANGUAGE,
LanguageWriter.write(entity.getLanguages()), headers);
if (entity.getLocationRef() != null) {
addHeader(HeaderConstants.HEADER_CONTENT_LOCATION, entity
.getLocationRef().getTargetRef().toString(), headers);
}
if (entity.getRange() != null) {
addHeader(HeaderConstants.HEADER_CONTENT_RANGE,
RangeWriter.write(entity.getRange(), entity.getSize()),
headers);
}
if (entity.getMediaType() != null) {
addHeader(HeaderConstants.HEADER_CONTENT_TYPE,
ContentType.writeHeader(entity), headers);
}
if (entity.getExpirationDate() != null) {
addHeader(HeaderConstants.HEADER_EXPIRES,
DateWriter.write(entity.getExpirationDate()), headers);
}
if (entity.getModificationDate() != null) {
addHeader(HeaderConstants.HEADER_LAST_MODIFIED,
DateWriter.write(entity.getModificationDate()), headers);
}
if (entity.getTag() != null) {
addHeader(HeaderConstants.HEADER_ETAG,
TagWriter.write(entity.getTag()), headers);
}
if (entity.getDisposition() != null
&& !Disposition.TYPE_NONE.equals(entity.getDisposition()
.getType())) {
addHeader(HeaderConstants.HEADER_CONTENT_DISPOSITION,
DispositionWriter.write(entity.getDisposition()),
headers);
}
}
}
/**
* Adds extension headers if they are non-standard headers.
*
* @param existingHeaders
* The headers to update.
* @param additionalHeaders
* The headers to add.
*/
public static void addExtensionHeaders(Series<Header> existingHeaders,
Series<Header> additionalHeaders) {
if (additionalHeaders != null) {
for (Header param : additionalHeaders) {
if (STANDARD_HEADERS.contains(param.getName())) {
// Standard headers that can't be overridden
Context.getCurrentLogger()
.warning(
"Addition of the standard header \""
+ param.getName()
+ "\" is not allowed. Please use the equivalent property in the Restlet API.");
} else if (UNSUPPORTED_STANDARD_HEADERS.contains(param
.getName())) {
Context.getCurrentLogger()
.warning(
"Addition of the standard header \""
+ param.getName()
+ "\" is discouraged as a future version of the Restlet API will directly support it.");
existingHeaders.add(param);
} else {
existingHeaders.add(param);
}
}
}
}
/**
* Adds the general headers from the {@link Message} to the {@link Series}.
*
* @param message
* The source {@link Message}.
* @param headers
* The target headers {@link Series}.
*/
public static void addGeneralHeaders(Message message, Series<Header> headers) {
addHeader(HeaderConstants.HEADER_CACHE_CONTROL,
CacheDirectiveWriter.write(message.getCacheDirectives()),
headers);
if (message.getDate() == null) {
message.setDate(new Date());
}
addHeader(HeaderConstants.HEADER_DATE,
DateWriter.write(message.getDate()), headers);
addHeader(HeaderConstants.HEADER_VIA,
RecipientInfoWriter.write(message.getRecipientsInfo()), headers);
addHeader(HeaderConstants.HEADER_WARNING,
WarningWriter.write(message.getWarnings()), headers);
}
/**
* Adds a header to the given list. Checks for exceptions and logs them.
*
* @param headerName
* The header name.
* @param headerValue
* The header value.
* @param headers
* The headers list.
*/
public static void addHeader(String headerName, String headerValue,
Series<Header> headers) {
if ((headerName != null) && (headerValue != null)
&& (headerValue.length() > 0)) {
try {
headers.add(headerName, headerValue);
} catch (Throwable t) {
Context.getCurrentLogger().log(Level.WARNING,
"Unable to format the " + headerName + " header", t);
}
}
}
/**
* Adds the entity headers based on the {@link Representation} to the
* {@link Series} when a 304 (Not Modified) status is returned.
*
* @param entity
* The source entity {@link Representation}.
* @param headers
* The target headers {@link Series}.
*/
public static void addNotModifiedEntityHeaders(Representation entity,
Series<Header> headers) {
if (entity != null) {
if (entity.getTag() != null) {
HeaderUtils.addHeader(HeaderConstants.HEADER_ETAG,
TagWriter.write(entity.getTag()), headers);
}
if (entity.getLocationRef() != null) {
HeaderUtils.addHeader(HeaderConstants.HEADER_CONTENT_LOCATION,
entity.getLocationRef().getTargetRef().toString(),
headers);
}
}
}
/**
* Adds the headers based on the {@link Request} to the given {@link Series}
* .
*
* @param request
* The {@link Request} to copy the headers from.
* @param headers
* The {@link Series} to copy the headers to.
*/
@SuppressWarnings("unchecked")
public static void addRequestHeaders(Request request, Series<Header> headers) {
ClientInfo clientInfo = request.getClientInfo();
if (!clientInfo.getAcceptedMediaTypes().isEmpty()) {
addHeader(HeaderConstants.HEADER_ACCEPT,
PreferenceWriter.write(clientInfo.getAcceptedMediaTypes()),
headers);
} else {
addHeader(HeaderConstants.HEADER_ACCEPT, MediaType.ALL.getName(),
headers);
}
if (!clientInfo.getAcceptedCharacterSets().isEmpty()) {
addHeader(HeaderConstants.HEADER_ACCEPT_CHARSET,
PreferenceWriter.write(clientInfo
.getAcceptedCharacterSets()), headers);
}
if (!clientInfo.getAcceptedEncodings().isEmpty()) {
addHeader(HeaderConstants.HEADER_ACCEPT_ENCODING,
PreferenceWriter.write(clientInfo.getAcceptedEncodings()),
headers);
}
if (!clientInfo.getAcceptedLanguages().isEmpty()) {
addHeader(HeaderConstants.HEADER_ACCEPT_LANGUAGE,
PreferenceWriter.write(clientInfo.getAcceptedLanguages()),
headers);
}
if (clientInfo.getFrom() != null) {
addHeader(HeaderConstants.HEADER_FROM, request.getClientInfo()
.getFrom(), headers);
}
// Manually add the host name and port when it is potentially
// different from the one specified in the target resource reference.
Reference hostRef = (request.getResourceRef().getBaseRef() != null) ? request
.getResourceRef().getBaseRef() : request.getResourceRef();
if (hostRef.getHostDomain() != null) {
String host = hostRef.getHostDomain();
int hostRefPortValue = hostRef.getHostPort();
if ((hostRefPortValue != -1)
&& (hostRefPortValue != request.getProtocol()
.getDefaultPort())) {
host = host + ':' + hostRefPortValue;
}
addHeader(HeaderConstants.HEADER_HOST, host, headers);
}
Conditions conditions = request.getConditions();
addHeader(HeaderConstants.HEADER_IF_MATCH,
TagWriter.write(conditions.getMatch()), headers);
addHeader(HeaderConstants.HEADER_IF_NONE_MATCH,
TagWriter.write(conditions.getNoneMatch()), headers);
if (conditions.getModifiedSince() != null) {
addHeader(HeaderConstants.HEADER_IF_MODIFIED_SINCE,
DateWriter.write(conditions.getModifiedSince()), headers);
}
if (conditions.getRangeTag() != null
&& conditions.getRangeDate() != null) {
Context.getCurrentLogger()
.log(Level.WARNING,
"Unable to format the HTTP If-Range header due to the presence of both entity tag and modification date.");
} else if (conditions.getRangeTag() != null) {
addHeader(HeaderConstants.HEADER_IF_RANGE,
TagWriter.write(conditions.getRangeTag()), headers);
} else if (conditions.getRangeDate() != null) {
addHeader(HeaderConstants.HEADER_IF_RANGE,
DateWriter.write(conditions.getRangeDate()), headers);
}
if (conditions.getUnmodifiedSince() != null) {
addHeader(HeaderConstants.HEADER_IF_UNMODIFIED_SINCE,
DateWriter.write(conditions.getUnmodifiedSince()), headers);
}
if (request.getMaxForwards() > -1) {
addHeader(HeaderConstants.HEADER_MAX_FORWARDS,
Integer.toString(request.getMaxForwards()), headers);
}
if (!request.getRanges().isEmpty()) {
addHeader(HeaderConstants.HEADER_RANGE,
RangeWriter.write(request.getRanges()), headers);
}
if (request.getReferrerRef() != null) {
addHeader(HeaderConstants.HEADER_REFERRER, request.getReferrerRef()
.toString(), headers);
}
if (request.getClientInfo().getAgent() != null) {
addHeader(HeaderConstants.HEADER_USER_AGENT, request
.getClientInfo().getAgent(), headers);
}
// ----------------------------------
// 3) Add supported extension headers
// ----------------------------------
if (request.getCookies().size() > 0) {
addHeader(HeaderConstants.HEADER_COOKIE,
CookieWriter.write(request.getCookies()), headers);
}
// -------------------------------------
// 4) Add user-defined extension headers
// -------------------------------------
Series<Header> additionalHeaders = (Series<Header>) request
.getAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS);
addExtensionHeaders(headers, additionalHeaders);
// ---------------------------------------
// 5) Add authorization headers at the end
// ---------------------------------------
}
/**
* Copies headers into a response.
*
* @param headers
* The headers to copy.
* @param response
* The response to update.
*/
public static void copyResponseTransportHeaders(Series<Header> headers,
Response response) {
if (headers != null) {
for (Header header : headers) {
if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_LOCATION)) {
response.setLocationRef(header.getValue());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_AGE)) {
try {
response.setAge(Integer.parseInt(header.getValue()));
} catch (NumberFormatException nfe) {
Context.getCurrentLogger().log(
Level.WARNING,
"Error during Age header parsing. Header: "
+ header.getValue(), nfe);
}
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_DATE)) {
Date date = DateUtils.parse(header.getValue());
if (date == null) {
date = new Date();
}
response.setDate(date);
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_RETRY_AFTER)) {
} else if ((header.getName()
.equalsIgnoreCase(HeaderConstants.HEADER_SET_COOKIE))
|| (header.getName()
.equalsIgnoreCase(HeaderConstants.HEADER_SET_COOKIE2))) {
try {
CookieSettingReader cr = new CookieSettingReader(
header.getValue());
response.getCookieSettings().add(cr.readValue());
} catch (Exception e) {
Context.getCurrentLogger().log(
Level.WARNING,
"Error during cookie setting parsing. Header: "
+ header.getValue(), e);
}
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_WWW_AUTHENTICATE)) {
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_PROXY_AUTHENTICATE)) {
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_AUTHENTICATION_INFO)) {
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_SERVER)) {
response.getServerInfo().setAgent(header.getValue());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_ALLOW)) {
MethodReader
.addValues(header, response.getAllowedMethods());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_VARY)) {
DimensionReader.addValues(header, response.getDimensions());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_VIA)) {
RecipientInfoReader.addValues(header,
response.getRecipientsInfo());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_WARNING)) {
WarningReader.addValues(header, response.getWarnings());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CACHE_CONTROL)) {
CacheDirectiveReader.addValues(header,
response.getCacheDirectives());
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_ACCEPT_RANGES)) {
TokenReader tr = new TokenReader(header.getValue());
response.getServerInfo().setAcceptingRanges(
tr.readValues().contains("bytes"));
}
}
}
}
/**
* Extracts entity headers and updates a given representation or create an
* empty one when at least one entity header is present.
*
* @param headers
* The headers to copy.
* @param representation
* The representation to update or null.
* @return a representation updated with the given entity headers.
* @throws NumberFormatException
* @see HeaderUtils#copyResponseTransportHeaders(Series, Response)
*/
public static Representation extractEntityHeaders(Iterable<Header> headers,
Representation representation) throws NumberFormatException {
Representation result = (representation == null) ? new EmptyRepresentation()
: representation;
boolean entityHeaderFound = false;
if (headers != null) {
for (Header header : headers) {
if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_TYPE)) {
ContentType contentType = new ContentType(header.getValue());
result.setMediaType(contentType.getMediaType());
if ((result.getCharacterSet() == null)
|| (contentType.getCharacterSet() != null)) {
result.setCharacterSet(contentType.getCharacterSet());
}
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_LENGTH)) {
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_EXPIRES)) {
result.setExpirationDate(HeaderReader.readDate(
header.getValue(), false));
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_ENCODING)) {
new EncodingReader(header.getValue()).addValues(result
.getEncodings());
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_LANGUAGE)) {
new LanguageReader(header.getValue()).addValues(result
.getLanguages());
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_LAST_MODIFIED)) {
result.setModificationDate(HeaderReader.readDate(
header.getValue(), false));
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_ETAG)) {
result.setTag(Tag.parse(header.getValue()));
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_LOCATION)) {
result.setLocationRef(header.getValue());
entityHeaderFound = true;
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_DISPOSITION)) {
try {
result.setDisposition(new DispositionReader(header
.getValue()).readValue());
entityHeaderFound = true;
} catch (IOException ioe) {
Context.getCurrentLogger().log(
Level.WARNING,
"Error during Content-Disposition header parsing. Header: "
+ header.getValue(), ioe);
}
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_RANGE)) {
} else if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_MD5)) {
}
}
}
// If no representation was initially expected and no entity header
// is found, then do not return any representation
if ((representation == null) && !entityHeaderFound) {
result = null;
}
return result;
}
/**
* Returns the content length of the request entity if know,
* {@link Representation#UNKNOWN_SIZE} otherwise.
*
* @return The request content length.
*/
public static long getContentLength(Series<Header> headers) {
long contentLength = Representation.UNKNOWN_SIZE;
if (headers != null) {
// Extract the content length header
for (Header header : headers) {
if (header.getName().equalsIgnoreCase(
HeaderConstants.HEADER_CONTENT_LENGTH)) {
try {
contentLength = Long.parseLong(header.getValue());
} catch (NumberFormatException e) {
contentLength = Representation.UNKNOWN_SIZE;
}
}
}
}
return contentLength;
}
/**
* Indicates if the given character is alphabetical (a-z or A-Z).
*
* @param character
* The character to test.
* @return True if the given character is alphabetical (a-z or A-Z).
*/
public static boolean isAlpha(int character) {
return isUpperCase(character) || isLowerCase(character);
}
/**
* Indicates if the given character is in ASCII range.
*
* @param character
* The character to test.
* @return True if the given character is in ASCII range.
*/
public static boolean isAsciiChar(int character) {
return (character >= 0) && (character <= 127);
}
/**
* Indicates if the given character is a carriage return.
*
* @param character
* The character to test.
* @return True if the given character is a carriage return.
*/
public static boolean isCarriageReturn(int character) {
return (character == 13);
}
/**
* Indicates if the entity is chunked.
*
* @return True if the entity is chunked.
*/
public static boolean isChunkedEncoding(Series<Header> headers) {
boolean result = false;
if (headers != null) {
final String header = headers.getFirstValue(
HeaderConstants.HEADER_TRANSFER_ENCODING, true);
result = "chunked".equalsIgnoreCase(header);
}
return result;
}
/**
* Indicates if the given character is a comma, the character used as header
* value separator.
*
* @param character
* The character to test.
* @return True if the given character is a comma.
*/
public static boolean isComma(int character) {
return (character == ',');
}
/**
* Indicates if the given character is a comment text. It means
* {@link #isText(int)} returns true and the character is not '(' or ')'.
*
* @param character
* The character to test.
* @return True if the given character is a quoted text.
*/
public static boolean isCommentText(int character) {
return isText(character) && (character != '(') && (character != ')');
}
/**
* Indicates if the connection must be closed.
*
* @param headers
* The headers to test.
* @return True if the connection must be closed.
*/
public static boolean isConnectionClose(Series<Header> headers) {
boolean result = false;
if (headers != null) {
String header = headers.getFirstValue(
HeaderConstants.HEADER_CONNECTION, true);
result = "close".equalsIgnoreCase(header);
}
return result;
}
/**
* Indicates if the given character is a control character.
*
* @param character
* The character to test.
* @return True if the given character is a control character.
*/
public static boolean isControlChar(int character) {
return ((character >= 0) && (character <= 31)) || (character == 127);
}
/**
* Indicates if the given character is a digit (0-9).
*
* @param character
* The character to test.
* @return True if the given character is a digit (0-9).
*/
public static boolean isDigit(int character) {
return (character >= '0') && (character <= '9');
}
/**
* Indicates if the given character is a double quote.
*
* @param character
* The character to test.
* @return True if the given character is a double quote.
*/
public static boolean isDoubleQuote(int character) {
return (character == 34);
}
/**
* Indicates if the given character is an horizontal tab.
*
* @param character
* The character to test.
* @return True if the given character is an horizontal tab.
*/
public static boolean isHorizontalTab(int character) {
return (character == 9);
}
/**
* Indicates if the given character is in ISO Latin 1 (8859-1) range. Note
* that this range is a superset of ASCII and a subrange of Unicode (UTF-8).
*
* @param character
* The character to test.
* @return True if the given character is in ISO Latin 1 range.
*/
public static boolean isLatin1Char(int character) {
return (character >= 0) && (character <= 255);
}
/**
* Indicates if the given character is a value separator.
*
* @param character
* The character to test.
* @return True if the given character is a value separator.
*/
public static boolean isLinearWhiteSpace(int character) {
return (isCarriageReturn(character) || isSpace(character)
|| isLineFeed(character) || HeaderUtils
.isHorizontalTab(character));
}
/**
* Indicates if the given character is a line feed.
*
* @param character
* The character to test.
* @return True if the given character is a line feed.
*/
public static boolean isLineFeed(int character) {
return (character == 10);
}
/**
* Indicates if the given character is lower case (a-z).
*
* @param character
* The character to test.
* @return True if the given character is lower case (a-z).
*/
public static boolean isLowerCase(int character) {
return (character >= 'a') && (character <= 'z');
}
/**
* Indicates if the given character marks the start of a quoted pair.
*
* @param character
* The character to test.
* @return True if the given character marks the start of a quoted pair.
*/
public static boolean isQuoteCharacter(int character) {
return (character == '\\');
}
/**
* Indicates if the given character is a quoted text. It means
* {@link #isText(int)} returns true and {@link #isDoubleQuote(int)} returns
* false.
*
* @param character
* The character to test.
* @return True if the given character is a quoted text.
*/
public static boolean isQuotedText(int character) {
return isText(character) && !isDoubleQuote(character);
}
/**
* Indicates if the given character is a semicolon, the character used as
* header parameter separator.
*
* @param character
* The character to test.
* @return True if the given character is a semicolon.
*/
public static boolean isSemiColon(int character) {
return (character == ';');
}
/**
* Indicates if the given character is a separator.
*
* @param character
* The character to test.
* @return True if the given character is a separator.
*/
public static boolean isSeparator(int character) {
switch (character) {
case '(':
case ')':
case '<':
case '>':
case '@':
case ',':
case ';':
case ':':
case '\\':
case '"':
case '/':
case '[':
case ']':
case '?':
case '=':
case '{':
case '}':
case ' ':
case '\t':
return true;
default:
return false;
}
}
/**
* Indicates if the given character is a space.
*
* @param character
* The character to test.
* @return True if the given character is a space.
*/
public static boolean isSpace(int character) {
return (character == 32);
}
/**
* Indicates if the given character is textual (ISO Latin 1 and not a
* control character).
*
* @param character
* The character to test.
* @return True if the given character is textual.
*/
public static boolean isText(int character) {
return isLatin1Char(character) && !isControlChar(character);
}
/**
* Indicates if the token is valid.<br>
* Only contains valid token characters.
*
* @param token
* The token to check
* @return True if the token is valid.
*/
public static boolean isToken(CharSequence token) {
for (int i = 0; i < token.length(); i++) {
if (!isTokenChar(token.charAt(i))) {
return false;
}
}
return true;
}
/**
* Indicates if the given character is a token character (text and not a
* separator).
*
* @param character
* The character to test.
* @return True if the given character is a token character (text and not a
* separator).
*/
public static boolean isTokenChar(int character) {
return isAsciiChar(character) && !isSeparator(character);
}
/**
* Indicates if the given character is upper case (A-Z).
*
* @param character
* The character to test.
* @return True if the given character is upper case (A-Z).
*/
public static boolean isUpperCase(int character) {
return (character >= 'A') && (character <= 'Z');
}
/**
* Private constructor to ensure that the class acts as a true utility class
* i.e. it isn't instantiable and extensible.
*/
private HeaderUtils() {
}
}