aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJisi Liu <liujisi@google.com>2015-02-17 14:06:19 -0800
committerJisi Liu <liujisi@google.com>2015-02-17 14:06:19 -0800
commit2cb2358cba7eb296d6243912758b9e9c78d1502e (patch)
tree394e85b2d3ad685e30beff18d391fa699c1755e8
parentdb87a9c07a5e1e82ebcf606d8254ff6acad1dce8 (diff)
parentc265fbe3ca6e1811524831fbf89a3d7cfdf97412 (diff)
downloadprotobuf-2cb2358cba7eb296d6243912758b9e9c78d1502e.tar.gz
protobuf-2cb2358cba7eb296d6243912758b9e9c78d1502e.tar.bz2
protobuf-2cb2358cba7eb296d6243912758b9e9c78d1502e.zip
Merge pull request #204 from pherl/master
Implement maps for JavaNano
-rw-r--r--javanano/pom.xml1
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java40
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java124
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/Extension.java73
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/InternalNano.java223
-rw-r--r--javanano/src/main/java/com/google/protobuf/nano/MapFactories.java67
-rw-r--r--javanano/src/test/java/com/google/protobuf/nano/NanoTest.java340
-rw-r--r--javanano/src/test/java/com/google/protobuf/nano/map_test.proto70
-rw-r--r--src/Makefile.am22
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_field.cc7
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_helpers.cc11
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_helpers.h8
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_map_field.cc186
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_map_field.h70
-rw-r--r--src/google/protobuf/compiler/javanano/javanano_message.cc8
15 files changed, 1163 insertions, 87 deletions
diff --git a/javanano/pom.xml b/javanano/pom.xml
index 3d98a5e0..409d8d61 100644
--- a/javanano/pom.xml
+++ b/javanano/pom.xml
@@ -94,6 +94,7 @@
<arg value="src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto" />
+ <arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" />
diff --git a/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java b/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
index b147f69d..b4f20fde 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
@@ -638,4 +638,44 @@ public final class CodedInputByteBufferNano {
throw InvalidProtocolBufferNanoException.truncatedMessage();
}
}
+
+ // Read a primitive type.
+ Object readPrimitiveField(int type) throws IOException {
+ switch (type) {
+ case InternalNano.TYPE_DOUBLE:
+ return readDouble();
+ case InternalNano.TYPE_FLOAT:
+ return readFloat();
+ case InternalNano.TYPE_INT64:
+ return readInt64();
+ case InternalNano.TYPE_UINT64:
+ return readUInt64();
+ case InternalNano.TYPE_INT32:
+ return readInt32();
+ case InternalNano.TYPE_FIXED64:
+ return readFixed64();
+ case InternalNano.TYPE_FIXED32:
+ return readFixed32();
+ case InternalNano.TYPE_BOOL:
+ return readBool();
+ case InternalNano.TYPE_STRING:
+ return readString();
+ case InternalNano.TYPE_BYTES:
+ return readBytes();
+ case InternalNano.TYPE_UINT32:
+ return readUInt32();
+ case InternalNano.TYPE_ENUM:
+ return readEnum();
+ case InternalNano.TYPE_SFIXED32:
+ return readSFixed32();
+ case InternalNano.TYPE_SFIXED64:
+ return readSFixed64();
+ case InternalNano.TYPE_SINT32:
+ return readSInt32();
+ case InternalNano.TYPE_SINT64:
+ return readSInt64();
+ default:
+ throw new IllegalArgumentException("Unknown type " + type);
+ }
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
index 37982b57..2777f34c 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
@@ -876,4 +876,128 @@ public final class CodedOutputByteBufferNano {
// Note: the right-shift must be arithmetic
return (n << 1) ^ (n >> 63);
}
+
+ static int computeFieldSize(int number, int type, Object object) {
+ switch (type) {
+ case InternalNano.TYPE_BOOL:
+ return computeBoolSize(number, (Boolean) object);
+ case InternalNano.TYPE_BYTES:
+ return computeBytesSize(number, (byte[]) object);
+ case InternalNano.TYPE_STRING:
+ return computeStringSize(number, (String) object);
+ case InternalNano.TYPE_FLOAT:
+ return computeFloatSize(number, (Float) object);
+ case InternalNano.TYPE_DOUBLE:
+ return computeDoubleSize(number, (Double) object);
+ case InternalNano.TYPE_ENUM:
+ return computeEnumSize(number, (Integer) object);
+ case InternalNano.TYPE_FIXED32:
+ return computeFixed32Size(number, (Integer) object);
+ case InternalNano.TYPE_INT32:
+ return computeInt32Size(number, (Integer) object);
+ case InternalNano.TYPE_UINT32:
+ return computeUInt32Size(number, (Integer) object);
+ case InternalNano.TYPE_SINT32:
+ return computeSInt32Size(number, (Integer) object);
+ case InternalNano.TYPE_SFIXED32:
+ return computeSFixed32Size(number, (Integer) object);
+ case InternalNano.TYPE_INT64:
+ return computeInt64Size(number, (Long) object);
+ case InternalNano.TYPE_UINT64:
+ return computeUInt64Size(number, (Long) object);
+ case InternalNano.TYPE_SINT64:
+ return computeSInt64Size(number, (Long) object);
+ case InternalNano.TYPE_FIXED64:
+ return computeFixed64Size(number, (Long) object);
+ case InternalNano.TYPE_SFIXED64:
+ return computeSFixed64Size(number, (Long) object);
+ case InternalNano.TYPE_MESSAGE:
+ return computeMessageSize(number, (MessageNano) object);
+ case InternalNano.TYPE_GROUP:
+ return computeGroupSize(number, (MessageNano) object);
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
+ void writeField(int number, int type, Object value)
+ throws IOException {
+ switch (type) {
+ case InternalNano.TYPE_DOUBLE:
+ Double doubleValue = (Double) value;
+ writeDouble(number, doubleValue);
+ break;
+ case InternalNano.TYPE_FLOAT:
+ Float floatValue = (Float) value;
+ writeFloat(number, floatValue);
+ break;
+ case InternalNano.TYPE_INT64:
+ Long int64Value = (Long) value;
+ writeInt64(number, int64Value);
+ break;
+ case InternalNano.TYPE_UINT64:
+ Long uint64Value = (Long) value;
+ writeUInt64(number, uint64Value);
+ break;
+ case InternalNano.TYPE_INT32:
+ Integer int32Value = (Integer) value;
+ writeInt32(number, int32Value);
+ break;
+ case InternalNano.TYPE_FIXED64:
+ Long fixed64Value = (Long) value;
+ writeFixed64(number, fixed64Value);
+ break;
+ case InternalNano.TYPE_FIXED32:
+ Integer fixed32Value = (Integer) value;
+ writeFixed32(number, fixed32Value);
+ break;
+ case InternalNano.TYPE_BOOL:
+ Boolean boolValue = (Boolean) value;
+ writeBool(number, boolValue);
+ break;
+ case InternalNano.TYPE_STRING:
+ String stringValue = (String) value;
+ writeString(number, stringValue);
+ break;
+ case InternalNano.TYPE_BYTES:
+ byte[] bytesValue = (byte[]) value;
+ writeBytes(number, bytesValue);
+ break;
+ case InternalNano.TYPE_UINT32:
+ Integer uint32Value = (Integer) value;
+ writeUInt32(number, uint32Value);
+ break;
+ case InternalNano.TYPE_ENUM:
+ Integer enumValue = (Integer) value;
+ writeEnum(number, enumValue);
+ break;
+ case InternalNano.TYPE_SFIXED32:
+ Integer sfixed32Value = (Integer) value;
+ writeSFixed32(number, sfixed32Value);
+ break;
+ case InternalNano.TYPE_SFIXED64:
+ Long sfixed64Value = (Long) value;
+ writeSFixed64(number, sfixed64Value);
+ break;
+ case InternalNano.TYPE_SINT32:
+ Integer sint32Value = (Integer) value;
+ writeSInt32(number, sint32Value);
+ break;
+ case InternalNano.TYPE_SINT64:
+ Long sint64Value = (Long) value;
+ writeSInt64(number, sint64Value);
+ break;
+ case InternalNano.TYPE_MESSAGE:
+ MessageNano messageValue = (MessageNano) value;
+ writeMessage(number, messageValue);
+ break;
+ case InternalNano.TYPE_GROUP:
+ MessageNano groupValue = (MessageNano) value;
+ writeGroup(number, groupValue);
+ break;
+ default:
+ throw new IOException("Unknown type: " + type);
+ }
+ }
+
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/Extension.java b/javanano/src/main/java/com/google/protobuf/nano/Extension.java
index 5d18ae6e..c29b030f 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/Extension.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/Extension.java
@@ -55,24 +55,24 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* PrimitiveExtension // for primitive/enum typed extensions
*/
- public static final int TYPE_DOUBLE = 1;
- public static final int TYPE_FLOAT = 2;
- public static final int TYPE_INT64 = 3;
- public static final int TYPE_UINT64 = 4;
- public static final int TYPE_INT32 = 5;
- public static final int TYPE_FIXED64 = 6;
- public static final int TYPE_FIXED32 = 7;
- public static final int TYPE_BOOL = 8;
- public static final int TYPE_STRING = 9;
- public static final int TYPE_GROUP = 10;
- public static final int TYPE_MESSAGE = 11;
- public static final int TYPE_BYTES = 12;
- public static final int TYPE_UINT32 = 13;
- public static final int TYPE_ENUM = 14;
- public static final int TYPE_SFIXED32 = 15;
- public static final int TYPE_SFIXED64 = 16;
- public static final int TYPE_SINT32 = 17;
- public static final int TYPE_SINT64 = 18;
+ public static final int TYPE_DOUBLE = InternalNano.TYPE_DOUBLE;
+ public static final int TYPE_FLOAT = InternalNano.TYPE_FLOAT;
+ public static final int TYPE_INT64 = InternalNano.TYPE_INT64;
+ public static final int TYPE_UINT64 = InternalNano.TYPE_UINT64;
+ public static final int TYPE_INT32 = InternalNano.TYPE_INT32;
+ public static final int TYPE_FIXED64 = InternalNano.TYPE_FIXED64;
+ public static final int TYPE_FIXED32 = InternalNano.TYPE_FIXED32;
+ public static final int TYPE_BOOL = InternalNano.TYPE_BOOL;
+ public static final int TYPE_STRING = InternalNano.TYPE_STRING;
+ public static final int TYPE_GROUP = InternalNano.TYPE_GROUP;
+ public static final int TYPE_MESSAGE = InternalNano.TYPE_MESSAGE;
+ public static final int TYPE_BYTES = InternalNano.TYPE_BYTES;
+ public static final int TYPE_UINT32 = InternalNano.TYPE_UINT32;
+ public static final int TYPE_ENUM = InternalNano.TYPE_ENUM;
+ public static final int TYPE_SFIXED32 = InternalNano.TYPE_SFIXED32;
+ public static final int TYPE_SFIXED64 = InternalNano.TYPE_SFIXED64;
+ public static final int TYPE_SINT32 = InternalNano.TYPE_SINT32;
+ public static final int TYPE_SINT64 = InternalNano.TYPE_SINT64;
/**
* Creates an {@code Extension} of the given message type and tag number.
@@ -338,42 +338,7 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
@Override
protected Object readData(CodedInputByteBufferNano input) {
try {
- switch (type) {
- case TYPE_DOUBLE:
- return input.readDouble();
- case TYPE_FLOAT:
- return input.readFloat();
- case TYPE_INT64:
- return input.readInt64();
- case TYPE_UINT64:
- return input.readUInt64();
- case TYPE_INT32:
- return input.readInt32();
- case TYPE_FIXED64:
- return input.readFixed64();
- case TYPE_FIXED32:
- return input.readFixed32();
- case TYPE_BOOL:
- return input.readBool();
- case TYPE_STRING:
- return input.readString();
- case TYPE_BYTES:
- return input.readBytes();
- case TYPE_UINT32:
- return input.readUInt32();
- case TYPE_ENUM:
- return input.readEnum();
- case TYPE_SFIXED32:
- return input.readSFixed32();
- case TYPE_SFIXED64:
- return input.readSFixed64();
- case TYPE_SINT32:
- return input.readSInt32();
- case TYPE_SINT64:
- return input.readSInt64();
- default:
- throw new IllegalArgumentException("Unknown type " + type);
- }
+ return input.readPrimitiveField(type);
} catch (IOException e) {
throw new IllegalArgumentException("Error reading extension field", e);
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
index e08bb4b7..c92ec417 100644
--- a/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
+++ b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
@@ -30,8 +30,13 @@
package com.google.protobuf.nano;
+import com.google.protobuf.nano.MapFactories.MapFactory;
+
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
+import java.util.Map;
+import java.util.Map.Entry;
/**
* The classes contained within are used internally by the Protocol Buffer
@@ -43,6 +48,26 @@ import java.util.Arrays;
*/
public final class InternalNano {
+ public static final int TYPE_DOUBLE = 1;
+ public static final int TYPE_FLOAT = 2;
+ public static final int TYPE_INT64 = 3;
+ public static final int TYPE_UINT64 = 4;
+ public static final int TYPE_INT32 = 5;
+ public static final int TYPE_FIXED64 = 6;
+ public static final int TYPE_FIXED32 = 7;
+ public static final int TYPE_BOOL = 8;
+ public static final int TYPE_STRING = 9;
+ public static final int TYPE_GROUP = 10;
+ public static final int TYPE_MESSAGE = 11;
+ public static final int TYPE_BYTES = 12;
+ public static final int TYPE_UINT32 = 13;
+ public static final int TYPE_ENUM = 14;
+ public static final int TYPE_SFIXED32 = 15;
+ public static final int TYPE_SFIXED64 = 16;
+ public static final int TYPE_SINT32 = 17;
+ public static final int TYPE_SINT64 = 18;
+
+
private InternalNano() {}
/**
@@ -329,5 +354,203 @@ public final class InternalNano {
}
return result;
}
+ private static final byte[] EMPTY_BYTES = new byte[0];
+ private static Object primitiveDefaultValue(int type) {
+ switch (type) {
+ case TYPE_BOOL:
+ return Boolean.FALSE;
+ case TYPE_BYTES:
+ return EMPTY_BYTES;
+ case TYPE_STRING:
+ return "";
+ case TYPE_FLOAT:
+ return Float.valueOf(0);
+ case TYPE_DOUBLE:
+ return Double.valueOf(0);
+ case TYPE_ENUM:
+ case TYPE_FIXED32:
+ case TYPE_INT32:
+ case TYPE_UINT32:
+ case TYPE_SINT32:
+ case TYPE_SFIXED32:
+ return Integer.valueOf(0);
+ case TYPE_INT64:
+ case TYPE_UINT64:
+ case TYPE_SINT64:
+ case TYPE_FIXED64:
+ case TYPE_SFIXED64:
+ return Long.valueOf(0L);
+ case TYPE_MESSAGE:
+ case TYPE_GROUP:
+ default:
+ throw new IllegalArgumentException(
+ "Type: " + type + " is not a primitive type.");
+ }
+ }
+
+ /**
+ * Merges the map entry into the map field. Note this is only supposed to
+ * be called by generated messages.
+ *
+ * @param map the map field; may be null, in which case a map will be
+ * instantiated using the {@link MapFactories.MapFactory}
+ * @param input the input byte buffer
+ * @param keyType key type, as defined in InternalNano.TYPE_*
+ * @param valueType value type, as defined in InternalNano.TYPE_*
+ * @param value an new instance of the value, if the value is a TYPE_MESSAGE;
+ * otherwise this parameter can be null and will be ignored.
+ * @param keyTag wire tag for the key
+ * @param valueTag wire tag for the value
+ * @return the map field
+ * @throws IOException
+ */
+ @SuppressWarnings("unchecked")
+ public static final <K, V> Map<K, V> mergeMapEntry(
+ CodedInputByteBufferNano input,
+ Map<K, V> map,
+ MapFactory mapFactory,
+ int keyType,
+ int valueType,
+ V value,
+ int keyTag,
+ int valueTag) throws IOException {
+ map = mapFactory.forMap(map);
+ final int length = input.readRawVarint32();
+ final int oldLimit = input.pushLimit(length);
+ K key = null;
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+ if (tag == keyTag) {
+ key = (K) input.readPrimitiveField(keyType);
+ } else if (tag == valueTag) {
+ if (valueType == TYPE_MESSAGE) {
+ input.readMessage((MessageNano) value);
+ } else {
+ value = (V) input.readPrimitiveField(valueType);
+ }
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
+ }
+ }
+ input.checkLastTagWas(0);
+ input.popLimit(oldLimit);
+
+ if (key == null) {
+ // key can only be primitive types.
+ key = (K) primitiveDefaultValue(keyType);
+ }
+
+ if (value == null) {
+ // message type value will be initialized by code-gen.
+ value = (V) primitiveDefaultValue(valueType);
+ }
+
+ map.put(key, value);
+ return map;
+ }
+
+ public static <K, V> void serializeMapField(
+ CodedOutputByteBufferNano output,
+ Map<K, V> map, int number, int keyType, int valueType)
+ throws IOException {
+ for (Entry<K, V> entry: map.entrySet()) {
+ K key = entry.getKey();
+ V value = entry.getValue();
+ if (key == null || value == null) {
+ throw new IllegalStateException(
+ "keys and values in maps cannot be null");
+ }
+ int entrySize =
+ CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
+ CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
+ output.writeTag(number, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(entrySize);
+ output.writeField(1, keyType, key);
+ output.writeField(2, valueType, value);
+ }
+ }
+
+ public static <K, V> int computeMapFieldSize(
+ Map<K, V> map, int number, int keyType, int valueType) {
+ int size = 0;
+ int tagSize = CodedOutputByteBufferNano.computeTagSize(number);
+ for (Entry<K, V> entry: map.entrySet()) {
+ K key = entry.getKey();
+ V value = entry.getValue();
+ if (key == null || value == null) {
+ throw new IllegalStateException(
+ "keys and values in maps cannot be null");
+ }
+ int entrySize =
+ CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
+ CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
+ size += tagSize + entrySize
+ + CodedOutputByteBufferNano.computeRawVarint32Size(entrySize);
+ }
+ return size;
+ }
+
+ /**
+ * Checks whether two {@link Map} are equal. We don't use the default equals
+ * method of {@link Map} because it compares by identity not by content for
+ * byte arrays.
+ */
+ public static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null) {
+ return b.size() == 0;
+ }
+ if (b == null) {
+ return a.size() == 0;
+ }
+ if (a.size() != b.size()) {
+ return false;
+ }
+ for (Entry<K, V> entry : a.entrySet()) {
+ if (!b.containsKey(entry.getKey())) {
+ return false;
+ }
+ if (!equalsMapValue(entry.getValue(), b.get(entry.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean equalsMapValue(Object a, Object b) {
+ if (a == null || b == null) {
+ throw new IllegalStateException(
+ "keys and values in maps cannot be null");
+ }
+ if (a instanceof byte[] && b instanceof byte[]) {
+ return Arrays.equals((byte[]) a, (byte[]) b);
+ }
+ return a.equals(b);
+ }
+
+ public static <K, V> int hashCode(Map<K, V> map) {
+ if (map == null) {
+ return 0;
+ }
+ int result = 0;
+ for (Entry<K, V> entry : map.entrySet()) {
+ result += hashCodeForMap(entry.getKey())
+ ^ hashCodeForMap(entry.getValue());
+ }
+ return result;
+ }
+ private static int hashCodeForMap(Object o) {
+ if (o instanceof byte[]) {
+ return Arrays.hashCode((byte[]) o);
+ }
+ return o.hashCode();
+ }
}
diff --git a/javanano/src/main/java/com/google/protobuf/nano/MapFactories.java b/javanano/src/main/java/com/google/protobuf/nano/MapFactories.java
new file mode 100644
index 00000000..98fa4877
--- /dev/null
+++ b/javanano/src/main/java/com/google/protobuf/nano/MapFactories.java
@@ -0,0 +1,67 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2013 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.protobuf.nano;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility class for maps support.
+ */
+public final class MapFactories {
+ public static interface MapFactory {
+ <K, V> Map<K, V> forMap(Map<K, V> oldMap);
+ }
+
+ // NOTE(liujisi): The factory setter is temporarily marked as package private.
+ // The way to provide customized implementations of maps for different
+ // platforms are still under discussion. Mark it as private to avoid exposing
+ // the API in proto3 alpha release.
+ /* public */ static void setMapFactory(MapFactory newMapFactory) {
+ mapFactory = newMapFactory;
+ }
+
+ public static MapFactory getMapFactory() {
+ return mapFactory;
+ }
+
+ private static class DefaultMapFactory implements MapFactory {
+ public <K, V> Map<K, V> forMap(Map<K, V> oldMap) {
+ if (oldMap == null) {
+ return new HashMap<K, V>();
+ }
+ return oldMap;
+ }
+ }
+ private static volatile MapFactory mapFactory = new DefaultMapFactory();
+
+ private MapFactories() {}
+}
diff --git a/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
index 442f0b74..aa279da2 100644
--- a/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
+++ b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
@@ -30,31 +30,11 @@
package com.google.protobuf.nano;
-import com.google.protobuf.nano.CodedInputByteBufferNano;
-import com.google.protobuf.nano.EnumClassNanoMultiple;
-import com.google.protobuf.nano.EnumClassNanos;
-import com.google.protobuf.nano.EnumValidity;
-import com.google.protobuf.nano.EnumValidityAccessors;
-import com.google.protobuf.nano.FileScopeEnumMultiple;
-import com.google.protobuf.nano.FileScopeEnumRefNano;
-import com.google.protobuf.nano.InternalNano;
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-import com.google.protobuf.nano.MessageScopeEnumRefNano;
-import com.google.protobuf.nano.MultipleImportingNonMultipleNano1;
-import com.google.protobuf.nano.MultipleImportingNonMultipleNano2;
-import com.google.protobuf.nano.MultipleNameClashNano;
+import com.google.protobuf.nano.MapTestProto.TestMap;
+import com.google.protobuf.nano.MapTestProto.TestMap.MessageValue;
import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors;
import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
-import com.google.protobuf.nano.NanoOuterClass;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
-import com.google.protobuf.nano.NanoReferenceTypes;
-import com.google.protobuf.nano.NanoRepeatedPackables;
-import com.google.protobuf.nano.PackedExtensions;
-import com.google.protobuf.nano.RepeatedExtensions;
-import com.google.protobuf.nano.SingularExtensions;
-import com.google.protobuf.nano.TestRepeatedMergeNano;
-import com.google.protobuf.nano.UnittestMultipleNano;
import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano;
import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
@@ -67,6 +47,8 @@ import junit.framework.TestCase;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
/**
* Test nano runtime.
@@ -3754,6 +3736,320 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools));
}
+ public void testMapsSerializeAndParse() throws Exception {
+ TestMap origin = new TestMap();
+ setMapMessage(origin);
+ assertMapMessageSet(origin);
+
+ byte[] output = MessageNano.toByteArray(origin);
+ TestMap parsed = new TestMap();
+ MessageNano.mergeFrom(parsed, output);
+ }
+
+ public void testMapSerializeRejectNull() throws Exception {
+ TestMap primitiveMap = new TestMap();
+ primitiveMap.int32ToInt32Field = new HashMap<Integer, Integer>();
+ primitiveMap.int32ToInt32Field.put(null, 1);
+ try {
+ MessageNano.toByteArray(primitiveMap);
+ fail("should reject null keys");
+ } catch (IllegalStateException e) {
+ // pass.
+ }
+
+ TestMap messageMap = new TestMap();
+ messageMap.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ messageMap.int32ToMessageField.put(0, null);
+ try {
+ MessageNano.toByteArray(messageMap);
+ fail("should reject null values");
+ } catch (IllegalStateException e) {
+ // pass.
+ }
+ }
+
+ /**
+ * Tests that merging bytes containing conflicting keys with override the
+ * message value instead of merging the message value into the existing entry.
+ */
+ public void testMapMergeOverrideMessageValues() throws Exception {
+ TestMap.MessageValue origValue = new TestMap.MessageValue();
+ origValue.value = 1;
+ origValue.value2 = 2;
+ TestMap.MessageValue newValue = new TestMap.MessageValue();
+ newValue.value = 3;
+
+ TestMap origMessage = new TestMap();
+ origMessage.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ origMessage.int32ToMessageField.put(1, origValue);
+
+ TestMap newMessage = new TestMap();
+ newMessage.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ newMessage.int32ToMessageField.put(1, newValue);
+ MessageNano.mergeFrom(origMessage,
+ MessageNano.toByteArray(newMessage));
+ TestMap.MessageValue mergedValue = origMessage.int32ToMessageField.get(1);
+ assertEquals(3, mergedValue.value);
+ assertEquals(0, mergedValue.value2);
+ }
+
+ /**
+ * Tests that when merging with empty entries,
+ * we will use default for the key and value, instead of null.
+ */
+ public void testMapMergeEmptyEntry() throws Exception {
+ TestMap testMap = new TestMap();
+ byte[] buffer = new byte[1024];
+ CodedOutputByteBufferNano output =
+ CodedOutputByteBufferNano.newInstance(buffer);
+ // An empty entry for int32_to_int32 map.
+ output.writeTag(1, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(0);
+ // An empty entry for int32_to_message map.
+ output.writeTag(5, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(0);
+
+ CodedInputByteBufferNano input = CodedInputByteBufferNano.newInstance(
+ buffer, 0, buffer.length - output.spaceLeft());
+ testMap.mergeFrom(input);
+ assertNotNull(testMap.int32ToInt32Field);;
+ assertEquals(1, testMap.int32ToInt32Field.size());
+ assertEquals(Integer.valueOf(0), testMap.int32ToInt32Field.get(0));
+ assertNotNull(testMap.int32ToMessageField);
+ assertEquals(1, testMap.int32ToMessageField.size());
+ TestMap.MessageValue messageValue = testMap.int32ToMessageField.get(0);
+ assertNotNull(messageValue);
+ assertEquals(0, messageValue.value);
+ assertEquals(0, messageValue.value2);
+ }
+
+ public void testMapEquals() throws Exception {
+ TestMap a = new TestMap();
+ TestMap b = new TestMap();
+
+ // empty and null map fields are equal.
+ assertTestMapEqual(a, b);
+ a.int32ToBytesField = new HashMap<Integer, byte[]>();
+ assertTestMapEqual(a, b);
+
+ a.int32ToInt32Field = new HashMap<Integer, Integer>();
+ b.int32ToInt32Field = new HashMap<Integer, Integer>();
+ setMap(a.int32ToInt32Field, deepCopy(int32Values), deepCopy(int32Values));
+ setMap(b.int32ToInt32Field, deepCopy(int32Values), deepCopy(int32Values));
+ assertTestMapEqual(a, b);
+
+ a.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ b.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ setMap(a.int32ToMessageField,
+ deepCopy(int32Values), deepCopy(messageValues));
+ setMap(b.int32ToMessageField,
+ deepCopy(int32Values), deepCopy(messageValues));
+ assertTestMapEqual(a, b);
+
+ a.stringToInt32Field = new HashMap<String, Integer>();
+ b.stringToInt32Field = new HashMap<String, Integer>();
+ setMap(a.stringToInt32Field, deepCopy(stringValues), deepCopy(int32Values));
+ setMap(b.stringToInt32Field, deepCopy(stringValues), deepCopy(int32Values));
+ assertTestMapEqual(a, b);
+
+ a.int32ToBytesField = new HashMap<Integer, byte[]>();
+ b.int32ToBytesField = new HashMap<Integer, byte[]>();
+ setMap(a.int32ToBytesField, deepCopy(int32Values), deepCopy(bytesValues));
+ setMap(b.int32ToBytesField, deepCopy(int32Values), deepCopy(bytesValues));
+ assertTestMapEqual(a, b);
+
+ // Make sure the map implementation does not matter.
+ a.int32ToStringField = new TreeMap<Integer, String>();
+ b.int32ToStringField = new HashMap<Integer, String>();
+ setMap(a.int32ToStringField, deepCopy(int32Values), deepCopy(stringValues));
+ setMap(b.int32ToStringField, deepCopy(int32Values), deepCopy(stringValues));
+ assertTestMapEqual(a, b);
+
+ a.clear();
+ b.clear();
+
+ // unequal cases: different value
+ a.int32ToInt32Field = new HashMap<Integer, Integer>();
+ b.int32ToInt32Field = new HashMap<Integer, Integer>();
+ a.int32ToInt32Field.put(1, 1);
+ b.int32ToInt32Field.put(1, 2);
+ assertTestMapUnequal(a, b);
+ // unequal case: additional entry
+ b.int32ToInt32Field.put(1, 1);
+ b.int32ToInt32Field.put(2, 1);
+ assertTestMapUnequal(a, b);
+ a.int32ToInt32Field.put(2, 1);
+ assertTestMapEqual(a, b);
+
+ // unequal case: different message value.
+ a.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ b.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ MessageValue va = new MessageValue();
+ va.value = 1;
+ MessageValue vb = new MessageValue();
+ vb.value = 1;
+ a.int32ToMessageField.put(1, va);
+ b.int32ToMessageField.put(1, vb);
+ assertTestMapEqual(a, b);
+ vb.value = 2;
+ assertTestMapUnequal(a, b);
+ }
+
+ private static void assertTestMapEqual(TestMap a, TestMap b)
+ throws Exception {
+ assertEquals(a.hashCode(), b.hashCode());
+ assertTrue(a.equals(b));
+ assertTrue(b.equals(a));
+ }
+
+ private static void assertTestMapUnequal(TestMap a, TestMap b)
+ throws Exception {
+ assertFalse(a.equals(b));
+ assertFalse(b.equals(a));
+ }
+
+ private static final Integer[] int32Values = new Integer[] {
+ 0, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE,
+ };
+
+ private static final Long[] int64Values = new Long[] {
+ 0L, 1L, -1L, Long.MAX_VALUE, Long.MIN_VALUE,
+ };
+
+ private static final String[] stringValues = new String[] {
+ "", "hello", "world", "foo", "bar",
+ };
+
+ private static final byte[][] bytesValues = new byte[][] {
+ new byte[] {},
+ new byte[] {0},
+ new byte[] {1, -1},
+ new byte[] {127, -128},
+ new byte[] {'a', 'b', '0', '1'},
+ };
+
+ private static final Boolean[] boolValues = new Boolean[] {
+ false, true,
+ };
+
+ private static final Integer[] enumValues = new Integer[] {
+ TestMap.FOO, TestMap.BAR, TestMap.BAZ, TestMap.QUX,
+ Integer.MAX_VALUE /* unknown */,
+ };
+
+ private static final TestMap.MessageValue[] messageValues =
+ new TestMap.MessageValue[] {
+ newMapValueMessage(0),
+ newMapValueMessage(1),
+ newMapValueMessage(-1),
+ newMapValueMessage(Integer.MAX_VALUE),
+ newMapValueMessage(Integer.MIN_VALUE),
+ };
+
+ private static TestMap.MessageValue newMapValueMessage(int value) {
+ TestMap.MessageValue result = new TestMap.MessageValue();
+ result.value = value;
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> T[] deepCopy(T[] orig) throws Exception {
+ if (orig instanceof MessageValue[]) {
+ MessageValue[] result = new MessageValue[orig.length];
+ for (int i = 0; i < orig.length; i++) {
+ result[i] = new MessageValue();
+ MessageNano.mergeFrom(
+ result[i], MessageNano.toByteArray((MessageValue) orig[i]));
+ }
+ return (T[]) result;
+ }
+ if (orig instanceof byte[][]) {
+ byte[][] result = new byte[orig.length][];
+ for (int i = 0; i < orig.length; i++) {
+ byte[] origBytes = (byte[]) orig[i];
+ result[i] = Arrays.copyOf(origBytes, origBytes.length);
+ }
+ }
+ return Arrays.copyOf(orig, orig.length);
+ }
+
+ private <K, V> void setMap(Map<K, V> map, K[] keys, V[] values) {
+ assert(keys.length == values.length);
+ for (int i = 0; i < keys.length; i++) {
+ map.put(keys[i], values[i]);
+ }
+ }
+
+ private <K, V> void assertMapSet(
+ Map<K, V> map, K[] keys, V[] values) throws Exception {
+ assert(keys.length == values.length);
+ for (int i = 0; i < values.length; i++) {
+ assertEquals(values[i], map.get(keys[i]));
+ }
+ assertEquals(keys.length, map.size());
+ }
+
+ private void setMapMessage(TestMap testMap) {
+ testMap.int32ToInt32Field = new HashMap<Integer, Integer>();
+ testMap.int32ToBytesField = new HashMap<Integer, byte[]>();
+ testMap.int32ToEnumField = new HashMap<Integer, Integer>();
+ testMap.int32ToMessageField =
+ new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
+ testMap.int32ToStringField = new HashMap<Integer, String>();
+ testMap.stringToInt32Field = new HashMap<String, Integer>();
+ testMap.boolToBoolField = new HashMap<Boolean, Boolean>();
+ testMap.uint32ToUint32Field = new HashMap<Integer, Integer>();
+ testMap.sint32ToSint32Field = new HashMap<Integer, Integer>();
+ testMap.fixed32ToFixed32Field = new HashMap<Integer, Integer>();
+ testMap.sfixed32ToSfixed32Field = new HashMap<Integer, Integer>();
+ testMap.int64ToInt64Field = new HashMap<Long, Long>();
+ testMap.uint64ToUint64Field = new HashMap<Long, Long>();
+ testMap.sint64ToSint64Field = new HashMap<Long, Long>();
+ testMap.fixed64ToFixed64Field = new HashMap<Long, Long>();
+ testMap.sfixed64ToSfixed64Field = new HashMap<Long, Long>();
+ setMap(testMap.int32ToInt32Field, int32Values, int32Values);
+ setMap(testMap.int32ToBytesField, int32Values, bytesValues);
+ setMap(testMap.int32ToEnumField, int32Values, enumValues);
+ setMap(testMap.int32ToMessageField, int32Values, messageValues);
+ setMap(testMap.int32ToStringField, int32Values, stringValues);
+ setMap(testMap.stringToInt32Field, stringValues, int32Values);
+ setMap(testMap.boolToBoolField, boolValues, boolValues);
+ setMap(testMap.uint32ToUint32Field, int32Values, int32Values);
+ setMap(testMap.sint32ToSint32Field, int32Values, int32Values);
+ setMap(testMap.fixed32ToFixed32Field, int32Values, int32Values);
+ setMap(testMap.sfixed32ToSfixed32Field, int32Values, int32Values);
+ setMap(testMap.int64ToInt64Field, int64Values, int64Values);
+ setMap(testMap.uint64ToUint64Field, int64Values, int64Values);
+ setMap(testMap.sint64ToSint64Field, int64Values, int64Values);
+ setMap(testMap.fixed64ToFixed64Field, int64Values, int64Values);
+ setMap(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
+ }
+ private void assertMapMessageSet(TestMap testMap) throws Exception {
+ assertMapSet(testMap.int32ToInt32Field, int32Values, int32Values);
+ assertMapSet(testMap.int32ToBytesField, int32Values, bytesValues);
+ assertMapSet(testMap.int32ToEnumField, int32Values, enumValues);
+ assertMapSet(testMap.int32ToMessageField, int32Values, messageValues);
+ assertMapSet(testMap.int32ToStringField, int32Values, stringValues);
+ assertMapSet(testMap.stringToInt32Field, stringValues, int32Values);
+ assertMapSet(testMap.boolToBoolField, boolValues, boolValues);
+ assertMapSet(testMap.uint32ToUint32Field, int32Values, int32Values);
+ assertMapSet(testMap.sint32ToSint32Field, int32Values, int32Values);
+ assertMapSet(testMap.fixed32ToFixed32Field, int32Values, int32Values);
+ assertMapSet(testMap.sfixed32ToSfixed32Field, int32Values, int32Values);
+ assertMapSet(testMap.int64ToInt64Field, int64Values, int64Values);
+ assertMapSet(testMap.uint64ToUint64Field, int64Values, int64Values);
+ assertMapSet(testMap.sint64ToSint64Field, int64Values, int64Values);
+ assertMapSet(testMap.fixed64ToFixed64Field, int64Values, int64Values);
+ assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
+ }
+
private void assertRepeatedPackablesEqual(
NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
// Not using MessageNano.equals() -- that belongs to a separate test.
diff --git a/javanano/src/test/java/com/google/protobuf/nano/map_test.proto b/javanano/src/test/java/com/google/protobuf/nano/map_test.proto
new file mode 100644
index 00000000..f72833ad
--- /dev/null
+++ b/javanano/src/test/java/com/google/protobuf/nano/map_test.proto
@@ -0,0 +1,70 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto3";
+
+package map_test;
+
+option java_package = "com.google.protobuf.nano";
+option java_outer_classname = "MapTestProto";
+
+message TestMap {
+ message MessageValue {
+ int32 value = 1;
+ int32 value2 = 2;
+ }
+ enum EnumValue {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ QUX = 3;
+ }
+
+ map<int32, int32> int32_to_int32_field = 1;
+ map<int32, string> int32_to_string_field = 2;
+ map<int32, bytes> int32_to_bytes_field = 3;
+ map<int32, EnumValue> int32_to_enum_field = 4;
+ map<int32, MessageValue> int32_to_message_field = 5;
+ map<string, int32> string_to_int32_field = 6;
+ map<bool, bool> bool_to_bool_field = 7;
+
+ // Test all the other primitive types. As the key and value are not coupled in
+ // the implementation, we do not test all the combinations of key/value pairs,
+ // so that we can keep the number of test cases manageable
+ map<uint32, uint32> uint32_to_uint32_field = 11;
+ map<sint32, sint32> sint32_to_sint32_field = 12;
+ map<fixed32, fixed32> fixed32_to_fixed32_field = 13;
+ map<sfixed32, sfixed32> sfixed32_to_sfixed32_field = 14;
+ map<int64, int64> int64_to_int64_field = 15;
+ map<uint64, uint64> uint64_to_uint64_field = 16;
+ map<sint64, sint64> sint64_to_sint64_field = 17;
+ map<fixed64, fixed64> fixed64_to_fixed64_field = 18;
+ map<sfixed64, sfixed64> sfixed64_to_sfixed64_field = 19;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 6fd8bd2b..0f0bd089 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -243,26 +243,28 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/java/java_doc_comment.cc \
google/protobuf/compiler/java/java_doc_comment.h \
google/protobuf/compiler/javanano/javanano_enum.cc \
+ google/protobuf/compiler/javanano/javanano_enum.h \
+ google/protobuf/compiler/javanano/javanano_enum_field.cc \
google/protobuf/compiler/javanano/javanano_enum_field.h \
google/protobuf/compiler/javanano/javanano_extension.cc \
+ google/protobuf/compiler/javanano/javanano_extension.h \
google/protobuf/compiler/javanano/javanano_field.cc \
+ google/protobuf/compiler/javanano/javanano_field.h \
google/protobuf/compiler/javanano/javanano_file.cc \
+ google/protobuf/compiler/javanano/javanano_file.h \
google/protobuf/compiler/javanano/javanano_generator.cc \
+ google/protobuf/compiler/javanano/javanano_generator.h \
google/protobuf/compiler/javanano/javanano_helpers.cc \
+ google/protobuf/compiler/javanano/javanano_helpers.h \
+ google/protobuf/compiler/javanano/javanano_map_field.cc \
+ google/protobuf/compiler/javanano/javanano_map_field.h \
google/protobuf/compiler/javanano/javanano_message.cc \
+ google/protobuf/compiler/javanano/javanano_message.h \
+ google/protobuf/compiler/javanano/javanano_message_field.cc \
google/protobuf/compiler/javanano/javanano_message_field.h \
google/protobuf/compiler/javanano/javanano_params.h \
- google/protobuf/compiler/javanano/javanano_primitive_field.h \
- google/protobuf/compiler/javanano/javanano_enum_field.cc \
- google/protobuf/compiler/javanano/javanano_enum.h \
- google/protobuf/compiler/javanano/javanano_extension.h \
- google/protobuf/compiler/javanano/javanano_field.h \
- google/protobuf/compiler/javanano/javanano_file.h \
- google/protobuf/compiler/javanano/javanano_generator.h \
- google/protobuf/compiler/javanano/javanano_helpers.h \
- google/protobuf/compiler/javanano/javanano_message_field.cc \
- google/protobuf/compiler/javanano/javanano_message.h \
google/protobuf/compiler/javanano/javanano_primitive_field.cc \
+ google/protobuf/compiler/javanano/javanano_primitive_field.h \
google/protobuf/compiler/python/python_generator.cc \
google/protobuf/compiler/ruby/ruby_generator.cc
diff --git a/src/google/protobuf/compiler/javanano/javanano_field.cc b/src/google/protobuf/compiler/javanano/javanano_field.cc
index e3e4cefe..b9be90b1 100644
--- a/src/google/protobuf/compiler/javanano/javanano_field.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_field.cc
@@ -36,6 +36,7 @@
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/compiler/javanano/javanano_primitive_field.h>
#include <google/protobuf/compiler/javanano/javanano_enum_field.h>
+#include <google/protobuf/compiler/javanano/javanano_map_field.h>
#include <google/protobuf/compiler/javanano/javanano_message_field.h>
#include <google/protobuf/stubs/common.h>
@@ -97,7 +98,11 @@ FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field,
if (field->is_repeated()) {
switch (java_type) {
case JAVATYPE_MESSAGE:
- return new RepeatedMessageFieldGenerator(field, params);
+ if (IsMapEntry(field->message_type())) {
+ return new MapFieldGenerator(field, params);
+ } else {
+ return new RepeatedMessageFieldGenerator(field, params);
+ }
case JAVATYPE_ENUM:
return new RepeatedEnumFieldGenerator(field, params);
default:
diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
index 2149418a..7b23d9f4 100644
--- a/src/google/protobuf/compiler/javanano/javanano_helpers.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc
@@ -560,6 +560,17 @@ void SetBitOperationVariables(const string name,
(*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
}
+bool HasMapField(const Descriptor* descriptor) {
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* field = descriptor->field(i);
+ if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
+ IsMapEntry(field->message_type())) {
+ return true;
+ }
+ }
+ return false;
+}
+
} // namespace javanano
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.h b/src/google/protobuf/compiler/javanano/javanano_helpers.h
index 29310743..b4fee649 100644
--- a/src/google/protobuf/compiler/javanano/javanano_helpers.h
+++ b/src/google/protobuf/compiler/javanano/javanano_helpers.h
@@ -181,6 +181,14 @@ string GenerateDifferentBit(int bit_index);
void SetBitOperationVariables(const string name,
int bitIndex, map<string, string>* variables);
+inline bool IsMapEntry(const Descriptor* descriptor) {
+ // TODO(liujisi): Add an option to turn on maps for proto2 syntax as well.
+ return descriptor->options().map_entry() &&
+ descriptor->file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
+}
+
+bool HasMapField(const Descriptor* descriptor);
+
} // namespace javanano
} // namespace compiler
} // namespace protobuf
diff --git a/src/google/protobuf/compiler/javanano/javanano_map_field.cc b/src/google/protobuf/compiler/javanano/javanano_map_field.cc
new file mode 100644
index 00000000..83b2b0ce
--- /dev/null
+++ b/src/google/protobuf/compiler/javanano/javanano_map_field.cc
@@ -0,0 +1,186 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/compiler/javanano/javanano_map_field.h>
+#include <google/protobuf/compiler/javanano/javanano_helpers.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/io/printer.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace javanano {
+
+namespace {
+
+string TypeName(const Params& params, const FieldDescriptor* field,
+ bool boxed) {
+ JavaType java_type = GetJavaType(field);
+ switch (java_type) {
+ case JAVATYPE_MESSAGE:
+ return ClassName(params, field->message_type());
+ case JAVATYPE_INT:
+ case JAVATYPE_LONG:
+ case JAVATYPE_FLOAT:
+ case JAVATYPE_DOUBLE:
+ case JAVATYPE_BOOLEAN:
+ case JAVATYPE_STRING:
+ case JAVATYPE_BYTES:
+ case JAVATYPE_ENUM:
+ if (boxed) {
+ return BoxedPrimitiveTypeName(java_type);
+ } else {
+ return PrimitiveTypeName(java_type);
+ }
+ // No default because we want the compiler to complain if any new JavaTypes
+ // are added..
+ }
+
+ GOOGLE_LOG(FATAL) << "should not reach here.";
+ return "";
+}
+
+const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) {
+ GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
+ const Descriptor* message = descriptor->message_type();
+ GOOGLE_CHECK(message->options().map_entry());
+ return message->FindFieldByName("key");
+}
+
+const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) {
+ GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
+ const Descriptor* message = descriptor->message_type();
+ GOOGLE_CHECK(message->options().map_entry());
+ return message->FindFieldByName("value");
+}
+
+void SetMapVariables(const Params& params,
+ const FieldDescriptor* descriptor, map<string, string>* variables) {
+ const FieldDescriptor* key = KeyField(descriptor);
+ const FieldDescriptor* value = ValueField(descriptor);
+ (*variables)["name"] =
+ RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
+ (*variables)["number"] = SimpleItoa(descriptor->number());
+ (*variables)["key_type"] = TypeName(params, key, false);
+ (*variables)["boxed_key_type"] = TypeName(params,key, true);
+ (*variables)["key_desc_type"] =
+ "TYPE_" + ToUpper(FieldDescriptor::TypeName(key->type()));
+ (*variables)["key_tag"] = SimpleItoa(internal::WireFormat::MakeTag(key));
+ (*variables)["value_type"] = TypeName(params, value, false);
+ (*variables)["boxed_value_type"] = TypeName(params, value, true);
+ (*variables)["value_desc_type"] =
+ "TYPE_" + ToUpper(FieldDescriptor::TypeName(value->type()));
+ (*variables)["value_tag"] = SimpleItoa(internal::WireFormat::MakeTag(value));
+ (*variables)["type_parameters"] =
+ (*variables)["boxed_key_type"] + ", " + (*variables)["boxed_value_type"];
+ (*variables)["value_default"] =
+ value->type() == FieldDescriptor::TYPE_MESSAGE
+ ? "new " + (*variables)["value_type"] + "()"
+ : "null";
+}
+} // namespace
+
+// ===================================================================
+MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
+ const Params& params)
+ : FieldGenerator(params), descriptor_(descriptor) {
+ SetMapVariables(params, descriptor, &variables_);
+}
+
+MapFieldGenerator::~MapFieldGenerator() {}
+
+void MapFieldGenerator::
+GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
+ printer->Print(variables_,
+ "public java.util.Map<$type_parameters$> $name$;\n");
+}
+
+void MapFieldGenerator::
+GenerateClearCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "$name$ = null;\n");
+}
+
+void MapFieldGenerator::
+GenerateMergingCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "this.$name$ = com.google.protobuf.nano.InternalNano.mergeMapEntry(\n"
+ " input, this.$name$, mapFactory,\n"
+ " com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
+ " com.google.protobuf.nano.InternalNano.$value_desc_type$,\n"
+ " $value_default$,\n"
+ " $key_tag$, $value_tag$);\n"
+ "\n");
+}
+
+void MapFieldGenerator::
+GenerateSerializationCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null) {\n"
+ " com.google.protobuf.nano.InternalNano.serializeMapField(\n"
+ " output, this.$name$, $number$,\n"
+ " com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
+ " com.google.protobuf.nano.InternalNano.$value_desc_type$);\n"
+ "}\n");
+}
+
+void MapFieldGenerator::
+GenerateSerializedSizeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (this.$name$ != null) {\n"
+ " size += com.google.protobuf.nano.InternalNano.computeMapFieldSize(\n"
+ " this.$name$, $number$,\n"
+ " com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
+ " com.google.protobuf.nano.InternalNano.$value_desc_type$);\n"
+ "}\n");
+}
+
+void MapFieldGenerator::
+GenerateEqualsCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "if (!com.google.protobuf.nano.InternalNano.equals(\n"
+ " this.$name$, other.$name$)) {\n"
+ " return false;\n"
+ "}\n");
+}
+
+void MapFieldGenerator::
+GenerateHashCodeCode(io::Printer* printer) const {
+ printer->Print(variables_,
+ "result = 31 * result +\n"
+ " com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n");
+}
+
+} // namespace javanano
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/compiler/javanano/javanano_map_field.h b/src/google/protobuf/compiler/javanano/javanano_map_field.h
new file mode 100644
index 00000000..c01bde38
--- /dev/null
+++ b/src/google/protobuf/compiler/javanano/javanano_map_field.h
@@ -0,0 +1,70 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__
+#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__
+
+#include <map>
+#include <string>
+#include <vector>
+#include <google/protobuf/compiler/javanano/javanano_field.h>
+
+namespace google {
+namespace protobuf {
+namespace compiler {
+namespace javanano {
+
+class MapFieldGenerator : public FieldGenerator {
+ public:
+ explicit MapFieldGenerator(
+ const FieldDescriptor* descriptor, const Params& params);
+ ~MapFieldGenerator();
+
+ // implements FieldGenerator ---------------------------------------
+ void GenerateMembers(io::Printer* printer, bool lazy_init) const;
+ void GenerateClearCode(io::Printer* printer) const;
+ void GenerateMergingCode(io::Printer* printer) const;
+ void GenerateSerializationCode(io::Printer* printer) const;
+ void GenerateSerializedSizeCode(io::Printer* printer) const;
+ void GenerateEqualsCode(io::Printer* printer) const;
+ void GenerateHashCodeCode(io::Printer* printer) const;
+
+ private:
+ const FieldDescriptor* descriptor_;
+ map<string, string> variables_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
+};
+
+} // namespace javanano
+} // namespace compiler
+} // namespace protobuf
+} // namespace google
+#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__
diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc
index 7c52ca31..da7c656f 100644
--- a/src/google/protobuf/compiler/javanano/javanano_message.cc
+++ b/src/google/protobuf/compiler/javanano/javanano_message.cc
@@ -90,6 +90,7 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
// Generate static members for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
+ if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariables(printer);
}
@@ -100,6 +101,7 @@ void MessageGenerator::GenerateStaticVariableInitializers(
// Generate static member initializers for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
+ if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariableInitializers(printer);
}
@@ -159,6 +161,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+ if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer);
}
@@ -342,6 +345,11 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
"classname", descriptor_->name());
printer->Indent();
+ if (HasMapField(descriptor_)) {
+ printer->Print(
+ "com.google.protobuf.nano.MapFactories.MapFactory mapFactory =\n"
+ " com.google.protobuf.nano.MapFactories.getMapFactory();\n");
+ }
printer->Print(
"while (true) {\n");