/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   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.bodington.util;

import java.io.UnsupportedEncodingException;

public class URLDecoder
{
    
    /**
     * Decodes a &quot;x-www-form-urlencoded&quot;
     * to a <tt>String</tt>.
     * @param s the <code>String</code> to decode
     * @return the newly decoded <code>String</code>
     */
    public static String decode( String s, String encoding )
    throws UnsupportedEncodingException
    {
	if ( "bodington_underscore".equalsIgnoreCase( encoding ) )
	    return decodeFromBodURL_2_1( s );
	if ( "bodington_escape".equalsIgnoreCase( encoding ) )
	    return decodeFromBodURL_2_0( s );
	return java.net.URLDecoder.decode( s, encoding );
    }
    
    
    private static String decodeFromBodURL_2_1( String url )
    {
	try
	{
	    // URLDecode then find four digit hexadecimal coded
	    // UTF16 characters escaped by 0x1b
	    String utf16escaped = URLDecoder.decode( url, "US-ASCII" );
	    
	    // if it doesn't have underscore at start then it's a normal
	    // url.
	    
	    if ( !utf16escaped.startsWith( "_" ) )
		return utf16escaped;
	    
	    StringBuffer name = new StringBuffer( utf16escaped.length() );
	    int c, i, j;
	    String hex;
	    
	    // Find four digit hexadecimal coded
	    // UTF16 characters escaped by underscore
	    for ( i=1; i< utf16escaped.length(); i++ )
	    {
		c=utf16escaped.charAt( i );
		
		if ( c == '_' )
		{
		    // followed immediately by another underscore this is an
		    // escaped underscore.
		    if ( (i+1) < utf16escaped.length() && utf16escaped.charAt( i+1 ) == '_' )
		    {
			name.append( "_" );
			i++;
		    }
		    // otherwise it is an escaped 16 bit character
		    else
		    {
			//find first invalid digit, up to four digits
			for ( j=0; j<4 && (i+j+1)<utf16escaped.length(); j++ )
			{
			    c = utf16escaped.charAt( i+j+1 );
			    if ( c<'0' || (c>'9' && c<'A') || (c>'F' && c<'a') || c>'f' )
				break;
			}
			
			hex = utf16escaped.substring( i+1, i+j+1 );
			// guaranteed to be a valid number now
			if ( hex.length()>0 )
			{
			    try
			    {
				c = Integer.parseInt( hex, 16 );
			    }
			    catch ( NumberFormatException nfex )
			    {
				c = '?';  //should never happen!
			    }
			    name.append( (char)c );
			}
			i+=j;
		    }
		}
		else
		    name.append( (char)c );
	    }
	    
	    return name.toString();
	}
	catch ( UnsupportedEncodingException e )
	{
	    // can never happen with US_ASCII?
	    return null;
	}
    }
    
    
    private static String decodeFromBodURL_2_0( String url )
    {
	try
	{
	    // URLDecode then find four digit hexadecimal coded
	    // UTF16 characters escaped by 0x1b
	    String utf16escaped = URLDecoder.decode( url, "US-ASCII" );
	    StringBuffer name = new StringBuffer( utf16escaped.length() );
	    int c, i, j;
	    String hex;
	    
	    for ( i=0; i< utf16escaped.length(); i++ )
	    {
		c=utf16escaped.charAt( i );
		
		if ( c == 0x1b )
		{
		    //find first invalid digit, up to four digits
		    for ( j=0; j<4 && (i+j+1)<utf16escaped.length(); j++ )
		    {
			c = utf16escaped.charAt( i+j+1 );
			if ( c<'0' || (c>'9' && c<'A') || (c>'F' && c<'a') || c>'f' )
			    break;
		    }
		    
		    hex = utf16escaped.substring( i+1, i+j+1 );
		    // guaranteed to be a valid number now
		    if ( hex.length()>0 )
		    {
			try
			{
			    c = Integer.parseInt( hex, 16 );
			}
			catch ( NumberFormatException nfex )
			{
			    c = '?';  //should never happen!
			}
			name.append( (char)c );
		    }
		    i+=j;
		}
		else
		    name.append( (char)c );
	    }
	    
	    return name.toString();
	}
	catch ( UnsupportedEncodingException e )
	{
	    // can never happen with US_ASCII?
	    return null;
	}
    }
    
}
