/*
* Copyright (c) 2011 Lockheed Martin Corporation
*
* 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.
*/
/*
* The following file is taken from the Apache Tomahawk Sandbox and incorporated here so that Eureka Streams would not
* need to depend on an entire library for one method or rely on sandbox project. (The project pom.xml states "there
* is no guarantee of API stability or continued maintenance for any code that is in the sandbox".) Modifications
* were made to comply with the Eureka Stream checkstyle rules. The source was retrieved on 1/25/2011 from the folder
* http://svn.apache.org/repos/asf/myfaces/tomahawk/trunk/sandbox/core/src/main/java/org/apache/myfaces/custom/util
* The original copyright notice appears below.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.myfaces.custom.util;
/**
* This utility class provides an equivalent to the JavaScript decodeURIComponent(encodedURI) function.
*
* @author Gerald M\u00FCllan Date: 11.02.2007 Time: 23:30:08
*/
public final class URIComponentUtils
{
/** For UTF-8 decoding. */
private static final int MASK_MATCH_F8 = 0xf8;
/** For UTF-8 decoding. */
private static final int MASK_MATCH_F0 = 0xf0;
/** For UTF-8 decoding. */
private static final int MASK_MATCH_E0 = 0xe0;
/** For UTF-8 decoding. */
private static final int MASK_MATCH_C0 = 0xc0;
/** For UTF-8 decoding. */
private static final int MASK_MATCH_80 = 0x80;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_3F = 0x3f;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_0F = 0x0f;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_FC = 0xfc;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_F8 = 0xf8;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_F0 = 0xf0;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_1F = 0x1f;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_80 = 0x80;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_E0 = 0xe0;
/** For UTF-8 decoding. */
private static final int UTF8_MASK_C0 = 0xc0;
/** For converting hexadecimal digits. */
private static final int HEX_DIGIT_MASK = 0xF;
/** For converting hexadecimal digits. */
private static final char HEX_LETTER_OFFSET = 10;
/** Hide constructor to prevent instantiation. */
private URIComponentUtils()
{
}
/**
* In case of incoming Strings - encoded with js encodeURIComponent() e.g. %C3%B6 - it is not sufficient to set
* CharacterEncoding on the ResponseWriter accordingly. Passing the uri to decodeURIComponent(String encodedURI)
* decodes e.g. %C3%B6 back to o-umlaut.
*
* @return decoded uri String
* @param encodedURI
* uri String
*/
public static String decodeURIComponent(final String encodedURI)
{
char actualChar;
StringBuffer buffer = new StringBuffer();
int bytePattern, sumb = 0;
for (int i = 0, more = -1; i < encodedURI.length(); i++)
{
actualChar = encodedURI.charAt(i);
switch (actualChar)
{
case '%':
actualChar = encodedURI.charAt(++i);
int hb = (Character.isDigit(actualChar) ? actualChar - '0' : Character.toLowerCase(actualChar) - 'a'
+ HEX_LETTER_OFFSET)
& HEX_DIGIT_MASK;
actualChar = encodedURI.charAt(++i);
int lb = (Character.isDigit(actualChar) ? actualChar - '0' : Character.toLowerCase(actualChar) - 'a'
+ HEX_LETTER_OFFSET)
& HEX_DIGIT_MASK;
bytePattern = (hb << 4) | lb;
break;
case '+':
bytePattern = ' ';
break;
default:
bytePattern = actualChar;
}
// Decode byte bytePattern as UTF-8, sumb collects incomplete chars
if ((bytePattern & UTF8_MASK_C0) == MASK_MATCH_80)
{ // 10xxxxxx
sumb = (sumb << 6) | (bytePattern & UTF8_MASK_3F);
if (--more == 0)
{
buffer.append((char) sumb);
}
}
else if ((bytePattern & UTF8_MASK_80) == 0x00)
{ // 0xxxxxxx
buffer.append((char) bytePattern);
}
else if ((bytePattern & UTF8_MASK_E0) == MASK_MATCH_C0)
{ // 110xxxxx
sumb = bytePattern & UTF8_MASK_1F;
more = 1;
}
else if ((bytePattern & UTF8_MASK_F0) == MASK_MATCH_E0)
{ // 1110xxxx
sumb = bytePattern & UTF8_MASK_0F;
more = 2;
}
else if ((bytePattern & UTF8_MASK_F8) == MASK_MATCH_F0)
{ // 11110xxx
sumb = bytePattern & 0x07;
more = 3;
}
else if ((bytePattern & UTF8_MASK_FC) == MASK_MATCH_F8)
{ // 111110xx
sumb = bytePattern & 0x03;
more = 4;
}
else
{ // 1111110x
sumb = bytePattern & 0x01;
more = 5;
}
}
return buffer.toString();
}
}