diff --git a/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/io/CharArrayWriter.java b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/io/CharArrayWriter.java new file mode 100644 index 00000000..fd5207c2 --- /dev/null +++ b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/io/CharArrayWriter.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package emul.java.io; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; + +/** + * This class implements a character buffer that can be used as a Writer. + * The buffer automatically grows when data is written to the stream. The data + * can be retrieved using toCharArray() and toString(). + *

+ * Note: Invoking close() on this class has no effect, and methods + * of this class can be called after the stream has closed + * without generating an IOException. + * + * @author Herb Jellinek + * @since 1.1 + */ +public class CharArrayWriter extends Writer { + /** + * The buffer where data is stored. + */ + protected char buf[]; + + /** + * The number of chars in the buffer. + */ + protected int count; + + /** + * Creates a new CharArrayWriter. + */ + public CharArrayWriter() { + this(32); + } + + /** + * Creates a new CharArrayWriter with the specified initial size. + * + * @param initialSize an int specifying the initial buffer size. + * @throws IllegalArgumentException if initialSize is negative + */ + public CharArrayWriter(int initialSize) { + if (initialSize < 0) { + throw new IllegalArgumentException("Negative initial size: " + + initialSize); + } + buf = new char[initialSize]; + } + + /** + * Writes a character to the buffer. + */ + public void write(int c) { + //synchronized (lock) { + int newcount = count + 1; + if (newcount > buf.length) { + buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); + } + buf[count] = (char)c; + count = newcount; + //} + } + + /** + * Writes characters to the buffer. + * @param c the data to be written + * @param off the start offset in the data + * @param len the number of chars that are written + * + * @throws IndexOutOfBoundsException + * If {@code off} is negative, or {@code len} is negative, + * or {@code off + len} is negative or greater than the length + * of the given array + */ + public void write(char[] c, int off, int len) { + if ((off < 0) || (off > c.length) || (len < 0) || + ((off + len) > c.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + //synchronized (lock) { + int newcount = count + len; + if (newcount > buf.length) { + buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); + } + System.arraycopy(c, off, buf, count, len); + count = newcount; + //} + } + + /** + * Write a portion of a string to the buffer. + * @param str String to be written from + * @param off Offset from which to start reading characters + * @param len Number of characters to be written + * + * @throws IndexOutOfBoundsException + * If {@code off} is negative, or {@code len} is negative, + * or {@code off + len} is negative or greater than the length + * of the given string + */ + public void write(String str, int off, int len) { + //synchronized (lock) { + int newcount = count + len; + if (newcount > buf.length) { + buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount)); + } + str.getChars(off, off + len, buf, count); + count = newcount; + //} + } + + /** + * Writes the contents of the buffer to another character stream. + * + * @param out the output stream to write to + * @throws IOException If an I/O error occurs. + */ + public void writeTo(Writer out) throws IOException { + //synchronized (lock) { + out.write(buf, 0, count); + //} + } + + /** + * Appends the specified character sequence to this writer. + * + *

An invocation of this method of the form {@code out.append(csq)} + * behaves in exactly the same way as the invocation + * + *

+     *     out.write(csq.toString()) 
+ * + *

Depending on the specification of {@code toString} for the + * character sequence {@code csq}, the entire sequence may not be + * appended. For instance, invoking the {@code toString} method of a + * character buffer will return a subsequence whose content depends upon + * the buffer's position and limit. + * + * @param csq + * The character sequence to append. If {@code csq} is + * {@code null}, then the four characters {@code "null"} are + * appended to this writer. + * + * @return This writer + * + * @since 1.5 + */ + public CharArrayWriter append(CharSequence csq) { + String s = String.valueOf(csq); + write(s, 0, s.length()); + return this; + } + + /** + * Appends a subsequence of the specified character sequence to this writer. + * + *

An invocation of this method of the form + * {@code out.append(csq, start, end)} when + * {@code csq} is not {@code null}, behaves in + * exactly the same way as the invocation + * + *

+     *     out.write(csq.subSequence(start, end).toString()) 
+ * + * @param csq + * The character sequence from which a subsequence will be + * appended. If {@code csq} is {@code null}, then characters + * will be appended as if {@code csq} contained the four + * characters {@code "null"}. + * + * @param start + * The index of the first character in the subsequence + * + * @param end + * The index of the character following the last character in the + * subsequence + * + * @return This writer + * + * @throws IndexOutOfBoundsException + * If {@code start} or {@code end} are negative, {@code start} + * is greater than {@code end}, or {@code end} is greater than + * {@code csq.length()} + * + * @since 1.5 + */ + public CharArrayWriter append(CharSequence csq, int start, int end) { + if (csq == null) csq = "null"; + return append(csq.subSequence(start, end)); + } + + /** + * Appends the specified character to this writer. + * + *

An invocation of this method of the form {@code out.append(c)} + * behaves in exactly the same way as the invocation + * + *

+     *     out.write(c) 
+ * + * @param c + * The 16-bit character to append + * + * @return This writer + * + * @since 1.5 + */ + public CharArrayWriter append(char c) { + write(c); + return this; + } + + /** + * Resets the buffer so that you can use it again without + * throwing away the already allocated buffer. + */ + public void reset() { + count = 0; + } + + /** + * Returns a copy of the input data. + * + * @return an array of chars copied from the input data. + */ + public char[] toCharArray() { + //synchronized (lock) { + return Arrays.copyOf(buf, count); + //} + } + + /** + * Returns the current size of the buffer. + * + * @return an int representing the current size of the buffer. + */ + public int size() { + return count; + } + + /** + * Converts input data to a string. + * @return the string. + */ + public String toString() { + //synchronized (lock) { + return new String(buf, 0, count); + //} + } + + /** + * Flush the stream. + * + *

The {@code flush} method of {@code CharArrayWriter} does nothing. + */ + public void flush() { } + + /** + * Close the stream. This method does not release the buffer, since its + * contents might still be required. Note: Invoking this method in this class + * will have no effect. + */ + public void close() { } + +} diff --git a/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLDecoder.java b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLDecoder.java new file mode 100644 index 00000000..8e54fe20 --- /dev/null +++ b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLDecoder.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package emul.java.net; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Objects; + +/** + * Utility class for HTML form decoding. This class contains static methods + * for decoding a String from the application/x-www-form-urlencoded + * MIME format. + *

+ * The conversion process is the reverse of that used by the URLEncoder class. It is assumed + * that all characters in the encoded string are one of the following: + * "{@code a}" through "{@code z}", + * "{@code A}" through "{@code Z}", + * "{@code 0}" through "{@code 9}", and + * "{@code -}", "{@code _}", + * "{@code .}", and "{@code *}". The + * character "{@code %}" is allowed but is interpreted + * as the start of a special escaped sequence. + *

+ * The following rules are applied in the conversion: + * + *

+ *

+ * There are two possible ways in which this decoder could deal with + * illegal strings. It could either leave illegal characters alone or + * it could throw an {@link IllegalArgumentException}. + * Which approach the decoder takes is left to the + * implementation. + * + * @see Charset#defaultCharset() + * + * @author Mark Chamness + * @author Michael McCloskey + * @since 1.2 + */ + +public class URLDecoder { + + /** + * Do not call. + */ + private URLDecoder() {} + + // The default charset + static String dfltEncName = URLEncoder.dfltEncName; + + /** + * Decodes a {@code x-www-form-urlencoded} string. + * The default charset is used to determine what characters + * are represented by any consecutive sequences of the form + * "{@code %xy}". + * @param s the {@code String} to decode + * @deprecated The resulting string may vary depending on the + * default charset. Instead, use the decode(String,String) method + * to specify the encoding. + * @return the newly decoded {@code String} + */ + @Deprecated + public static String decode(String s) { + + String str = null; + + try { + str = decode(s, dfltEncName); + } catch (UnsupportedEncodingException e) { + // The system should always have the default charset + } + + return str; + } + + /** + * Decodes an {@code application/x-www-form-urlencoded} string using + * a specific encoding scheme. + * + *

+ * This method behaves the same as {@linkplain decode(String s, Charset charset)} + * except that it will {@linkplain Charset#forName look up the charset} + * using the given encoding name. + * + * @implNote This implementation will throw an {@link IllegalArgumentException} + * when illegal strings are encountered. + * + * @param s the {@code String} to decode + * @param enc The name of a supported + * character + * encoding. + * @return the newly decoded {@code String} + * @throws UnsupportedEncodingException + * If character encoding needs to be consulted, but + * named character encoding is not supported + * @see URLEncoder#encode(String, String) + * @since 1.4 + */ + public static String decode(String s, String enc) throws UnsupportedEncodingException { + if (enc.isEmpty()) { + throw new UnsupportedEncodingException ("URLDecoder: empty string enc parameter"); + } + + try { + Charset charset = Charset.forName(enc); + return decode(s, charset); + } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { + throw new UnsupportedEncodingException(enc); + } + } + + /** + * Decodes an {@code application/x-www-form-urlencoded} string using + * a specific {@linkplain Charset Charset}. + * The supplied charset is used to determine + * what characters are represented by any consecutive sequences of the + * form "{@code %xy}". + *

+ * Note: The + * World Wide Web Consortium Recommendation states that + * UTF-8 should be used. Not doing so may introduce + * incompatibilities. + * + * @implNote This implementation will throw an {@link IllegalArgumentException} + * when illegal strings are encountered. + * + * @param s the {@code String} to decode + * @param charset the given charset + * @return the newly decoded {@code String} + * @throws NullPointerException if {@code s} or {@code charset} is {@code null} + * @throws IllegalArgumentException if the implementation encounters illegal + * characters + * @see URLEncoder#encode(String, Charset) + * @since 10 + */ + public static String decode(String s, Charset charset) { + Objects.requireNonNull(charset, "Charset"); + boolean needToChange = false; + int numChars = s.length(); + StringBuilder sb = new StringBuilder(numChars > 500 ? numChars / 2 : numChars); + int i = 0; + + char c; + byte[] bytes = null; + while (i < numChars) { + c = s.charAt(i); + switch (c) { + case '+': + sb.append(' '); + i++; + needToChange = true; + break; + case '%': + /* + * Starting with this instance of %, process all + * consecutive substrings of the form %xy. Each + * substring %xy will yield a byte. Convert all + * consecutive bytes obtained this way to whatever + * character(s) they represent in the provided + * encoding. + */ + + try { + + // (numChars-i)/3 is an upper bound for the number + // of remaining bytes + if (bytes == null) + bytes = new byte[(numChars-i)/3]; + int pos = 0; + + while ( ((i+2) < numChars) && + (c=='%')) { + int v = /*Integer.*/parseInt(s, i + 1, i + 3, 16); + if (v < 0) + throw new IllegalArgumentException( + "URLDecoder: Illegal hex characters in escape " + + "(%) pattern - negative value"); + bytes[pos++] = (byte) v; + i+= 3; + if (i < numChars) + c = s.charAt(i); + } + + // A trailing, incomplete byte encoding such as + // "%x" will cause an exception to be thrown + + if ((i < numChars) && (c=='%')) + throw new IllegalArgumentException( + "URLDecoder: Incomplete trailing escape (%) pattern"); + + sb.append(new String(bytes, 0, pos, charset)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "URLDecoder: Illegal hex characters in escape (%) pattern - " + + e.getMessage()); + } + needToChange = true; + break; + default: + sb.append(c); + i++; + break; + } + } + + return (needToChange? sb.toString() : s); + } + + private static int parseInt(CharSequence s, int beginIndex, int endIndex, int radix) + throws NumberFormatException { + Objects.requireNonNull(s); + //Objects.checkFromToIndex(beginIndex, endIndex, s.length()); + + if (radix < Character.MIN_RADIX) { + throw new NumberFormatException("radix " + radix + + " less than Character.MIN_RADIX"); + } + if (radix > Character.MAX_RADIX) { + throw new NumberFormatException("radix " + radix + + " greater than Character.MAX_RADIX"); + } + + boolean negative = false; + int i = beginIndex; + int limit = -Integer.MAX_VALUE; + + if (i < endIndex) { + char firstChar = s.charAt(i); + if (firstChar < '0') { // Possible leading "+" or "-" + if (firstChar == '-') { + negative = true; + limit = Integer.MIN_VALUE; + } else if (firstChar != '+') { + throw /*NumberFormatException.*/forCharSequence(s, beginIndex, + endIndex, i); + } + i++; + if (i == endIndex) { // Cannot have lone "+" or "-" + throw /*NumberFormatException.*/forCharSequence(s, beginIndex, + endIndex, i); + } + } + int multmin = limit / radix; + int result = 0; + while (i < endIndex) { + // Accumulating negatively avoids surprises near MAX_VALUE + int digit = Character.digit(s.charAt(i), radix); + if (digit < 0 || result < multmin) { + throw /*NumberFormatException.*/forCharSequence(s, beginIndex, + endIndex, i); + } + result *= radix; + if (result < limit + digit) { + throw /*NumberFormatException.*/forCharSequence(s, beginIndex, + endIndex, i); + } + i++; + result -= digit; + } + return negative ? result : -result; + } else { + throw /*NumberFormatException.*/forInputString("", radix); + } + } + + static NumberFormatException forCharSequence(CharSequence s, + int beginIndex, int endIndex, int errorIndex) { + return new NumberFormatException("Error at index " + + (errorIndex - beginIndex) + " in: \"" + + s.subSequence(beginIndex, endIndex) + "\""); + } + + static NumberFormatException forInputString(String s, int radix) { + return new NumberFormatException("For input string: \"" + s + "\"" + + (radix == 10 ? + "" : + " under radix " + radix)); + } + +} diff --git a/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLEncoder.java b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLEncoder.java new file mode 100644 index 00000000..e437a73f --- /dev/null +++ b/webfx-platform-javabase-emul-gwt/src/main/java/emul/java/net/URLEncoder.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package emul.java.net; + +import java.io.CharArrayWriter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.BitSet; +import java.util.Objects; + +//import jdk.internal.util.StaticProperty; + +/** + * Utility class for HTML form encoding. This class contains static methods + * for converting a String to the application/x-www-form-urlencoded MIME + * format. For more information about HTML form encoding, consult the HTML + * specification. + * + *

+ * When encoding a String, the following rules apply: + * + *

+ * + *

+ * For example using UTF-8 as the encoding scheme the string "The + * string ü@foo-bar" would get converted to + * "The+string+%C3%BC%40foo-bar" because in UTF-8 the character + * ü is encoded as two bytes C3 (hex) and BC (hex), and the + * character @ is encoded as one byte 40 (hex). + * + * @see Charset#defaultCharset() + * + * @author Herb Jellinek + * @since 1.0 + */ +public class URLEncoder { + static BitSet dontNeedEncoding; + static final int caseDiff = ('a' - 'A'); + static String dfltEncName; + + static { + + /* The list of characters that are not encoded has been + * determined as follows: + * + * RFC 2396 states: + * ----- + * Data characters that are allowed in a URI but do not have a + * reserved purpose are called unreserved. These include upper + * and lower case letters, decimal digits, and a limited set of + * punctuation marks and symbols. + * + * unreserved = alphanum | mark + * + * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + * + * Unreserved characters can be escaped without changing the + * semantics of the URI, but this should not be done unless the + * URI is being used in a context that does not allow the + * unescaped character to appear. + * ----- + * + * It appears that both Netscape and Internet Explorer escape + * all special characters from this list with the exception + * of "-", "_", ".", "*". While it is not clear why they are + * escaping the other characters, perhaps it is safest to + * assume that there might be contexts in which the others + * are unsafe if not escaped. Therefore, we will use the same + * list. It is also noteworthy that this is consistent with + * O'Reilly's "HTML: The Definitive Guide" (page 164). + * + * As a last note, Internet Explorer does not encode the "@" + * character which is clearly not unreserved according to the + * RFC. We are being consistent with the RFC in this matter, + * as is Netscape. + * + */ + + dontNeedEncoding = new BitSet(256); + int i; + for (i = 'a'; i <= 'z'; i++) { + dontNeedEncoding.set(i); + } + for (i = 'A'; i <= 'Z'; i++) { + dontNeedEncoding.set(i); + } + for (i = '0'; i <= '9'; i++) { + dontNeedEncoding.set(i); + } + dontNeedEncoding.set(' '); /* encoding a space to a + is done + * in the encode() method */ + dontNeedEncoding.set('-'); + dontNeedEncoding.set('_'); + dontNeedEncoding.set('.'); + dontNeedEncoding.set('*'); + + dfltEncName = "UTF8"; //StaticProperty.fileEncoding(); + } + + /** + * You can't call the constructor. + */ + private URLEncoder() { } + + /** + * Translates a string into {@code x-www-form-urlencoded} + * format. This method uses the default charset + * as the encoding scheme to obtain the bytes for unsafe characters. + * + * @param s {@code String} to be translated. + * @deprecated The resulting string may vary depending on the + * default charset. Instead, use the encode(String,String) + * method to specify the encoding. + * @return the translated {@code String}. + */ + @Deprecated + public static String encode(String s) { + + String str = null; + + try { + str = encode(s, dfltEncName); + } catch (UnsupportedEncodingException e) { + // The system should always have the default charset + } + + return str; + } + + /** + * Translates a string into {@code application/x-www-form-urlencoded} + * format using a specific encoding scheme. + *

+ * This method behaves the same as {@linkplain #encode(String s, Charset charset)} + * except that it will {@linkplain Charset#forName look up the charset} + * using the given encoding name. + * + * @param s {@code String} to be translated. + * @param enc The name of a supported + * character + * encoding. + * @return the translated {@code String}. + * @throws UnsupportedEncodingException + * If the named encoding is not supported + * @see URLDecoder#decode(String, String) + * @since 1.4 + */ + public static String encode(String s, String enc) + throws UnsupportedEncodingException { + if (enc == null) { + throw new NullPointerException("charsetName"); + } + + try { + Charset charset = Charset.forName(enc); + return encode(s, charset); + } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { + throw new UnsupportedEncodingException(enc); + } + } + + /** + * Translates a string into {@code application/x-www-form-urlencoded} + * format using a specific {@linkplain Charset Charset}. + * This method uses the supplied charset to obtain the bytes for unsafe + * characters. + *

+ * Note: The + * World Wide Web Consortium Recommendation states that + * UTF-8 should be used. Not doing so may introduce incompatibilities. + * + * @param s {@code String} to be translated. + * @param charset the given charset + * @return the translated {@code String}. + * @throws NullPointerException if {@code s} or {@code charset} is {@code null}. + * @see URLDecoder#decode(String, Charset) + * @since 10 + */ + public static String encode(String s, Charset charset) { + Objects.requireNonNull(charset, "charset"); + + boolean needToChange = false; + StringBuilder out = new StringBuilder(s.length()); + CharArrayWriter charArrayWriter = new CharArrayWriter(); + + for (int i = 0; i < s.length();) { + int c = s.charAt(i); + //System.out.println("Examining character: " + c); + if (dontNeedEncoding.get(c)) { + if (c == ' ') { + c = '+'; + needToChange = true; + } + //System.out.println("Storing: " + c); + out.append((char)c); + i++; + } else { + // convert to external encoding before hex conversion + do { + charArrayWriter.write(c); + /* + * If this character represents the start of a Unicode + * surrogate pair, then pass in two characters. It's not + * clear what should be done if a byte reserved in the + * surrogate pairs range occurs outside of a legal + * surrogate pair. For now, just treat it as if it were + * any other character. + */ + if (c >= 0xD800 && c <= 0xDBFF) { + /* + System.out.println(Integer.toHexString(c) + + " is high surrogate"); + */ + if ( (i+1) < s.length()) { + int d = s.charAt(i+1); + /* + System.out.println("\tExamining " + + Integer.toHexString(d)); + */ + if (d >= 0xDC00 && d <= 0xDFFF) { + /* + System.out.println("\t" + + Integer.toHexString(d) + + " is low surrogate"); + */ + charArrayWriter.write(d); + i++; + } + } + } + i++; + } while (i < s.length() && !dontNeedEncoding.get((c = s.charAt(i)))); + + charArrayWriter.flush(); + String str = charArrayWriter.toString(); + byte[] ba = str.getBytes(charset); + for (byte b : ba) { + out.append('%'); + char ch = Character.forDigit((b >> 4) & 0xF, 16); + // converting to use uppercase letter as part of + // the hex value if ch is a letter. + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + ch = Character.forDigit(b & 0xF, 16); + if (Character.isLetter(ch)) { + ch -= caseDiff; + } + out.append(ch); + } + charArrayWriter.reset(); + needToChange = true; + } + } + + return (needToChange? out.toString() : s); + } +}