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™ 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™ 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
|