Home | History | Annotate | Download | only in protobuf
      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 (at) google.com Wenbo Zhu
     54  * @author kenton (at) google.com Kenton Varda
     55  */
     56 public final class 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    */
     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}. */
     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    */
     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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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 
    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 
    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    */
    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    */
    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 
    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 
    295     private Printer() {}
    296 
    297     /** Setter of singleLineMode */
    298     private Printer setSingleLineMode(boolean singleLineMode) {
    299       this.singleLineMode = singleLineMode;
    300       return this;
    301     }
    302 
    303     /** Setter of escapeNonAscii */
    304     private Printer setEscapeNonAscii(boolean escapeNonAscii) {
    305       this.escapeNonAscii = escapeNonAscii;
    306       return this;
    307     }
    308 
    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 
    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 
    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 
    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 
    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 
    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. */
    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. */
    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 
    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      */
    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      */
    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      */
    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 
    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. */
    657     private Tokenizer(final CharSequence text) {
    658       this.text = text;
    659       this.matcher = WHITESPACE.matcher(text);
    660       skipWhitespace();
    661       nextToken();
    662     }
    663 
    664     int getLine() {
    665       return line;
    666     }
    667 
    668     int getColumn() {
    669       return column;
    670     }
    671 
    672     /** Are we at the end of the input? */
    673     public boolean atEnd() {
    674       return currentToken.length() == 0;
    675     }
    676 
    677     /** Advance to the next token. */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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. */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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    */
   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    */
   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    */
   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    */
   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    */
   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 
   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      */
   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        */
   1300       public Builder setSingularOverwritePolicy(SingularOverwritePolicy p) {
   1301         this.singularOverwritePolicy = p;
   1302         return this;
   1303       }
   1304 
   1305       public Builder setParseInfoTreeBuilder(
   1306           TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
   1307         this.parseInfoTreeBuilder = parseInfoTreeBuilder;
   1308         return this;
   1309       }
   1310 
   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      */
   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      */
   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      */
   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
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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      */
   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    */
   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    */
   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    */
   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 
   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    */
   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    */
   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    */
   1912   static String unescapeText(final String input)
   1913                              throws InvalidEscapeSequenceException {
   1914     return unescapeBytes(input).toStringUtf8();
   1915   }
   1916 
   1917   /** Is this an octal digit? */
   1918   private static boolean isOctal(final byte c) {
   1919     return '0' <= c && c <= '7';
   1920   }
   1921 
   1922   /** Is this a hex digit? */
   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    */
   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    */
   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    */
   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    */
   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    */
   1980   static long parseUInt64(final String text) throws NumberFormatException {
   1981     return parseInteger(text, false, true);
   1982   }
   1983 
   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