Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
   *
   * Copyright (c) 2010-2011 Oracle and/or its affiliates. All rights reserved.
   *
   * The contents of this file are subject to the terms of either the GNU
   * General Public License Version 2 only ("GPL") or the Common Development
   * and Distribution License("CDDL") (collectively, the "License").  You
   * may not use this file except in compliance with the License.  You can
  * obtain a copy of the License at
  * http://glassfish.java.net/public/CDDL+GPL_1_1.html
  * or packager/legal/LICENSE.txt.  See the License for the specific
  * language governing permissions and limitations under the License.
  *
  * When distributing the software, include this License Header Notice in each
  * file and include the License file at packager/legal/LICENSE.txt.
  *
  * GPL Classpath Exception:
  * Oracle designates this particular file as subject to the "Classpath"
  * exception as provided by Oracle in the GPL Version 2 section of the License
  * file that accompanied this code.
  *
  * Modifications:
  * If applicable, add the following below the License Header, with the fields
  * enclosed by brackets [] replaced by your own identifying information:
  * "Portions Copyright [year] [name of copyright owner]"
  *
  * Contributor(s):
  * If you wish your version of this file to be governed by only the CDDL or
  * only the GPL Version 2, indicate your decision by adding "[Contributor]
  * elects to include this software in this distribution under the [CDDL or GPL
  * Version 2] license."  If you don't indicate a single choice of license, a
  * recipient has the option to distribute your version of this file under
  * either the CDDL, the GPL Version 2 or to extend the choice of license to
  * its licensees as provided above.  However, if you add GPL Version 2 code
  * and therefore, elected the GPL Version 2 license, then the option applies
  * only if the new code is made subject to such option by the copyright
  * holder.
  */
 package com.sun.jersey.api.uri;
 
 
 import java.net.URI;
 import java.util.List;

Utility class for validating, encoding and decoding components of a URI.

Author(s):
Paul.Sandoz@Sun.Com
 
 public class UriComponent {
 
     // TODO rewrite to use masks and not lookup tables
     
The URI component type.
 
     public enum Type {

        
ALPHA / DIGIT / "-" / "." / "_" / "~" characters
 
         UNRESERVED,
        
The URI scheme component type.
 
         SCHEME,
        
The URI authority component type.
 
         AUTHORITY,
        
The URI user info component type.
 
         USER_INFO,
        
The URI host component type.
 
         HOST,
        
The URI port component type.
 
         PORT,
        
The URI path component type.
 
         PATH,
        
The URI path component type that is a path segment.
        PATH_SEGMENT,
        
The URI path component type that is a matrix parameter.
        MATRIX_PARAM,
        
The URI query component type.
        QUERY,
        
The URI query component type that is a query parameter.
        QUERY_PARAM,
        
The URI fragment component type.
        FRAGMENT,
    }
    private UriComponent() {
    }

    
Validates the legal characters of a percent-encoded string that represents a URI component type.

Parameters:
s the encoded string.
t the URI component type identifying the legal characters.
Throws:
java.lang.IllegalArgumentException if the encoded string contains illegal characters.
    public static void validate(String sType t) {
        validate(stfalse);
    }

    
Validates the legal characters of a percent-encoded string that represents a URI component type.

Parameters:
s the encoded string.
t the URI component type identifying the legal characters.
template true if the encoded string contains URI template variables
Throws:
java.lang.IllegalArgumentException if the encoded string contains illegal characters.
    public static void validate(String sType tboolean template) {
        int i = _valid(sttemplate);
        if (i > -1) // TODO localize
        {
            throw new IllegalArgumentException("The string '" + s +
                    "' for the URI component " + t +
                    " contains an invalid character, '" + s.charAt(i) + "', at index " + i);
        }
    }

    
Validates the legal characters of a percent-encoded string that represents a URI component type.

Parameters:
s the encoded string.
t the URI component type identifying the legal characters.
Returns:
true if the encoded string is valid, otherwise false.
    public static boolean valid(String sType t) {
        return valid(stfalse);
    }

    
Validates the legal characters of a percent-encoded string that represents a URI component type.

Parameters:
s the encoded string.
t the URI component type identifying the legal characters.
template true if the encoded string contains URI template variables
Returns:
true if the encoded string is valid, otherwise false.
    public static boolean valid(String sType tboolean template) {
        return _valid(sttemplate) == -1;
    }
    private static int _valid(String sType tboolean template) {
        boolean[] table = [t.ordinal()];
        for (int i = 0; i < s.length(); i++) {
            final char c = s.charAt(i);
            if ((c < 0x80 && c != '%' && !table[c]) || c >= 0x80) {
                if (!template || (c != '{' && c != '}')) {
                    return i;
                }
            }
        }
        return -1;
    }

    
Contextually encodes the characters of string that are either non-ASCII characters or are ASCII characters that must be percent-encoded using the UTF-8 encoding. Percent-encoded characters will be recognized and not double encoded.

Parameters:
s the string to be encoded.
t the URI component type identifying the ASCII characters that must be percent-encoded.
Returns:
the encoded string.
    public static String contextualEncode(String sType t) {
        return _encode(stfalsetrue);
    }

    
Contextually encodes the characters of string that are either non-ASCII characters or are ASCII characters that must be percent-encoded using the UTF-8 encoding. Percent-encoded characters will be recognized and not double encoded.

Parameters:
s the string to be encoded.
t the URI component type identifying the ASCII characters that must be percent-encoded.
template true if the encoded string contains URI template variables
Returns:
the encoded string.
    public static String contextualEncode(String sType tboolean template) {
        return _encode(sttemplatetrue);
    }

    
Encodes the characters of string that are either non-ASCII characters or are ASCII characters that must be percent-encoded using the UTF-8 encoding.

Parameters:
s the string to be encoded.
t the URI component type identifying the ASCII characters that must be percent-encoded.
Returns:
the encoded string.
    public static String encode(String sType t) {
        return _encode(stfalsefalse);
    }

    
Encodes the characters of string that are either non-ASCII characters or are ASCII characters that must be percent-encoded using the UTF-8 encoding.

Parameters:
s the string to be encoded.
t the URI component type identifying the ASCII characters that must be percent-encoded.
template true if the encoded string contains URI template variables
Returns:
the encoded string.
    public static String encode(String sType tboolean template) {
        return _encode(sttemplatefalse);
    }

    
Encodes a string with template parameters names present, specifically the characters '{' and '}' will be percent-encoded.

Parameters:
s the string with zero or more template parameters names
Returns:
the string with encoded template parameters names.
    public static String encodeTemplateNames(String s) {
        int i = s.indexOf('{');
        if (i != -1)
            s = s.replace("{""%7B");
        i = s.indexOf('}');
        if (i != -1)
            s = s.replace("}""%7D");
        return s;
    }
    private static String _encode(String sType tboolean templateboolean contextualEncode) {
        final boolean[] table = [t.ordinal()];
        StringBuilder sb = null;
        for (int i = 0; i < s.length(); i++) {
            final char c = s.charAt(i);
            if (c < 0x80 && table[c]) {
                if (sb != nullsb.append(c);
            } else {
                if (template && (c == '{' || c == '}')) {
                    if (sb != nullsb.append(c);
                    continue;
                } else if (contextualEncode) {
                    if (c == '%' && i + 2 < s.length()) {
                        if (isHexCharacter(s.charAt(i + 1)) &&
                                isHexCharacter(s.charAt(i + 2))) {
                            if (sb != null)
                                sb.append('%').append(s.charAt(i + 1)).append(s.charAt(i + 2));
                            i += 2;
                            continue;
                        }
                    }
                }
                if (sb == null) {
                    sb = new StringBuilder();
                    sb.append(s.substring(0, i));
                }
                if (c < 0x80) {
                    if (c == ' ' && (t == .)) {
                        sb.append('+');
                    } else {
                        appendPercentEncodedOctet(sbc);
                    }
                } else {
                    appendUTF8EncodedCharacter(sbc);
                }
            }
        }
        return (sb == null) ? s : sb.toString();
    }
    private final static char[] HEX_DIGITS = {
        '0''1''2''3''4''5''6''7',
        '8''9''A''B''C''D''E''F'
    };
    private static void appendPercentEncodedOctet(StringBuilder sbint b) {
        sb.append('%');
        sb.append([b >> 4]);
        sb.append([b & 0x0F]);
    }
    private static void appendUTF8EncodedCharacter(StringBuilder sbchar c) {
        final ByteBuffer bb = .encode("" + c);
        while (bb.hasRemaining()) {
            appendPercentEncodedOctet(sbbb.get() & 0xFF);
        }
    }
    private static final String[] SCHEME = {"0-9""A-Z""a-z""+""-""."};
    private static final String[] UNRESERVED = {"0-9""A-Z""a-z""-"".""_""~"};
    private static final String[] SUB_DELIMS = {"!""$""&""'""("")""*""+"","";""="};
    private static final boolean[][] ENCODING_TABLES = initEncodingTables();
    private static boolean[][] initEncodingTables() {
        boolean[][] tables = new boolean[Type.values().length][];
        List<Stringl = new ArrayList<String>();
        l.addAll(Arrays.asList());
        tables[..ordinal()] = initEncodingTable(l);
        l.clear();
        l.addAll(Arrays.asList());
        tables[..ordinal()] = initEncodingTable(l);
        l.addAll(Arrays.asList());
        tables[..ordinal()] = initEncodingTable(l);
        tables[..ordinal()] = initEncodingTable(Arrays.asList("0-9"));
        l.add(":");
        tables[..ordinal()] = initEncodingTable(l);
        l.add("@");
        tables[..ordinal()] = initEncodingTable(l);
        tables[..ordinal()] = initEncodingTable(l);
        tables[..ordinal()][';'] = false;
        tables[..ordinal()] = tables[..ordinal()].clone();
        tables[..ordinal()]['='] = false;
        l.add("/");
        tables[..ordinal()] = initEncodingTable(l);
        l.add("?");
        tables[..ordinal()] = initEncodingTable(l);
        tables[..ordinal()] = tables[..ordinal()];
        tables[..ordinal()] = initEncodingTable(l);
        tables[..ordinal()]['='] = false;
        tables[..ordinal()]['+'] = false;
        tables[..ordinal()]['&'] = false;
        return tables;
    }
    private static boolean[] initEncodingTable(List<Stringallowed) {
        boolean[] table = new boolean[0x80];
        for (String range : allowed) {
            if (range.length() == 1) {
                table[range.charAt(0)] = true;
            } else if (range.length() == 3 && range.charAt(1) == '-') {
                for (int i = range.charAt(0); i <= range.charAt(2); i++) {
                    table[i] = true;
                }
            }
        }
        return table;
    }
    private static final Charset UTF_8_CHARSET = Charset.forName("UTF-8");

    
Decodes characters of a string that are percent-encoded octets using UTF-8 decoding (if needed).

It is assumed that the string is valid according to an (unspecified) URI component type. If a sequence of contiguous percent-encoded octets is not a valid UTF-8 character then the octets are replaced with '\uFFFD'.

If the URI component is of type HOST then any "%" found between "[]" is left alone. It is an IPv6 literal with a scope_id.

If the URI component is of type QUERY_PARAM then any "+" is decoded as as ' '.

Parameters:
s the string to be decoded.
t the URI component type, may be null.
Returns:
the decoded string.
Throws:
java.lang.IllegalArgumentException if a malformed percent-encoded octet is detected
    public static String decode(String sType t) {
        if (s == null) {
            throw new IllegalArgumentException();
        }
        final int n = s.length();
        if (n == 0) {
            return s;
        }
        // If there are no percent-escaped octets
        if (s.indexOf('%') < 0) {
            // If there are no '+' characters for query param
            if (t == .) {
                if (s.indexOf('+') < 0) {
                    return s;
                }
            } else {
                return s;
            }
        } else {
            // Malformed percent-escaped octet at the end
            if (n < 2) // TODO localize
            {
                throw new IllegalArgumentException("Malformed percent-encoded octet at index 1");
            }
            // Malformed percent-escaped octet at the end
            if (s.charAt(n - 2) == '%'// TODO localize
            {
                throw new IllegalArgumentException("Malformed percent-encoded octet at index " + (n - 2));
            }
        }
        if (t == null)
            return decode(sn);
        
        switch (t) {
            case  :
                return decodeHost(sn);
            case  :
                return decodeQueryParam(sn);
            default :
                return decode(sn);
        }
    }

    
Decode the query component of a URI.

Parameters:
u the URI.
decode true if the query parameters of the query component should be in decoded form.
Returns:
the multivalued map of query parameters.
    public static MultivaluedMap<StringStringdecodeQuery(URI uboolean decode) {
        return decodeQuery(u.getRawQuery(), decode);
    }

    
Decode the query component of a URI.

Parameters:
q the query component in encoded form.
decode true of the query parameters of the query component should be in decoded form.
Returns:
the multivalued map of query parameters.
    public static MultivaluedMap<StringStringdecodeQuery(String qboolean decode) {
        MultivaluedMap<StringStringqueryParameters = new MultivaluedMapImpl();
        if (q == null || q.length() == 0) {
            return queryParameters;
        }
        int s = 0, e = 0;
        do {
            e = q.indexOf('&'s);
            if (e == -1) {
                decodeQueryParam(queryParametersq.substring(s), decode);
            } else if (e > s) {
                decodeQueryParam(queryParametersq.substring(se), decode);
            }
            s = e + 1;
        } while (s > 0 && s < q.length());
        return queryParameters;
    }
    private static void decodeQueryParam(MultivaluedMap<StringStringparams,
            String paramboolean decode) {
        try {
            int equals = param.indexOf('=');
            if (equals > 0) {
                params.add(
                        URLDecoder.decode(param.substring(0, equals), "UTF-8"),
                        (decode) ? URLDecoder.decode(param.substring(equals + 1), "UTF-8") : param.substring(equals + 1));
            } else if (equals == 0) {
                // no key declared, ignore
            } else if (param.length() > 0) {
                params.add(
                        URLDecoder.decode(param"UTF-8"),
                        "");
            }
        } catch (UnsupportedEncodingException ex) {
            // This should never occur
            throw new IllegalArgumentException(ex);
        }
    }
    private static final class PathSegmentImpl implements PathSegment {
        private static final PathSegment EMPTY_PATH_SEGMENT = new PathSegmentImpl(""false);
        private final String path;
        private final MultivaluedMap<StringStringmatrixParameters;
        PathSegmentImpl(String pathboolean decode) {
            this(pathdecodenew MultivaluedMapImpl());
        }
        PathSegmentImpl(String pathboolean decodeMultivaluedMap<StringStringmatrixParameters) {
            this. = (decode) ? UriComponent.decode(path..) : path;
            this. = matrixParameters;
        }
        @Override
        public String getPath() {
            return ;
        }
        @Override
        public MultivaluedMap<StringStringgetMatrixParameters() {
            return ;
        }
    }

    
Decode the path component of a URI as path segments.

Parameters:
u the URI. If the path component is an absolute path component then the leading '/' is ignored and is not considered a delimiator of a path segment.
decode true if the path segments of the path component should be in decoded form.
Returns:
the list of path segments.
    public static List<PathSegmentdecodePath(URI uboolean decode) {
        String rawPath = u.getRawPath();
        if (rawPath != null && rawPath.length() > 0 && rawPath.charAt(0) == '/') {
            rawPath = rawPath.substring(1);
        }
        return decodePath(rawPathdecode);
    }

    
Decode the path component of a URI as path segments.

Any '/' character in the path is considered to be a deliminator between two path segments. Thus if the path is '/' then the path segment list will contain two empty path segments. If the path is "//" then the path segment list will contain three empty path segments. If the path is "/a/" the path segment list will consist of the following path segments in order: "", "a" and "".

Parameters:
path the path component in encoded form.
decode true if the path segments of the path component should be in decoded form.
Returns:
the list of path segments.
    public static List<PathSegmentdecodePath(String pathboolean decode) {
        List<PathSegmentsegments = new LinkedList<PathSegment>();
        if (path == null) {
            return segments;
        }
        int s = 0;
        int e = -1;
        do {
            s = e + 1;
            e = path.indexOf('/'s);
            if (e > s) {
                decodePathSegment(segmentspath.substring(se), decode);
            } else if (e == s) {
                segments.add(.);
            }
        } while (e != -1);
        if (s < path.length()) {
            decodePathSegment(segmentspath.substring(s), decode);
        } else {
            segments.add(.);
        }
        return segments;
    }
    public static void decodePathSegment(List<PathSegmentsegmentsString segmentboolean decode) {
        int colon = segment.indexOf(';');
        if (colon != -1) {
            segments.add(new PathSegmentImpl(
                    (colon == 0) ? "" : segment.substring(0, colon),
                    decode,
                    decodeMatrix(segmentdecode)));
        } else {
            segments.add(new PathSegmentImpl(
                    segment,
                    decode));
        }
    }

    
Decode the matrix component of a URI path segment.

Parameters:
pathSegment the path segment component in encoded form.
decode true if the matrix parameters of the path segment component should be in decoded form.
Returns:
the multivalued map of matrix parameters.
    public static MultivaluedMap<StringStringdecodeMatrix(String pathSegmentboolean decode) {
        MultivaluedMap<StringStringmatrixMap = new MultivaluedMapImpl();
        // Skip over path segment
        int s = pathSegment.indexOf(';') + 1;
        if (s == 0 || s == pathSegment.length()) {
            return matrixMap;
        }
        int e = 0;
        do {
            e = pathSegment.indexOf(';'s);
            if (e == -1) {
                decodeMatrixParam(matrixMappathSegment.substring(s), decode);
            } else if (e > s) {
                decodeMatrixParam(matrixMappathSegment.substring(se), decode);
            }
            s = e + 1;
        } while (s > 0 && s < pathSegment.length());
        return matrixMap;
    }
    private static void decodeMatrixParam(MultivaluedMap<StringStringparams,
            String paramboolean decode) {
        int equals = param.indexOf('=');
        if (equals > 0) {
            params.add(
                    UriComponent.decode(param.substring(0, equals), ..),
                    (decode) ? UriComponent.decode(param.substring(equals + 1), ..) : param.substring(equals + 1));
        } else if (equals == 0) {
            // no key declared, ignore
        } else if (param.length() > 0) {
            params.add(
                    UriComponent.decode(param..),
                    "");
        }
    }
    private static String decode(String sint n) {
        final StringBuilder sb = new StringBuilder(n);
        ByteBuffer bb = null;
        for (int i = 0; i < n;) {
            final char c = s.charAt(i++);
            if (c != '%') {
                sb.append(c);
            } else {
                bb = decodePercentEncodedOctets(sibb);
                i = decodeOctets(ibbsb);
            }
        }
        return sb.toString();
    }
    private static String decodeQueryParam(String sint n) {
        final StringBuilder sb = new StringBuilder(n);
        ByteBuffer bb = null;
        for (int i = 0; i < n;) {
            final char c = s.charAt(i++);
            if (c != '%') {
                if (c != '+')
                    sb.append(c);
                else
                    sb.append(' ');
            } else {
                bb = decodePercentEncodedOctets(sibb);
                i = decodeOctets(ibbsb);
            }
        }
        return sb.toString();
    }
    private static String decodeHost(String sint n) {
        final StringBuilder sb = new StringBuilder(n);
        ByteBuffer bb = null;
        boolean betweenBrackets = false;
        for (int i = 0; i < n;) {
            final char c = s.charAt(i++);
            if (c == '[') {
                betweenBrackets = true;
            } else if (betweenBrackets && c == ']') {
                betweenBrackets = false;
            }
            if (c != '%' || betweenBrackets) {
                sb.append(c);
            } else {
                bb = decodePercentEncodedOctets(sibb);
                i = decodeOctets(ibbsb);
            }
        }
        return sb.toString();
    }

    
Decode a contigious sequence of percent encoded octets.

Assumes the index, i, starts that the first hex digit of the first percent-encoded octet.

    private static ByteBuffer decodePercentEncodedOctets(String sint iByteBuffer bb) {
        if (bb == null)
            bb = ByteBuffer.allocate(1);
        else
            bb.clear();
        while (true) {
            // Decode the hex digits
            bb.put((byte) (decodeHex(si++) << 4 | decodeHex(si++)));
            // Finish if at the end of the string
            if (i == s.length()) {
                break;
            }
            // Finish if no more percent-encoded octets follow
            if (s.charAt(i++) != '%') {
                break;
            }
            // Check if the byte buffer needs to be increased in size
            if (bb.position() == bb.capacity()) {
                bb.flip();
                // Create a new byte buffer with the maximum number of possible
                // octets, hence resize should only occur once
                ByteBuffer bb_new = ByteBuffer.allocate(s.length() / 3);
                bb_new.put(bb);
                bb = bb_new;
            }
        }
        bb.flip();
        return bb;
    }

    
Decodes octets to characters using the UTF-8 decoding and appends the characters to a StringBuffer.

Returns:
the index to the next unchecked character in the string to decode
    private static int decodeOctets(int iByteBuffer bbStringBuilder sb) {
        // If there is only one octet and is an ASCII character
        if (bb.limit() == 1 && (bb.get(0) & 0xFF) < 0x80) {
            // Octet can be appended directly
            sb.append((charbb.get(0));
            return i + 2;
        } else {
            // 
            CharBuffer cb = .decode(bb);
            sb.append(cb.toString());
            return i + bb.limit() * 3 - 1;
        }
    }
    private static int decodeHex(String sint i) {
        final int v = decodeHex(s.charAt(i));
        if (v == -1) // TODO localize
        {
            throw new IllegalArgumentException("Malformed percent-encoded octet at index " + i +
                    ", invalid hexadecimal digit '" + s.charAt(i) + "'");
        }
        return v;
    }
    private static final int[] HEX_TABLE = initHexTable();
    private static int[] initHexTable() {
        int[] table = new int[0x80];
        Arrays.fill(table, -1);
        for (char c = '0'c <= '9'c++) {
            table[c] = c - '0';
        }
        for (char c = 'A'c <= 'F'c++) {
            table[c] = c - 'A' + 10;
        }
        for (char c = 'a'c <= 'f'c++) {
            table[c] = c - 'a' + 10;
        }
        return table;
    }
    private static int decodeHex(char c) {
        return (c < 128) ? [c] : -1;
    }
    private static boolean isHexCharacter(char c) {
        return c < 128 && [c] != -1;
    }
New to GrepCode? Check out our FAQ X