1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import com.google.protobuf.Descriptors.Descriptor; 34 import com.google.protobuf.Descriptors.EnumDescriptor; 35 import com.google.protobuf.Descriptors.EnumValueDescriptor; 36 import com.google.protobuf.Descriptors.FieldDescriptor; 37 38 import java.io.IOException; 39 import java.math.BigInteger; 40 import java.nio.CharBuffer; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.Locale; 44 import java.util.Map; 45 import java.util.logging.Logger; 46 import java.util.regex.Matcher; 47 import java.util.regex.Pattern; 48 49 /** 50 * Provide text parsing and formatting support for proto2 instances. 51 * The implementation largely follows google/protobuf/text_format.cc. 52 * 53 * @author wenboz@google.com Wenbo Zhu 54 * @author kenton@google.com Kenton Varda 55 */ 56 public final class TextFormat { TextFormat()57 private TextFormat() {} 58 59 private static final Logger logger = 60 Logger.getLogger(TextFormat.class.getName()); 61 62 private static final Printer DEFAULT_PRINTER = new Printer(); 63 private static final Printer SINGLE_LINE_PRINTER = 64 (new Printer()).setSingleLineMode(true); 65 private static final Printer UNICODE_PRINTER = 66 (new Printer()).setEscapeNonAscii(false); 67 68 /** 69 * Outputs a textual representation of the Protocol Message supplied into 70 * the parameter output. (This representation is the new version of the 71 * classic "ProtocolPrinter" output from the original Protocol Buffer system) 72 */ print( final MessageOrBuilder message, final Appendable output)73 public static void print( 74 final MessageOrBuilder message, final Appendable output) 75 throws IOException { 76 DEFAULT_PRINTER.print(message, new TextGenerator(output)); 77 } 78 79 /** Outputs a textual representation of {@code fields} to {@code output}. */ print(final UnknownFieldSet fields, final Appendable output)80 public static void print(final UnknownFieldSet fields, 81 final Appendable output) 82 throws IOException { 83 DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output)); 84 } 85 86 /** 87 * Same as {@code print()}, except that non-ASCII characters are not 88 * escaped. 89 */ printUnicode( final MessageOrBuilder message, final Appendable output)90 public static void printUnicode( 91 final MessageOrBuilder message, final Appendable output) 92 throws IOException { 93 UNICODE_PRINTER.print(message, new TextGenerator(output)); 94 } 95 96 /** 97 * Same as {@code print()}, except that non-ASCII characters are not 98 * escaped. 99 */ printUnicode(final UnknownFieldSet fields, final Appendable output)100 public static void printUnicode(final UnknownFieldSet fields, 101 final Appendable output) 102 throws IOException { 103 UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(output)); 104 } 105 106 /** 107 * Generates a human readable form of this message, useful for debugging and 108 * other purposes, with no newline characters. 109 */ shortDebugString(final MessageOrBuilder message)110 public static String shortDebugString(final MessageOrBuilder message) { 111 try { 112 final StringBuilder sb = new StringBuilder(); 113 SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb)); 114 // Single line mode currently might have an extra space at the end. 115 return sb.toString().trim(); 116 } catch (IOException e) { 117 throw new IllegalStateException(e); 118 } 119 } 120 121 /** 122 * Generates a human readable form of the field, useful for debugging 123 * and other purposes, with no newline characters. 124 */ shortDebugString(final FieldDescriptor field, final Object value)125 public static String shortDebugString(final FieldDescriptor field, 126 final Object value) { 127 try { 128 final StringBuilder sb = new StringBuilder(); 129 SINGLE_LINE_PRINTER.printField(field, value, new TextGenerator(sb)); 130 return sb.toString().trim(); 131 } catch (IOException e) { 132 throw new IllegalStateException(e); 133 } 134 } 135 136 /** 137 * Generates a human readable form of the unknown fields, useful for debugging 138 * and other purposes, with no newline characters. 139 */ shortDebugString(final UnknownFieldSet fields)140 public static String shortDebugString(final UnknownFieldSet fields) { 141 try { 142 final StringBuilder sb = new StringBuilder(); 143 SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb)); 144 // Single line mode currently might have an extra space at the end. 145 return sb.toString().trim(); 146 } catch (IOException e) { 147 throw new IllegalStateException(e); 148 } 149 } 150 151 /** 152 * Like {@code print()}, but writes directly to a {@code String} and 153 * returns it. 154 */ printToString(final MessageOrBuilder message)155 public static String printToString(final MessageOrBuilder message) { 156 try { 157 final StringBuilder text = new StringBuilder(); 158 print(message, text); 159 return text.toString(); 160 } catch (IOException e) { 161 throw new IllegalStateException(e); 162 } 163 } 164 165 /** 166 * Like {@code print()}, but writes directly to a {@code String} and 167 * returns it. 168 */ printToString(final UnknownFieldSet fields)169 public static String printToString(final UnknownFieldSet fields) { 170 try { 171 final StringBuilder text = new StringBuilder(); 172 print(fields, text); 173 return text.toString(); 174 } catch (IOException e) { 175 throw new IllegalStateException(e); 176 } 177 } 178 179 /** 180 * Same as {@code printToString()}, except that non-ASCII characters 181 * in string type fields are not escaped in backslash+octals. 182 */ printToUnicodeString(final MessageOrBuilder message)183 public static String printToUnicodeString(final MessageOrBuilder message) { 184 try { 185 final StringBuilder text = new StringBuilder(); 186 UNICODE_PRINTER.print(message, new TextGenerator(text)); 187 return text.toString(); 188 } catch (IOException e) { 189 throw new IllegalStateException(e); 190 } 191 } 192 193 /** 194 * Same as {@code printToString()}, except that non-ASCII characters 195 * in string type fields are not escaped in backslash+octals. 196 */ printToUnicodeString(final UnknownFieldSet fields)197 public static String printToUnicodeString(final UnknownFieldSet fields) { 198 try { 199 final StringBuilder text = new StringBuilder(); 200 UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(text)); 201 return text.toString(); 202 } catch (IOException e) { 203 throw new IllegalStateException(e); 204 } 205 } 206 printField(final FieldDescriptor field, final Object value, final Appendable output)207 public static void printField(final FieldDescriptor field, 208 final Object value, 209 final Appendable output) 210 throws IOException { 211 DEFAULT_PRINTER.printField(field, value, new TextGenerator(output)); 212 } 213 printFieldToString(final FieldDescriptor field, final Object value)214 public static String printFieldToString(final FieldDescriptor field, 215 final Object value) { 216 try { 217 final StringBuilder text = new StringBuilder(); 218 printField(field, value, text); 219 return text.toString(); 220 } catch (IOException e) { 221 throw new IllegalStateException(e); 222 } 223 } 224 225 /** 226 * Outputs a textual representation of the value of given field value. 227 * 228 * @param field the descriptor of the field 229 * @param value the value of the field 230 * @param output the output to which to append the formatted value 231 * @throws ClassCastException if the value is not appropriate for the 232 * given field descriptor 233 * @throws IOException if there is an exception writing to the output 234 */ printFieldValue(final FieldDescriptor field, final Object value, final Appendable output)235 public static void printFieldValue(final FieldDescriptor field, 236 final Object value, 237 final Appendable output) 238 throws IOException { 239 DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output)); 240 } 241 242 /** 243 * Outputs a textual representation of the value of an unknown field. 244 * 245 * @param tag the field's tag number 246 * @param value the value of the field 247 * @param output the output to which to append the formatted value 248 * @throws ClassCastException if the value is not appropriate for the 249 * given field descriptor 250 * @throws IOException if there is an exception writing to the output 251 */ printUnknownFieldValue(final int tag, final Object value, final Appendable output)252 public static void printUnknownFieldValue(final int tag, 253 final Object value, 254 final Appendable output) 255 throws IOException { 256 printUnknownFieldValue(tag, value, new TextGenerator(output)); 257 } 258 printUnknownFieldValue(final int tag, final Object value, final TextGenerator generator)259 private static void printUnknownFieldValue(final int tag, 260 final Object value, 261 final TextGenerator generator) 262 throws IOException { 263 switch (WireFormat.getTagWireType(tag)) { 264 case WireFormat.WIRETYPE_VARINT: 265 generator.print(unsignedToString((Long) value)); 266 break; 267 case WireFormat.WIRETYPE_FIXED32: 268 generator.print( 269 String.format((Locale) null, "0x%08x", (Integer) value)); 270 break; 271 case WireFormat.WIRETYPE_FIXED64: 272 generator.print(String.format((Locale) null, "0x%016x", (Long) value)); 273 break; 274 case WireFormat.WIRETYPE_LENGTH_DELIMITED: 275 generator.print("\""); 276 generator.print(escapeBytes((ByteString) value)); 277 generator.print("\""); 278 break; 279 case WireFormat.WIRETYPE_START_GROUP: 280 DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator); 281 break; 282 default: 283 throw new IllegalArgumentException("Bad tag: " + tag); 284 } 285 } 286 287 /** Helper class for converting protobufs to text. */ 288 private static final class Printer { 289 /** Whether to omit newlines from the output. */ 290 boolean singleLineMode = false; 291 292 /** Whether to escape non ASCII characters with backslash and octal. */ 293 boolean escapeNonAscii = true; 294 Printer()295 private Printer() {} 296 297 /** Setter of singleLineMode */ setSingleLineMode(boolean singleLineMode)298 private Printer setSingleLineMode(boolean singleLineMode) { 299 this.singleLineMode = singleLineMode; 300 return this; 301 } 302 303 /** Setter of escapeNonAscii */ setEscapeNonAscii(boolean escapeNonAscii)304 private Printer setEscapeNonAscii(boolean escapeNonAscii) { 305 this.escapeNonAscii = escapeNonAscii; 306 return this; 307 } 308 print( final MessageOrBuilder message, final TextGenerator generator)309 private void print( 310 final MessageOrBuilder message, final TextGenerator generator) 311 throws IOException { 312 for (Map.Entry<FieldDescriptor, Object> field 313 : message.getAllFields().entrySet()) { 314 printField(field.getKey(), field.getValue(), generator); 315 } 316 printUnknownFields(message.getUnknownFields(), generator); 317 } 318 printField(final FieldDescriptor field, final Object value, final TextGenerator generator)319 private void printField(final FieldDescriptor field, final Object value, 320 final TextGenerator generator) throws IOException { 321 if (field.isRepeated()) { 322 // Repeated field. Print each element. 323 for (Object element : (List<?>) value) { 324 printSingleField(field, element, generator); 325 } 326 } else { 327 printSingleField(field, value, generator); 328 } 329 } 330 printSingleField(final FieldDescriptor field, final Object value, final TextGenerator generator)331 private void printSingleField(final FieldDescriptor field, 332 final Object value, 333 final TextGenerator generator) 334 throws IOException { 335 if (field.isExtension()) { 336 generator.print("["); 337 // We special-case MessageSet elements for compatibility with proto1. 338 if (field.getContainingType().getOptions().getMessageSetWireFormat() 339 && (field.getType() == FieldDescriptor.Type.MESSAGE) 340 && (field.isOptional()) 341 // object equality 342 && (field.getExtensionScope() == field.getMessageType())) { 343 generator.print(field.getMessageType().getFullName()); 344 } else { 345 generator.print(field.getFullName()); 346 } 347 generator.print("]"); 348 } else { 349 if (field.getType() == FieldDescriptor.Type.GROUP) { 350 // Groups must be serialized with their original capitalization. 351 generator.print(field.getMessageType().getName()); 352 } else { 353 generator.print(field.getName()); 354 } 355 } 356 357 if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 358 if (singleLineMode) { 359 generator.print(" { "); 360 } else { 361 generator.print(" {\n"); 362 generator.indent(); 363 } 364 } else { 365 generator.print(": "); 366 } 367 368 printFieldValue(field, value, generator); 369 370 if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 371 if (singleLineMode) { 372 generator.print("} "); 373 } else { 374 generator.outdent(); 375 generator.print("}\n"); 376 } 377 } else { 378 if (singleLineMode) { 379 generator.print(" "); 380 } else { 381 generator.print("\n"); 382 } 383 } 384 } 385 printFieldValue(final FieldDescriptor field, final Object value, final TextGenerator generator)386 private void printFieldValue(final FieldDescriptor field, 387 final Object value, 388 final TextGenerator generator) 389 throws IOException { 390 switch (field.getType()) { 391 case INT32: 392 case SINT32: 393 case SFIXED32: 394 generator.print(((Integer) value).toString()); 395 break; 396 397 case INT64: 398 case SINT64: 399 case SFIXED64: 400 generator.print(((Long) value).toString()); 401 break; 402 403 case BOOL: 404 generator.print(((Boolean) value).toString()); 405 break; 406 407 case FLOAT: 408 generator.print(((Float) value).toString()); 409 break; 410 411 case DOUBLE: 412 generator.print(((Double) value).toString()); 413 break; 414 415 case UINT32: 416 case FIXED32: 417 generator.print(unsignedToString((Integer) value)); 418 break; 419 420 case UINT64: 421 case FIXED64: 422 generator.print(unsignedToString((Long) value)); 423 break; 424 425 case STRING: 426 generator.print("\""); 427 generator.print(escapeNonAscii 428 ? TextFormatEscaper.escapeText((String) value) 429 : escapeDoubleQuotesAndBackslashes((String) value) 430 .replace("\n", "\\n")); 431 generator.print("\""); 432 break; 433 434 case BYTES: 435 generator.print("\""); 436 if (value instanceof ByteString) { 437 generator.print(escapeBytes((ByteString) value)); 438 } else { 439 generator.print(escapeBytes((byte[]) value)); 440 } 441 generator.print("\""); 442 break; 443 444 case ENUM: 445 generator.print(((EnumValueDescriptor) value).getName()); 446 break; 447 448 case MESSAGE: 449 case GROUP: 450 print((Message) value, generator); 451 break; 452 } 453 } 454 printUnknownFields(final UnknownFieldSet unknownFields, final TextGenerator generator)455 private void printUnknownFields(final UnknownFieldSet unknownFields, 456 final TextGenerator generator) 457 throws IOException { 458 for (Map.Entry<Integer, UnknownFieldSet.Field> entry : 459 unknownFields.asMap().entrySet()) { 460 final int number = entry.getKey(); 461 final UnknownFieldSet.Field field = entry.getValue(); 462 printUnknownField(number, WireFormat.WIRETYPE_VARINT, 463 field.getVarintList(), generator); 464 printUnknownField(number, WireFormat.WIRETYPE_FIXED32, 465 field.getFixed32List(), generator); 466 printUnknownField(number, WireFormat.WIRETYPE_FIXED64, 467 field.getFixed64List(), generator); 468 printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED, 469 field.getLengthDelimitedList(), generator); 470 for (final UnknownFieldSet value : field.getGroupList()) { 471 generator.print(entry.getKey().toString()); 472 if (singleLineMode) { 473 generator.print(" { "); 474 } else { 475 generator.print(" {\n"); 476 generator.indent(); 477 } 478 printUnknownFields(value, generator); 479 if (singleLineMode) { 480 generator.print("} "); 481 } else { 482 generator.outdent(); 483 generator.print("}\n"); 484 } 485 } 486 } 487 } 488 printUnknownField(final int number, final int wireType, final List<?> values, final TextGenerator generator)489 private void printUnknownField(final int number, 490 final int wireType, 491 final List<?> values, 492 final TextGenerator generator) 493 throws IOException { 494 for (final Object value : values) { 495 generator.print(String.valueOf(number)); 496 generator.print(": "); 497 printUnknownFieldValue(wireType, value, generator); 498 generator.print(singleLineMode ? " " : "\n"); 499 } 500 } 501 } 502 503 /** Convert an unsigned 32-bit integer to a string. */ unsignedToString(final int value)504 public static String unsignedToString(final int value) { 505 if (value >= 0) { 506 return Integer.toString(value); 507 } else { 508 return Long.toString(value & 0x00000000FFFFFFFFL); 509 } 510 } 511 512 /** Convert an unsigned 64-bit integer to a string. */ unsignedToString(final long value)513 public static String unsignedToString(final long value) { 514 if (value >= 0) { 515 return Long.toString(value); 516 } else { 517 // Pull off the most-significant bit so that BigInteger doesn't think 518 // the number is negative, then set it again using setBit(). 519 return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL) 520 .setBit(63).toString(); 521 } 522 } 523 524 /** 525 * An inner class for writing text to the output stream. 526 */ 527 private static final class TextGenerator { 528 private final Appendable output; 529 private final StringBuilder indent = new StringBuilder(); 530 private boolean atStartOfLine = true; 531 TextGenerator(final Appendable output)532 private TextGenerator(final Appendable output) { 533 this.output = output; 534 } 535 536 /** 537 * Indent text by two spaces. After calling Indent(), two spaces will be 538 * inserted at the beginning of each line of text. Indent() may be called 539 * multiple times to produce deeper indents. 540 */ indent()541 public void indent() { 542 indent.append(" "); 543 } 544 545 /** 546 * Reduces the current indent level by two spaces, or crashes if the indent 547 * level is zero. 548 */ outdent()549 public void outdent() { 550 final int length = indent.length(); 551 if (length == 0) { 552 throw new IllegalArgumentException( 553 " Outdent() without matching Indent()."); 554 } 555 indent.delete(length - 2, length); 556 } 557 558 /** 559 * Print text to the output stream. 560 */ print(final CharSequence text)561 public void print(final CharSequence text) throws IOException { 562 final int size = text.length(); 563 int pos = 0; 564 565 for (int i = 0; i < size; i++) { 566 if (text.charAt(i) == '\n') { 567 write(text.subSequence(pos, i + 1)); 568 pos = i + 1; 569 atStartOfLine = true; 570 } 571 } 572 write(text.subSequence(pos, size)); 573 } 574 write(final CharSequence data)575 private void write(final CharSequence data) throws IOException { 576 if (data.length() == 0) { 577 return; 578 } 579 if (atStartOfLine) { 580 atStartOfLine = false; 581 output.append(indent); 582 } 583 output.append(data); 584 } 585 } 586 587 // ================================================================= 588 // Parsing 589 590 /** 591 * Represents a stream of tokens parsed from a {@code String}. 592 * 593 * <p>The Java standard library provides many classes that you might think 594 * would be useful for implementing this, but aren't. For example: 595 * 596 * <ul> 597 * <li>{@code java.io.StreamTokenizer}: This almost does what we want -- or, 598 * at least, something that would get us close to what we want -- except 599 * for one fatal flaw: It automatically un-escapes strings using Java 600 * escape sequences, which do not include all the escape sequences we 601 * need to support (e.g. '\x'). 602 * <li>{@code java.util.Scanner}: This seems like a great way at least to 603 * parse regular expressions out of a stream (so we wouldn't have to load 604 * the entire input into a single string before parsing). Sadly, 605 * {@code Scanner} requires that tokens be delimited with some delimiter. 606 * Thus, although the text "foo:" should parse to two tokens ("foo" and 607 * ":"), {@code Scanner} would recognize it only as a single token. 608 * Furthermore, {@code Scanner} provides no way to inspect the contents 609 * of delimiters, making it impossible to keep track of line and column 610 * numbers. 611 * </ul> 612 * 613 * <p>Luckily, Java's regular expression support does manage to be useful to 614 * us. (Barely: We need {@code Matcher.usePattern()}, which is new in 615 * Java 1.5.) So, we can use that, at least. Unfortunately, this implies 616 * that we need to have the entire input in one contiguous string. 617 */ 618 private static final class Tokenizer { 619 private final CharSequence text; 620 private final Matcher matcher; 621 private String currentToken; 622 623 // The character index within this.text at which the current token begins. 624 private int pos = 0; 625 626 // The line and column numbers of the current token. 627 private int line = 0; 628 private int column = 0; 629 630 // The line and column numbers of the previous token (allows throwing 631 // errors *after* consuming). 632 private int previousLine = 0; 633 private int previousColumn = 0; 634 635 // We use possessive quantifiers (*+ and ++) because otherwise the Java 636 // regex matcher has stack overflows on large inputs. 637 private static final Pattern WHITESPACE = 638 Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); 639 private static final Pattern TOKEN = Pattern.compile( 640 "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier 641 "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number 642 "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string 643 "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string 644 Pattern.MULTILINE); 645 646 private static final Pattern DOUBLE_INFINITY = Pattern.compile( 647 "-?inf(inity)?", 648 Pattern.CASE_INSENSITIVE); 649 private static final Pattern FLOAT_INFINITY = Pattern.compile( 650 "-?inf(inity)?f?", 651 Pattern.CASE_INSENSITIVE); 652 private static final Pattern FLOAT_NAN = Pattern.compile( 653 "nanf?", 654 Pattern.CASE_INSENSITIVE); 655 656 /** Construct a tokenizer that parses tokens from the given text. */ Tokenizer(final CharSequence text)657 private Tokenizer(final CharSequence text) { 658 this.text = text; 659 this.matcher = WHITESPACE.matcher(text); 660 skipWhitespace(); 661 nextToken(); 662 } 663 getLine()664 int getLine() { 665 return line; 666 } 667 getColumn()668 int getColumn() { 669 return column; 670 } 671 672 /** Are we at the end of the input? */ atEnd()673 public boolean atEnd() { 674 return currentToken.length() == 0; 675 } 676 677 /** Advance to the next token. */ nextToken()678 public void nextToken() { 679 previousLine = line; 680 previousColumn = column; 681 682 // Advance the line counter to the current position. 683 while (pos < matcher.regionStart()) { 684 if (text.charAt(pos) == '\n') { 685 ++line; 686 column = 0; 687 } else { 688 ++column; 689 } 690 ++pos; 691 } 692 693 // Match the next token. 694 if (matcher.regionStart() == matcher.regionEnd()) { 695 // EOF 696 currentToken = ""; 697 } else { 698 matcher.usePattern(TOKEN); 699 if (matcher.lookingAt()) { 700 currentToken = matcher.group(); 701 matcher.region(matcher.end(), matcher.regionEnd()); 702 } else { 703 // Take one character. 704 currentToken = String.valueOf(text.charAt(pos)); 705 matcher.region(pos + 1, matcher.regionEnd()); 706 } 707 708 skipWhitespace(); 709 } 710 } 711 712 /** 713 * Skip over any whitespace so that the matcher region starts at the next 714 * token. 715 */ skipWhitespace()716 private void skipWhitespace() { 717 matcher.usePattern(WHITESPACE); 718 if (matcher.lookingAt()) { 719 matcher.region(matcher.end(), matcher.regionEnd()); 720 } 721 } 722 723 /** 724 * If the next token exactly matches {@code token}, consume it and return 725 * {@code true}. Otherwise, return {@code false} without doing anything. 726 */ tryConsume(final String token)727 public boolean tryConsume(final String token) { 728 if (currentToken.equals(token)) { 729 nextToken(); 730 return true; 731 } else { 732 return false; 733 } 734 } 735 736 /** 737 * If the next token exactly matches {@code token}, consume it. Otherwise, 738 * throw a {@link ParseException}. 739 */ consume(final String token)740 public void consume(final String token) throws ParseException { 741 if (!tryConsume(token)) { 742 throw parseException("Expected \"" + token + "\"."); 743 } 744 } 745 746 /** 747 * Returns {@code true} if the next token is an integer, but does 748 * not consume it. 749 */ lookingAtInteger()750 public boolean lookingAtInteger() { 751 if (currentToken.length() == 0) { 752 return false; 753 } 754 755 final char c = currentToken.charAt(0); 756 return ('0' <= c && c <= '9') 757 || c == '-' || c == '+'; 758 } 759 760 /** 761 * Returns {@code true} if the current token's text is equal to that 762 * specified. 763 */ lookingAt(String text)764 public boolean lookingAt(String text) { 765 return currentToken.equals(text); 766 } 767 768 /** 769 * If the next token is an identifier, consume it and return its value. 770 * Otherwise, throw a {@link ParseException}. 771 */ consumeIdentifier()772 public String consumeIdentifier() throws ParseException { 773 for (int i = 0; i < currentToken.length(); i++) { 774 final char c = currentToken.charAt(i); 775 if (('a' <= c && c <= 'z') 776 || ('A' <= c && c <= 'Z') 777 || ('0' <= c && c <= '9') 778 || (c == '_') || (c == '.')) { 779 // OK 780 } else { 781 throw parseException( 782 "Expected identifier. Found '" + currentToken + "'"); 783 } 784 } 785 786 final String result = currentToken; 787 nextToken(); 788 return result; 789 } 790 791 /** 792 * If the next token is an identifier, consume it and return {@code true}. 793 * Otherwise, return {@code false} without doing anything. 794 */ tryConsumeIdentifier()795 public boolean tryConsumeIdentifier() { 796 try { 797 consumeIdentifier(); 798 return true; 799 } catch (ParseException e) { 800 return false; 801 } 802 } 803 804 /** 805 * If the next token is a 32-bit signed integer, consume it and return its 806 * value. Otherwise, throw a {@link ParseException}. 807 */ consumeInt32()808 public int consumeInt32() throws ParseException { 809 try { 810 final int result = parseInt32(currentToken); 811 nextToken(); 812 return result; 813 } catch (NumberFormatException e) { 814 throw integerParseException(e); 815 } 816 } 817 818 /** 819 * If the next token is a 32-bit unsigned integer, consume it and return its 820 * value. Otherwise, throw a {@link ParseException}. 821 */ consumeUInt32()822 public int consumeUInt32() throws ParseException { 823 try { 824 final int result = parseUInt32(currentToken); 825 nextToken(); 826 return result; 827 } catch (NumberFormatException e) { 828 throw integerParseException(e); 829 } 830 } 831 832 /** 833 * If the next token is a 64-bit signed integer, consume it and return its 834 * value. Otherwise, throw a {@link ParseException}. 835 */ consumeInt64()836 public long consumeInt64() throws ParseException { 837 try { 838 final long result = parseInt64(currentToken); 839 nextToken(); 840 return result; 841 } catch (NumberFormatException e) { 842 throw integerParseException(e); 843 } 844 } 845 846 /** 847 * If the next token is a 64-bit signed integer, consume it and return 848 * {@code true}. Otherwise, return {@code false} without doing anything. 849 */ tryConsumeInt64()850 public boolean tryConsumeInt64() { 851 try { 852 consumeInt64(); 853 return true; 854 } catch (ParseException e) { 855 return false; 856 } 857 } 858 859 /** 860 * If the next token is a 64-bit unsigned integer, consume it and return its 861 * value. Otherwise, throw a {@link ParseException}. 862 */ consumeUInt64()863 public long consumeUInt64() throws ParseException { 864 try { 865 final long result = parseUInt64(currentToken); 866 nextToken(); 867 return result; 868 } catch (NumberFormatException e) { 869 throw integerParseException(e); 870 } 871 } 872 873 /** 874 * If the next token is a 64-bit unsigned integer, consume it and return 875 * {@code true}. Otherwise, return {@code false} without doing anything. 876 */ tryConsumeUInt64()877 public boolean tryConsumeUInt64() { 878 try { 879 consumeUInt64(); 880 return true; 881 } catch (ParseException e) { 882 return false; 883 } 884 } 885 886 /** 887 * If the next token is a double, consume it and return its value. 888 * Otherwise, throw a {@link ParseException}. 889 */ consumeDouble()890 public double consumeDouble() throws ParseException { 891 // We need to parse infinity and nan separately because 892 // Double.parseDouble() does not accept "inf", "infinity", or "nan". 893 if (DOUBLE_INFINITY.matcher(currentToken).matches()) { 894 final boolean negative = currentToken.startsWith("-"); 895 nextToken(); 896 return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; 897 } 898 if (currentToken.equalsIgnoreCase("nan")) { 899 nextToken(); 900 return Double.NaN; 901 } 902 try { 903 final double result = Double.parseDouble(currentToken); 904 nextToken(); 905 return result; 906 } catch (NumberFormatException e) { 907 throw floatParseException(e); 908 } 909 } 910 911 /** 912 * If the next token is a double, consume it and return {@code true}. 913 * Otherwise, return {@code false} without doing anything. 914 */ tryConsumeDouble()915 public boolean tryConsumeDouble() { 916 try { 917 consumeDouble(); 918 return true; 919 } catch (ParseException e) { 920 return false; 921 } 922 } 923 924 /** 925 * If the next token is a float, consume it and return its value. 926 * Otherwise, throw a {@link ParseException}. 927 */ consumeFloat()928 public float consumeFloat() throws ParseException { 929 // We need to parse infinity and nan separately because 930 // Float.parseFloat() does not accept "inf", "infinity", or "nan". 931 if (FLOAT_INFINITY.matcher(currentToken).matches()) { 932 final boolean negative = currentToken.startsWith("-"); 933 nextToken(); 934 return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; 935 } 936 if (FLOAT_NAN.matcher(currentToken).matches()) { 937 nextToken(); 938 return Float.NaN; 939 } 940 try { 941 final float result = Float.parseFloat(currentToken); 942 nextToken(); 943 return result; 944 } catch (NumberFormatException e) { 945 throw floatParseException(e); 946 } 947 } 948 949 /** 950 * If the next token is a float, consume it and return {@code true}. 951 * Otherwise, return {@code false} without doing anything. 952 */ tryConsumeFloat()953 public boolean tryConsumeFloat() { 954 try { 955 consumeFloat(); 956 return true; 957 } catch (ParseException e) { 958 return false; 959 } 960 } 961 962 /** 963 * If the next token is a boolean, consume it and return its value. 964 * Otherwise, throw a {@link ParseException}. 965 */ consumeBoolean()966 public boolean consumeBoolean() throws ParseException { 967 if (currentToken.equals("true") 968 || currentToken.equals("True") 969 || currentToken.equals("t") 970 || currentToken.equals("1")) { 971 nextToken(); 972 return true; 973 } else if (currentToken.equals("false") 974 || currentToken.equals("False") 975 || currentToken.equals("f") 976 || currentToken.equals("0")) { 977 nextToken(); 978 return false; 979 } else { 980 throw parseException("Expected \"true\" or \"false\"."); 981 } 982 } 983 984 /** 985 * If the next token is a string, consume it and return its (unescaped) 986 * value. Otherwise, throw a {@link ParseException}. 987 */ consumeString()988 public String consumeString() throws ParseException { 989 return consumeByteString().toStringUtf8(); 990 } 991 992 /** 993 * If the next token is a string, consume it and return true. Otherwise, 994 * return false. 995 */ tryConsumeString()996 public boolean tryConsumeString() { 997 try { 998 consumeString(); 999 return true; 1000 } catch (ParseException e) { 1001 return false; 1002 } 1003 } 1004 1005 /** 1006 * If the next token is a string, consume it, unescape it as a 1007 * {@link ByteString}, and return it. Otherwise, throw a 1008 * {@link ParseException}. 1009 */ consumeByteString()1010 public ByteString consumeByteString() throws ParseException { 1011 List<ByteString> list = new ArrayList<ByteString>(); 1012 consumeByteString(list); 1013 while (currentToken.startsWith("'") || currentToken.startsWith("\"")) { 1014 consumeByteString(list); 1015 } 1016 return ByteString.copyFrom(list); 1017 } 1018 1019 /** 1020 * Like {@link #consumeByteString()} but adds each token of the string to 1021 * the given list. String literals (whether bytes or text) may come in 1022 * multiple adjacent tokens which are automatically concatenated, like in 1023 * C or Python. 1024 */ consumeByteString(List<ByteString> list)1025 private void consumeByteString(List<ByteString> list) 1026 throws ParseException { 1027 final char quote = currentToken.length() > 0 1028 ? currentToken.charAt(0) 1029 : '\0'; 1030 if (quote != '\"' && quote != '\'') { 1031 throw parseException("Expected string."); 1032 } 1033 1034 if (currentToken.length() < 2 1035 || currentToken.charAt(currentToken.length() - 1) != quote) { 1036 throw parseException("String missing ending quote."); 1037 } 1038 1039 try { 1040 final String escaped = 1041 currentToken.substring(1, currentToken.length() - 1); 1042 final ByteString result = unescapeBytes(escaped); 1043 nextToken(); 1044 list.add(result); 1045 } catch (InvalidEscapeSequenceException e) { 1046 throw parseException(e.getMessage()); 1047 } 1048 } 1049 1050 /** 1051 * Returns a {@link ParseException} with the current line and column 1052 * numbers in the description, suitable for throwing. 1053 */ parseException(final String description)1054 public ParseException parseException(final String description) { 1055 // Note: People generally prefer one-based line and column numbers. 1056 return new ParseException( 1057 line + 1, column + 1, description); 1058 } 1059 1060 /** 1061 * Returns a {@link ParseException} with the line and column numbers of 1062 * the previous token in the description, suitable for throwing. 1063 */ parseExceptionPreviousToken( final String description)1064 public ParseException parseExceptionPreviousToken( 1065 final String description) { 1066 // Note: People generally prefer one-based line and column numbers. 1067 return new ParseException( 1068 previousLine + 1, previousColumn + 1, description); 1069 } 1070 1071 /** 1072 * Constructs an appropriate {@link ParseException} for the given 1073 * {@code NumberFormatException} when trying to parse an integer. 1074 */ integerParseException( final NumberFormatException e)1075 private ParseException integerParseException( 1076 final NumberFormatException e) { 1077 return parseException("Couldn't parse integer: " + e.getMessage()); 1078 } 1079 1080 /** 1081 * Constructs an appropriate {@link ParseException} for the given 1082 * {@code NumberFormatException} when trying to parse a float or double. 1083 */ floatParseException(final NumberFormatException e)1084 private ParseException floatParseException(final NumberFormatException e) { 1085 return parseException("Couldn't parse number: " + e.getMessage()); 1086 } 1087 1088 /** 1089 * Returns a {@link UnknownFieldParseException} with the line and column 1090 * numbers of the previous token in the description, and the unknown field 1091 * name, suitable for throwing. 1092 */ unknownFieldParseExceptionPreviousToken( final String unknownField, final String description)1093 public UnknownFieldParseException unknownFieldParseExceptionPreviousToken( 1094 final String unknownField, final String description) { 1095 // Note: People generally prefer one-based line and column numbers. 1096 return new UnknownFieldParseException( 1097 previousLine + 1, previousColumn + 1, unknownField, description); 1098 } 1099 } 1100 1101 /** Thrown when parsing an invalid text format message. */ 1102 public static class ParseException extends IOException { 1103 private static final long serialVersionUID = 3196188060225107702L; 1104 1105 private final int line; 1106 private final int column; 1107 1108 /** Create a new instance, with -1 as the line and column numbers. */ ParseException(final String message)1109 public ParseException(final String message) { 1110 this(-1, -1, message); 1111 } 1112 1113 /** 1114 * Create a new instance 1115 * 1116 * @param line the line number where the parse error occurred, 1117 * using 1-offset. 1118 * @param column the column number where the parser error occurred, 1119 * using 1-offset. 1120 */ ParseException(final int line, final int column, final String message)1121 public ParseException(final int line, final int column, 1122 final String message) { 1123 super(Integer.toString(line) + ":" + column + ": " + message); 1124 this.line = line; 1125 this.column = column; 1126 } 1127 1128 /** 1129 * Return the line where the parse exception occurred, or -1 when 1130 * none is provided. The value is specified as 1-offset, so the first 1131 * line is line 1. 1132 */ getLine()1133 public int getLine() { 1134 return line; 1135 } 1136 1137 /** 1138 * Return the column where the parse exception occurred, or -1 when 1139 * none is provided. The value is specified as 1-offset, so the first 1140 * line is line 1. 1141 */ getColumn()1142 public int getColumn() { 1143 return column; 1144 } 1145 } 1146 1147 /** 1148 * Thrown when encountering an unknown field while parsing 1149 * a text format message. 1150 */ 1151 public static class UnknownFieldParseException extends ParseException { 1152 private final String unknownField; 1153 1154 /** 1155 * Create a new instance, with -1 as the line and column numbers, and an 1156 * empty unknown field name. 1157 */ UnknownFieldParseException(final String message)1158 public UnknownFieldParseException(final String message) { 1159 this(-1, -1, "", message); 1160 } 1161 1162 /** 1163 * Create a new instance 1164 * 1165 * @param line the line number where the parse error occurred, 1166 * using 1-offset. 1167 * @param column the column number where the parser error occurred, 1168 * using 1-offset. 1169 * @param unknownField the name of the unknown field found while parsing. 1170 */ UnknownFieldParseException(final int line, final int column, final String unknownField, final String message)1171 public UnknownFieldParseException(final int line, final int column, 1172 final String unknownField, final String message) { 1173 super(line, column, message); 1174 this.unknownField = unknownField; 1175 } 1176 1177 /** 1178 * Return the name of the unknown field encountered while parsing the 1179 * protocol buffer string. 1180 */ getUnknownField()1181 public String getUnknownField() { 1182 return unknownField; 1183 } 1184 } 1185 1186 private static final Parser PARSER = Parser.newBuilder().build(); 1187 1188 /** 1189 * Return a {@link Parser} instance which can parse text-format 1190 * messages. The returned instance is thread-safe. 1191 */ getParser()1192 public static Parser getParser() { 1193 return PARSER; 1194 } 1195 1196 /** 1197 * Parse a text-format message from {@code input} and merge the contents 1198 * into {@code builder}. 1199 */ merge(final Readable input, final Message.Builder builder)1200 public static void merge(final Readable input, 1201 final Message.Builder builder) 1202 throws IOException { 1203 PARSER.merge(input, builder); 1204 } 1205 1206 /** 1207 * Parse a text-format message from {@code input} and merge the contents 1208 * into {@code builder}. 1209 */ merge(final CharSequence input, final Message.Builder builder)1210 public static void merge(final CharSequence input, 1211 final Message.Builder builder) 1212 throws ParseException { 1213 PARSER.merge(input, builder); 1214 } 1215 1216 /** 1217 * Parse a text-format message from {@code input} and merge the contents 1218 * into {@code builder}. Extensions will be recognized if they are 1219 * registered in {@code extensionRegistry}. 1220 */ merge(final Readable input, final ExtensionRegistry extensionRegistry, final Message.Builder builder)1221 public static void merge(final Readable input, 1222 final ExtensionRegistry extensionRegistry, 1223 final Message.Builder builder) 1224 throws IOException { 1225 PARSER.merge(input, extensionRegistry, builder); 1226 } 1227 1228 1229 /** 1230 * Parse a text-format message from {@code input} and merge the contents 1231 * into {@code builder}. Extensions will be recognized if they are 1232 * registered in {@code extensionRegistry}. 1233 */ merge(final CharSequence input, final ExtensionRegistry extensionRegistry, final Message.Builder builder)1234 public static void merge(final CharSequence input, 1235 final ExtensionRegistry extensionRegistry, 1236 final Message.Builder builder) 1237 throws ParseException { 1238 PARSER.merge(input, extensionRegistry, builder); 1239 } 1240 1241 1242 /** 1243 * Parser for text-format proto2 instances. This class is thread-safe. 1244 * The implementation largely follows google/protobuf/text_format.cc. 1245 * 1246 * <p>Use {@link TextFormat#getParser()} to obtain the default parser, or 1247 * {@link Builder} to control the parser behavior. 1248 */ 1249 public static class Parser { 1250 /** 1251 * Determines if repeated values for non-repeated fields and 1252 * oneofs are permitted. For example, given required/optional field "foo" 1253 * and a oneof containing "baz" and "qux": 1254 * <ul> 1255 * <li>"foo: 1 foo: 2" 1256 * <li>"baz: 1 qux: 2" 1257 * <li>merging "foo: 2" into a proto in which foo is already set, or 1258 * <li>merging "qux: 2" into a proto in which baz is already set. 1259 * </ul> 1260 */ 1261 public enum SingularOverwritePolicy { 1262 /** The last value is retained. */ 1263 ALLOW_SINGULAR_OVERWRITES, 1264 /** An error is issued. */ 1265 FORBID_SINGULAR_OVERWRITES 1266 } 1267 1268 private final boolean allowUnknownFields; 1269 private final SingularOverwritePolicy singularOverwritePolicy; 1270 private TextFormatParseInfoTree.Builder parseInfoTreeBuilder; 1271 Parser( boolean allowUnknownFields, SingularOverwritePolicy singularOverwritePolicy, TextFormatParseInfoTree.Builder parseInfoTreeBuilder)1272 private Parser( 1273 boolean allowUnknownFields, SingularOverwritePolicy singularOverwritePolicy, 1274 TextFormatParseInfoTree.Builder parseInfoTreeBuilder) { 1275 this.allowUnknownFields = allowUnknownFields; 1276 this.singularOverwritePolicy = singularOverwritePolicy; 1277 this.parseInfoTreeBuilder = parseInfoTreeBuilder; 1278 } 1279 1280 /** 1281 * Returns a new instance of {@link Builder}. 1282 */ newBuilder()1283 public static Builder newBuilder() { 1284 return new Builder(); 1285 } 1286 1287 /** 1288 * Builder that can be used to obtain new instances of {@link Parser}. 1289 */ 1290 public static class Builder { 1291 private boolean allowUnknownFields = false; 1292 private SingularOverwritePolicy singularOverwritePolicy = 1293 SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES; 1294 private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null; 1295 1296 1297 /** 1298 * Sets parser behavior when a non-repeated field appears more than once. 1299 */ setSingularOverwritePolicy(SingularOverwritePolicy p)1300 public Builder setSingularOverwritePolicy(SingularOverwritePolicy p) { 1301 this.singularOverwritePolicy = p; 1302 return this; 1303 } 1304 setParseInfoTreeBuilder( TextFormatParseInfoTree.Builder parseInfoTreeBuilder)1305 public Builder setParseInfoTreeBuilder( 1306 TextFormatParseInfoTree.Builder parseInfoTreeBuilder) { 1307 this.parseInfoTreeBuilder = parseInfoTreeBuilder; 1308 return this; 1309 } 1310 build()1311 public Parser build() { 1312 return new Parser( 1313 allowUnknownFields, singularOverwritePolicy, parseInfoTreeBuilder); 1314 } 1315 } 1316 1317 /** 1318 * Parse a text-format message from {@code input} and merge the contents 1319 * into {@code builder}. 1320 */ merge(final Readable input, final Message.Builder builder)1321 public void merge(final Readable input, 1322 final Message.Builder builder) 1323 throws IOException { 1324 merge(input, ExtensionRegistry.getEmptyRegistry(), builder); 1325 } 1326 1327 /** 1328 * Parse a text-format message from {@code input} and merge the contents 1329 * into {@code builder}. 1330 */ merge(final CharSequence input, final Message.Builder builder)1331 public void merge(final CharSequence input, 1332 final Message.Builder builder) 1333 throws ParseException { 1334 merge(input, ExtensionRegistry.getEmptyRegistry(), builder); 1335 } 1336 1337 /** 1338 * Parse a text-format message from {@code input} and merge the contents 1339 * into {@code builder}. Extensions will be recognized if they are 1340 * registered in {@code extensionRegistry}. 1341 */ merge(final Readable input, final ExtensionRegistry extensionRegistry, final Message.Builder builder)1342 public void merge(final Readable input, 1343 final ExtensionRegistry extensionRegistry, 1344 final Message.Builder builder) 1345 throws IOException { 1346 // Read the entire input to a String then parse that. 1347 1348 // If StreamTokenizer were not quite so crippled, or if there were a kind 1349 // of Reader that could read in chunks that match some particular regex, 1350 // or if we wanted to write a custom Reader to tokenize our stream, then 1351 // we would not have to read to one big String. Alas, none of these is 1352 // the case. Oh well. 1353 1354 merge(toStringBuilder(input), extensionRegistry, builder); 1355 } 1356 1357 1358 private static final int BUFFER_SIZE = 4096; 1359 1360 // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) 1361 // overhead is worthwhile toStringBuilder(final Readable input)1362 private static StringBuilder toStringBuilder(final Readable input) 1363 throws IOException { 1364 final StringBuilder text = new StringBuilder(); 1365 final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); 1366 while (true) { 1367 final int n = input.read(buffer); 1368 if (n == -1) { 1369 break; 1370 } 1371 buffer.flip(); 1372 text.append(buffer, 0, n); 1373 } 1374 return text; 1375 } 1376 1377 /** 1378 * Parse a text-format message from {@code input} and merge the contents 1379 * into {@code builder}. Extensions will be recognized if they are 1380 * registered in {@code extensionRegistry}. 1381 */ merge(final CharSequence input, final ExtensionRegistry extensionRegistry, final Message.Builder builder)1382 public void merge(final CharSequence input, 1383 final ExtensionRegistry extensionRegistry, 1384 final Message.Builder builder) 1385 throws ParseException { 1386 final Tokenizer tokenizer = new Tokenizer(input); 1387 MessageReflection.BuilderAdapter target = 1388 new MessageReflection.BuilderAdapter(builder); 1389 1390 while (!tokenizer.atEnd()) { 1391 mergeField(tokenizer, extensionRegistry, target); 1392 } 1393 } 1394 1395 1396 /** 1397 * Parse a single field from {@code tokenizer} and merge it into 1398 * {@code builder}. 1399 */ mergeField(final Tokenizer tokenizer, final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target)1400 private void mergeField(final Tokenizer tokenizer, 1401 final ExtensionRegistry extensionRegistry, 1402 final MessageReflection.MergeTarget target) 1403 throws ParseException { 1404 mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder); 1405 } 1406 1407 /** 1408 * Parse a single field from {@code tokenizer} and merge it into 1409 * {@code builder}. 1410 */ mergeField(final Tokenizer tokenizer, final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target, TextFormatParseInfoTree.Builder parseTreeBuilder)1411 private void mergeField(final Tokenizer tokenizer, 1412 final ExtensionRegistry extensionRegistry, 1413 final MessageReflection.MergeTarget target, 1414 TextFormatParseInfoTree.Builder parseTreeBuilder) 1415 throws ParseException { 1416 FieldDescriptor field = null; 1417 int startLine = tokenizer.getLine(); 1418 int startColumn = tokenizer.getColumn(); 1419 final Descriptor type = target.getDescriptorForType(); 1420 ExtensionRegistry.ExtensionInfo extension = null; 1421 1422 if (tokenizer.tryConsume("[")) { 1423 // An extension. 1424 final StringBuilder name = 1425 new StringBuilder(tokenizer.consumeIdentifier()); 1426 while (tokenizer.tryConsume(".")) { 1427 name.append('.'); 1428 name.append(tokenizer.consumeIdentifier()); 1429 } 1430 1431 extension = target.findExtensionByName( 1432 extensionRegistry, name.toString()); 1433 1434 if (extension == null) { 1435 if (!allowUnknownFields) { 1436 throw tokenizer.parseExceptionPreviousToken( 1437 "Extension \"" + name + "\" not found in the ExtensionRegistry."); 1438 } else { 1439 logger.warning( 1440 "Extension \"" + name + "\" not found in the ExtensionRegistry."); 1441 } 1442 } else { 1443 if (extension.descriptor.getContainingType() != type) { 1444 throw tokenizer.parseExceptionPreviousToken( 1445 "Extension \"" + name + "\" does not extend message type \"" 1446 + type.getFullName() + "\"."); 1447 } 1448 field = extension.descriptor; 1449 } 1450 1451 tokenizer.consume("]"); 1452 } else { 1453 final String name = tokenizer.consumeIdentifier(); 1454 field = type.findFieldByName(name); 1455 1456 // Group names are expected to be capitalized as they appear in the 1457 // .proto file, which actually matches their type names, not their field 1458 // names. 1459 if (field == null) { 1460 // Explicitly specify US locale so that this code does not break when 1461 // executing in Turkey. 1462 final String lowerName = name.toLowerCase(Locale.US); 1463 field = type.findFieldByName(lowerName); 1464 // If the case-insensitive match worked but the field is NOT a group, 1465 if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { 1466 field = null; 1467 } 1468 } 1469 // Again, special-case group names as described above. 1470 if (field != null && field.getType() == FieldDescriptor.Type.GROUP 1471 && !field.getMessageType().getName().equals(name)) { 1472 field = null; 1473 } 1474 1475 if (field == null) { 1476 if (!allowUnknownFields) { 1477 throw tokenizer.unknownFieldParseExceptionPreviousToken( 1478 name, 1479 "Message type \"" + type.getFullName() 1480 + "\" has no field named \"" + name + "\"."); 1481 } else { 1482 logger.warning( 1483 "Message type \"" + type.getFullName() 1484 + "\" has no field named \"" + name + "\"."); 1485 } 1486 } 1487 } 1488 1489 // Skips unknown fields. 1490 if (field == null) { 1491 // Try to guess the type of this field. 1492 // If this field is not a message, there should be a ":" between the 1493 // field name and the field value and also the field value should not 1494 // start with "{" or "<" which indicates the beginning of a message body. 1495 // If there is no ":" or there is a "{" or "<" after ":", this field has 1496 // to be a message or the input is ill-formed. 1497 if (tokenizer.tryConsume(":") 1498 && !tokenizer.lookingAt("{") 1499 && !tokenizer.lookingAt("<")) { 1500 skipFieldValue(tokenizer); 1501 } else { 1502 skipFieldMessage(tokenizer); 1503 } 1504 return; 1505 } 1506 1507 // Handle potential ':'. 1508 if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 1509 tokenizer.tryConsume(":"); // optional 1510 if (parseTreeBuilder != null) { 1511 TextFormatParseInfoTree.Builder childParseTreeBuilder = 1512 parseTreeBuilder.getBuilderForSubMessageField(field); 1513 consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, 1514 childParseTreeBuilder); 1515 } else { 1516 consumeFieldValues(tokenizer, extensionRegistry, target, field, extension, 1517 parseTreeBuilder); 1518 } 1519 } else { 1520 tokenizer.consume(":"); // required 1521 consumeFieldValues( 1522 tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); 1523 } 1524 1525 if (parseTreeBuilder != null) { 1526 parseTreeBuilder.setLocation( 1527 field, TextFormatParseLocation.create(startLine, startColumn)); 1528 } 1529 1530 // For historical reasons, fields may optionally be separated by commas or 1531 // semicolons. 1532 if (!tokenizer.tryConsume(";")) { 1533 tokenizer.tryConsume(","); 1534 } 1535 } 1536 1537 /** 1538 * Parse a one or more field values from {@code tokenizer} and merge it into 1539 * {@code builder}. 1540 */ consumeFieldValues( final Tokenizer tokenizer, final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target, final FieldDescriptor field, final ExtensionRegistry.ExtensionInfo extension, final TextFormatParseInfoTree.Builder parseTreeBuilder)1541 private void consumeFieldValues( 1542 final Tokenizer tokenizer, 1543 final ExtensionRegistry extensionRegistry, 1544 final MessageReflection.MergeTarget target, 1545 final FieldDescriptor field, 1546 final ExtensionRegistry.ExtensionInfo extension, 1547 final TextFormatParseInfoTree.Builder parseTreeBuilder) 1548 throws ParseException { 1549 // Support specifying repeated field values as a comma-separated list. 1550 // Ex."foo: [1, 2, 3]" 1551 if (field.isRepeated() && tokenizer.tryConsume("[")) { 1552 while (true) { 1553 consumeFieldValue(tokenizer, extensionRegistry, target, field, extension, 1554 parseTreeBuilder); 1555 if (tokenizer.tryConsume("]")) { 1556 // End of list. 1557 break; 1558 } 1559 tokenizer.consume(","); 1560 } 1561 } else { 1562 consumeFieldValue( 1563 tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder); 1564 } 1565 } 1566 1567 /** 1568 * Parse a single field value from {@code tokenizer} and merge it into 1569 * {@code builder}. 1570 */ consumeFieldValue( final Tokenizer tokenizer, final ExtensionRegistry extensionRegistry, final MessageReflection.MergeTarget target, final FieldDescriptor field, final ExtensionRegistry.ExtensionInfo extension, final TextFormatParseInfoTree.Builder parseTreeBuilder)1571 private void consumeFieldValue( 1572 final Tokenizer tokenizer, 1573 final ExtensionRegistry extensionRegistry, 1574 final MessageReflection.MergeTarget target, 1575 final FieldDescriptor field, 1576 final ExtensionRegistry.ExtensionInfo extension, 1577 final TextFormatParseInfoTree.Builder parseTreeBuilder) 1578 throws ParseException { 1579 Object value = null; 1580 1581 if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { 1582 final String endToken; 1583 if (tokenizer.tryConsume("<")) { 1584 endToken = ">"; 1585 } else { 1586 tokenizer.consume("{"); 1587 endToken = "}"; 1588 } 1589 1590 final MessageReflection.MergeTarget subField; 1591 subField = target.newMergeTargetForField(field, 1592 (extension == null) ? null : extension.defaultInstance); 1593 1594 while (!tokenizer.tryConsume(endToken)) { 1595 if (tokenizer.atEnd()) { 1596 throw tokenizer.parseException( 1597 "Expected \"" + endToken + "\"."); 1598 } 1599 mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder); 1600 } 1601 1602 value = subField.finish(); 1603 1604 } else { 1605 switch (field.getType()) { 1606 case INT32: 1607 case SINT32: 1608 case SFIXED32: 1609 value = tokenizer.consumeInt32(); 1610 break; 1611 1612 case INT64: 1613 case SINT64: 1614 case SFIXED64: 1615 value = tokenizer.consumeInt64(); 1616 break; 1617 1618 case UINT32: 1619 case FIXED32: 1620 value = tokenizer.consumeUInt32(); 1621 break; 1622 1623 case UINT64: 1624 case FIXED64: 1625 value = tokenizer.consumeUInt64(); 1626 break; 1627 1628 case FLOAT: 1629 value = tokenizer.consumeFloat(); 1630 break; 1631 1632 case DOUBLE: 1633 value = tokenizer.consumeDouble(); 1634 break; 1635 1636 case BOOL: 1637 value = tokenizer.consumeBoolean(); 1638 break; 1639 1640 case STRING: 1641 value = tokenizer.consumeString(); 1642 break; 1643 1644 case BYTES: 1645 value = tokenizer.consumeByteString(); 1646 break; 1647 1648 case ENUM: 1649 final EnumDescriptor enumType = field.getEnumType(); 1650 1651 if (tokenizer.lookingAtInteger()) { 1652 final int number = tokenizer.consumeInt32(); 1653 value = enumType.findValueByNumber(number); 1654 if (value == null) { 1655 throw tokenizer.parseExceptionPreviousToken( 1656 "Enum type \"" + enumType.getFullName() 1657 + "\" has no value with number " + number + '.'); 1658 } 1659 } else { 1660 final String id = tokenizer.consumeIdentifier(); 1661 value = enumType.findValueByName(id); 1662 if (value == null) { 1663 throw tokenizer.parseExceptionPreviousToken( 1664 "Enum type \"" + enumType.getFullName() 1665 + "\" has no value named \"" + id + "\"."); 1666 } 1667 } 1668 1669 break; 1670 1671 case MESSAGE: 1672 case GROUP: 1673 throw new RuntimeException("Can't get here."); 1674 } 1675 } 1676 1677 if (field.isRepeated()) { 1678 target.addRepeatedField(field, value); 1679 } else if ((singularOverwritePolicy 1680 == SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES) 1681 && target.hasField(field)) { 1682 throw tokenizer.parseExceptionPreviousToken("Non-repeated field \"" 1683 + field.getFullName() + "\" cannot be overwritten."); 1684 } else if ((singularOverwritePolicy 1685 == SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES) 1686 && field.getContainingOneof() != null 1687 && target.hasOneof(field.getContainingOneof())) { 1688 Descriptors.OneofDescriptor oneof = field.getContainingOneof(); 1689 throw tokenizer.parseExceptionPreviousToken("Field \"" 1690 + field.getFullName() + "\" is specified along with field \"" 1691 + target.getOneofFieldDescriptor(oneof).getFullName() 1692 + "\", another member of oneof \"" + oneof.getName() + "\"."); 1693 } else { 1694 target.setField(field, value); 1695 } 1696 } 1697 1698 /** 1699 * Skips the next field including the field's name and value. 1700 */ skipField(Tokenizer tokenizer)1701 private void skipField(Tokenizer tokenizer) throws ParseException { 1702 if (tokenizer.tryConsume("[")) { 1703 // Extension name. 1704 do { 1705 tokenizer.consumeIdentifier(); 1706 } while (tokenizer.tryConsume(".")); 1707 tokenizer.consume("]"); 1708 } else { 1709 tokenizer.consumeIdentifier(); 1710 } 1711 1712 // Try to guess the type of this field. 1713 // If this field is not a message, there should be a ":" between the 1714 // field name and the field value and also the field value should not 1715 // start with "{" or "<" which indicates the beginning of a message body. 1716 // If there is no ":" or there is a "{" or "<" after ":", this field has 1717 // to be a message or the input is ill-formed. 1718 if (tokenizer.tryConsume(":") 1719 && !tokenizer.lookingAt("<") 1720 && !tokenizer.lookingAt("{")) { 1721 skipFieldValue(tokenizer); 1722 } else { 1723 skipFieldMessage(tokenizer); 1724 } 1725 // For historical reasons, fields may optionally be separated by commas or 1726 // semicolons. 1727 if (!tokenizer.tryConsume(";")) { 1728 tokenizer.tryConsume(","); 1729 } 1730 } 1731 1732 /** 1733 * Skips the whole body of a message including the beginning delimiter and 1734 * the ending delimiter. 1735 */ skipFieldMessage(Tokenizer tokenizer)1736 private void skipFieldMessage(Tokenizer tokenizer) throws ParseException { 1737 final String delimiter; 1738 if (tokenizer.tryConsume("<")) { 1739 delimiter = ">"; 1740 } else { 1741 tokenizer.consume("{"); 1742 delimiter = "}"; 1743 } 1744 while (!tokenizer.lookingAt(">") && !tokenizer.lookingAt("}")) { 1745 skipField(tokenizer); 1746 } 1747 tokenizer.consume(delimiter); 1748 } 1749 1750 /** 1751 * Skips a field value. 1752 */ skipFieldValue(Tokenizer tokenizer)1753 private void skipFieldValue(Tokenizer tokenizer) throws ParseException { 1754 if (tokenizer.tryConsumeString()) { 1755 while (tokenizer.tryConsumeString()) {} 1756 return; 1757 } 1758 if (!tokenizer.tryConsumeIdentifier() // includes enum & boolean 1759 && !tokenizer.tryConsumeInt64() // includes int32 1760 && !tokenizer.tryConsumeUInt64() // includes uint32 1761 && !tokenizer.tryConsumeDouble() 1762 && !tokenizer.tryConsumeFloat()) { 1763 throw tokenizer.parseException( 1764 "Invalid field value: " + tokenizer.currentToken); 1765 } 1766 } 1767 } 1768 1769 // ================================================================= 1770 // Utility functions 1771 // 1772 // Some of these methods are package-private because Descriptors.java uses 1773 // them. 1774 1775 /** 1776 * Escapes bytes in the format used in protocol buffer text format, which 1777 * is the same as the format used for C string literals. All bytes 1778 * that are not printable 7-bit ASCII characters are escaped, as well as 1779 * backslash, single-quote, and double-quote characters. Characters for 1780 * which no defined short-hand escape sequence is defined will be escaped 1781 * using 3-digit octal sequences. 1782 */ escapeBytes(ByteString input)1783 public static String escapeBytes(ByteString input) { 1784 return TextFormatEscaper.escapeBytes(input); 1785 } 1786 1787 /** 1788 * Like {@link #escapeBytes(ByteString)}, but used for byte array. 1789 */ escapeBytes(byte[] input)1790 public static String escapeBytes(byte[] input) { 1791 return TextFormatEscaper.escapeBytes(input); 1792 } 1793 1794 /** 1795 * Un-escape a byte sequence as escaped using 1796 * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with 1797 * "\x") are also recognized. 1798 */ unescapeBytes(final CharSequence charString)1799 public static ByteString unescapeBytes(final CharSequence charString) 1800 throws InvalidEscapeSequenceException { 1801 // First convert the Java character sequence to UTF-8 bytes. 1802 ByteString input = ByteString.copyFromUtf8(charString.toString()); 1803 // Then unescape certain byte sequences introduced by ASCII '\\'. The valid 1804 // escapes can all be expressed with ASCII characters, so it is safe to 1805 // operate on bytes here. 1806 // 1807 // Unescaping the input byte array will result in a byte sequence that's no 1808 // longer than the input. That's because each escape sequence is between 1809 // two and four bytes long and stands for a single byte. 1810 final byte[] result = new byte[input.size()]; 1811 int pos = 0; 1812 for (int i = 0; i < input.size(); i++) { 1813 byte c = input.byteAt(i); 1814 if (c == '\\') { 1815 if (i + 1 < input.size()) { 1816 ++i; 1817 c = input.byteAt(i); 1818 if (isOctal(c)) { 1819 // Octal escape. 1820 int code = digitValue(c); 1821 if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { 1822 ++i; 1823 code = code * 8 + digitValue(input.byteAt(i)); 1824 } 1825 if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { 1826 ++i; 1827 code = code * 8 + digitValue(input.byteAt(i)); 1828 } 1829 // TODO: Check that 0 <= code && code <= 0xFF. 1830 result[pos++] = (byte) code; 1831 } else { 1832 switch (c) { 1833 case 'a' : result[pos++] = 0x07; break; 1834 case 'b' : result[pos++] = '\b'; break; 1835 case 'f' : result[pos++] = '\f'; break; 1836 case 'n' : result[pos++] = '\n'; break; 1837 case 'r' : result[pos++] = '\r'; break; 1838 case 't' : result[pos++] = '\t'; break; 1839 case 'v' : result[pos++] = 0x0b; break; 1840 case '\\': result[pos++] = '\\'; break; 1841 case '\'': result[pos++] = '\''; break; 1842 case '"' : result[pos++] = '\"'; break; 1843 1844 case 'x': 1845 // hex escape 1846 int code = 0; 1847 if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { 1848 ++i; 1849 code = digitValue(input.byteAt(i)); 1850 } else { 1851 throw new InvalidEscapeSequenceException( 1852 "Invalid escape sequence: '\\x' with no digits"); 1853 } 1854 if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { 1855 ++i; 1856 code = code * 16 + digitValue(input.byteAt(i)); 1857 } 1858 result[pos++] = (byte) code; 1859 break; 1860 1861 default: 1862 throw new InvalidEscapeSequenceException( 1863 "Invalid escape sequence: '\\" + (char) c + '\''); 1864 } 1865 } 1866 } else { 1867 throw new InvalidEscapeSequenceException( 1868 "Invalid escape sequence: '\\' at end of string."); 1869 } 1870 } else { 1871 result[pos++] = c; 1872 } 1873 } 1874 1875 return result.length == pos 1876 ? ByteString.wrap(result) // This reference has not been out of our control. 1877 : ByteString.copyFrom(result, 0, pos); 1878 } 1879 1880 /** 1881 * Thrown by {@link TextFormat#unescapeBytes} and 1882 * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. 1883 */ 1884 public static class InvalidEscapeSequenceException extends IOException { 1885 private static final long serialVersionUID = -8164033650142593304L; 1886 InvalidEscapeSequenceException(final String description)1887 InvalidEscapeSequenceException(final String description) { 1888 super(description); 1889 } 1890 } 1891 1892 /** 1893 * Like {@link #escapeBytes(ByteString)}, but escapes a text string. 1894 * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped 1895 * individually as a 3-digit octal escape. Yes, it's weird. 1896 */ escapeText(final String input)1897 static String escapeText(final String input) { 1898 return escapeBytes(ByteString.copyFromUtf8(input)); 1899 } 1900 1901 /** 1902 * Escape double quotes and backslashes in a String for unicode output of a message. 1903 */ escapeDoubleQuotesAndBackslashes(final String input)1904 public static String escapeDoubleQuotesAndBackslashes(final String input) { 1905 return TextFormatEscaper.escapeDoubleQuotesAndBackslashes(input); 1906 } 1907 1908 /** 1909 * Un-escape a text string as escaped using {@link #escapeText(String)}. 1910 * Two-digit hex escapes (starting with "\x") are also recognized. 1911 */ unescapeText(final String input)1912 static String unescapeText(final String input) 1913 throws InvalidEscapeSequenceException { 1914 return unescapeBytes(input).toStringUtf8(); 1915 } 1916 1917 /** Is this an octal digit? */ isOctal(final byte c)1918 private static boolean isOctal(final byte c) { 1919 return '0' <= c && c <= '7'; 1920 } 1921 1922 /** Is this a hex digit? */ isHex(final byte c)1923 private static boolean isHex(final byte c) { 1924 return ('0' <= c && c <= '9') 1925 || ('a' <= c && c <= 'f') 1926 || ('A' <= c && c <= 'F'); 1927 } 1928 1929 /** 1930 * Interpret a character as a digit (in any base up to 36) and return the 1931 * numeric value. This is like {@code Character.digit()} but we don't accept 1932 * non-ASCII digits. 1933 */ digitValue(final byte c)1934 private static int digitValue(final byte c) { 1935 if ('0' <= c && c <= '9') { 1936 return c - '0'; 1937 } else if ('a' <= c && c <= 'z') { 1938 return c - 'a' + 10; 1939 } else { 1940 return c - 'A' + 10; 1941 } 1942 } 1943 1944 /** 1945 * Parse a 32-bit signed integer from the text. Unlike the Java standard 1946 * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" 1947 * and "0" to signify hexadecimal and octal numbers, respectively. 1948 */ parseInt32(final String text)1949 static int parseInt32(final String text) throws NumberFormatException { 1950 return (int) parseInteger(text, true, false); 1951 } 1952 1953 /** 1954 * Parse a 32-bit unsigned integer from the text. Unlike the Java standard 1955 * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" 1956 * and "0" to signify hexadecimal and octal numbers, respectively. The 1957 * result is coerced to a (signed) {@code int} when returned since Java has 1958 * no unsigned integer type. 1959 */ parseUInt32(final String text)1960 static int parseUInt32(final String text) throws NumberFormatException { 1961 return (int) parseInteger(text, false, false); 1962 } 1963 1964 /** 1965 * Parse a 64-bit signed integer from the text. Unlike the Java standard 1966 * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" 1967 * and "0" to signify hexadecimal and octal numbers, respectively. 1968 */ parseInt64(final String text)1969 static long parseInt64(final String text) throws NumberFormatException { 1970 return parseInteger(text, true, true); 1971 } 1972 1973 /** 1974 * Parse a 64-bit unsigned integer from the text. Unlike the Java standard 1975 * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" 1976 * and "0" to signify hexadecimal and octal numbers, respectively. The 1977 * result is coerced to a (signed) {@code long} when returned since Java has 1978 * no unsigned long type. 1979 */ parseUInt64(final String text)1980 static long parseUInt64(final String text) throws NumberFormatException { 1981 return parseInteger(text, false, true); 1982 } 1983 parseInteger(final String text, final boolean isSigned, final boolean isLong)1984 private static long parseInteger(final String text, 1985 final boolean isSigned, 1986 final boolean isLong) 1987 throws NumberFormatException { 1988 int pos = 0; 1989 1990 boolean negative = false; 1991 if (text.startsWith("-", pos)) { 1992 if (!isSigned) { 1993 throw new NumberFormatException("Number must be positive: " + text); 1994 } 1995 ++pos; 1996 negative = true; 1997 } 1998 1999 int radix = 10; 2000 if (text.startsWith("0x", pos)) { 2001 pos += 2; 2002 radix = 16; 2003 } else if (text.startsWith("0", pos)) { 2004 radix = 8; 2005 } 2006 2007 final String numberText = text.substring(pos); 2008 2009 long result = 0; 2010 if (numberText.length() < 16) { 2011 // Can safely assume no overflow. 2012 result = Long.parseLong(numberText, radix); 2013 if (negative) { 2014 result = -result; 2015 } 2016 2017 // Check bounds. 2018 // No need to check for 64-bit numbers since they'd have to be 16 chars 2019 // or longer to overflow. 2020 if (!isLong) { 2021 if (isSigned) { 2022 if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) { 2023 throw new NumberFormatException( 2024 "Number out of range for 32-bit signed integer: " + text); 2025 } 2026 } else { 2027 if (result >= (1L << 32) || result < 0) { 2028 throw new NumberFormatException( 2029 "Number out of range for 32-bit unsigned integer: " + text); 2030 } 2031 } 2032 } 2033 } else { 2034 BigInteger bigValue = new BigInteger(numberText, radix); 2035 if (negative) { 2036 bigValue = bigValue.negate(); 2037 } 2038 2039 // Check bounds. 2040 if (!isLong) { 2041 if (isSigned) { 2042 if (bigValue.bitLength() > 31) { 2043 throw new NumberFormatException( 2044 "Number out of range for 32-bit signed integer: " + text); 2045 } 2046 } else { 2047 if (bigValue.bitLength() > 32) { 2048 throw new NumberFormatException( 2049 "Number out of range for 32-bit unsigned integer: " + text); 2050 } 2051 } 2052 } else { 2053 if (isSigned) { 2054 if (bigValue.bitLength() > 63) { 2055 throw new NumberFormatException( 2056 "Number out of range for 64-bit signed integer: " + text); 2057 } 2058 } else { 2059 if (bigValue.bitLength() > 64) { 2060 throw new NumberFormatException( 2061 "Number out of range for 64-bit unsigned integer: " + text); 2062 } 2063 } 2064 } 2065 2066 result = bigValue.longValue(); 2067 } 2068 2069 return result; 2070 } 2071 } 2072