Re: Customising T5 URL Encoding

2012-03-12 Thread David Canteros
+1
I have several problems with the integration of my Tapestry tools and other
non-tapestry tools, all of this caused by tapestry URLEncoder. I solved
this by overriding URLEncoder, but your proposal would be a more elegant
solution


--
David Germán Canteros


2012/3/11 Paul Stanton p...@mapshed.com.au

 +1

 possibly with 3 options? :

 a) tomcat compatible default encoding
 b) jetty compatible default encoding
 c) tapestry encoding

 On 21/06/2010 6:34 AM, Joel Halbert wrote:

 Agreed, it would be good to have this as a configuration option.

 On 20/06/10 19:20, Kai Weber wrote:

 * Nicolas Bouillonnico...@bouil.org:

  The Tapestry URL encoding is not a problem for me in general, just for
 one
 use case when i wanted to migrate a site to tapestry and keeping the
 same
 URL (with accents, spaces, dashes, underscores and so on).

 It is a problem if you get called by other webapps. If they call your
 page with the standard URL encoding scheme you have a problem.  Say you
 have

 onActivate(String emailadress)

 no one can call your page because the expected encoding for @ in T5 is
 $0040 where the rest of the world would send you an @.

 We use only query strings or numerical IDs as parameters for pages which
 can get called from external sites.

 I would like to see configuration option to switch or disable the
 URLEncoder completly.

 Kai

 --**--**
 -
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org




 --**--**-
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org



 --**--**-
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org




Re: Customising T5 URL Encoding

2012-03-12 Thread Jochen Frey
+1
On Mar 12, 2012, at 8:57 AM, David Canteros wrote:

 +1
 I have several problems with the integration of my Tapestry tools and other
 non-tapestry tools, all of this caused by tapestry URLEncoder. I solved
 this by overriding URLEncoder, but your proposal would be a more elegant
 solution
 
 
 --
 David Germán Canteros
 
 
 2012/3/11 Paul Stanton p...@mapshed.com.au
 
 +1
 
 possibly with 3 options? :
 
 a) tomcat compatible default encoding
 b) jetty compatible default encoding
 c) tapestry encoding
 
 On 21/06/2010 6:34 AM, Joel Halbert wrote:
 
 Agreed, it would be good to have this as a configuration option.
 
 On 20/06/10 19:20, Kai Weber wrote:
 
 * Nicolas Bouillonnico...@bouil.org:
 
 The Tapestry URL encoding is not a problem for me in general, just for
 one
 use case when i wanted to migrate a site to tapestry and keeping the
 same
 URL (with accents, spaces, dashes, underscores and so on).
 
 It is a problem if you get called by other webapps. If they call your
 page with the standard URL encoding scheme you have a problem.  Say you
 have
 
 onActivate(String emailadress)
 
 no one can call your page because the expected encoding for @ in T5 is
 $0040 where the rest of the world would send you an @.
 
 We use only query strings or numerical IDs as parameters for pages which
 can get called from external sites.
 
 I would like to see configuration option to switch or disable the
 URLEncoder completly.
 
 Kai
 
 --**--**
 -
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org
 
 
 
 
 --**--**-
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org
 
 
 
 --**--**-
 To unsubscribe, e-mail: 
 users-unsubscribe@tapestry.**apache.orgusers-unsubscr...@tapestry.apache.org
 For additional commands, e-mail: users-h...@tapestry.apache.org
 
 

---
  joc...@jochenfrey.com
  +1.415.366.0450
  @jochen_frey



Re: Customising T5 URL Encoding

2012-03-10 Thread Paul Stanton

+1

possibly with 3 options? :

a) tomcat compatible default encoding
b) jetty compatible default encoding
c) tapestry encoding

On 21/06/2010 6:34 AM, Joel Halbert wrote:

Agreed, it would be good to have this as a configuration option.

On 20/06/10 19:20, Kai Weber wrote:

* Nicolas Bouillonnico...@bouil.org:

The Tapestry URL encoding is not a problem for me in general, just 
for one
use case when i wanted to migrate a site to tapestry and keeping the 
same

URL (with accents, spaces, dashes, underscores and so on).

It is a problem if you get called by other webapps. If they call your
page with the standard URL encoding scheme you have a problem.  Say you
have

onActivate(String emailadress)

no one can call your page because the expected encoding for @ in T5 is
$0040 where the rest of the world would send you an @.

We use only query strings or numerical IDs as parameters for pages which
can get called from external sites.

I would like to see configuration option to switch or disable the
URLEncoder completly.

Kai

-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org





-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org




-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org



Re: Customising T5 URL Encoding

2010-06-20 Thread Nicolas Bouillon
The Tapestry URL encoding is not a problem for me in general, just for one
use case when i wanted to migrate a site to tapestry and keeping the same
URL (with accents, spaces, dashes, underscores and so on).

On Fri, 18 Jun 2010 17:20:36 -0700, Howard Lewis Ship hls...@gmail.com
wrote:
 Tapestry does its own encoding because Jetty and Tomcat differ on
 whether you get the decoded or raw strings.  Creating another option,
 that would work the same across servlet containers, seemed to make
 sense at the time.
 
 On Fri, Jun 18, 2010 at 8:50 AM, Nicolas Bouillon nico...@bouil.org
 wrote:
 Hi,

 Here is how i've overrided this behavior, to allow URL with %20 or
 other
 chars as incomming request. It quite a copy/paste tweaking of the
 original
 UrlEncoderImpl from Tapestry 5.1.0.5.

 public class AppModule {
    .
    public static void contributeServiceOverride(
            MappedConfigurationClass, Object configuration) {
        configuration.add(URLEncoder.class, new MyURLEncoderImpl());
    }
    .
 }


 package org.bouil.tapestry.services;

 import java.io.UnsupportedEncodingException;
 import java.util.BitSet;

 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.services.URLEncoder;

 public class MyURLEncoderImpl implements URLEncoder {
    static final String ENCODED_NULL = $N;
    static final String ENCODED_BLANK = $B;

    /**
     * Bit set indicating which character are safe to pass through (when
     * encoding or decoding) as-is. All other characters are encoded as
a
 kind
     * of unicode escape.
     */
    private final BitSet safeForInput = new BitSet(128);
    private final BitSet safeForOutput = new BitSet(128);

    {

 markSafeForInput(aàâäbcçĉdeéèêëfgĝhĥiïîjĵklmnoôöpqrsŝtuùûüvwxyzæœ);

 markSafeForInput(AÀÂÄBCÇĈDEÉÈÊËFGĜHĤIÏÎĤJĴKLMNOÔÖPQRSŜTUÙÛÜVWXYZÆŒ);
        markSafeForInput(01234567890-_.:,');

        markSafeForOuput(abcdefghijklmnopqrstuvwxyz);
        markSafeForOuput(ABCDEFGHIJKLMNOPQRSTUVWXYZ);
        markSafeForOuput(01234567890-_.:,');
    }

    private void markSafeForInput(String s) {
        for (char ch : s.toCharArray()) {
            safeForInput.set(ch);
        }
    }

    private void markSafeForOuput(String s) {
        for (char ch : s.toCharArray()) {
            safeForOutput.set(ch);
        }
    }

    public String encode(String input) {
        if (input == null)
            return ENCODED_NULL;

        if (input.equals())
            return ENCODED_BLANK;

        boolean dirty = false;

        int length = input.length();

        StringBuilder output = new StringBuilder(length * 2);

        for (int i = 0; i  length; i++) {
            char ch = input.charAt(i);

            if (ch == '$') {
                output.append($$);
                dirty = true;
                continue;
            }

            int chAsInt = ch;

            if (safeForOutput.get(chAsInt)) {
                output.append(ch);
                continue;
            }

            try {
                return  java.net.URLEncoder.encode(new String(input),
 UTF-8);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException(e);
            }
            // output.append(String.format($%04x, chAsInt));
            // dirty = true;
        }

        return dirty ? output.toString() : input;
    }

    public String decode(String input) {
        Defense.notNull(input, input);

        if (input.equals(ENCODED_NULL))
            return null;

        if (input.equals(ENCODED_BLANK))
            return ;

        boolean dirty = false;

        int length = input.length();

        StringBuilder output = new StringBuilder(length * 2);

        for (int i = 0; i  length; i++) {
            char ch = input.charAt(i);

            if (ch == '$') {
                dirty = true;

                if (i + 1  length  input.charAt(i + 1) == '$') {
                    output.append('$');
                    i++;

                    dirty = true;
                    continue;
                }

                if (i + 4  length) {
                    String hex = input.substring(i + 1, i + 5);

                    try {
                        int unicode = Integer.parseInt(hex, 16);

                        output.append((char) unicode);
                        i += 4;
                        dirty = true;
                        continue;
                    } catch (NumberFormatException ex) {
                        // Ignore.
                    }
                }

                throw new IllegalArgumentException(
                        String
                                .format(
                                        Input string '%s' is not valid;
 the '$' character at position %d should be followed by another '$' or a
 four digit hex number (a unicode value).,
                                        input, i + 1));
            }

            if (!safeForInput.get(ch)) {
          

Re: Customising T5 URL Encoding

2010-06-20 Thread Joel Halbert
Makes sense. I must admit I was curious as to why T5 uses custom 
encoding, so thanks for the explanation.


I'll use Nicolas's solution to override this behaviour.

On 19/06/10 01:20, Howard Lewis Ship wrote:

Tapestry does its own encoding because Jetty and Tomcat differ on
whether you get the decoded or raw strings.  Creating another option,
that would work the same across servlet containers, seemed to make
sense at the time.

On Fri, Jun 18, 2010 at 8:50 AM, Nicolas Bouillonnico...@bouil.org  wrote:
   

Hi,

Here is how i've overrided this behavior, to allow URL with %20 or other
chars as incomming request. It quite a copy/paste tweaking of the original
UrlEncoderImpl from Tapestry 5.1.0.5.

public class AppModule {
.
public static void contributeServiceOverride(
MappedConfigurationClass, Object  configuration) {
configuration.add(URLEncoder.class, new MyURLEncoderImpl());
}
.
}


package org.bouil.tapestry.services;

import java.io.UnsupportedEncodingException;
import java.util.BitSet;

import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.services.URLEncoder;

public class MyURLEncoderImpl implements URLEncoder {
static final String ENCODED_NULL = $N;
static final String ENCODED_BLANK = $B;

/**
 * Bit set indicating which character are safe to pass through (when
 * encoding or decoding) as-is. All other characters are encoded as a
kind
 * of unicode escape.
 */
private final BitSet safeForInput = new BitSet(128);
private final BitSet safeForOutput = new BitSet(128);

{

markSafeForInput(aàâäbcçĉdeéèêëfgĝhĥiïîjĵklmnoôöpqrsŝtuùûüvwxyzæœ);

markSafeForInput(AÀÂÄBCÇĈDEÉÈÊËFGĜHĤIÏÎĤJĴKLMNOÔÖPQRSŜTUÙÛÜVWXYZÆŒ);
markSafeForInput(01234567890-_.:,');

markSafeForOuput(abcdefghijklmnopqrstuvwxyz);
markSafeForOuput(ABCDEFGHIJKLMNOPQRSTUVWXYZ);
markSafeForOuput(01234567890-_.:,');
}

private void markSafeForInput(String s) {
for (char ch : s.toCharArray()) {
safeForInput.set(ch);
}
}

private void markSafeForOuput(String s) {
for (char ch : s.toCharArray()) {
safeForOutput.set(ch);
}
}

public String encode(String input) {
if (input == null)
return ENCODED_NULL;

if (input.equals())
return ENCODED_BLANK;

boolean dirty = false;

int length = input.length();

StringBuilder output = new StringBuilder(length * 2);

for (int i = 0; i  length; i++) {
char ch = input.charAt(i);

if (ch == '$') {
output.append($$);
dirty = true;
continue;
}

int chAsInt = ch;

if (safeForOutput.get(chAsInt)) {
output.append(ch);
continue;
}

try {
return  java.net.URLEncoder.encode(new String(input),
UTF-8);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
// output.append(String.format($%04x, chAsInt));
// dirty = true;
}

return dirty ? output.toString() : input;
}

public String decode(String input) {
Defense.notNull(input, input);

if (input.equals(ENCODED_NULL))
return null;

if (input.equals(ENCODED_BLANK))
return ;

boolean dirty = false;

int length = input.length();

StringBuilder output = new StringBuilder(length * 2);

for (int i = 0; i  length; i++) {
char ch = input.charAt(i);

if (ch == '$') {
dirty = true;

if (i + 1  length  input.charAt(i + 1) == '$') {
output.append('$');
i++;

dirty = true;
continue;
}

if (i + 4  length) {
String hex = input.substring(i + 1, i + 5);

try {
int unicode = Integer.parseInt(hex, 16);

output.append((char) unicode);
i += 4;
dirty = true;
continue;
} catch (NumberFormatException ex) {
// Ignore.
}
}

throw new IllegalArgumentException(
String
.format(
Input string '%s' is not valid;
the '$' character at position %d should be followed by another '$' or a
four digit hex number (a unicode value).,
input, i + 1));
}

if (!safeForInput.get(ch)) {
throw new IllegalArgumentException(
String

Re: Customising T5 URL Encoding

2010-06-20 Thread Kai Weber
* Nicolas Bouillon nico...@bouil.org:

 The Tapestry URL encoding is not a problem for me in general, just for one
 use case when i wanted to migrate a site to tapestry and keeping the same
 URL (with accents, spaces, dashes, underscores and so on).

It is a problem if you get called by other webapps. If they call your
page with the standard URL encoding scheme you have a problem.  Say you
have

onActivate(String emailadress)

no one can call your page because the expected encoding for @ in T5 is
$0040 where the rest of the world would send you an @.

We use only query strings or numerical IDs as parameters for pages which
can get called from external sites.

I would like to see configuration option to switch or disable the
URLEncoder completly.

Kai

-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org



Re: Customising T5 URL Encoding

2010-06-20 Thread Joel Halbert

Agreed, it would be good to have this as a configuration option.

On 20/06/10 19:20, Kai Weber wrote:

* Nicolas Bouillonnico...@bouil.org:

   

The Tapestry URL encoding is not a problem for me in general, just for one
use case when i wanted to migrate a site to tapestry and keeping the same
URL (with accents, spaces, dashes, underscores and so on).
 

It is a problem if you get called by other webapps. If they call your
page with the standard URL encoding scheme you have a problem.  Say you
have

onActivate(String emailadress)

no one can call your page because the expected encoding for @ in T5 is
$0040 where the rest of the world would send you an @.

We use only query strings or numerical IDs as parameters for pages which
can get called from external sites.

I would like to see configuration option to switch or disable the
URLEncoder completly.

Kai

-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org


   



-
To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org
For additional commands, e-mail: users-h...@tapestry.apache.org



Re: Customising T5 URL Encoding

2010-06-18 Thread Christophe Cordenier
You can override every service of Tapestry, you have three ways to achieve
this :

1. Decoration

http://tapestry.apache.org/tapestry5.1/tapestry-ioc/decorator.html

2. Advice

http://tapestry.apache.org/tapestry5.1/tapestry-ioc/advice.html

3. Complete override

http://tapestry.apache.org/tapestry5.1/guide/alias.html

2010/6/18 Joel Halbert j...@su3analytics.com

 Tapestry appears to URL encode spaces as $0020

 e.g.
 http://localhost:8080/web/buy/sports$0020shoes


 I would much prefer to use standard encoding such as:

 http://localhost:8080/web/buy/sports+shoes
 or
 http://localhost:8080/web/buy/sports%20shoes


 Is it possible to configure or override this behaviour?




-- 
Regards,
Christophe Cordenier.

Developer of wooki @wookicentral.com


Re: Customising T5 URL Encoding

2010-06-18 Thread Nicolas Bouillon
Hi,

Here is how i've overrided this behavior, to allow URL with %20 or other
chars as incomming request. It quite a copy/paste tweaking of the original
UrlEncoderImpl from Tapestry 5.1.0.5.

public class AppModule {
.
public static void contributeServiceOverride(
MappedConfigurationClass, Object configuration) {
configuration.add(URLEncoder.class, new MyURLEncoderImpl());
}
.
}


package org.bouil.tapestry.services;

import java.io.UnsupportedEncodingException;
import java.util.BitSet;

import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.services.URLEncoder;

public class MyURLEncoderImpl implements URLEncoder {
static final String ENCODED_NULL = $N;
static final String ENCODED_BLANK = $B;

/**
 * Bit set indicating which character are safe to pass through (when
 * encoding or decoding) as-is. All other characters are encoded as a
kind
 * of unicode escape.
 */
private final BitSet safeForInput = new BitSet(128);
private final BitSet safeForOutput = new BitSet(128);

{
   
markSafeForInput(aàâäbcçĉdeéèêëfgĝhĥiïîjĵklmnoôöpqrsŝtuùûüvwxyzæœ);
   
markSafeForInput(AÀÂÄBCÇĈDEÉÈÊËFGĜHĤIÏÎĤJĴKLMNOÔÖPQRSŜTUÙÛÜVWXYZÆŒ);
markSafeForInput(01234567890-_.:,');

markSafeForOuput(abcdefghijklmnopqrstuvwxyz);
markSafeForOuput(ABCDEFGHIJKLMNOPQRSTUVWXYZ);
markSafeForOuput(01234567890-_.:,');
}

private void markSafeForInput(String s) {
for (char ch : s.toCharArray()) {
safeForInput.set(ch);
}
}

private void markSafeForOuput(String s) {
for (char ch : s.toCharArray()) {
safeForOutput.set(ch);
}
}

public String encode(String input) {
if (input == null)
return ENCODED_NULL;

if (input.equals())
return ENCODED_BLANK;

boolean dirty = false;

int length = input.length();

StringBuilder output = new StringBuilder(length * 2);

for (int i = 0; i  length; i++) {
char ch = input.charAt(i);

if (ch == '$') {
output.append($$);
dirty = true;
continue;
}

int chAsInt = ch;

if (safeForOutput.get(chAsInt)) {
output.append(ch);
continue;
}

try {
return  java.net.URLEncoder.encode(new String(input),
UTF-8);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
// output.append(String.format($%04x, chAsInt));
// dirty = true;
}

return dirty ? output.toString() : input;
}

public String decode(String input) {
Defense.notNull(input, input);

if (input.equals(ENCODED_NULL))
return null;

if (input.equals(ENCODED_BLANK))
return ;

boolean dirty = false;

int length = input.length();

StringBuilder output = new StringBuilder(length * 2);

for (int i = 0; i  length; i++) {
char ch = input.charAt(i);

if (ch == '$') {
dirty = true;

if (i + 1  length  input.charAt(i + 1) == '$') {
output.append('$');
i++;

dirty = true;
continue;
}

if (i + 4  length) {
String hex = input.substring(i + 1, i + 5);

try {
int unicode = Integer.parseInt(hex, 16);

output.append((char) unicode);
i += 4;
dirty = true;
continue;
} catch (NumberFormatException ex) {
// Ignore.
}
}

throw new IllegalArgumentException(
String
.format(
Input string '%s' is not valid;
the '$' character at position %d should be followed by another '$' or a
four digit hex number (a unicode value).,
input, i + 1));
}

if (!safeForInput.get(ch)) {
throw new IllegalArgumentException(
String
.format(
Input string '%s' is not valid;
the character '%s' at position %d is not valid.,
input, ch, i + 1));
}

output.append(ch);
}

return dirty ? output.toString() : input;
}
}



On Fri, 18 Jun 2010 16:08:09 +0100, Joel Halbert j...@su3analytics.com
wrote:
 Tapestry appears to URL encode spaces as $0020
 
 e.g.
 

Re: Customising T5 URL Encoding

2010-06-18 Thread Howard Lewis Ship
Tapestry does its own encoding because Jetty and Tomcat differ on
whether you get the decoded or raw strings.  Creating another option,
that would work the same across servlet containers, seemed to make
sense at the time.

On Fri, Jun 18, 2010 at 8:50 AM, Nicolas Bouillon nico...@bouil.org wrote:
 Hi,

 Here is how i've overrided this behavior, to allow URL with %20 or other
 chars as incomming request. It quite a copy/paste tweaking of the original
 UrlEncoderImpl from Tapestry 5.1.0.5.

 public class AppModule {
    .
    public static void contributeServiceOverride(
            MappedConfigurationClass, Object configuration) {
        configuration.add(URLEncoder.class, new MyURLEncoderImpl());
    }
    .
 }


 package org.bouil.tapestry.services;

 import java.io.UnsupportedEncodingException;
 import java.util.BitSet;

 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.services.URLEncoder;

 public class MyURLEncoderImpl implements URLEncoder {
    static final String ENCODED_NULL = $N;
    static final String ENCODED_BLANK = $B;

    /**
     * Bit set indicating which character are safe to pass through (when
     * encoding or decoding) as-is. All other characters are encoded as a
 kind
     * of unicode escape.
     */
    private final BitSet safeForInput = new BitSet(128);
    private final BitSet safeForOutput = new BitSet(128);

    {

 markSafeForInput(aàâäbcçĉdeéèêëfgĝhĥiïîjĵklmnoôöpqrsŝtuùûüvwxyzæœ);

 markSafeForInput(AÀÂÄBCÇĈDEÉÈÊËFGĜHĤIÏÎĤJĴKLMNOÔÖPQRSŜTUÙÛÜVWXYZÆŒ);
        markSafeForInput(01234567890-_.:,');

        markSafeForOuput(abcdefghijklmnopqrstuvwxyz);
        markSafeForOuput(ABCDEFGHIJKLMNOPQRSTUVWXYZ);
        markSafeForOuput(01234567890-_.:,');
    }

    private void markSafeForInput(String s) {
        for (char ch : s.toCharArray()) {
            safeForInput.set(ch);
        }
    }

    private void markSafeForOuput(String s) {
        for (char ch : s.toCharArray()) {
            safeForOutput.set(ch);
        }
    }

    public String encode(String input) {
        if (input == null)
            return ENCODED_NULL;

        if (input.equals())
            return ENCODED_BLANK;

        boolean dirty = false;

        int length = input.length();

        StringBuilder output = new StringBuilder(length * 2);

        for (int i = 0; i  length; i++) {
            char ch = input.charAt(i);

            if (ch == '$') {
                output.append($$);
                dirty = true;
                continue;
            }

            int chAsInt = ch;

            if (safeForOutput.get(chAsInt)) {
                output.append(ch);
                continue;
            }

            try {
                return  java.net.URLEncoder.encode(new String(input),
 UTF-8);
            } catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException(e);
            }
            // output.append(String.format($%04x, chAsInt));
            // dirty = true;
        }

        return dirty ? output.toString() : input;
    }

    public String decode(String input) {
        Defense.notNull(input, input);

        if (input.equals(ENCODED_NULL))
            return null;

        if (input.equals(ENCODED_BLANK))
            return ;

        boolean dirty = false;

        int length = input.length();

        StringBuilder output = new StringBuilder(length * 2);

        for (int i = 0; i  length; i++) {
            char ch = input.charAt(i);

            if (ch == '$') {
                dirty = true;

                if (i + 1  length  input.charAt(i + 1) == '$') {
                    output.append('$');
                    i++;

                    dirty = true;
                    continue;
                }

                if (i + 4  length) {
                    String hex = input.substring(i + 1, i + 5);

                    try {
                        int unicode = Integer.parseInt(hex, 16);

                        output.append((char) unicode);
                        i += 4;
                        dirty = true;
                        continue;
                    } catch (NumberFormatException ex) {
                        // Ignore.
                    }
                }

                throw new IllegalArgumentException(
                        String
                                .format(
                                        Input string '%s' is not valid;
 the '$' character at position %d should be followed by another '$' or a
 four digit hex number (a unicode value).,
                                        input, i + 1));
            }

            if (!safeForInput.get(ch)) {
                throw new IllegalArgumentException(
                        String
                                .format(
                                        Input string '%s' is not valid;
 the character '%s' at position %d is not valid.,
                                        input, ch, i + 1));