Commits

Anonymous committed 71073ad

In Java's TextFormat, correcty concatenate adjacent string literals, as C++ does. Also fix a bug in handling of single-quoted strings.

  • Participants
  • Parent commits 56022d3

Comments (0)

Files changed (3)

java/src/main/java/com/google/protobuf/ByteString.java

 import java.io.FilterOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
+import java.util.List;
 
 /**
  * Immutable array of bytes.
     }
   }
 
+  /**
+   * Concatenates all byte strings in the list and returns the result.
+   *
+   * <p>The returned {@code ByteString} is not necessarily a unique object.
+   * If the list is empty, the returned object is the singleton empty
+   * {@code ByteString}.  If the list has only one element, that
+   * {@code ByteString} will be returned without copying.
+   */
+  public static ByteString copyFrom(List<ByteString> list) {
+    if (list.size() == 0) {
+      return EMPTY;
+    } else if (list.size() == 1) {
+      return list.get(0);
+    }
+
+    int size = 0;
+    for (ByteString str : list) {
+      size += str.size();
+    }
+    byte[] bytes = new byte[size];
+    int pos = 0;
+    for (ByteString str : list) {
+      System.arraycopy(str.bytes, 0, bytes, pos, str.size());
+      pos += str.size();
+    }
+    return new ByteString(bytes);
+  }
+
   // =================================================================
   // ByteString -> byte[]
 

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

 import java.io.IOException;
 import java.nio.CharBuffer;
 import java.math.BigInteger;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
     }
     printUnknownFields(message.getUnknownFields(), generator);
   }
-  
+
   public static void printField(final FieldDescriptor field,
                                 final Object value,
                                 final Appendable output)
     } catch (IOException e) {
       throw new RuntimeException(
         "Writing to a StringBuilder threw an IOException (should never " +
-        "happen).", e);  
+        "happen).", e);
     }
   }
-  
+
   private static void printField(final FieldDescriptor field,
                                 final Object value,
                                 final TextGenerator generator)
       "[a-zA-Z_][0-9a-zA-Z_+-]*+|" +                // an identifier
       "[.]?[0-9+-][0-9a-zA-Z_.+-]*+|" +             // a number
       "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" +       // a double-quoted string
-      "\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)",         // a single-quoted string
+      "\'([^\'\n\\\\]|\\\\.)*+(\'|\\\\?$)",         // a single-quoted string
       Pattern.MULTILINE);
 
     private static final Pattern DOUBLE_INFINITY = Pattern.compile(
      * {@link ParseException}.
      */
     public ByteString consumeByteString() throws ParseException {
+      List<ByteString> list = new ArrayList<ByteString>();
+      consumeByteString(list);
+      while (currentToken.startsWith("'") || currentToken.startsWith("\"")) {
+        consumeByteString(list);
+      }
+      return ByteString.copyFrom(list);
+    }
+
+    public void consumeByteString(List<ByteString> list) throws ParseException {
       final char quote = currentToken.length() > 0 ? currentToken.charAt(0)
                                                    : '\0';
       if (quote != '\"' && quote != '\'') {
             currentToken.substring(1, currentToken.length() - 1);
         final ByteString result = unescapeBytes(escaped);
         nextToken();
-        return result;
+        list.add(result);
       } catch (InvalidEscapeSequenceException e) {
         throw parseException(e.getMessage());
       }

java/src/test/java/com/google/protobuf/TextFormatTest.java

     final FieldDescriptor optionalField =
       TestAllTypes.getDescriptor().findFieldByName("optional_nested_message");
     final Object value = NestedMessage.newBuilder().setBb(42).build();
-    
+
     assertEquals(
       "optional_nested_message {\n  bb: 42\n}\n",
       TextFormat.printFieldToString(optionalField, value));
   }
-  
+
   /**
    * Helper to construct a ByteString from a String containing only 8-bit
    * characters.  The characters are converted directly to bytes, *not*
     TextFormat.merge("optional_string: \"" + longText + "\"", builder);
     assertEquals(longText, builder.getOptionalString());
   }
+
+  public void testParseAdjacentStringLiterals() throws Exception {
+    TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+    TextFormat.merge("optional_string: \"foo\" 'corge' \"grault\"", builder);
+    assertEquals("foocorgegrault", builder.getOptionalString());
+  }
 }