Merge pull request #204 from pherl/master

Implement maps for JavaNano
pull/212/head^2
Jisi Liu 10 years ago
commit 2cb2358cba
  1. 1
      javanano/pom.xml
  2. 40
      javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
  3. 124
      javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
  4. 73
      javanano/src/main/java/com/google/protobuf/nano/Extension.java
  5. 223
      javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
  6. 67
      javanano/src/main/java/com/google/protobuf/nano/MapFactories.java
  7. 340
      javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
  8. 70
      javanano/src/test/java/com/google/protobuf/nano/map_test.proto
  9. 22
      src/Makefile.am
  10. 7
      src/google/protobuf/compiler/javanano/javanano_field.cc
  11. 11
      src/google/protobuf/compiler/javanano/javanano_helpers.cc
  12. 8
      src/google/protobuf/compiler/javanano/javanano_helpers.h
  13. 186
      src/google/protobuf/compiler/javanano/javanano_map_field.cc
  14. 70
      src/google/protobuf/compiler/javanano/javanano_map_field.h
  15. 8
      src/google/protobuf/compiler/javanano/javanano_message.cc

@ -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" />

@ -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);
}
}
}

@ -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);
}
}
}

@ -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);
}

@ -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();
}
}

@ -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() {}
}

@ -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.

@ -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;
}

@ -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

@ -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:

@ -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

@ -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

@ -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

@ -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__

@ -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");

Loading…
Cancel
Save