org.simpleframework.http.parse.AddressParser Maven / Gradle / Ivy
Show all versions of simple-http Show documentation
/*
* AddressParser.java February 2001
*
* Copyright (C) 2001, Niall Gallagher
*
* 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 org.simpleframework.http.parse;
import org.simpleframework.common.KeyMap;
import org.simpleframework.common.parse.Parser;
import org.simpleframework.http.Address;
import org.simpleframework.http.Path;
import org.simpleframework.http.Query;
/**
* This parser is used to parse uniform resource identifiers.
* The uniform resource identifier syntax is given in RFC 2396.
* This parser can parse relative and absolute URI's. The
* uniform resource identifier syntax that this parser will
* parse are based on the generic web based URL similar to
* the syntax represented in RFC 2616 section 3.2.2. The syntax
* used to parse this URI is a modified version of RFC 2396
*
*
* URI = (absoluteURI | relativeURI)
* absoluteURI = scheme ":" ("//" netpath | relativeURI)
* relativeURI = path ["?" querypart]
* netpath = domain [":" port] relativeURI
* path = *("/" segment)
* segment = *pchar *( ";" param )
*
*
* This implements the Address
interface and provides
* methods that access the various parts of the URI. The parameters
* in the path segments of the uniform resource identifier are
* stored in name value pairs. If parameter names are not unique
* across the path segments then only the deepest parameter will be
* stored from the path segment. For example if the URI represented
* was http://domain/path1;x=y/path2;x=z
the value for
* the parameter named x
would be z
.
*
* This will normalize the path part of the uniform resource
* identifier. A normalized path is one that contains no back
* references like "./" and "../". The normalized path will not
* contain the path parameters.
*
* The setPath
method is used to reset the path this
* uniform resource identifier has, it also resets the parameters.
* The parameters are extracted from the new path given.
*
* @author Niall Gallagher
*/
public class AddressParser extends Parser implements Address {
/**
* Parameters are stored so that the can be viewed.
*/
private ParameterMap param;
/**
* This is the path used to represent the address path.
*/
private Path normal;
/**
* This contains the query parameters for the address.
*/
private Query data;
/**
* Used to track the characters that form the path.
*/
private Token path;
/**
* Used to track the characters that form the domain.
*/
private Token domain;
/**
* Used to track the characters that form the query.
*/
private Token query;
/**
* Used to track the name characters of a parameter.
*/
private Token name;
/**
* Used to track the value characters of a parameter.
*/
private Token value;
/**
* References the scheme that this URI contains.
*/
private Token scheme;
/**
* Contains the port number if it was specified.
*/
private int port;
/**
* Default constructor will create a AddressParser
* that contains no specifics. The instance will return
* null
for all the get methods. The parsers
* get methods are populated by using the parse
* method.
*/
public AddressParser(){
this.param = new ParameterMap();
this.path = new Token();
this.domain = new Token();
this.query = new Token();
this.scheme = new Token();
this.name = new Token();
this.value = new Token();
}
/**
* This is primarily a convenience constructor. This will parse
* the String
given to extract the specifics. This
* could be achieved by calling the default no-arg constructor
* and then using the instance to invoke the parse
* method on that String
to extract the parts.
*
* @param text a String
containing a URI value
*/
public AddressParser(String text){
this();
parse(text);
}
/**
* This allows the scheme of the URL given to be returned.
* If the URI does not contain a scheme then this will
* return null. The scheme of the URI is the part that
* specifies the type of protocol that the URI is used
* for, an example gopher://domain/path
is
* a URI that is intended for the gopher protocol. The
* scheme is the string gopher
.
*
* @return this returns the scheme tag for the URI if
* there is one specified for it
*/
public String getScheme(){
return scheme.toString();
}
/**
* This is used to retrieve the domain of this URI. The
* domain part in the URI is an optional part, an example
* http://domain/path?querypart
. This will
* return the value of the domain part. If there is no
* domain part then this will return null otherwise the
* domain value found in the uniform resource identifier.
*
* @return the domain part of this uniform resource
* identifier this represents
*/
public String getDomain(){
return domain.toString();
}
/**
* This is used to retrieve the path of this URI. The path part
* is the most fundamental part of the URI. This will return
* the value of the path. If there is no path part then this
* will return /
to indicate the root.
*
* The Path
object returned by this will contain
* no path parameters. The path parameters are available using
* the Address
methods. The reason that this does not
* contain any of the path parameters is so that if the path is
* needed to be converted into an OS specific path then the path
* parameters will not need to be separately parsed out.
*
* @return the path that this URI contains, this value will not
* contain any back references such as "./" and "../" or any
* path parameters
*/
public Path getPath(){
if(normal == null) {
String text = path.toString();
if(text == null) {
normal = new PathParser("/");
}
if(normal == null){
normal = new PathParser(text);
}
}
return normal;
}
/**
* This is used to retrieve the query of this URI. The query part
* in the URI is an optional part. This will return the value
* of the query part. If there is no query part then this will
* return an empty Query
object. The query is
* an optional member of a URI and comes after the path part, it
* is preceded by a question mark, ?
character.
* For example the following URI contains query
for
* its query part, http://host:port/path?query
.
*
* This returns a org.simpleframework.http.Query
* object that can be used to interact directly with the query
* values. The Query
object is a read-only interface
* to the query parameters, and so will not affect the URI.
*
* @return a Query
object for the query part
*/
public Query getQuery(){
if(data == null) {
String text = query.toString();
if(text == null) {
data = new QueryParser();
}
if(data == null){
data = new QueryParser(text);
}
}
return data;
}
/**
* This is used to retrieve the port of the uniform resource
* identifier. The port part in this is an optional part, an
* example http://host:port/path?querypart
. This
* will return the value of the port. If there is no port then
* this will return -1
because this represents
* an impossible uniform resource identifier port. The port
* is an optional part.
*
* @return this returns the port of the uniform resource
* identifier
*/
public int getPort(){
return port <= 0? -1 : port;
}
/**
* This extracts the parameter values from the uniform resource
* identifier represented by this object. The parameters that a
* uniform resource identifier contains are embedded in the path
* part of the URI. If the path contains no parameters then this
* will return an empty Map
instance.
*
* This will produce unique name and value parameters. Thus if the
* URI contains several path segments with similar parameter names
* this will return the deepest parameter. For example if the URI
* represented was http://domain/path1;x=y/path2;x=z
* the value for the parameter named x
would be
* z
.
*
* @return this will return the parameter names found in the URI
*/
public KeyMap getParameters(){
return param;
}
/**
* This allows the scheme for the URI to be specified.
* If the URI does not contain a scheme then this will
* attach the scheme and the ://
identifier
* to ensure that the Address.toString
will
* produce the correct syntax.
*
* Caution must be taken to ensure that the port and
* the scheme are consistent. So if the original URI
* was http://domain:80/path
and the scheme
* was changed to ftp
the port number that
* remains is the standard HTTP port not the FTP port.
*
* @param value this specifies the protocol this URI
* is intended for
*/
public void setScheme(String value){
scheme.value = value;
}
/**
* This will set the domain to whatever value is in the
* string parameter. If the string is null then this URI
* objects toString
method will not contain
* the domain. The result of the toString
* method will be /path/path?query
. If the
* path is non-null this URI will contain the path.
*
* @param value this will be the new domain of this
* uniform resource identifier, if it is not null
*/
public void setDomain(String value){
path.toString();
query.toString();
scheme.toString();
domain.clear();
parseDomain(value);
}
/**
* This will set the domain to whatever value is in the
* string parameter. If the string is null then this URI
* objects toString
method will not contain
* the domain. The result of the toString
* method will be /path/path?query
. If the
* path is non-null this URI will contain the path.
*
* @param value this will be the new domain of this
* uniform resource identifier, if it is not null
*/
private void parseDomain(String value){
count = value.length();
ensureCapacity(count);
value.getChars(0, count, buf, 0);
normal = null;
off = 0;
hostPort();
}
/**
* This will set the port to whatever value it is given. If
* the value is 0 or less then the toString
will
* will not contain the optional port. If port number is above
* 0 then the toString
method will produce a URI
* like http://host:123/path
but only if there is
* a valid domain.
*
* @param port the port value that this URI is to have
*/
public void setPort(int port) {
this.port = port;
}
/**
* This will set the path to whatever value it is given. If the
* value is null then this Address.toString
method will
* not contain the path, that is if path is null then it will be
* interpreted as /
.
*
* This will reset the parameters this URI has. If the value
* given to this method has embedded parameters these will form
* the parameters of this URI. The value given may not be the
* same value that the getPath
produces. The path
* will have all back references and parameters stripped.
*
* @param text the path that this URI is to be set with
*/
public void setPath(String text) {
if(!text.startsWith("/")){
text = "/" + text;
}
domain.toString();
query.toString();
scheme.toString();
param.clear();
path.clear();
parsePath(text); /*extract params*/
}
/**
* This will set the path to whatever value it is given. If the
* value is null then this Address.toString
method
* will not contain the path, that is if path is null then it will
* be interpreted as /
.
*
* This will reset the parameters this URI has. If the value
* given to this method has embedded parameters these will form
* the parameters of this URI. The value given may not be the
* same value that the getPath
produces. The path
* will have all back references and parameters stripped.
*
* @param path the path that this URI is to be set with
*/
public void setPath(Path path) {
if(path != null){
normal = path;
}else {
setPath("/");
}
}
/**
* This is used to parse the path given with the setPath
* method. The path contains name and value pairs. These parameters
* are embedded into the path segments using a semicolon character,
* ';'. Since the parameters to not form part of the actual path
* mapping they are removed from the path and stored. Each parameter
* can then be extracted from this parser using the methods provided
* by the Address
interface.
*
* @param path this is the path that is to be parsed and have the
* parameter values extracted
*/
private void parsePath(String path){
count = path.length();
ensureCapacity(count);
path.getChars(0, count, buf, 0);
normal = null;
off = 0;
path();
}
/**
* This will set the query to whatever value it is given. If the
* value is null then this Address.toString
method
* will not contain the query. If the query was abc
* then the toString
method would produce a string
* like http://host:port/path?abc
. If the query is
* null this URI would have no query part. The query must not
* contain the ?
character.
*
* @param value the query that this uniform resource identifier
* is to be set to if it is non-null
*/
public void setQuery(String value) {
query.value = value;
data = null;
}
/**
* This will set the query to whatever value it is given. If the
* value is null then this Address.toString
method
* will not contain the query. If the Query.toString
* returns null then the query will be empty. This is basically
* the setQuery(String)
method with the string value
* from the issued Query.toString
method.
*
* @param query a Query
object that contains
* the name value parameters for the query
*/
public void setQuery(Query query) {
if(value != null) {
data = query;
}else {
setQuery("");
}
}
/**
* This will check to see what type of URI this is if it is an
* absoluteURI
or a relativeURI
. To
* see the definition of a URI see RFC 2616 for the definition
* of a URL and for more specifics see RFC 2396 for the
* expressions.
*/
protected void parse(){
if(count > 0){
if(buf[0] == '/'){
relativeURI();
}else{
absoluteURI();
}
}
}
/**
* This will empty each tokens cache. A tokens cache is used
* to represent a token once the token's toString
* method has been called. Thus when the toString
* method is called then the token depends on the value of the
* cache alone in further calls to toString
.
* However if a URI has just been parsed and that method has
* not been invoked then the cache is created from the buf if
* its length is greater than zero.
*/
protected void init(){
param.clear();
domain.clear();
path.clear();
query.clear();
scheme.clear();
off =port = 0;
normal = null;
data = null;
}
/**
* This is a specific definition of a type of URI. An absolute
* URI is a URI that contains a host and port. It is the most
* frequently used type of URI. This will define the host and
* the optional port part. As well as the relative URI part.
* This uses a simpler syntax than the one specified in RFC 2396
*
*
* absoluteURI = scheme ":" ("//" netpath | relativeURI)
* relativeURI = path ["?" querypart]
* netpath = domain [":" port] relativeURI
* path = *("/" segment)
* segment = *pchar *( ";" param )
*
*
* This syntax is sufficient to handle HTTP style URI's as well
* as GOPHER and FTP and various other 'simple' schemes. See
* RFC 2396 for the syntax of an absoluteURI
.
*/
private void absoluteURI(){
scheme();
netPath();
}
/**
* This will check to see if there is a scheme in the URI. If
* there is a scheme found in the URI this returns true and
* removes that scheme tag of the form "ftp:" or "http:"
* or whatever the protocol scheme tag may be for the URI.
*
* The syntax for the scheme is given in RFC 2396 as follows
*
*
* scheme = alpha *( alpha | digit | "+" | "-" | "." )
*
*
* This will however also skips the "://" from the tag
* so of the URI was gopher://domain/path
then
* the URI would be domain/path
afterwards.
*/
private void scheme(){
int mark = off;
int pos = off;
if(alpha(buf[off])){
while(off < count){
char next = buf[off++];
if(schemeChar(next)){
pos++;
}else if(next == ':'){
if(!skip("//")) {
off = mark;
pos = mark;
}
break;
}else{
off = mark;
pos = mark;
break;
}
}
scheme.len = pos - mark;
scheme.off = mark;
}
}
/**
* This method is used to assist the scheme method. This will
* check to see if the type of the character is the same as
* those described in RFC 2396 for a scheme character. The
* scheme tag can contain an alphanumeric of the following
* "+", "-", "."
.
*
* @param c this is the character that is being checked
*
* @return this returns true if the character is a valid
* scheme character
*/
private boolean schemeChar(char c){
switch(c){
case '+': case '-':
case '.':
return true;
default:
return alphanum(c);
}
}
/**
* The network path is the path that contains the network
* address of the host that this URI is targeted at. This
* will parse the domain name of the host and also a port
* number before parsing a relativeURI
*
*
* netpath = domain [":" port] relativeURI
*
*
* This syntax is modified from the URI specification on
* RFC 2396.
*/
private void netPath(){
hostPort();
relativeURI();
}
/**
* This is used to extract the host and port combination.
* Typically a URI will not explicitly specify a port, however
* if there is a semicolon at the end of the domain it should
* be interpreted as the port part of the URI.
*/
private void hostPort() {
domain();
if(skip(":")){
port();
}
}
/**
* This is a specific definition of a type of URI. A relative
* URI is a URI that contains no host or port. It is basically
* the resource within the host. This will extract the path and
* the optional query part of the URI. Rfc2396 has the proper
* definition of a relativeURI
.
*/
private void relativeURI(){
path();
if(skip("?")){
query();
}
}
/**
* This is used to extract the optional port from a given URI.
* This will read a sequence of digit characters and convert
* the String
of digit characters into a decimal
* number. The digits will be added to the port variable. If
* there is no port number this will not update the read offset.
*/
private void port() {
while(off < count){
if(!digit(buf[off])){
break;
}
port *= 10;
port += buf[off];
port -= '0';
off++;
}
}
/**
* This is used to extract the domain from the given URI. This
* will firstly initialize the token object that represents the
* domain. This allows the token's toString
method to
* return the extracted value of the token rather than getting
* confused with previous values set by a previous parse method.
*
* This uses the following delimiters to determine the end of the
* domain ?
,:
and /. This
* ensures that the read offset does not go out of bounds and
* consequently throw an IndexOutOfBoundsException
.
*/
private void domain(){
int mark = off;
loop: while(off < count){
switch(buf[off]){
case '/': case ':':
case '?':
break loop;
default:
off++;
}
}
domain.len = off - mark;
domain.off = mark;
}
/**
* This is used to extract the segments from the given URI. This
* will firstly initialize the token object that represents the
* path. This allows the token's toString
method to
* return the extracted value of the token rather than getting
* confused with previous values set by a previous parse method.
*
* This is slightly different from RFC 2396 in that it defines a
* pchar as the RFC 2396 definition of a pchar without the escaped
* chars. So this method has to ensure that no escaped chars go
* unchecked. This ensures that the read offset does not go out
* of bounds and throw an IndexOutOfBoundsException
.
*/
private void path(){
int mark = off;
int pos = off;
while(skip("/")) {
buf[pos++] = '/';
while(off < count){
if(buf[off]==';'){
while(skip(";")){
param();
insert();
}
break;
}
if(buf[off]=='%'){
escape();
}else if(!pchar(buf[off])){
break;
}
buf[pos++]=buf[off++];
}
}
path.len = pos -mark;
path.off = mark;
}
/**
* This is used to extract the query from the given URI. This
* will firstly initialize the token object that represents the
* query. This allows the token's toString
method
* to return the extracted value of the token rather than getting
* confused with previous values set by a previous parse method.
* The calculation of the query part of a URI is basically the
* end of the URI.
*/
private void query() {
query.len = count - off;
query.off = off;
}
/**
* This is an expression that is defined by RFC 2396 it is used
* in the definition of a segment expression. This is basically
* a list of pchars.
*
* This method has to ensure that no escaped chars go unchecked.
* This ensures that the read offset does not goe out of bounds
* and consequently throw an out of bounds exception.
*/
private void param() {
name();
if(skip("=")){ /* in case of error*/
value();
}
}
/**
* This extracts the name of the parameter from the character
* buffer. The name of a parameter is defined as a set of
* pchars including escape sequences. This will extract the
* parameter name and buffer the chars. The name ends when a
* equals character, "=", is encountered or in the case of a
* malformed parameter when the next character is not a pchar.
*/
private void name(){
int mark = off;
int pos = off;
while(off < count){
if(buf[off]=='%'){ /* escaped */
escape();
}else if(buf[off]=='=') {
break;
}else if(!pchar(buf[off])){
break;
}
buf[pos++] = buf[off++];
}
name.len = pos - mark;
name.off = mark;
}
/**
* This extracts a parameter value from a path segment. The
* parameter value consists of a sequence of pchars and some
* escape sequences. The parameter value is buffered so that
* the name and values can be paired. The end of the value
* is determined as the end of the buffer or the last pchar.
*/
private void value(){
int mark = off;
int pos = off;
while(off < count){
if(buf[off]=='%'){ /* escaped */
escape();
}else if(!pchar(buf[off])) {
break;
}
buf[pos++] = buf[off++];
}
value.len = pos - mark;
value.off = mark;
}
/**
* This method adds the name and value to a map so that the next
* name and value can be collected. The name and value are added
* to the map as string objects. Once added to the map the
* Token
objects are set to have zero length so they
* can be reused to collect further values. This will add the
* values to the map as an array of type string. This is done so
* that if there are multiple values that they can be stored.
*/
private void insert(){
if(value.length() > 0){
if(name.length() > 0)
insert(name,value);
}
name.clear();
value.clear();
}
/**
* This will add the given name and value to the parameters map.
* This will only store a single value per parameter name, so
* only the parameter that was latest encountered will be saved.
* The getQuery
method can be used to collect
* the parameter values using the parameter name.
*
* @param name this is the name of the value to be inserted
* @param value this is the value of a that is to be inserted
*/
private void insert(Token name, Token value){
insert(name.toString(), value.toString());
}
/**
* This will add the given name and value to the parameters map.
* This will only store a single value per parameter name, so
* only the parameter that was latest encountered will be saved.
* The getQuery
method can be used to collect
* the parameter values using the parameter name.
*
* @param name this is the name of the value to be inserted
* @param value this is the value of a that is to be inserted
*/
private void insert(String name, String value) {
param.put(name, value);
}
/**
* This converts an encountered escaped sequence, that is all
* embedded hexidecimal characters into a native UCS character
* value. This does not take any characters from the stream it
* just prepares the buffer with the correct byte. The escaped
* sequence within the URI will be interpreded as UTF-8.
*
* This will leave the next character to read from the buffer
* as the character encoded from the URI. If there is a fully
* valid escaped sequence, that is "%" HEX HEX
.
* This decodes the escaped sequence using UTF-8 encoding, all
* encoded sequences should be in UCS-2 to fit in a Java char.
*/
private void escape() {
int peek = peek(off);
if(!unicode(peek)) {
binary(peek);
}
}
/**
* This method determines, using a peek character, whether the
* sequence of escaped characters within the URI is binary data.
* If the data within the escaped sequence is binary then this
* will ensure that the next character read from the URI is the
* binary octet. This is used strictly for backward compatible
* parsing of URI strings, binary data should never appear.
*
* @param peek this is the first escaped character from the URI
*
* @return currently this implementation always returns true
*/
private boolean binary(int peek) {
if(off + 2 < count) {
off += 2;
buf[off]= bits(peek);
}
return true;
}
/**
* This method determines, using a peek character, whether the
* sequence of escaped characters within the URI is in UTF-8. If
* a UTF-8 character can be successfully decoded from the URI it
* will be the next character read from the buffer. This can
* check for both UCS-2 and UCS-4 characters. However, because
* the Java char
can only hold UCS-2, the UCS-4
* characters will have only the low order octets stored.
*
* The WWW Consortium provides a reference implementation of a
* UTF-8 decoding for Java, in this the low order octets in the
* UCS-4 sequence are used for the character. So, in the
* absence of a defined behaviour, the W3C behaviour is assumed.
*
* @param peek this is the first escaped character from the URI
*
* @return this returns true if a UTF-8 character is decoded
*/
private boolean unicode(int peek) {
if((peek & 0x80) == 0x00){
return unicode(peek, 0);
}
if((peek & 0xe0) == 0xc0){
return unicode(peek & 0x1f, 1);
}
if((peek & 0xf0) == 0xe0){
return unicode(peek & 0x0f, 2);
}
if((peek & 0xf8) == 0xf0){
return unicode(peek & 0x07, 3);
}
if((peek & 0xfc) == 0xf8){
return unicode(peek & 0x03, 4);
}
if((peek & 0xfe) == 0xfc){
return unicode(peek & 0x01, 5);
}
return false;
}
/**
* This method will decode the specified amount of escaped
* characters from the URI and convert them into a single Java
* UCS-2 character. If there are not enough characters within
* the URI then this will return false and leave the URI alone.
*
* The number of characters left is determined from the first
* UTF-8 octet, as specified in RFC 2279, and because this is
* a URI there must that number of "%" HEX HEX
* sequences left. If successful the next character read is
* the UTF-8 sequence decoded into a native UCS-2 character.
*
* @param peek contains the bits read from the first UTF octet
* @param more this specifies the number of UTF octets left
*
* @return this returns true if a UTF-8 character is decoded
*/
private boolean unicode(int peek, int more) {
if(off + more * 3 >= count) {
return false;
}
return unicode(peek,more,off);
}
/**
* This will decode the specified amount of trailing UTF-8 bits
* from the URI. The trailing bits are those following the first
* UTF-8 octet, which specifies the length, in octets, of the
* sequence. The trailing octets are if the form 10xxxxxx, for
* each of these octets only the last six bits are valid UCS
* bits. So a conversion is basically an accumulation of these.
*
* If at any point during the accumulation of the UTF-8 bits
* there is a parsing error, then parsing is aborted an false
* is returned, as a result the URI is left unchanged.
*
* @param peek bytes that have been accumulated from the URI
* @param more this specifies the number of UTF octets left
* @param pos this specifies the position the parsing begins
*
* @return this returns true if a UTF-8 character is decoded
*/
private boolean unicode(int peek, int more, int pos) {
while(more-- > 0) {
if(buf[pos] == '%'){
int next = pos + 3;
int hex = peek(next);
if((hex & 0xc0) == 0x80){
peek = (peek<<6)|(hex&0x3f);
pos = next;
continue;
}
}
return false;
}
if(pos + 2 < count) {
off = pos + 2;
buf[off]= bits(peek);
}
return true;
}
/**
* Defines behaviour for UCS-2 versus UCS-4 conversion from four
* octets. The UTF-8 encoding scheme enables UCS-4 characters to
* be encoded and decodeded. However, Java supports the 16-bit
* UCS-2 character set, and so the 32-bit UCS-4 character set is
* not compatable. This basically decides what to do with UCS-4.
*
* @param data up to four octets to be converted to UCS-2 format
*
* @return this returns a native UCS-2 character from the int
*/
private char bits(int data) {
return (char)data;
}
/**
* This will return the escape expression specified from the URI
* as an integer value of the hexidecimal sequence. This does
* not make any changes to the buffer it simply checks to see if
* the characters at the position specified are an escaped set
* characters of the form "%" HEX HEX
, if so, then
* it will convert that hexidecimal string in to an integer
* value, or -1 if the expression is not hexidecimal.
*
* @param pos this is the position the expression starts from
*
* @return the integer value of the hexidecimal expression
*/
private int peek(int pos) {
if(buf[pos] == '%'){
if(count <= pos + 2) {
return -1;
}
char high = buf[pos + 1];
char low = buf[pos + 2];
return convert(high, low);
}
return -1;
}
/**
* This will convert the two hexidecimal characters to a real
* integer value, which is returned. This requires characters
* within the range of 'A' to 'F' and 'a' to 'f', and also
* the digits '0' to '9'. The characters encoded using the
* ISO-8859-1 encoding scheme, if the characters are not with
* in the range specified then this returns -1.
*
* @param high this is the high four bits within the integer
* @param low this is the low four bits within the integer
*
* @return this returns the indeger value of the conversion
*/
private int convert(char high, char low) {
int hex = 0x00;
if(hex(high) && hex(low)){
if('A' <= high && high <= 'F'){
high -= 'A' - 'a';
}
if(high >= 'a') {
hex ^= (high-'a')+10;
} else {
hex ^= high -'0';
}
hex <<= 4;
if('A' <= low && low <= 'F') {
low -= 'A' - 'a';
}
if(low >= 'a') {
hex ^= (low-'a')+10;
} else {
hex ^= low-'0';
}
return hex;
}
return -1;
}
/**
* This is used to determine wheather a char is a hexidecimal
* char
or not. A hexidecimal character is consdered
* to be a character within the range of 0 - 9
and
* between a - f
and A - F
. This will
* return true
if the character is in this range.
*
* @param ch this is the character which is to be determined here
*
* @return true if the character given has a hexidecimal value
*/
private boolean hex(char ch) {
if(ch >= '0' && ch <= '9') {
return true;
} else if(ch >='a' && ch <= 'f') {
return true;
} else if(ch >= 'A' && ch <= 'F') {
return true;
}
return false;
}
/**
* This is a character set defined by RFC 2396 it is used to
* determine the valididity of certain chars
* within a Uniform Resource Identifier. RFC 2396 defines
* an unreserved char as alphanum | mark
.
*
* @param c the character value that is being checked
*
* @return true if the character has an unreserved value
*/
private boolean unreserved(char c){
return mark(c) || alphanum(c);
}
/**
* This is used to determine wheather or not a given unicode
* character is an alphabetic character or a digit character.
* That is withing the range 0 - 9
and between
* a - z
it uses iso-8859-1
to
* compare the character.
*
* @param c the character value that is being checked
*
* @return true if the character has an alphanumeric value
*/
private boolean alphanum(char c){
return digit(c) || alpha(c);
}
/**
* This is used to determine wheather or not a given unicode
* character is an alphabetic character. This uses encoding
* iso-8859-1
to compare the characters.
*
* @param c the character value that is being checked
*
* @return true if the character has an alphabetic value
*/
private boolean alpha(char c){
return (c <= 'z' && 'a' <= c) ||
(c <= 'Z' && 'A' <= c);
}
/**
* This is a character set defined by RFC 2396 it checks
* the valididity of cetain chars within a uniform resource
* identifier. The RFC 2396 defines a mark char as "-",
* "_", ".", "!", "~", "*", "'", "(", ")"
.
*
* @param c the character value that is being checked
*
* @return true if the character is a mark character
*/
private boolean mark(char c){
switch(c){
case '-': case '_': case '.':
case '!': case '~': case '*':
case '\'': case '(': case ')':
return true;
default:
return false;
}
}
/**
* This is a character set defined by RFC 2396 it is used to check
* the valididity of cetain chars within a generic uniform resource
* identifier. The RFC 2396 defines a pchar char as unreserved or
* escaped or one of the following characters ":", "@", "=",
* "&", "+", "$", ","
this will not check to see if the
* char is an escaped char, that is % HEX HEX
. Because
* this takes 3 chars.
*
* @param c the character value that is being checked
*
* @return true if the character is a pchar character
*/
private boolean pchar(char c){
switch(c){
case '@': case '&': case '=':
case '+': case '$': case ',':
case ':':
return true;
default:
return unreserved(c);
}
}
/**
* This is a character set defined by RFC 2396, it checks the
* valididity of certain chars in a uniform resource identifier.
* The RFC 2396 defines a reserved char as ";", "/", "?",
* ":", "@", "&", "=", "+", "$", ","
.
*
* @param c the character value that is being checked
*
* @return true if the character is a reserved character
*/
private boolean reserved(char c){
switch(c){
case ';': case '/': case '?':
case '@': case '&': case ':':
case '=': case '+': case '$':
case ',':
return true;
default:
return false;
}
}
/**
* This is used to convert this URI object into a String
* object. This will only convert the parts of the URI that exist, so
* the URI may not contain the domain or the query part and it will
* not contain the path parameters. If the URI contains all these
* parts then it will return somthing like
*
* scheme://host:port/path/path?querypart
*
*
* It can return /path/path?querypart
style relative
* URI's. If any of the parts are set to null then that part will be
* missing, for example if setDomain
method is invoked
* with a null parameter then the domain and port will be missing
* from the resulting URI. If the path part is set to null using the
* setPath
then the path will be /
. An
* example URI with the path part of null would be
*
* scheme://host:port/?querypart
*
*
* @return the URI with only the path part and the non-null optional
* parts of the uniform resource identifier
*/
public String toString() {
return (scheme.length() > 0 ? scheme +"://": "") +
(domain.length() > 0 ? domain +
(port > 0 ? ":"+port : "") : "")+ getPath() +
(param.size() > 0 ? param : "")+
(query.length()>0?"?"+query :"");
}
/**
* The ParameterMap
is uses to store the parameters
* that are to be encoded in to the address. This will append all
* of the parameters to the end of the path. These can later be
* extracted by parsing the address.
*
* @author Niall Gallagher
*/
private class ParameterMap extends KeyMap {
/**
* This will return the parameters encoded in such a way that
* it can be appended to the end of the path. These parameters
* can be added to the address such that they do not form a
* query parameter. Values such as session identifiers are
* often added as the path parameters to the address.
*
* @return this returns the representation of the parameters
*/
private String encode() {
StringBuilder text = new StringBuilder();
for(String name : param) {
String value = param.get(name);
text.append(";");
text.append(name);
if(value != null) {
text.append("=");
text.append(value);;
}
}
return text.toString();
}
/**
* This will return the parameters encoded in such a way that
* it can be appended to the end of the path. These parameters
* can be added to the address such that they do not form a
* query parameter. Values such as session identifiers are
* often added as the path parameters to the address.
*
* @return this returns the representation of the parameters
*/
public String toString() {
return encode();
}
}
/**
* This is used as an alternative to the ParseBuffer
* for extracting tokens from the URI without allocating memory.
* This will basically mark out regions within the buffer which are
* used to represent the token. When the token value is required
* the region is used to create a String
object.
*/
private class Token {
/**
* This can be used to override the value for this token.
*/
public String value;
/**
* This represents the start offset within the buffer.
*/
public int off;
/**
* This represents the number of charters in the token.
*/
public int len;
/**
* If the Token
is to be reused this will clear
* all previous data. Clearing the buffer allows it to be
* reused if there is a new URI to be parsed. This ensures
* that a null is returned if the token length is zero.
*/
public void clear() {
value = null;
len = 0;
}
/**
* This is used to determine the number of characters this
* token contains. This is used rather than accessing the
* length directly so that the value the token represents
* can be overridden easily without upsetting the token.
*
* @return this returns the number of characters this uses
*/
public int length() {
if(value == null){
return len;
}
return value.length();
}
/**
* This method will convert the Token
into it's
* String
equivelant. This will firstly check
* to see if there is a value, for the string representation,
* if there is the value is returned, otherwise the region
* is converted into a String
and returned.
*
* @return this returns a value representing the token
*/
public String toString() {
if(value != null) {
return value;
}
if(len > 0) {
value = new String(buf,off,len);
}
return value;
}
}
}