public static String percentDecode(final String input) {
        if (input.isEmpty()) {
            return input;
        }
        try {
            final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            int idx = 0;
            while (idx < input.length()) {

                boolean isEOF = idx >= input.length();
                int c = (isEOF)? 0x00 : input.codePointAt(idx);

                while (!isEOF && c != '%') {
                    if (c <= 0x7F) { // String.getBytes is slow, so do not perform encoding
                                     // if not needed
                        bytes.write((byte) c);
                        idx++;
                    } else {
                        bytes.write(new String(Character.toChars(c)).getBytes(UTF_8));
                        idx += Character.charCount(c);
                    }
                    isEOF = idx >= input.length();
                    c = (isEOF)? 0x00 : input.codePointAt(idx);
                }

                if (c == '%' && (input.length() <= idx + 2 ||
                        !isASCIIHexDigit(input.charAt(idx + 1)) ||
                        !isASCIIHexDigit(input.charAt(idx + 2)))) {
                    if (c <= 0x7F) { // String.getBytes is slow, so do not perform encoding
                        // if not needed
                        bytes.write((byte) c);
                        idx++;
                    } else {
                        bytes.write(new String(Character.toChars(c)).getBytes(UTF_8));
                        idx += Character.charCount(c);
                    }
                } else {
                    while (c == '%' && input.length() > idx + 2 &&
                            isASCIIHexDigit(input.charAt(idx + 1)) &&
                            isASCIIHexDigit(input.charAt(idx + 2))) {
                        bytes.write(hexToInt(input.charAt(idx + 1), input.charAt(idx + 2)));
                        idx += 3;
                        c = (input.length() <= idx)? 0x00 : input.codePointAt(idx);
                    }
                }
            }
            return new String(bytes.toByteArray(), UTF_8);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }
public static String percentDecode(final String input) {
        if (input.isEmpty()) {
            return input;
        }
        try {
            final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            int idx = 0;
            while (idx < input.length()) {

                boolean isEOF = idx >= input.length();
                int c = (isEOF)? 0x00 : input.codePointAt(idx);

                while (!isEOF && c != '%') {
                    if (c <= 0x7F) { 
                                     
                        bytes.write((byte) c);
                        idx++;
                    } else {
                        bytes.write(new String(Character.toChars(c)).getBytes(UTF_8));
                        idx += Character.charCount(c);
                    }
                    isEOF = idx >= input.length();
                    c = (isEOF)? 0x00 : input.codePointAt(idx);
                }

                if (c == '%' && (input.length() <= idx + 2 ||
                        !isASCIIHexDigit(input.charAt(idx + 1)) ||
                        !isASCIIHexDigit(input.charAt(idx + 2)))) {
                    if (c <= 0x7F) { 
                        
                        bytes.write((byte) c);
                        idx++;
                    } else {
                        bytes.write(new String(Character.toChars(c)).getBytes(UTF_8));
                        idx += Character.charCount(c);
                    }
                } else {
                    while (c == '%' && input.length() > idx + 2 &&
                            isASCIIHexDigit(input.charAt(idx + 1)) &&
                            isASCIIHexDigit(input.charAt(idx + 2))) {
                        bytes.write(hexToInt(input.charAt(idx + 1), input.charAt(idx + 2)));
                        idx += 3;
                        c = (input.length() <= idx)? 0x00 : input.codePointAt(idx);
                    }
                }
            }
            return new String(bytes.toByteArray(), UTF_8);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }
public static String percentDecode ( final String input ) { if ( input . isEmpty ( ) ) { return input ; } try { final ByteArrayOutputStream bytes = new ByteArrayOutputStream ( ) ; int idx = 0 ; while ( idx < input . length ( ) ) { boolean isEOF = idx >= input . length ( ) ; int c = ( isEOF ) ? 0x00 : input . codePointAt ( idx ) ; while ( ! isEOF && c != ' ' ) { if ( c <= 0x7F ) { // String.getBytes is slow, so do not perform encoding // if not needed bytes . write ( ( byte ) c ) ; idx ++ ; } else { bytes . write ( new String ( Character . toChars ( c ) ) . getBytes ( UTF_8 ) ) ; idx += Character . charCount ( c ) ; } isEOF = idx >= input . length ( ) ; c = ( isEOF ) ? 0x00 : input . codePointAt ( idx ) ; } if ( c == ' ' && ( input . length ( ) <= idx + 2 || ! isASCIIHexDigit ( input . charAt ( idx + 1 ) ) || ! isASCIIHexDigit ( input . charAt ( idx + 2 ) ) ) ) { if ( c <= 0x7F ) { // String.getBytes is slow, so do not perform encoding // if not needed bytes . write ( ( byte ) c ) ; idx ++ ; } else { bytes . write ( new String ( Character . toChars ( c ) ) . getBytes ( UTF_8 ) ) ; idx += Character . charCount ( c ) ; } } else { while ( c == ' ' && input . length ( ) > idx + 2 && isASCIIHexDigit ( input . charAt ( idx + 1 ) ) && isASCIIHexDigit ( input . charAt ( idx + 2 ) ) ) { bytes . write ( hexToInt ( input . charAt ( idx + 1 ) , input . charAt ( idx + 2 ) ) ) ; idx += 3 ; c = ( input . length ( ) <= idx ) ? 0x00 : input . codePointAt ( idx ) ; } } } return new String ( bytes . toByteArray ( ) , UTF_8 ) ; } catch ( IOException ex ) { throw new RuntimeException ( ex ) ; } }
Percent-decodes a string.

Percent-encoded bytes are assumed to represent UTF-8 characters.

@see <a href="http://url.spec.whatwg.org/#percent-encoded-bytes">WHATWG URL Standard: Percent-encoded bytes</a>

@param input
@return
Percent - decodes a string .