PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/game_server/src/com/google/protobuf/TextFormat.java

http://mmorpg-client-server-learning.googlecode.com/
Java | 1476 lines | 976 code | 146 blank | 354 comment | 190 complexity | 784a5660765f5a5bbb78220688def806 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // http://code.google.com/p/protobuf/
  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. package com.google.protobuf;
  31. import com.google.protobuf.Descriptors.Descriptor;
  32. import com.google.protobuf.Descriptors.FieldDescriptor;
  33. import com.google.protobuf.Descriptors.EnumDescriptor;
  34. import com.google.protobuf.Descriptors.EnumValueDescriptor;
  35. import java.io.IOException;
  36. import java.nio.CharBuffer;
  37. import java.math.BigInteger;
  38. import java.util.ArrayList;
  39. import java.util.List;
  40. import java.util.Locale;
  41. import java.util.Map;
  42. import java.util.regex.Matcher;
  43. import java.util.regex.Pattern;
  44. /**
  45. * Provide text parsing and formatting support for proto2 instances.
  46. * The implementation largely follows google/protobuf/text_format.cc.
  47. *
  48. * @author wenboz@google.com Wenbo Zhu
  49. * @author kenton@google.com Kenton Varda
  50. */
  51. public final class TextFormat {
  52. private TextFormat() {}
  53. private static final Printer DEFAULT_PRINTER = new Printer(false);
  54. private static final Printer SINGLE_LINE_PRINTER = new Printer(true);
  55. /**
  56. * Outputs a textual representation of the Protocol Message supplied into
  57. * the parameter output. (This representation is the new version of the
  58. * classic "ProtocolPrinter" output from the original Protocol Buffer system)
  59. */
  60. public static void print(final Message message, final Appendable output)
  61. throws IOException {
  62. DEFAULT_PRINTER.print(message, new TextGenerator(output));
  63. }
  64. /** Outputs a textual representation of {@code fields} to {@code output}. */
  65. public static void print(final UnknownFieldSet fields,
  66. final Appendable output)
  67. throws IOException {
  68. DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output));
  69. }
  70. /**
  71. * Generates a human readable form of this message, useful for debugging and
  72. * other purposes, with no newline characters.
  73. */
  74. public static String shortDebugString(final Message message) {
  75. try {
  76. final StringBuilder sb = new StringBuilder();
  77. SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb));
  78. // Single line mode currently might have an extra space at the end.
  79. return sb.toString().trim();
  80. } catch (IOException e) {
  81. throw new IllegalStateException(e);
  82. }
  83. }
  84. /**
  85. * Generates a human readable form of the unknown fields, useful for debugging
  86. * and other purposes, with no newline characters.
  87. */
  88. public static String shortDebugString(final UnknownFieldSet fields) {
  89. try {
  90. final StringBuilder sb = new StringBuilder();
  91. SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb));
  92. // Single line mode currently might have an extra space at the end.
  93. return sb.toString().trim();
  94. } catch (IOException e) {
  95. throw new IllegalStateException(e);
  96. }
  97. }
  98. /**
  99. * Like {@code print()}, but writes directly to a {@code String} and
  100. * returns it.
  101. */
  102. public static String printToString(final Message message) {
  103. try {
  104. final StringBuilder text = new StringBuilder();
  105. print(message, text);
  106. return text.toString();
  107. } catch (IOException e) {
  108. throw new IllegalStateException(e);
  109. }
  110. }
  111. /**
  112. * Like {@code print()}, but writes directly to a {@code String} and
  113. * returns it.
  114. */
  115. public static String printToString(final UnknownFieldSet fields) {
  116. try {
  117. final StringBuilder text = new StringBuilder();
  118. print(fields, text);
  119. return text.toString();
  120. } catch (IOException e) {
  121. throw new IllegalStateException(e);
  122. }
  123. }
  124. public static void printField(final FieldDescriptor field,
  125. final Object value,
  126. final Appendable output)
  127. throws IOException {
  128. DEFAULT_PRINTER.printField(field, value, new TextGenerator(output));
  129. }
  130. public static String printFieldToString(final FieldDescriptor field,
  131. final Object value) {
  132. try {
  133. final StringBuilder text = new StringBuilder();
  134. printField(field, value, text);
  135. return text.toString();
  136. } catch (IOException e) {
  137. throw new IllegalStateException(e);
  138. }
  139. }
  140. /**
  141. * Outputs a textual representation of the value of given field value.
  142. *
  143. * @param field the descriptor of the field
  144. * @param value the value of the field
  145. * @param output the output to which to append the formatted value
  146. * @throws ClassCastException if the value is not appropriate for the
  147. * given field descriptor
  148. * @throws java.io.IOException if there is an exception writing to the output
  149. */
  150. public static void printFieldValue(final FieldDescriptor field,
  151. final Object value,
  152. final Appendable output)
  153. throws IOException {
  154. DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output));
  155. }
  156. /**
  157. * Outputs a textual representation of the value of an unknown field.
  158. *
  159. * @param tag the field's tag number
  160. * @param value the value of the field
  161. * @param output the output to which to append the formatted value
  162. * @throws ClassCastException if the value is not appropriate for the
  163. * given field descriptor
  164. * @throws java.io.IOException if there is an exception writing to the output
  165. */
  166. public static void printUnknownFieldValue(final int tag,
  167. final Object value,
  168. final Appendable output)
  169. throws IOException {
  170. printUnknownFieldValue(tag, value, new TextGenerator(output));
  171. }
  172. private static void printUnknownFieldValue(final int tag,
  173. final Object value,
  174. final TextGenerator generator)
  175. throws IOException {
  176. switch (WireFormat.getTagWireType(tag)) {
  177. case WireFormat.WIRETYPE_VARINT:
  178. generator.print(unsignedToString((Long) value));
  179. break;
  180. case WireFormat.WIRETYPE_FIXED32:
  181. generator.print(
  182. String.format((Locale) null, "0x%08x", (Integer) value));
  183. break;
  184. case WireFormat.WIRETYPE_FIXED64:
  185. generator.print(String.format((Locale) null, "0x%016x", (Long) value));
  186. break;
  187. case WireFormat.WIRETYPE_LENGTH_DELIMITED:
  188. generator.print("\"");
  189. generator.print(escapeBytes((ByteString) value));
  190. generator.print("\"");
  191. break;
  192. case WireFormat.WIRETYPE_START_GROUP:
  193. DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator);
  194. break;
  195. default:
  196. throw new IllegalArgumentException("Bad tag: " + tag);
  197. }
  198. }
  199. /** Helper class for converting protobufs to text. */
  200. private static final class Printer {
  201. /** Whether to omit newlines from the output. */
  202. final boolean singleLineMode;
  203. private Printer(final boolean singleLineMode) {
  204. this.singleLineMode = singleLineMode;
  205. }
  206. private void print(final Message message, final TextGenerator generator)
  207. throws IOException {
  208. for (Map.Entry<FieldDescriptor, Object> field
  209. : message.getAllFields().entrySet()) {
  210. printField(field.getKey(), field.getValue(), generator);
  211. }
  212. printUnknownFields(message.getUnknownFields(), generator);
  213. }
  214. private void printField(final FieldDescriptor field, final Object value,
  215. final TextGenerator generator) throws IOException {
  216. if (field.isRepeated()) {
  217. // Repeated field. Print each element.
  218. for (Object element : (List<?>) value) {
  219. printSingleField(field, element, generator);
  220. }
  221. } else {
  222. printSingleField(field, value, generator);
  223. }
  224. }
  225. private void printSingleField(final FieldDescriptor field,
  226. final Object value,
  227. final TextGenerator generator)
  228. throws IOException {
  229. if (field.isExtension()) {
  230. generator.print("[");
  231. // We special-case MessageSet elements for compatibility with proto1.
  232. if (field.getContainingType().getOptions().getMessageSetWireFormat()
  233. && (field.getType() == FieldDescriptor.Type.MESSAGE)
  234. && (field.isOptional())
  235. // object equality
  236. && (field.getExtensionScope() == field.getMessageType())) {
  237. generator.print(field.getMessageType().getFullName());
  238. } else {
  239. generator.print(field.getFullName());
  240. }
  241. generator.print("]");
  242. } else {
  243. if (field.getType() == FieldDescriptor.Type.GROUP) {
  244. // Groups must be serialized with their original capitalization.
  245. generator.print(field.getMessageType().getName());
  246. } else {
  247. generator.print(field.getName());
  248. }
  249. }
  250. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
  251. if (singleLineMode) {
  252. generator.print(" { ");
  253. } else {
  254. generator.print(" {\n");
  255. generator.indent();
  256. }
  257. } else {
  258. generator.print(": ");
  259. }
  260. printFieldValue(field, value, generator);
  261. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
  262. if (singleLineMode) {
  263. generator.print("} ");
  264. } else {
  265. generator.outdent();
  266. generator.print("}\n");
  267. }
  268. } else {
  269. if (singleLineMode) {
  270. generator.print(" ");
  271. } else {
  272. generator.print("\n");
  273. }
  274. }
  275. }
  276. private void printFieldValue(final FieldDescriptor field,
  277. final Object value,
  278. final TextGenerator generator)
  279. throws IOException {
  280. switch (field.getType()) {
  281. case INT32:
  282. case SINT32:
  283. case SFIXED32:
  284. generator.print(((Integer) value).toString());
  285. break;
  286. case INT64:
  287. case SINT64:
  288. case SFIXED64:
  289. generator.print(((Long) value).toString());
  290. break;
  291. case BOOL:
  292. generator.print(((Boolean) value).toString());
  293. break;
  294. case FLOAT:
  295. generator.print(((Float) value).toString());
  296. break;
  297. case DOUBLE:
  298. generator.print(((Double) value).toString());
  299. break;
  300. case UINT32:
  301. case FIXED32:
  302. generator.print(unsignedToString((Integer) value));
  303. break;
  304. case UINT64:
  305. case FIXED64:
  306. generator.print(unsignedToString((Long) value));
  307. break;
  308. case STRING:
  309. generator.print("\"");
  310. generator.print(escapeText((String) value));
  311. generator.print("\"");
  312. break;
  313. case BYTES:
  314. generator.print("\"");
  315. generator.print(escapeBytes((ByteString) value));
  316. generator.print("\"");
  317. break;
  318. case ENUM:
  319. generator.print(((EnumValueDescriptor) value).getName());
  320. break;
  321. case MESSAGE:
  322. case GROUP:
  323. print((Message) value, generator);
  324. break;
  325. }
  326. }
  327. private void printUnknownFields(final UnknownFieldSet unknownFields,
  328. final TextGenerator generator)
  329. throws IOException {
  330. for (Map.Entry<Integer, UnknownFieldSet.Field> entry :
  331. unknownFields.asMap().entrySet()) {
  332. final int number = entry.getKey();
  333. final UnknownFieldSet.Field field = entry.getValue();
  334. printUnknownField(number, WireFormat.WIRETYPE_VARINT,
  335. field.getVarintList(), generator);
  336. printUnknownField(number, WireFormat.WIRETYPE_FIXED32,
  337. field.getFixed32List(), generator);
  338. printUnknownField(number, WireFormat.WIRETYPE_FIXED64,
  339. field.getFixed64List(), generator);
  340. printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED,
  341. field.getLengthDelimitedList(), generator);
  342. for (final UnknownFieldSet value : field.getGroupList()) {
  343. generator.print(entry.getKey().toString());
  344. if (singleLineMode) {
  345. generator.print(" { ");
  346. } else {
  347. generator.print(" {\n");
  348. generator.indent();
  349. }
  350. printUnknownFields(value, generator);
  351. if (singleLineMode) {
  352. generator.print("} ");
  353. } else {
  354. generator.outdent();
  355. generator.print("}\n");
  356. }
  357. }
  358. }
  359. }
  360. private void printUnknownField(final int number,
  361. final int wireType,
  362. final List<?> values,
  363. final TextGenerator generator)
  364. throws IOException {
  365. for (final Object value : values) {
  366. generator.print(String.valueOf(number));
  367. generator.print(": ");
  368. printUnknownFieldValue(wireType, value, generator);
  369. generator.print(singleLineMode ? " " : "\n");
  370. }
  371. }
  372. }
  373. /** Convert an unsigned 32-bit integer to a string. */
  374. private static String unsignedToString(final int value) {
  375. if (value >= 0) {
  376. return Integer.toString(value);
  377. } else {
  378. return Long.toString(((long) value) & 0x00000000FFFFFFFFL);
  379. }
  380. }
  381. /** Convert an unsigned 64-bit integer to a string. */
  382. private static String unsignedToString(final long value) {
  383. if (value >= 0) {
  384. return Long.toString(value);
  385. } else {
  386. // Pull off the most-significant bit so that BigInteger doesn't think
  387. // the number is negative, then set it again using setBit().
  388. return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL)
  389. .setBit(63).toString();
  390. }
  391. }
  392. /**
  393. * An inner class for writing text to the output stream.
  394. */
  395. private static final class TextGenerator {
  396. private final Appendable output;
  397. private final StringBuilder indent = new StringBuilder();
  398. private boolean atStartOfLine = true;
  399. private TextGenerator(final Appendable output) {
  400. this.output = output;
  401. }
  402. /**
  403. * Indent text by two spaces. After calling Indent(), two spaces will be
  404. * inserted at the beginning of each line of text. Indent() may be called
  405. * multiple times to produce deeper indents.
  406. */
  407. public void indent() {
  408. indent.append(" ");
  409. }
  410. /**
  411. * Reduces the current indent level by two spaces, or crashes if the indent
  412. * level is zero.
  413. */
  414. public void outdent() {
  415. final int length = indent.length();
  416. if (length == 0) {
  417. throw new IllegalArgumentException(
  418. " Outdent() without matching Indent().");
  419. }
  420. indent.delete(length - 2, length);
  421. }
  422. /**
  423. * Print text to the output stream.
  424. */
  425. public void print(final CharSequence text) throws IOException {
  426. final int size = text.length();
  427. int pos = 0;
  428. for (int i = 0; i < size; i++) {
  429. if (text.charAt(i) == '\n') {
  430. write(text.subSequence(pos, size), i - pos + 1);
  431. pos = i + 1;
  432. atStartOfLine = true;
  433. }
  434. }
  435. write(text.subSequence(pos, size), size - pos);
  436. }
  437. private void write(final CharSequence data, final int size)
  438. throws IOException {
  439. if (size == 0) {
  440. return;
  441. }
  442. if (atStartOfLine) {
  443. atStartOfLine = false;
  444. output.append(indent);
  445. }
  446. output.append(data);
  447. }
  448. }
  449. // =================================================================
  450. // Parsing
  451. /**
  452. * Represents a stream of tokens parsed from a {@code String}.
  453. *
  454. * <p>The Java standard library provides many classes that you might think
  455. * would be useful for implementing this, but aren't. For example:
  456. *
  457. * <ul>
  458. * <li>{@code java.io.StreamTokenizer}: This almost does what we want -- or,
  459. * at least, something that would get us close to what we want -- except
  460. * for one fatal flaw: It automatically un-escapes strings using Java
  461. * escape sequences, which do not include all the escape sequences we
  462. * need to support (e.g. '\x').
  463. * <li>{@code java.util.Scanner}: This seems like a great way at least to
  464. * parse regular expressions out of a stream (so we wouldn't have to load
  465. * the entire input into a single string before parsing). Sadly,
  466. * {@code Scanner} requires that tokens be delimited with some delimiter.
  467. * Thus, although the text "foo:" should parse to two tokens ("foo" and
  468. * ":"), {@code Scanner} would recognize it only as a single token.
  469. * Furthermore, {@code Scanner} provides no way to inspect the contents
  470. * of delimiters, making it impossible to keep track of line and column
  471. * numbers.
  472. * </ul>
  473. *
  474. * <p>Luckily, Java's regular expression support does manage to be useful to
  475. * us. (Barely: We need {@code Matcher.usePattern()}, which is new in
  476. * Java 1.5.) So, we can use that, at least. Unfortunately, this implies
  477. * that we need to have the entire input in one contiguous string.
  478. */
  479. private static final class Tokenizer {
  480. private final CharSequence text;
  481. private final Matcher matcher;
  482. private String currentToken;
  483. // The character index within this.text at which the current token begins.
  484. private int pos = 0;
  485. // The line and column numbers of the current token.
  486. private int line = 0;
  487. private int column = 0;
  488. // The line and column numbers of the previous token (allows throwing
  489. // errors *after* consuming).
  490. private int previousLine = 0;
  491. private int previousColumn = 0;
  492. // We use possesive quantifiers (*+ and ++) because otherwise the Java
  493. // regex matcher has stack overflows on large inputs.
  494. private static final Pattern WHITESPACE =
  495. Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE);
  496. private static final Pattern TOKEN = Pattern.compile(
  497. "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier
  498. "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number
  499. "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string
  500. "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string
  501. Pattern.MULTILINE);
  502. private static final Pattern DOUBLE_INFINITY = Pattern.compile(
  503. "-?inf(inity)?",
  504. Pattern.CASE_INSENSITIVE);
  505. private static final Pattern FLOAT_INFINITY = Pattern.compile(
  506. "-?inf(inity)?f?",
  507. Pattern.CASE_INSENSITIVE);
  508. private static final Pattern FLOAT_NAN = Pattern.compile(
  509. "nanf?",
  510. Pattern.CASE_INSENSITIVE);
  511. /** Construct a tokenizer that parses tokens from the given text. */
  512. private Tokenizer(final CharSequence text) {
  513. this.text = text;
  514. this.matcher = WHITESPACE.matcher(text);
  515. skipWhitespace();
  516. nextToken();
  517. }
  518. /** Are we at the end of the input? */
  519. public boolean atEnd() {
  520. return currentToken.length() == 0;
  521. }
  522. /** Advance to the next token. */
  523. public void nextToken() {
  524. previousLine = line;
  525. previousColumn = column;
  526. // Advance the line counter to the current position.
  527. while (pos < matcher.regionStart()) {
  528. if (text.charAt(pos) == '\n') {
  529. ++line;
  530. column = 0;
  531. } else {
  532. ++column;
  533. }
  534. ++pos;
  535. }
  536. // Match the next token.
  537. if (matcher.regionStart() == matcher.regionEnd()) {
  538. // EOF
  539. currentToken = "";
  540. } else {
  541. matcher.usePattern(TOKEN);
  542. if (matcher.lookingAt()) {
  543. currentToken = matcher.group();
  544. matcher.region(matcher.end(), matcher.regionEnd());
  545. } else {
  546. // Take one character.
  547. currentToken = String.valueOf(text.charAt(pos));
  548. matcher.region(pos + 1, matcher.regionEnd());
  549. }
  550. skipWhitespace();
  551. }
  552. }
  553. /**
  554. * Skip over any whitespace so that the matcher region starts at the next
  555. * token.
  556. */
  557. private void skipWhitespace() {
  558. matcher.usePattern(WHITESPACE);
  559. if (matcher.lookingAt()) {
  560. matcher.region(matcher.end(), matcher.regionEnd());
  561. }
  562. }
  563. /**
  564. * If the next token exactly matches {@code token}, consume it and return
  565. * {@code true}. Otherwise, return {@code false} without doing anything.
  566. */
  567. public boolean tryConsume(final String token) {
  568. if (currentToken.equals(token)) {
  569. nextToken();
  570. return true;
  571. } else {
  572. return false;
  573. }
  574. }
  575. /**
  576. * If the next token exactly matches {@code token}, consume it. Otherwise,
  577. * throw a {@link com.google.protobuf.TextFormat.ParseException}.
  578. */
  579. public void consume(final String token) throws ParseException {
  580. if (!tryConsume(token)) {
  581. throw parseException("Expected \"" + token + "\".");
  582. }
  583. }
  584. /**
  585. * Returns {@code true} if the next token is an integer, but does
  586. * not consume it.
  587. */
  588. public boolean lookingAtInteger() {
  589. if (currentToken.length() == 0) {
  590. return false;
  591. }
  592. final char c = currentToken.charAt(0);
  593. return ('0' <= c && c <= '9') ||
  594. c == '-' || c == '+';
  595. }
  596. /**
  597. * If the next token is an identifier, consume it and return its value.
  598. * Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  599. */
  600. public String consumeIdentifier() throws ParseException {
  601. for (int i = 0; i < currentToken.length(); i++) {
  602. final char c = currentToken.charAt(i);
  603. if (('a' <= c && c <= 'z') ||
  604. ('A' <= c && c <= 'Z') ||
  605. ('0' <= c && c <= '9') ||
  606. (c == '_') || (c == '.')) {
  607. // OK
  608. } else {
  609. throw parseException("Expected identifier.");
  610. }
  611. }
  612. final String result = currentToken;
  613. nextToken();
  614. return result;
  615. }
  616. /**
  617. * If the next token is a 32-bit signed integer, consume it and return its
  618. * value. Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  619. */
  620. public int consumeInt32() throws ParseException {
  621. try {
  622. final int result = parseInt32(currentToken);
  623. nextToken();
  624. return result;
  625. } catch (NumberFormatException e) {
  626. throw integerParseException(e);
  627. }
  628. }
  629. /**
  630. * If the next token is a 32-bit unsigned integer, consume it and return its
  631. * value. Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  632. */
  633. public int consumeUInt32() throws ParseException {
  634. try {
  635. final int result = parseUInt32(currentToken);
  636. nextToken();
  637. return result;
  638. } catch (NumberFormatException e) {
  639. throw integerParseException(e);
  640. }
  641. }
  642. /**
  643. * If the next token is a 64-bit signed integer, consume it and return its
  644. * value. Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  645. */
  646. public long consumeInt64() throws ParseException {
  647. try {
  648. final long result = parseInt64(currentToken);
  649. nextToken();
  650. return result;
  651. } catch (NumberFormatException e) {
  652. throw integerParseException(e);
  653. }
  654. }
  655. /**
  656. * If the next token is a 64-bit unsigned integer, consume it and return its
  657. * value. Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  658. */
  659. public long consumeUInt64() throws ParseException {
  660. try {
  661. final long result = parseUInt64(currentToken);
  662. nextToken();
  663. return result;
  664. } catch (NumberFormatException e) {
  665. throw integerParseException(e);
  666. }
  667. }
  668. /**
  669. * If the next token is a double, consume it and return its value.
  670. * Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  671. */
  672. public double consumeDouble() throws ParseException {
  673. // We need to parse infinity and nan separately because
  674. // Double.parseDouble() does not accept "inf", "infinity", or "nan".
  675. if (DOUBLE_INFINITY.matcher(currentToken).matches()) {
  676. final boolean negative = currentToken.startsWith("-");
  677. nextToken();
  678. return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
  679. }
  680. if (currentToken.equalsIgnoreCase("nan")) {
  681. nextToken();
  682. return Double.NaN;
  683. }
  684. try {
  685. final double result = Double.parseDouble(currentToken);
  686. nextToken();
  687. return result;
  688. } catch (NumberFormatException e) {
  689. throw floatParseException(e);
  690. }
  691. }
  692. /**
  693. * If the next token is a float, consume it and return its value.
  694. * Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  695. */
  696. public float consumeFloat() throws ParseException {
  697. // We need to parse infinity and nan separately because
  698. // Float.parseFloat() does not accept "inf", "infinity", or "nan".
  699. if (FLOAT_INFINITY.matcher(currentToken).matches()) {
  700. final boolean negative = currentToken.startsWith("-");
  701. nextToken();
  702. return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
  703. }
  704. if (FLOAT_NAN.matcher(currentToken).matches()) {
  705. nextToken();
  706. return Float.NaN;
  707. }
  708. try {
  709. final float result = Float.parseFloat(currentToken);
  710. nextToken();
  711. return result;
  712. } catch (NumberFormatException e) {
  713. throw floatParseException(e);
  714. }
  715. }
  716. /**
  717. * If the next token is a boolean, consume it and return its value.
  718. * Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  719. */
  720. public boolean consumeBoolean() throws ParseException {
  721. if (currentToken.equals("true") ||
  722. currentToken.equals("t") ||
  723. currentToken.equals("1")) {
  724. nextToken();
  725. return true;
  726. } else if (currentToken.equals("false") ||
  727. currentToken.equals("f") ||
  728. currentToken.equals("0")) {
  729. nextToken();
  730. return false;
  731. } else {
  732. throw parseException("Expected \"true\" or \"false\".");
  733. }
  734. }
  735. /**
  736. * If the next token is a string, consume it and return its (unescaped)
  737. * value. Otherwise, throw a {@link com.google.protobuf.TextFormat.ParseException}.
  738. */
  739. public String consumeString() throws ParseException {
  740. return consumeByteString().toStringUtf8();
  741. }
  742. /**
  743. * If the next token is a string, consume it, unescape it as a
  744. * {@link ByteString}, and return it. Otherwise, throw a
  745. * {@link com.google.protobuf.TextFormat.ParseException}.
  746. */
  747. public ByteString consumeByteString() throws ParseException {
  748. List<ByteString> list = new ArrayList<ByteString>();
  749. consumeByteString(list);
  750. while (currentToken.startsWith("'") || currentToken.startsWith("\"")) {
  751. consumeByteString(list);
  752. }
  753. return ByteString.copyFrom(list);
  754. }
  755. /**
  756. * Like {@link #consumeByteString()} but adds each token of the string to
  757. * the given list. String literals (whether bytes or text) may come in
  758. * multiple adjacent tokens which are automatically concatenated, like in
  759. * C or Python.
  760. */
  761. private void consumeByteString(List<ByteString> list) throws ParseException {
  762. final char quote = currentToken.length() > 0 ? currentToken.charAt(0)
  763. : '\0';
  764. if (quote != '\"' && quote != '\'') {
  765. throw parseException("Expected string.");
  766. }
  767. if (currentToken.length() < 2 ||
  768. currentToken.charAt(currentToken.length() - 1) != quote) {
  769. throw parseException("String missing ending quote.");
  770. }
  771. try {
  772. final String escaped =
  773. currentToken.substring(1, currentToken.length() - 1);
  774. final ByteString result = unescapeBytes(escaped);
  775. nextToken();
  776. list.add(result);
  777. } catch (InvalidEscapeSequenceException e) {
  778. throw parseException(e.getMessage());
  779. }
  780. }
  781. /**
  782. * Returns a {@link com.google.protobuf.TextFormat.ParseException} with the current line and column
  783. * numbers in the description, suitable for throwing.
  784. */
  785. public ParseException parseException(final String description) {
  786. // Note: People generally prefer one-based line and column numbers.
  787. return new ParseException(
  788. (line + 1) + ":" + (column + 1) + ": " + description);
  789. }
  790. /**
  791. * Returns a {@link com.google.protobuf.TextFormat.ParseException} with the line and column numbers of
  792. * the previous token in the description, suitable for throwing.
  793. */
  794. public ParseException parseExceptionPreviousToken(
  795. final String description) {
  796. // Note: People generally prefer one-based line and column numbers.
  797. return new ParseException(
  798. (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description);
  799. }
  800. /**
  801. * Constructs an appropriate {@link com.google.protobuf.TextFormat.ParseException} for the given
  802. * {@code NumberFormatException} when trying to parse an integer.
  803. */
  804. private ParseException integerParseException(
  805. final NumberFormatException e) {
  806. return parseException("Couldn't parse integer: " + e.getMessage());
  807. }
  808. /**
  809. * Constructs an appropriate {@link com.google.protobuf.TextFormat.ParseException} for the given
  810. * {@code NumberFormatException} when trying to parse a float or double.
  811. */
  812. private ParseException floatParseException(final NumberFormatException e) {
  813. return parseException("Couldn't parse number: " + e.getMessage());
  814. }
  815. }
  816. /** Thrown when parsing an invalid text format message. */
  817. public static class ParseException extends IOException {
  818. private static final long serialVersionUID = 3196188060225107702L;
  819. public ParseException(final String message) {
  820. super(message);
  821. }
  822. }
  823. /**
  824. * Parse a text-format message from {@code input} and merge the contents
  825. * into {@code builder}.
  826. */
  827. public static void merge(final Readable input,
  828. final Message.Builder builder)
  829. throws IOException {
  830. merge(input, ExtensionRegistry.getEmptyRegistry(), builder);
  831. }
  832. /**
  833. * Parse a text-format message from {@code input} and merge the contents
  834. * into {@code builder}.
  835. */
  836. public static void merge(final CharSequence input,
  837. final Message.Builder builder)
  838. throws ParseException {
  839. merge(input, ExtensionRegistry.getEmptyRegistry(), builder);
  840. }
  841. /**
  842. * Parse a text-format message from {@code input} and merge the contents
  843. * into {@code builder}. Extensions will be recognized if they are
  844. * registered in {@code extensionRegistry}.
  845. */
  846. public static void merge(final Readable input,
  847. final ExtensionRegistry extensionRegistry,
  848. final Message.Builder builder)
  849. throws IOException {
  850. // Read the entire input to a String then parse that.
  851. // If StreamTokenizer were not quite so crippled, or if there were a kind
  852. // of Reader that could read in chunks that match some particular regex,
  853. // or if we wanted to write a custom Reader to tokenize our stream, then
  854. // we would not have to read to one big String. Alas, none of these is
  855. // the case. Oh well.
  856. merge(toStringBuilder(input), extensionRegistry, builder);
  857. }
  858. private static final int BUFFER_SIZE = 4096;
  859. // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer)
  860. // overhead is worthwhile
  861. private static StringBuilder toStringBuilder(final Readable input)
  862. throws IOException {
  863. final StringBuilder text = new StringBuilder();
  864. final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE);
  865. while (true) {
  866. final int n = input.read(buffer);
  867. if (n == -1) {
  868. break;
  869. }
  870. buffer.flip();
  871. text.append(buffer, 0, n);
  872. }
  873. return text;
  874. }
  875. /**
  876. * Parse a text-format message from {@code input} and merge the contents
  877. * into {@code builder}. Extensions will be recognized if they are
  878. * registered in {@code extensionRegistry}.
  879. */
  880. public static void merge(final CharSequence input,
  881. final ExtensionRegistry extensionRegistry,
  882. final Message.Builder builder)
  883. throws ParseException {
  884. final Tokenizer tokenizer = new Tokenizer(input);
  885. while (!tokenizer.atEnd()) {
  886. mergeField(tokenizer, extensionRegistry, builder);
  887. }
  888. }
  889. /**
  890. * Parse a single field from {@code tokenizer} and merge it into
  891. * {@code builder}.
  892. */
  893. private static void mergeField(final Tokenizer tokenizer,
  894. final ExtensionRegistry extensionRegistry,
  895. final Message.Builder builder)
  896. throws ParseException {
  897. FieldDescriptor field;
  898. final Descriptor type = builder.getDescriptorForType();
  899. ExtensionRegistry.ExtensionInfo extension = null;
  900. if (tokenizer.tryConsume("[")) {
  901. // An extension.
  902. final StringBuilder name =
  903. new StringBuilder(tokenizer.consumeIdentifier());
  904. while (tokenizer.tryConsume(".")) {
  905. name.append('.');
  906. name.append(tokenizer.consumeIdentifier());
  907. }
  908. extension = extensionRegistry.findExtensionByName(name.toString());
  909. if (extension == null) {
  910. throw tokenizer.parseExceptionPreviousToken(
  911. "Extension \"" + name + "\" not found in the ExtensionRegistry.");
  912. } else if (extension.descriptor.getContainingType() != type) {
  913. throw tokenizer.parseExceptionPreviousToken(
  914. "Extension \"" + name + "\" does not extend message type \"" +
  915. type.getFullName() + "\".");
  916. }
  917. tokenizer.consume("]");
  918. field = extension.descriptor;
  919. } else {
  920. final String name = tokenizer.consumeIdentifier();
  921. field = type.findFieldByName(name);
  922. // Group names are expected to be capitalized as they appear in the
  923. // .proto file, which actually matches their type names, not their field
  924. // names.
  925. if (field == null) {
  926. // Explicitly specify US locale so that this code does not break when
  927. // executing in Turkey.
  928. final String lowerName = name.toLowerCase(Locale.US);
  929. field = type.findFieldByName(lowerName);
  930. // If the case-insensitive match worked but the field is NOT a group,
  931. if (field != null && field.getType() != FieldDescriptor.Type.GROUP) {
  932. field = null;
  933. }
  934. }
  935. // Again, special-case group names as described above.
  936. if (field != null && field.getType() == FieldDescriptor.Type.GROUP &&
  937. !field.getMessageType().getName().equals(name)) {
  938. field = null;
  939. }
  940. if (field == null) {
  941. throw tokenizer.parseExceptionPreviousToken(
  942. "Message type \"" + type.getFullName() +
  943. "\" has no field named \"" + name + "\".");
  944. }
  945. }
  946. Object value = null;
  947. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
  948. tokenizer.tryConsume(":"); // optional
  949. final String endToken;
  950. if (tokenizer.tryConsume("<")) {
  951. endToken = ">";
  952. } else {
  953. tokenizer.consume("{");
  954. endToken = "}";
  955. }
  956. final Message.Builder subBuilder;
  957. if (extension == null) {
  958. subBuilder = builder.newBuilderForField(field);
  959. } else {
  960. subBuilder = extension.defaultInstance.newBuilderForType();
  961. }
  962. while (!tokenizer.tryConsume(endToken)) {
  963. if (tokenizer.atEnd()) {
  964. throw tokenizer.parseException(
  965. "Expected \"" + endToken + "\".");
  966. }
  967. mergeField(tokenizer, extensionRegistry, subBuilder);
  968. }
  969. value = subBuilder.build();
  970. } else {
  971. tokenizer.consume(":");
  972. switch (field.getType()) {
  973. case INT32:
  974. case SINT32:
  975. case SFIXED32:
  976. value = tokenizer.consumeInt32();
  977. break;
  978. case INT64:
  979. case SINT64:
  980. case SFIXED64:
  981. value = tokenizer.consumeInt64();
  982. break;
  983. case UINT32:
  984. case FIXED32:
  985. value = tokenizer.consumeUInt32();
  986. break;
  987. case UINT64:
  988. case FIXED64:
  989. value = tokenizer.consumeUInt64();
  990. break;
  991. case FLOAT:
  992. value = tokenizer.consumeFloat();
  993. break;
  994. case DOUBLE:
  995. value = tokenizer.consumeDouble();
  996. break;
  997. case BOOL:
  998. value = tokenizer.consumeBoolean();
  999. break;
  1000. case STRING:
  1001. value = tokenizer.consumeString();
  1002. break;
  1003. case BYTES:
  1004. value = tokenizer.consumeByteString();
  1005. break;
  1006. case ENUM:
  1007. final EnumDescriptor enumType = field.getEnumType();
  1008. if (tokenizer.lookingAtInteger()) {
  1009. final int number = tokenizer.consumeInt32();
  1010. value = enumType.findValueByNumber(number);
  1011. if (value == null) {
  1012. throw tokenizer.parseExceptionPreviousToken(
  1013. "Enum type \"" + enumType.getFullName() +
  1014. "\" has no value with number " + number + '.');
  1015. }
  1016. } else {
  1017. final String id = tokenizer.consumeIdentifier();
  1018. value = enumType.findValueByName(id);
  1019. if (value == null) {
  1020. throw tokenizer.parseExceptionPreviousToken(
  1021. "Enum type \"" + enumType.getFullName() +
  1022. "\" has no value named \"" + id + "\".");
  1023. }
  1024. }
  1025. break;
  1026. case MESSAGE:
  1027. case GROUP:
  1028. throw new RuntimeException("Can't get here.");
  1029. }
  1030. }
  1031. if (field.isRepeated()) {
  1032. builder.addRepeatedField(field, value);
  1033. } else {
  1034. builder.setField(field, value);
  1035. }
  1036. }
  1037. // =================================================================
  1038. // Utility functions
  1039. //
  1040. // Some of these methods are package-private because Descriptors.java uses
  1041. // them.
  1042. /**
  1043. * Escapes bytes in the format used in protocol buffer text format, which
  1044. * is the same as the format used for C string literals. All bytes
  1045. * that are not printable 7-bit ASCII characters are escaped, as well as
  1046. * backslash, single-quote, and double-quote characters. Characters for
  1047. * which no defined short-hand escape sequence is defined will be escaped
  1048. * using 3-digit octal sequences.
  1049. */
  1050. static String escapeBytes(final ByteString input) {
  1051. final StringBuilder builder = new StringBuilder(input.size());
  1052. for (int i = 0; i < input.size(); i++) {
  1053. final byte b = input.byteAt(i);
  1054. switch (b) {
  1055. // Java does not recognize \a or \v, apparently.
  1056. case 0x07: builder.append("\\a" ); break;
  1057. case '\b': builder.append("\\b" ); break;
  1058. case '\f': builder.append("\\f" ); break;
  1059. case '\n': builder.append("\\n" ); break;
  1060. case '\r': builder.append("\\r" ); break;
  1061. case '\t': builder.append("\\t" ); break;
  1062. case 0x0b: builder.append("\\v" ); break;
  1063. case '\\': builder.append("\\\\"); break;
  1064. case '\'': builder.append("\\\'"); break;
  1065. case '"' : builder.append("\\\""); break;
  1066. default:
  1067. // Note: Bytes with the high-order bit set should be escaped. Since
  1068. // bytes are signed, such bytes will compare less than 0x20, hence
  1069. // the following line is correct.
  1070. if (b >= 0x20) {
  1071. builder.append((char) b);
  1072. } else {
  1073. builder.append('\\');
  1074. builder.append((char) ('0' + ((b >>> 6) & 3)));
  1075. builder.append((char) ('0' + ((b >>> 3) & 7)));
  1076. builder.append((char) ('0' + (b & 7)));
  1077. }
  1078. break;
  1079. }
  1080. }
  1081. return builder.toString();
  1082. }
  1083. /**
  1084. * Un-escape a byte sequence as escaped using
  1085. * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with
  1086. * "\x") are also recognized.
  1087. */
  1088. static ByteString unescapeBytes(final CharSequence charString)
  1089. throws InvalidEscapeSequenceException {
  1090. // First convert the Java characater sequence to UTF-8 bytes.
  1091. ByteString input = ByteString.copyFromUtf8(charString.toString());
  1092. // Then unescape certain byte sequences introduced by ASCII '\\'. The valid
  1093. // escapes can all be expressed with ASCII characters, so it is safe to
  1094. // operate on bytes here.
  1095. //
  1096. // Unescaping the input byte array will result in a byte sequence that's no
  1097. // longer than the input. That's because each escape sequence is between
  1098. // two and four bytes long and stands for a single byte.
  1099. final byte[] result = new byte[input.size()];
  1100. int pos = 0;
  1101. for (int i = 0; i < input.size(); i++) {
  1102. byte c = input.byteAt(i);
  1103. if (c == '\\') {
  1104. if (i + 1 < input.size()) {
  1105. ++i;
  1106. c = input.byteAt(i);
  1107. if (isOctal(c)) {
  1108. // Octal escape.
  1109. int code = digitValue(c);
  1110. if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
  1111. ++i;
  1112. code = code * 8 + digitValue(input.byteAt(i));
  1113. }
  1114. if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) {
  1115. ++i;
  1116. code = code * 8 + digitValue(input.byteAt(i));
  1117. }
  1118. // TODO: Check that 0 <= code && code <= 0xFF.
  1119. result[pos++] = (byte)code;
  1120. } else {
  1121. switch (c) {
  1122. case 'a' : result[pos++] = 0x07; break;
  1123. case 'b' : result[pos++] = '\b'; break;
  1124. case 'f' : result[pos++] = '\f'; break;
  1125. case 'n' : result[pos++] = '\n'; break;
  1126. case 'r' : result[pos++] = '\r'; break;
  1127. case 't' : result[pos++] = '\t'; break;
  1128. case 'v' : result[pos++] = 0x0b; break;
  1129. case '\\': result[pos++] = '\\'; break;
  1130. case '\'': result[pos++] = '\''; break;
  1131. case '"' : result[pos++] = '\"'; break;
  1132. case 'x':
  1133. // hex escape
  1134. int code = 0;
  1135. if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
  1136. ++i;
  1137. code = digitValue(input.byteAt(i));
  1138. } else {
  1139. throw new InvalidEscapeSequenceException(
  1140. "Invalid escape sequence: '\\x' with no digits");
  1141. }
  1142. if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) {
  1143. ++i;
  1144. code = code * 16 + digitValue(input.byteAt(i));
  1145. }
  1146. result[pos++] = (byte)code;
  1147. break;
  1148. default:
  1149. throw new InvalidEscapeSequenceException(
  1150. "Invalid escape sequence: '\\" + (char)c + '\'');
  1151. }
  1152. }
  1153. } else {
  1154. throw new InvalidEscapeSequenceException(
  1155. "Invalid escape sequence: '\\' at end of string.");
  1156. }
  1157. } else {
  1158. result[pos++] = c;
  1159. }
  1160. }
  1161. return ByteString.copyFrom(result, 0, pos);
  1162. }
  1163. /**
  1164. * Thrown by {@link TextFormat#unescapeBytes} and
  1165. * {@link TextFormat#unescapeText} when an invalid escape sequence is seen.
  1166. */
  1167. static class InvalidEscapeSequenceException extends IOException {
  1168. private static final long serialVersionUID = -8164033650142593304L;
  1169. InvalidEscapeSequenceException(final String description) {
  1170. super(description);
  1171. }
  1172. }
  1173. /**
  1174. * Like {@link #escapeBytes(ByteString)}, but escapes a text string.
  1175. * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped
  1176. * individually as a 3-digit octal escape. Yes, it's weird.
  1177. */
  1178. static String escapeText(final String input) {
  1179. return escapeBytes(ByteString.copyFromUtf8(input));
  1180. }
  1181. /**
  1182. * Un-escape a text string as escaped using {@link #escapeText(String)}.
  1183. * Two-digit hex escapes (starting with "\x") are also recognized.
  1184. */
  1185. static String unescapeText(final String input)
  1186. throws InvalidEscapeSequenceException {
  1187. return unescapeBytes(input).toStringUtf8();
  1188. }
  1189. /** Is this an octal digit? */
  1190. private static boolean isOctal(final byte c) {
  1191. return '0' <= c && c <= '7';
  1192. }
  1193. /** Is this a hex digit? */
  1194. private static boolean isHex(final byte c) {
  1195. return ('0' <= c && c <= '9') ||
  1196. ('a' <= c && c <= 'f') ||
  1197. ('A' <= c && c <= 'F');
  1198. }
  1199. /**
  1200. * Interpret a character as a digit (in any base up to 36) and return the
  1201. * numeric value. This is like {@code Character.digit()} but we don't accept
  1202. * non-ASCII digits.
  1203. */
  1204. private static int digitValue(final byte c) {
  1205. if ('0' <= c && c <= '9') {
  1206. return c - '0';
  1207. } else if ('a' <= c && c <= 'z') {
  1208. return c - 'a' + 10;
  1209. } else {
  1210. return c - 'A' + 10;
  1211. }
  1212. }
  1213. /**
  1214. * Parse a 32-bit signed integer from the text. Unlike the Java standard
  1215. * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
  1216. * and "0" to signify hexidecimal and octal numbers, respectively.
  1217. */
  1218. static int parseInt32(final String text) throws NumberFormatException {
  1219. return (int) parseInteger(text, true, false);
  1220. }
  1221. /**
  1222. * Parse a 32-bit unsigned integer from the text. Unlike the Java standard
  1223. * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
  1224. * and "0" to signify hexidecimal and octal numbers, respectively. The
  1225. * result is coerced to a (signed) {@code int} when returned since Java has
  1226. * no unsigned integer type.
  1227. */
  1228. static int parseUInt32(final String text) throws NumberFormatException {
  1229. return (int) parseInteger(text, false, false);
  1230. }
  1231. /**
  1232. * Parse a 64-bit signed integer from the text. Unlike the Java standard
  1233. * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
  1234. * and "0" to signify hexidecimal and octal numbers, respectively.
  1235. */
  1236. static long parseInt64(final String text) throws NumberFormatException {
  1237. return parseInteger(text, true, true);
  1238. }
  1239. /**
  1240. * Parse a 64-bit unsigned integer from the text. Unlike the Java standard
  1241. * {@code Integer.parseInt()}, this function recognizes the prefixes "0x"
  1242. * and "0" to signify hexidecimal and octal numbers, respectively. The
  1243. * result is coerced to a (signed) {@code long} when returned since Java has
  1244. * no unsigned long type.
  1245. */
  1246. static long parseUInt64(final String text) throws NumberFormatException {
  1247. return parseInteger(text, false, true);
  1248. }
  1249. private static long parseInteger(final String text,
  1250. final boolean isSigned,
  1251. final boolean isLong)
  1252. throws NumberFormatException {
  1253. int pos = 0;
  1254. boolean negative = false;
  1255. if (text.startsWith("-", pos)) {
  1256. if (!isSigned) {
  1257. throw new NumberFormatException("Number must be positive: " + text);
  1258. }
  1259. ++pos;
  1260. negative = true;
  1261. }
  1262. int radix = 10;
  1263. if (text.startsWith("0x", pos)) {
  1264. pos += 2;
  1265. radix = 16;
  1266. } else if (text.startsWith("0", pos)) {
  1267. radix = 8;
  1268. }
  1269. final String numberText = text.substring(pos);
  1270. long result = 0;
  1271. if (numberText.length() < 16) {
  1272. // Can safely assume no overflow.
  1273. result = Long.parseLong(numberText, radix);
  1274. if (negative) {
  1275. result = -result;
  1276. }
  1277. // Check bounds.
  1278. // No need to check for 64-bit numbers since they'd have to be 16 chars
  1279. // or longer to overflow.
  1280. if (!isLong) {
  1281. if (isSigned) {
  1282. if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) {
  1283. throw new NumberFormatException(
  1284. "Number out of range for 32-bit signed integer: " + text);
  1285. }
  1286. } else {
  1287. if (result >= (1L << 32) || result < 0) {
  1288. throw new NumberFormatException(

Large files files are truncated, but you can click here to view the full file