< prev index next >

src/java.base/share/classes/java/util/Properties.java

Print this page
rev 54989 : 8224240: Properties.load fails to throw IAE on malformed unicode in certain circumstances
Reviewed-by: smarks
rev 54990 : 8224202: Speed up Properties.load
Reviewed-by: rriggs


  31 import java.io.InputStream;
  32 import java.io.OutputStream;
  33 import java.io.Reader;
  34 import java.io.Writer;
  35 import java.io.OutputStreamWriter;
  36 import java.io.BufferedWriter;
  37 import java.io.ObjectInputStream;
  38 import java.io.ObjectOutputStream;
  39 import java.io.StreamCorruptedException;
  40 import java.io.UnsupportedEncodingException;
  41 import java.nio.charset.Charset;
  42 import java.nio.charset.IllegalCharsetNameException;
  43 import java.nio.charset.UnsupportedCharsetException;
  44 import java.util.concurrent.ConcurrentHashMap;
  45 import java.util.function.BiConsumer;
  46 import java.util.function.BiFunction;
  47 import java.util.function.Function;
  48 
  49 import jdk.internal.access.SharedSecrets;
  50 import jdk.internal.misc.Unsafe;

  51 import jdk.internal.util.xml.PropertiesDefaultHandler;
  52 
  53 /**
  54  * The {@code Properties} class represents a persistent set of
  55  * properties. The {@code Properties} can be saved to a stream
  56  * or loaded from a stream. Each key and its corresponding value in
  57  * the property list is a string.
  58  * <p>
  59  * A property list can contain another property list as its
  60  * "defaults"; this second property list is searched if
  61  * the property key is not found in the original property list.
  62  * <p>
  63  * Because {@code Properties} inherits from {@code Hashtable}, the
  64  * {@code put} and {@code putAll} methods can be applied to a
  65  * {@code Properties} object.  Their use is strongly discouraged as they
  66  * allow the caller to insert entries whose keys or values are not
  67  * {@code Strings}.  The {@code setProperty} method should be used
  68  * instead.  If the {@code store} or {@code save} method is called
  69  * on a "compromised" {@code Properties} object that contains a
  70  * non-{@code String} key or value, the call will fail. Similarly,


 387      * character. Characters not in Latin1, and certain special characters,
 388      * are represented in keys and elements using Unicode escapes as defined in
 389      * section 3.3 of
 390      * <cite>The Java&trade; Language Specification</cite>.
 391      * <p>
 392      * The specified stream remains open after this method returns.
 393      *
 394      * @param      inStream   the input stream.
 395      * @exception  IOException  if an error occurred when reading from the
 396      *             input stream.
 397      * @throws     IllegalArgumentException if the input stream contains a
 398      *             malformed Unicode escape sequence.
 399      * @throws     NullPointerException if {@code inStream} is null.
 400      * @since 1.2
 401      */
 402     public synchronized void load(InputStream inStream) throws IOException {
 403         Objects.requireNonNull(inStream, "inStream parameter is null");
 404         load0(new LineReader(inStream));
 405     }
 406 
 407     private void load0 (LineReader lr) throws IOException {
 408         char[] convtBuf = new char[1024];
 409         int limit;
 410         int keyLen;
 411         int valueStart;
 412         char c;
 413         boolean hasSep;
 414         boolean precedingBackslash;
 415 
 416         while ((limit = lr.readLine()) >= 0) {
 417             c = 0;
 418             keyLen = 0;
 419             valueStart = limit;
 420             hasSep = false;
 421 
 422             //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
 423             precedingBackslash = false;
 424             while (keyLen < limit) {
 425                 c = lr.lineBuf[keyLen];
 426                 //need check if escaped.
 427                 if ((c == '=' ||  c == ':') && !precedingBackslash) {
 428                     valueStart = keyLen + 1;
 429                     hasSep = true;
 430                     break;
 431                 } else if ((c == ' ' || c == '\t' ||  c == '\f') && !precedingBackslash) {
 432                     valueStart = keyLen + 1;
 433                     break;
 434                 }
 435                 if (c == '\\') {
 436                     precedingBackslash = !precedingBackslash;
 437                 } else {
 438                     precedingBackslash = false;
 439                 }
 440                 keyLen++;
 441             }
 442             while (valueStart < limit) {
 443                 c = lr.lineBuf[valueStart];
 444                 if (c != ' ' && c != '\t' &&  c != '\f') {
 445                     if (!hasSep && (c == '=' ||  c == ':')) {
 446                         hasSep = true;
 447                     } else {
 448                         break;
 449                     }
 450                 }
 451                 valueStart++;
 452             }
 453             String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
 454             String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
 455             put(key, value);
 456         }
 457     }
 458 
 459     /* Read in a "logical line" from an InputStream/Reader, skip all comment
 460      * and blank lines and filter out those leading whitespace characters
 461      * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
 462      * Method returns the char length of the "logical line" and stores
 463      * the line in "lineBuf".
 464      */
 465     class LineReader {
 466         public LineReader(InputStream inStream) {
 467             this.inStream = inStream;
 468             inByteBuf = new byte[8192];
 469         }
 470 
 471         public LineReader(Reader reader) {
 472             this.reader = reader;
 473             inCharBuf = new char[8192];
 474         }
 475 
 476         byte[] inByteBuf;
 477         char[] inCharBuf;
 478         char[] lineBuf = new char[1024];
 479         int inLimit = 0;
 480         int inOff = 0;
 481         InputStream inStream;
 482         Reader reader;


 483 
 484         int readLine() throws IOException {

 485             int len = 0;
 486             char c = 0;

 487 
 488             boolean skipWhiteSpace = true;
 489             boolean isCommentLine = false;
 490             boolean isNewLine = true;
 491             boolean appendedLineBegin = false;
 492             boolean precedingBackslash = false;
 493             boolean skipLF = false;



 494 
 495             while (true) {
 496                 if (inOff >= inLimit) {
 497                     inLimit = (inStream==null)?reader.read(inCharBuf)
 498                                               :inStream.read(inByteBuf);
 499                     inOff = 0;
 500                     if (inLimit <= 0) {
 501                         if (len == 0 || isCommentLine) {
 502                             return -1;
 503                         }
 504                         if (precedingBackslash) {
 505                             len--;
 506                         }
 507                         return len;
 508                     }

 509                 }
 510                 if (inStream != null) {
 511                     //The line below is equivalent to calling a
 512                     //ISO8859-1 decoder.
 513                     c = (char)(inByteBuf[inOff++] & 0xFF);
 514                 } else {
 515                     c = inCharBuf[inOff++];
 516                 }
 517                 if (skipLF) {
 518                     skipLF = false;
 519                     if (c == '\n') {
 520                         continue;
 521                     }
 522                 }
 523                 if (skipWhiteSpace) {
 524                     if (c == ' ' || c == '\t' || c == '\f') {
 525                         continue;
 526                     }
 527                     if (!appendedLineBegin && (c == '\r' || c == '\n')) {
 528                         continue;
 529                     }
 530                     skipWhiteSpace = false;
 531                     appendedLineBegin = false;

 532                 }
 533                 if (isNewLine) {
 534                     isNewLine = false;
 535                     if (c == '#' || c == '!') {
 536                         // Comment, quickly consume the rest of the line,
 537                         // resume on line-break and backslash.
 538                         if (inStream != null) {
 539                             while (inOff < inLimit) {
 540                                 byte b = inByteBuf[inOff++];
 541                                 if (b == '\n' || b == '\r' || b == '\\') {
 542                                     c = (char)(b & 0xFF);
 543                                     break;









 544                                 }

 545                             }
 546                         } else {
 547                             while (inOff < inLimit) {
 548                                 c = inCharBuf[inOff++];
 549                                 if (c == '\n' || c == '\r' || c == '\\') {
 550                                     break;





 551                                 }

 552                             }
 553                         }
 554                         isCommentLine = true;


 555                     }
 556                 }
 557 
 558                 if (c != '\n' && c != '\r') {
 559                     lineBuf[len++] = c;
 560                     if (len == lineBuf.length) {
 561                         int newLength = lineBuf.length * 2;
 562                         if (newLength < 0) {
 563                             newLength = Integer.MAX_VALUE;
 564                         }
 565                         char[] buf = new char[newLength];
 566                         System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
 567                         lineBuf = buf;
 568                     }
 569                     //flip the preceding backslash flag
 570                     if (c == '\\') {
 571                         precedingBackslash = !precedingBackslash;
 572                     } else {
 573                         precedingBackslash = false;
 574                     }
 575                 }
 576                 else {
 577                     // reached EOL
 578                     if (isCommentLine || len == 0) {
 579                         isCommentLine = false;
 580                         isNewLine = true;
 581                         skipWhiteSpace = true;
 582                         len = 0;
 583                         continue;
 584                     }
 585                     if (inOff >= inLimit) {
 586                         inLimit = (inStream==null)
 587                                   ?reader.read(inCharBuf)
 588                                   :inStream.read(inByteBuf);
 589                         inOff = 0;
 590                         if (inLimit <= 0) {
 591                             if (precedingBackslash) {
 592                                 len--;
 593                             }
 594                             return len;
 595                         }
 596                     }
 597                     if (precedingBackslash) {

 598                         len -= 1;
 599                         //skip the leading whitespace characters in following line
 600                         skipWhiteSpace = true;
 601                         appendedLineBegin = true;
 602                         precedingBackslash = false;

 603                         if (c == '\r') {
 604                             skipLF = true;


 605                         }
 606                     } else {







 607                         return len;
 608                     }
 609                 }
 610             }
 611         }
 612     }
 613 
 614     /*
 615      * Converts encoded \uxxxx to unicode chars
 616      * and changes special saved chars to their original forms
 617      */
 618     private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
 619         if (convtBuf.length < len) {
 620             int newLen = len * 2;
 621             if (newLen < 0) {
 622                 newLen = Integer.MAX_VALUE;
 623             }
 624             convtBuf = new char[newLen];
 625         }
 626         char aChar;
 627         char[] out = convtBuf;
 628         int outLen = 0;
 629         int end = off + len;
 630 
 631         while (off < end) {
 632             aChar = in[off++];
 633             if (aChar == '\\') {
 634                 // No need to bounds check since LineReader::readLine excludes
 635                 // unescaped \s at the end of the line
 636                 aChar = in[off++];
 637                 if(aChar == 'u') {
 638                     // Read the xxxx
 639                     if (off > end - 4)
 640                         throw new IllegalArgumentException(
 641                                      "Malformed \\uxxxx encoding.");
 642                     int value = 0;
 643                     for (int i = 0; i < 4; i++) {
 644                         aChar = in[off++];
 645                         switch (aChar) {
 646                           case '0': case '1': case '2': case '3': case '4':
 647                           case '5': case '6': case '7': case '8': case '9':
 648                              value = (value << 4) + aChar - '0';
 649                              break;
 650                           case 'a': case 'b': case 'c':
 651                           case 'd': case 'e': case 'f':
 652                              value = (value << 4) + 10 + aChar - 'a';
 653                              break;
 654                           case 'A': case 'B': case 'C':
 655                           case 'D': case 'E': case 'F':
 656                              value = (value << 4) + 10 + aChar - 'A';
 657                              break;
 658                           default:
 659                               throw new IllegalArgumentException(
 660                                            "Malformed \\uxxxx encoding.");
 661                         }
 662                      }
 663                     out[outLen++] = (char)value;
 664                 } else {
 665                     if (aChar == 't') aChar = '\t';
 666                     else if (aChar == 'r') aChar = '\r';
 667                     else if (aChar == 'n') aChar = '\n';
 668                     else if (aChar == 'f') aChar = '\f';
 669                     out[outLen++] = aChar;
 670                 }
 671             } else {
 672                 out[outLen++] = aChar;
 673             }
 674         }
 675         return new String (out, 0, outLen);
 676     }
 677 
 678     /*
 679      * Converts unicodes to encoded \uxxxx and escapes
 680      * special characters with a preceding slash
 681      */
 682     private String saveConvert(String theString,
 683                                boolean escapeSpace,
 684                                boolean escapeUnicode) {
 685         int len = theString.length();
 686         int bufLen = len * 2;
 687         if (bufLen < 0) {
 688             bufLen = Integer.MAX_VALUE;
 689         }
 690         StringBuilder outBuffer = new StringBuilder(bufLen);
 691 
 692         for(int x=0; x<len; x++) {
 693             char aChar = theString.charAt(x);
 694             // Handle common case first, selecting largest block that
 695             // avoids the specials below




  31 import java.io.InputStream;
  32 import java.io.OutputStream;
  33 import java.io.Reader;
  34 import java.io.Writer;
  35 import java.io.OutputStreamWriter;
  36 import java.io.BufferedWriter;
  37 import java.io.ObjectInputStream;
  38 import java.io.ObjectOutputStream;
  39 import java.io.StreamCorruptedException;
  40 import java.io.UnsupportedEncodingException;
  41 import java.nio.charset.Charset;
  42 import java.nio.charset.IllegalCharsetNameException;
  43 import java.nio.charset.UnsupportedCharsetException;
  44 import java.util.concurrent.ConcurrentHashMap;
  45 import java.util.function.BiConsumer;
  46 import java.util.function.BiFunction;
  47 import java.util.function.Function;
  48 
  49 import jdk.internal.access.SharedSecrets;
  50 import jdk.internal.misc.Unsafe;
  51 import jdk.internal.util.ArraysSupport;
  52 import jdk.internal.util.xml.PropertiesDefaultHandler;
  53 
  54 /**
  55  * The {@code Properties} class represents a persistent set of
  56  * properties. The {@code Properties} can be saved to a stream
  57  * or loaded from a stream. Each key and its corresponding value in
  58  * the property list is a string.
  59  * <p>
  60  * A property list can contain another property list as its
  61  * "defaults"; this second property list is searched if
  62  * the property key is not found in the original property list.
  63  * <p>
  64  * Because {@code Properties} inherits from {@code Hashtable}, the
  65  * {@code put} and {@code putAll} methods can be applied to a
  66  * {@code Properties} object.  Their use is strongly discouraged as they
  67  * allow the caller to insert entries whose keys or values are not
  68  * {@code Strings}.  The {@code setProperty} method should be used
  69  * instead.  If the {@code store} or {@code save} method is called
  70  * on a "compromised" {@code Properties} object that contains a
  71  * non-{@code String} key or value, the call will fail. Similarly,


 388      * character. Characters not in Latin1, and certain special characters,
 389      * are represented in keys and elements using Unicode escapes as defined in
 390      * section 3.3 of
 391      * <cite>The Java&trade; Language Specification</cite>.
 392      * <p>
 393      * The specified stream remains open after this method returns.
 394      *
 395      * @param      inStream   the input stream.
 396      * @exception  IOException  if an error occurred when reading from the
 397      *             input stream.
 398      * @throws     IllegalArgumentException if the input stream contains a
 399      *             malformed Unicode escape sequence.
 400      * @throws     NullPointerException if {@code inStream} is null.
 401      * @since 1.2
 402      */
 403     public synchronized void load(InputStream inStream) throws IOException {
 404         Objects.requireNonNull(inStream, "inStream parameter is null");
 405         load0(new LineReader(inStream));
 406     }
 407 
 408     private void load0(LineReader lr) throws IOException {
 409         StringBuilder outBuffer = new StringBuilder(128);
 410         int limit;
 411         int keyLen;
 412         int valueStart;

 413         boolean hasSep;
 414         boolean precedingBackslash;
 415 
 416         while ((limit = lr.readLine()) >= 0) {

 417             keyLen = 0;
 418             valueStart = limit;
 419             hasSep = false;
 420 
 421             //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
 422             precedingBackslash = false;
 423             while (keyLen < limit) {
 424                 char c = lr.lineBuf[keyLen];
 425                 //need check if escaped.
 426                 if ((c == '=' ||  c == ':') && !precedingBackslash) {
 427                     valueStart = keyLen + 1;
 428                     hasSep = true;
 429                     break;
 430                 } else if ((c == ' ' || c == '\t' ||  c == '\f') && !precedingBackslash) {
 431                     valueStart = keyLen + 1;
 432                     break;
 433                 }
 434                 if (c == '\\') {
 435                     precedingBackslash = !precedingBackslash;
 436                 } else {
 437                     precedingBackslash = false;
 438                 }
 439                 keyLen++;
 440             }
 441             while (valueStart < limit) {
 442                 char c = lr.lineBuf[valueStart];
 443                 if (c != ' ' && c != '\t' &&  c != '\f') {
 444                     if (!hasSep && (c == '=' ||  c == ':')) {
 445                         hasSep = true;
 446                     } else {
 447                         break;
 448                     }
 449                 }
 450                 valueStart++;
 451             }
 452             String key = loadConvert(lr.lineBuf, 0, keyLen, outBuffer);
 453             String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, outBuffer);
 454             put(key, value);
 455         }
 456     }
 457 
 458     /* Read in a "logical line" from an InputStream/Reader, skip all comment
 459      * and blank lines and filter out those leading whitespace characters
 460      * (\u0020, \u0009 and \u000c) from the beginning of a "natural line".
 461      * Method returns the char length of the "logical line" and stores
 462      * the line in "lineBuf".
 463      */
 464     private static class LineReader {
 465         LineReader(InputStream inStream) {
 466             this.inStream = inStream;
 467             inByteBuf = new byte[8192];
 468         }
 469 
 470         LineReader(Reader reader) {
 471             this.reader = reader;
 472             inCharBuf = new char[8192];
 473         }
 474 


 475         char[] lineBuf = new char[1024];
 476         private byte[] inByteBuf;
 477         private char[] inCharBuf;
 478         private int inLimit = 0;
 479         private int inOff = 0;
 480         private InputStream inStream;
 481         private Reader reader;
 482 
 483         int readLine() throws IOException {
 484             // use locals to optimize for interpreted performance
 485             int len = 0;
 486             int off = inOff;
 487             int limit = inLimit;
 488 
 489             boolean skipWhiteSpace = true;


 490             boolean appendedLineBegin = false;
 491             boolean precedingBackslash = false;
 492             boolean fromStream = inStream != null;
 493             byte[] byteBuf = inByteBuf;
 494             char[] charBuf = inCharBuf;
 495             char c;
 496 
 497             while (true) {
 498                 if (off >= limit) {
 499                     inLimit = limit = fromStream ? inStream.read(byteBuf)
 500                                                  : reader.read(charBuf);
 501                     if (limit <= 0) {
 502                         if (len == 0) {

 503                             return -1;
 504                         }
 505                         return precedingBackslash ? len - 1 : len;



 506                     }
 507                     off = 0;
 508                 }
 509                 if (fromStream) {
 510                     // The line below is equivalent to calling a
 511                     // ISO8859-1 decoder.
 512                     c = (char) (byteBuf[off++] & 0xFF);
 513                 } else {
 514                     c = charBuf[off++];






 515                 }
 516                 if (skipWhiteSpace) {
 517                     if (c == ' ' || c == '\t' || c == '\f') {
 518                         continue;
 519                     }
 520                     if (!appendedLineBegin && (c == '\r' || c == '\n')) {
 521                         continue;
 522                     }
 523                     skipWhiteSpace = false;
 524                     appendedLineBegin = false;
 525 
 526                 }
 527                 if (len == 0) { // still on a new logical line

 528                     if (c == '#' || c == '!') {
 529                         // Comment, quickly consume the rest of the line
 530 
 531                         // When checking for new line characters a range check,
 532                         // starting with the higher bound ('\r') means one less
 533                         // branch in the common case.
 534                         commentLoop: while (true) {
 535                             if (fromStream) {
 536                                 byte b;
 537                                 while (off < limit) {
 538                                     b = byteBuf[off++];
 539                                     if (b <= '\r' && (b == '\r' || b == '\n'))
 540                                         break commentLoop;
 541                                 }
 542                                 if (off == limit) {
 543                                     inLimit = limit = inStream.read(byteBuf);
 544                                     if (limit <= 0) { // EOF
 545                                         return -1;
 546                                     }
 547                                     off = 0;
 548                                 }
 549                             } else {
 550                                 while (off < limit) {
 551                                     c = charBuf[off++];
 552                                     if (c <= '\r' && (c == '\r' || c == '\n'))
 553                                         break commentLoop;
 554                                 }
 555                                 if (off == limit) {
 556                                     inLimit = limit = reader.read(charBuf);
 557                                     if (limit <= 0) { // EOF
 558                                         return -1;
 559                                     }
 560                                     off = 0;
 561                                 }
 562                             }
 563                         }
 564                         skipWhiteSpace = true;
 565                         continue;
 566                     }
 567                 }
 568 
 569                 if (c != '\n' && c != '\r') {
 570                     lineBuf[len++] = c;
 571                     if (len == lineBuf.length) {
 572                         int newLength = ArraysSupport.newLength(lineBuf.length, 1, lineBuf.length);



 573                         char[] buf = new char[newLength];
 574                         System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
 575                         lineBuf = buf;
 576                     }
 577                     // flip the preceding backslash flag
 578                     if (c == '\\') {
 579                         precedingBackslash = !precedingBackslash;
 580                     } else {
 581                         precedingBackslash = false;
 582                     }
 583                 } else {

 584                     // reached EOL
 585                     if (len == 0) {


 586                         skipWhiteSpace = true;
 587                         len = 0;
 588                         continue;
 589                     }
 590                     if (off >= limit) {
 591                         inLimit = limit = fromStream ? inStream.read(byteBuf)
 592                                                      : reader.read(charBuf);
 593                         off = 0;
 594                         if (limit <= 0) { // EOF
 595                             return precedingBackslash ? len - 1 : len;




 596                         }
 597                     }
 598                     if (precedingBackslash) {
 599                         // backslash at EOL is not part of the line
 600                         len -= 1;
 601                         // skip the leading whitespace characters in following line
 602                         skipWhiteSpace = true;
 603                         appendedLineBegin = true;
 604                         precedingBackslash = false;
 605                         // take care not to include any subsequent \n
 606                         if (c == '\r') {
 607                             if (fromStream) {
 608                                 if (byteBuf[off] == '\n') {
 609                                     off++;
 610                                 }
 611                             } else {
 612                                 if (charBuf[off] == '\n') {
 613                                     off++;
 614                                 }
 615                             }
 616                         }
 617                     } else {
 618                         inOff = off;
 619                         return len;
 620                     }
 621                 }
 622             }
 623         }
 624     }
 625 
 626     /*
 627      * Converts encoded \uxxxx to unicode chars
 628      * and changes special saved chars to their original forms
 629      */
 630     private String loadConvert(char[] in, int off, int len, StringBuilder out) {
 631         // Reset the shared buffer
 632         out.setLength(0);





 633         char aChar;


 634         int end = off + len;
 635 
 636         while (off < end) {
 637             aChar = in[off++];
 638             if (aChar == '\\') {
 639                 // No need to bounds check since LineReader::readLine excludes
 640                 // unescaped \s at the end of the line
 641                 aChar = in[off++];
 642                 if(aChar == 'u') {
 643                     // Read the xxxx
 644                     if (off > end - 4)
 645                         throw new IllegalArgumentException(
 646                                      "Malformed \\uxxxx encoding.");
 647                     int value = 0;
 648                     for (int i = 0; i < 4; i++) {
 649                         aChar = in[off++];
 650                         switch (aChar) {
 651                           case '0': case '1': case '2': case '3': case '4':
 652                           case '5': case '6': case '7': case '8': case '9':
 653                              value = (value << 4) + aChar - '0';
 654                              break;
 655                           case 'a': case 'b': case 'c':
 656                           case 'd': case 'e': case 'f':
 657                              value = (value << 4) + 10 + aChar - 'a';
 658                              break;
 659                           case 'A': case 'B': case 'C':
 660                           case 'D': case 'E': case 'F':
 661                              value = (value << 4) + 10 + aChar - 'A';
 662                              break;
 663                           default:
 664                               throw new IllegalArgumentException(
 665                                            "Malformed \\uxxxx encoding.");
 666                         }
 667                      }
 668                     out.append((char)value);
 669                 } else {
 670                     if (aChar == 't') aChar = '\t';
 671                     else if (aChar == 'r') aChar = '\r';
 672                     else if (aChar == 'n') aChar = '\n';
 673                     else if (aChar == 'f') aChar = '\f';
 674                     out.append(aChar);
 675                 }
 676             } else {
 677                 out.append(aChar);
 678             }
 679         }
 680         return out.toString();
 681     }
 682 
 683     /*
 684      * Converts unicodes to encoded \uxxxx and escapes
 685      * special characters with a preceding slash
 686      */
 687     private String saveConvert(String theString,
 688                                boolean escapeSpace,
 689                                boolean escapeUnicode) {
 690         int len = theString.length();
 691         int bufLen = len * 2;
 692         if (bufLen < 0) {
 693             bufLen = Integer.MAX_VALUE;
 694         }
 695         StringBuilder outBuffer = new StringBuilder(bufLen);
 696 
 697         for(int x=0; x<len; x++) {
 698             char aChar = theString.charAt(x);
 699             // Handle common case first, selecting largest block that
 700             // avoids the specials below


< prev index next >