diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-10 17:34:54 -0800 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-10 17:34:54 -0800 |
commit | 6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e (patch) | |
tree | d17c61ff9f3ae28224fbddac6d26bfc59e2cf755 /java/src/main | |
parent | baca1a8a1aa180c42de6278d3b8286c4496c6a10 (diff) | |
download | protobuf-6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e.tar.gz protobuf-6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e.tar.bz2 protobuf-6ef984af4b0c63c1c33127a12dcfc8e6359f0c9e.zip |
Down-integrate from internal code base.
Diffstat (limited to 'java/src/main')
22 files changed, 2563 insertions, 228 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index ae9d5e39..6de4cae3 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -37,6 +37,9 @@ import com.google.protobuf.Internal.EnumLite; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -144,6 +147,40 @@ public abstract class AbstractMessage extends AbstractMessageLite } /** + * Converts a list of MapEntry messages into a Map used for equals() and + * hashCode(). + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + private static Map convertMapEntryListToMap(List list) { + if (list.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new HashMap(); + Iterator iterator = list.iterator(); + Message entry = (Message) iterator.next(); + Descriptors.Descriptor descriptor = entry.getDescriptorForType(); + Descriptors.FieldDescriptor key = descriptor.findFieldByName("key"); + Descriptors.FieldDescriptor value = descriptor.findFieldByName("value"); + result.put(entry.getField(key), entry.getField(value)); + while (iterator.hasNext()) { + entry = (Message) iterator.next(); + result.put(entry.getField(key), entry.getField(value)); + } + return result; + } + + /** + * Compares two map fields. The parameters must be a list of MapEntry + * messages. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + private static boolean compareMapField(Object a, Object b) { + Map ma = convertMapEntryListToMap((List) a); + Map mb = convertMapEntryListToMap((List) b); + return MapFieldLite.equals(ma, mb); + } + + /** * Compares two set of fields. * This method is used to implement {@link AbstractMessage#equals(Object)} * and {@link AbstractMutableMessage#equals(Object)}. It takes special care @@ -181,6 +218,10 @@ public abstract class AbstractMessage extends AbstractMessageLite return false; } } + } else if (descriptor.isMapField()) { + if (!compareMapField(value1, value2)) { + return false; + } } else { // Compare non-bytes fields. if (!value1.equals(value2)) { @@ -190,6 +231,15 @@ public abstract class AbstractMessage extends AbstractMessageLite } return true; } + + /** + * Calculates the hash code of a map field. {@code value} must be a list of + * MapEntry messages. + */ + @SuppressWarnings("unchecked") + private static int hashMapField(Object value) { + return MapFieldLite.calculateHashCodeForMap(convertMapEntryListToMap((List) value)); + } /** Get a hash code for given fields and values, using the given seed. */ @SuppressWarnings("unchecked") @@ -198,7 +248,9 @@ public abstract class AbstractMessage extends AbstractMessageLite FieldDescriptor field = entry.getKey(); Object value = entry.getValue(); hash = (37 * hash) + field.getNumber(); - if (field.getType() != FieldDescriptor.Type.ENUM){ + if (field.isMapField()) { + hash = (53 * hash) + hashMapField(value); + } else if (field.getType() != FieldDescriptor.Type.ENUM){ hash = (53 * hash) + value.hashCode(); } else if (field.isRepeated()) { List<? extends EnumLite> list = (List<? extends EnumLite>) value; @@ -359,6 +411,12 @@ public abstract class AbstractMessage extends AbstractMessageLite "getFieldBuilder() called on an unsupported message type."); } + public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, + int index) { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on an unsupported message type."); + } + public String toString() { return TextFormat.printToString(this); } diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index a00ae86f..0ca00bab 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -612,16 +612,16 @@ public final class CodedInputStream { return x; } else if (bufferSize - pos < 9) { break fastpath; - } else if ((x ^= (buffer[pos++] << 7)) < 0L) { - x ^= (~0L << 7); - } else if ((x ^= (buffer[pos++] << 14)) >= 0L) { - x ^= (~0L << 7) ^ (~0L << 14); - } else if ((x ^= (buffer[pos++] << 21)) < 0L) { - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21); + } else if ((x ^= (buffer[pos++] << 7)) < 0) { + x ^= (~0 << 7); + } else if ((x ^= (buffer[pos++] << 14)) >= 0) { + x ^= (~0 << 7) ^ (~0 << 14); + } else if ((x ^= (buffer[pos++] << 21)) < 0) { + x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21); } else { int y = buffer[pos++]; x ^= y << 28; - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); + x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28); if (y < 0 && buffer[pos++] < 0 && buffer[pos++] < 0 && @@ -739,13 +739,13 @@ public final class CodedInputStream { return y; } else if (bufferSize - pos < 9) { break fastpath; - } else if ((x = y ^ (buffer[pos++] << 7)) < 0L) { - x ^= (~0L << 7); - } else if ((x ^= (buffer[pos++] << 14)) >= 0L) { - x ^= (~0L << 7) ^ (~0L << 14); - } else if ((x ^= (buffer[pos++] << 21)) < 0L) { - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21); - } else if ((x ^= ((long) buffer[pos++] << 28)) >= 0L) { + } else if ((y ^= (buffer[pos++] << 7)) < 0) { + x = y ^ (~0 << 7); + } else if ((y ^= (buffer[pos++] << 14)) >= 0) { + x = y ^ ((~0 << 7) ^ (~0 << 14)); + } else if ((y ^= (buffer[pos++] << 21)) < 0) { + x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21)); + } else if ((x = ((long) y) ^ ((long) buffer[pos++] << 28)) >= 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); } else if ((x ^= ((long) buffer[pos++] << 35)) < 0L) { x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35); @@ -882,7 +882,7 @@ public final class CodedInputStream { /** See setSizeLimit() */ private int sizeLimit = DEFAULT_SIZE_LIMIT; - private static final int DEFAULT_RECURSION_LIMIT = 64; + private static final int DEFAULT_RECURSION_LIMIT = 100; private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB private static final int BUFFER_SIZE = 4096; diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index caae0f77..d65e8b49 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -32,6 +32,7 @@ package com.google.protobuf; import com.google.protobuf.DescriptorProtos.*; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -40,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.logging.Logger; import java.io.UnsupportedEncodingException; @@ -123,6 +125,26 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(publicDependencies)); } + /** The syntax of the .proto file. */ + public enum Syntax { + UNKNOWN("unknown"), + PROTO2("proto2"), + PROTO3("proto3"); + + Syntax(String name) { + this.name = name; + } + private final String name; + } + + /** Get the syntax of the .proto file. */ + public Syntax getSyntax() { + if (Syntax.PROTO3.name.equals(proto.getSyntax())) { + return Syntax.PROTO3; + } + return Syntax.PROTO2; + } + /** * Find a message type in the file by name. Does not find nested types. * @@ -539,6 +561,10 @@ public final class Descriptors { extensions[i].setProto(proto.getExtension(i)); } } + + boolean supportsUnknownEnumValue() { + return getSyntax() == Syntax.PROTO3; + } } // ================================================================= @@ -871,6 +897,11 @@ public final class Descriptors { return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8()); } + boolean isMapField() { + return getType() == Type.MESSAGE && isRepeated() + && getMessageType().getOptions().getMapEntry(); + } + // I'm pretty sure values() constructs a new array every time, since there // is nothing stopping the caller from mutating the array. Therefore we // make a static copy here. @@ -1001,6 +1032,11 @@ public final class Descriptors { return getNumber() - other.getNumber(); } + @Override + public String toString() { + return getFullName(); + } + private final int index; private FieldDescriptorProto proto; @@ -1420,6 +1456,64 @@ public final class Descriptors { return file.pool.enumValuesByNumber.get( new DescriptorPool.DescriptorIntPair(this, number)); } + + /** + * Get the enum value for a number. If no enum value has this number, + * construct an EnumValueDescriptor for it. + */ + public EnumValueDescriptor findValueByNumberCreatingIfUnknown(final int number) { + EnumValueDescriptor result = findValueByNumber(number); + if (result != null) { + return result; + } + // The number represents an unknown enum value. + synchronized (this) { + // Descriptors are compared by object identity so for the same number + // we need to return the same EnumValueDescriptor object. This means + // we have to store created EnumValueDescriptors. However, as there + // are potentially 2G unknown enum values, storing all of these + // objects persistently will consume lots of memory for long-running + // services and it's also unnecessary as not many EnumValueDescriptors + // will be used at the same time. + // + // To solve the problem we take advantage of Java's weak references and + // rely on gc to release unused descriptors. + // + // Here is how it works: + // * We store unknown EnumValueDescriptors in a WeakHashMap with the + // value being a weak reference to the descriptor. + // * The descriptor holds a strong reference to the key so as long + // as the EnumValueDescriptor is in use, the key will be there + // and the corresponding map entry will be there. Following-up + // queries with the same number will return the same descriptor. + // * If the user no longer uses an unknown EnumValueDescriptor, + // it will be gc-ed since we only hold a weak reference to it in + // the map. The key in the corresponding map entry will also be + // gc-ed as the only strong reference to it is in the descriptor + // which is just gc-ed. With the key being gone WeakHashMap will + // then remove the whole entry. This way unknown descriptors will + // be freed automatically and we don't need to do anything to + // clean-up unused map entries. + + // Note: We must use "new Integer(number)" here because we don't want + // these Integer objects to be cached. + Integer key = new Integer(number); + WeakReference<EnumValueDescriptor> reference = unknownValues.get(key); + if (reference != null) { + result = reference.get(); + } + if (result == null) { + result = new EnumValueDescriptor(file, this, key); + unknownValues.put(key, new WeakReference<EnumValueDescriptor>(result)); + } + } + return result; + } + + // Used in tests only. + int getUnknownEnumValueDescriptorCount() { + return unknownValues.size(); + } private final int index; private EnumDescriptorProto proto; @@ -1427,6 +1521,8 @@ public final class Descriptors { private final FileDescriptor file; private final Descriptor containingType; private EnumValueDescriptor[] values; + private final WeakHashMap<Integer, WeakReference<EnumValueDescriptor>> unknownValues = + new WeakHashMap<Integer, WeakReference<EnumValueDescriptor>>(); private EnumDescriptor(final EnumDescriptorProto proto, final FileDescriptor file, @@ -1531,6 +1627,25 @@ public final class Descriptors { file.pool.addSymbol(this); file.pool.addEnumValueByNumber(this); } + + private Integer number; + // Create an unknown enum value. + private EnumValueDescriptor( + final FileDescriptor file, + final EnumDescriptor parent, + final Integer number) { + String name = "UNKNOWN_ENUM_VALUE_" + parent.getName() + "_" + number; + EnumValueDescriptorProto proto = EnumValueDescriptorProto + .newBuilder().setName(name).setNumber(number).build(); + this.index = -1; + this.proto = proto; + this.file = file; + this.type = parent; + this.fullName = parent.getFullName() + '.' + proto.getName(); + this.number = number; + + // Don't add this descriptor into pool. + } /** See {@link FileDescriptor#setProto}. */ private void setProto(final EnumValueDescriptorProto proto) { diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java index c9ce667b..06b30fff 100644 --- a/java/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -549,12 +549,22 @@ public final class DynamicMessage extends AbstractMessage { } public Builder setUnknownFields(UnknownFieldSet unknownFields) { + if (getDescriptorForType().getFile().getSyntax() + == Descriptors.FileDescriptor.Syntax.PROTO3) { + // Proto3 discards unknown fields. + return this; + } this.unknownFields = unknownFields; return this; } @Override public Builder mergeUnknownFields(UnknownFieldSet unknownFields) { + if (getDescriptorForType().getFile().getSyntax() + == Descriptors.FileDescriptor.Syntax.PROTO3) { + // Proto3 discards unknown fields. + return this; + } this.unknownFields = UnknownFieldSet.newBuilder(this.unknownFields) .mergeFrom(unknownFields) @@ -588,10 +598,12 @@ public final class DynamicMessage extends AbstractMessage { throw new IllegalArgumentException( "DynamicMessage should use EnumValueDescriptor to set Enum Value."); } - if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) { - throw new IllegalArgumentException( - "EnumValueDescriptor doesn't much Enum Field."); - } + // TODO(xiaofeng): Re-enable this check after Orgstore is fixed to not + // set incorrect EnumValueDescriptors. + // if (field.getEnumType() != ((EnumValueDescriptor) value).getType()) { + // throw new IllegalArgumentException( + // "EnumValueDescriptor doesn't match Enum Field."); + // } } /** Verifies the value for an enum field. */ @@ -618,5 +630,12 @@ public final class DynamicMessage extends AbstractMessage { throw new UnsupportedOperationException( "getFieldBuilder() called on a dynamic message type."); } + + @Override + public com.google.protobuf.Message.Builder getRepeatedFieldBuilder(FieldDescriptor field, + int index) { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a dynamic message type."); + } } } diff --git a/java/src/main/java/com/google/protobuf/Extension.java b/java/src/main/java/com/google/protobuf/Extension.java index 0baa22b3..68d29f33 100644 --- a/java/src/main/java/com/google/protobuf/Extension.java +++ b/java/src/main/java/com/google/protobuf/Extension.java @@ -35,27 +35,16 @@ package com.google.protobuf; * * @author liujisi@google.com (Jisi Liu) */ -public abstract class Extension<ContainingType extends MessageLite, Type> { - /** Returns the field number of the extension. */ - public abstract int getNumber(); - - /** Returns the type of the field. */ - public abstract WireFormat.FieldType getLiteType(); - - /** Returns whether it is a repeated field. */ - public abstract boolean isRepeated(); +public abstract class Extension<ContainingType extends MessageLite, Type> + extends ExtensionLite<ContainingType, Type> { /** Returns the descriptor of the extension. */ public abstract Descriptors.FieldDescriptor getDescriptor(); - /** Returns the default value of the extension field. */ - public abstract Type getDefaultValue(); - - /** - * Returns the default instance of the extension field, if it's a message - * extension. - */ - public abstract MessageLite getMessageDefaultInstance(); + /** Returns whether or not this extension is a Lite Extension. */ + final boolean isLite() { + return false; + } // All the methods below are extension implementation details. diff --git a/java/src/main/java/com/google/protobuf/ExtensionLite.java b/java/src/main/java/com/google/protobuf/ExtensionLite.java new file mode 100644 index 00000000..f8f5bd2c --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ExtensionLite.java @@ -0,0 +1,63 @@ +// 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. + +package com.google.protobuf; + +/** + * Lite interface that generated extensions implement. + * <p> + * Methods are for use by generated code only. You can hold a reference to + * extensions using this type name. + */ +public abstract class ExtensionLite<ContainingType extends MessageLite, Type> { + + /** Returns the field number of the extension. */ + public abstract int getNumber(); + + /** Returns the type of the field. */ + public abstract WireFormat.FieldType getLiteType(); + + /** Returns whether it is a repeated field. */ + public abstract boolean isRepeated(); + + /** Returns the default value of the extension field. */ + public abstract Type getDefaultValue(); + + /** + * Returns the default instance of the extension field, if it's a message + * extension. + */ + public abstract MessageLite getMessageDefaultInstance(); + + /** Returns whether or not this extension is a Lite Extension. */ + boolean isLite() { + return true; + } +} diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java index 392f4efc..ff9b5bc0 100644 --- a/java/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -672,7 +672,7 @@ final class FieldSet<FieldDescriptorType extends * {@link Message#getField(Descriptors.FieldDescriptor)} for * this field. */ - private static void writeElementNoTag( + static void writeElementNoTag( final CodedOutputStream output, final WireFormat.FieldType type, final Object value) throws IOException { @@ -830,7 +830,7 @@ final class FieldSet<FieldDescriptorType extends * {@link Message#getField(Descriptors.FieldDescriptor)} for * this field. */ - private static int computeElementSizeNoTag( + static int computeElementSizeNoTag( final WireFormat.FieldType type, final Object value) { switch (type) { // Note: Minor violation of 80-char limit rule here because this would diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index a6101cb0..156d1633 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -31,16 +31,20 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.FileDescriptor; import com.google.protobuf.Descriptors.OneofDescriptor; +import com.google.protobuf.GeneratedMessageLite.ExtendableMessage; +import com.google.protobuf.GeneratedMessageLite.GeneratedExtension; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -67,10 +71,15 @@ public abstract class GeneratedMessage extends AbstractMessage */ protected static boolean alwaysUseFieldBuilders = false; + /** For use by generated code only. */ + protected UnknownFieldSet unknownFields; + protected GeneratedMessage() { + unknownFields = UnknownFieldSet.getDefaultInstance(); } protected GeneratedMessage(Builder<?> builder) { + unknownFields = builder.getUnknownFields(); } public Parser<? extends GeneratedMessage> getParserForType() { @@ -291,13 +300,12 @@ public abstract class GeneratedMessage extends AbstractMessage return isClean; } - // This is implemented here only to work around an apparent bug in the - // Java compiler and/or build system. See bug #1898463. The mere presence - // of this dummy clone() implementation makes it go away. @Override public BuilderType clone() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); + BuilderType builder = + (BuilderType) getDefaultInstanceForType().newBuilderForType(); + builder.mergeFrom(buildPartial()); + return builder; } /** @@ -358,6 +366,13 @@ public abstract class GeneratedMessage extends AbstractMessage } //@Override (Java 1.6 override semantics, but we must support 1.5) + public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, + int index) { + return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder( + this, index); + } + + //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean hasOneof(final OneofDescriptor oneof) { return internalGetFieldAccessorTable().getOneof(oneof).has(this); } @@ -428,7 +443,7 @@ public abstract class GeneratedMessage extends AbstractMessage return (BuilderType) this; } - public final BuilderType setUnknownFields( + public BuilderType setUnknownFields( final UnknownFieldSet unknownFields) { this.unknownFields = unknownFields; onChanged(); @@ -436,7 +451,7 @@ public abstract class GeneratedMessage extends AbstractMessage } @Override - public final BuilderType mergeUnknownFields( + public BuilderType mergeUnknownFields( final UnknownFieldSet unknownFields) { this.unknownFields = UnknownFieldSet.newBuilder(this.unknownFields) @@ -529,6 +544,25 @@ public abstract class GeneratedMessage extends AbstractMessage isClean = false; } } + + /** + * Gets the map field with the given field number. This method should be + * overridden in the generated message class if the message contains map + * fields. + * + * Unlike other field types, reflection support for map fields can't be + * implemented based on generated public API because we need to access a + * map field as a list in reflection API but the generated API only allows + * us to access it as a map. This method returns the underlying map field + * directly and thus enables us to access the map field as a list. + */ + @SuppressWarnings({"unused", "rawtypes"}) + protected MapField internalGetMapField(int fieldNumber) { + // Note that we can't use descriptor names here because this method will + // be called when descriptor is being initialized. + throw new RuntimeException( + "No map fields found in " + getClass().getName()); + } } // ================================================================= @@ -541,19 +575,19 @@ public abstract class GeneratedMessage extends AbstractMessage /** Check if a singular extension is present. */ <Type> boolean hasExtension( - Extension<MessageType, Type> extension); + ExtensionLite<MessageType, Type> extension); /** Get the number of elements in a repeated extension. */ <Type> int getExtensionCount( - Extension<MessageType, List<Type>> extension); + ExtensionLite<MessageType, List<Type>> extension); /** Get the value of an extension. */ <Type> Type getExtension( - Extension<MessageType, Type> extension); + ExtensionLite<MessageType, Type> extension); /** Get one element of a repeated extension. */ <Type> Type getExtension( - Extension<MessageType, List<Type>> extension, + ExtensionLite<MessageType, List<Type>> extension, int index); } @@ -625,7 +659,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Check if a singular extension is present. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> boolean hasExtension( - final Extension<MessageType, Type> extension) { + final ExtensionLite<MessageType, Type> extensionLite) { + Extension<MessageType, Type> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); } @@ -633,7 +669,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( - final Extension<MessageType, List<Type>> extension) { + final ExtensionLite<MessageType, List<Type>> extensionLite) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); return extensions.getRepeatedFieldCount(descriptor); @@ -643,7 +681,9 @@ public abstract class GeneratedMessage extends AbstractMessage //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final Extension<MessageType, Type> extension) { + final ExtensionLite<MessageType, Type> extensionLite) { + Extension<MessageType, Type> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); final Object value = extensions.getField(descriptor); @@ -666,8 +706,10 @@ public abstract class GeneratedMessage extends AbstractMessage //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final Extension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); return (Type) extension.singularFromReflectionType( @@ -914,11 +956,10 @@ public abstract class GeneratedMessage extends AbstractMessage // This is implemented here only to work around an apparent bug in the // Java compiler and/or build system. See bug #1898463. The mere presence - // of this dummy clone() implementation makes it go away. + // of this clone() implementation makes it go away. @Override public BuilderType clone() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); + return super.clone(); } private void ensureExtensionsIsMutable() { @@ -943,7 +984,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Check if a singular extension is present. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> boolean hasExtension( - final Extension<MessageType, Type> extension) { + final ExtensionLite<MessageType, Type> extensionLite) { + Extension<MessageType, Type> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); } @@ -951,7 +994,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( - final Extension<MessageType, List<Type>> extension) { + final ExtensionLite<MessageType, List<Type>> extensionLite) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); return extensions.getRepeatedFieldCount(descriptor); @@ -960,7 +1005,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Get the value of an extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( - final Extension<MessageType, Type> extension) { + final ExtensionLite<MessageType, Type> extensionLite) { + Extension<MessageType, Type> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); final Object value = extensions.getField(descriptor); @@ -982,8 +1029,10 @@ public abstract class GeneratedMessage extends AbstractMessage /** Get one element of a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( - final Extension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); return (Type) extension.singularFromReflectionType( @@ -992,8 +1041,10 @@ public abstract class GeneratedMessage extends AbstractMessage /** Set the value of an extension. */ public final <Type> BuilderType setExtension( - final Extension<MessageType, Type> extension, + final ExtensionLite<MessageType, Type> extensionLite, final Type value) { + Extension<MessageType, Type> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); @@ -1004,8 +1055,10 @@ public abstract class GeneratedMessage extends AbstractMessage /** Set the value of one element of a repeated extension. */ public final <Type> BuilderType setExtension( - final Extension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extensionLite, final int index, final Type value) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); @@ -1018,8 +1071,10 @@ public abstract class GeneratedMessage extends AbstractMessage /** Append a value to a repeated extension. */ public final <Type> BuilderType addExtension( - final Extension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extensionLite, final Type value) { + Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); final FieldDescriptor descriptor = extension.getDescriptor(); @@ -1031,7 +1086,9 @@ public abstract class GeneratedMessage extends AbstractMessage /** Clear an extension. */ public final <Type> BuilderType clearExtension( - final Extension<MessageType, ?> extension) { + final ExtensionLite<MessageType, ?> extensionLite) { + Extension<MessageType, ?> extension = checkNotLite(extensionLite); + verifyExtensionContainingType(extension); ensureExtensionsIsMutable(); extensions.clearField(extension.getDescriptor()); @@ -1594,6 +1651,25 @@ public abstract class GeneratedMessage extends AbstractMessage } } } + + /** + * Gets the map field with the given field number. This method should be + * overridden in the generated message class if the message contains map + * fields. + * + * Unlike other field types, reflection support for map fields can't be + * implemented based on generated public API because we need to access a + * map field as a list in reflection API but the generated API only allows + * us to access it as a map. This method returns the underlying map field + * directly and thus enables us to access the map field as a list. + */ + @SuppressWarnings({"rawtypes", "unused"}) + protected MapField internalGetMapField(int fieldNumber) { + // Note that we can't use descriptor names here because this method will + // be called when descriptor is being initialized. + throw new RuntimeException( + "No map fields found in " + getClass().getName()); + } /** * Users should ignore this class. This class provides the implementation @@ -1633,6 +1709,11 @@ public abstract class GeneratedMessage extends AbstractMessage oneofs = new OneofAccessor[descriptor.getOneofs().size()]; initialized = false; } + + private boolean isMapFieldEnabled(FieldDescriptor field) { + boolean result = true; + return result; + } /** * Ensures the field accessors are initialized. This method is thread-safe. @@ -1657,8 +1738,13 @@ public abstract class GeneratedMessage extends AbstractMessage } if (field.isRepeated()) { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - fields[i] = new RepeatedMessageFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass); + if (field.isMapField() && isMapFieldEnabled(field)) { + fields[i] = new MapFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new RepeatedMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { fields[i] = new RepeatedEnumFieldAccessor( field, camelCaseNames[i], messageClass, builderClass); @@ -1744,6 +1830,8 @@ public abstract class GeneratedMessage extends AbstractMessage void clear(Builder builder); Message.Builder newBuilder(); Message.Builder getBuilder(GeneratedMessage.Builder builder); + Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, + int index); } /** OneofAccessor provides access to a single oneof. */ @@ -1799,9 +1887,9 @@ public abstract class GeneratedMessage extends AbstractMessage invokeOrDie(clearMethod, builder); } } - + private static boolean supportFieldPresence(FileDescriptor file) { - return true; + return file.getSyntax() == FileDescriptor.Syntax.PROTO2; } // --------------------------------------------------------------- @@ -1919,6 +2007,11 @@ public abstract class GeneratedMessage extends AbstractMessage throw new UnsupportedOperationException( "getFieldBuilder() called on a non-Message type."); } + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, + int index) { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a non-Message type."); + } } private static class RepeatedFieldAccessor implements FieldAccessor { @@ -2014,6 +2107,113 @@ public abstract class GeneratedMessage extends AbstractMessage throw new UnsupportedOperationException( "getFieldBuilder() called on a non-Message type."); } + public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, + int index) { + throw new UnsupportedOperationException( + "getRepeatedFieldBuilder() called on a non-Message type."); + } + } + + private static class MapFieldAccessor implements FieldAccessor { + MapFieldAccessor( + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { + field = descriptor; + Method getDefaultInstanceMethod = + getMethodOrDie(messageClass, "getDefaultInstance"); + MapField defaultMapField = getMapField( + (GeneratedMessage) invokeOrDie(getDefaultInstanceMethod, null)); + mapEntryMessageDefaultInstance = + defaultMapField.getMapEntryMessageDefaultInstance(); + } + + private final FieldDescriptor field; + private final Message mapEntryMessageDefaultInstance; + + private MapField<?, ?> getMapField(GeneratedMessage message) { + return (MapField<?, ?>) message.internalGetMapField(field.getNumber()); + } + + private MapField<?, ?> getMapField(GeneratedMessage.Builder builder) { + return (MapField<?, ?>) builder.internalGetMapField(field.getNumber()); + } + + public Object get(GeneratedMessage message) { + List result = new ArrayList(); + for (int i = 0; i < getRepeatedCount(message); i++) { + result.add(getRepeated(message, i)); + } + return Collections.unmodifiableList(result); + } + + public Object get(Builder builder) { + List result = new ArrayList(); + for (int i = 0; i < getRepeatedCount(builder); i++) { + result.add(getRepeated(builder, i)); + } + return Collections.unmodifiableList(result); + } + + public void set(Builder builder, Object value) { + clear(builder); + for (Object entry : (List) value) { + addRepeated(builder, entry); + } + } + + public Object getRepeated(GeneratedMessage message, int index) { + return getMapField(message).getList().get(index); + } + + public Object getRepeated(Builder builder, int index) { + return getMapField(builder).getList().get(index); + } + + public void setRepeated(Builder builder, int index, Object value) { + getMapField(builder).getMutableList().set(index, (Message) value); + } + + public void addRepeated(Builder builder, Object value) { + getMapField(builder).getMutableList().add((Message) value); + } + + public boolean has(GeneratedMessage message) { + throw new UnsupportedOperationException( + "hasField() is not supported for repeated fields."); + } + + public boolean has(Builder builder) { + throw new UnsupportedOperationException( + "hasField() is not supported for repeated fields."); + } + + public int getRepeatedCount(GeneratedMessage message) { + return getMapField(message).getList().size(); + } + + public int getRepeatedCount(Builder builder) { + return getMapField(builder).getList().size(); + } + + public void clear(Builder builder) { + getMapField(builder).getMutableList().clear(); + } + + public com.google.protobuf.Message.Builder newBuilder() { + return mapEntryMessageDefaultInstance.newBuilderForType(); + } + + public com.google.protobuf.Message.Builder getBuilder(Builder builder) { + throw new UnsupportedOperationException( + "Nested builder not supported for map fields."); + } + + public com.google.protobuf.Message.Builder getRepeatedBuilder( + Builder builder, int index) { + throw new UnsupportedOperationException( + "Nested builder not supported for map fields."); + } } // --------------------------------------------------------------- @@ -2026,28 +2226,60 @@ public abstract class GeneratedMessage extends AbstractMessage final Class<? extends Builder> builderClass, final String containingOneofCamelCaseName) { super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); + + enumDescriptor = descriptor.getEnumType(); valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class); getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor"); + + supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue(); + if (supportUnknownEnumValue) { + getValueMethod = + getMethodOrDie(messageClass, "get" + camelCaseName + "Value"); + getValueMethodBuilder = + getMethodOrDie(builderClass, "get" + camelCaseName + "Value"); + setValueMethod = + getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class); + } } + + private EnumDescriptor enumDescriptor; private Method valueOfMethod; private Method getValueDescriptorMethod; + + private boolean supportUnknownEnumValue; + private Method getValueMethod; + private Method getValueMethodBuilder; + private Method setValueMethod; @Override public Object get(final GeneratedMessage message) { + if (supportUnknownEnumValue) { + int value = (Integer) invokeOrDie(getValueMethod, message); + return enumDescriptor.findValueByNumberCreatingIfUnknown(value); + } return invokeOrDie(getValueDescriptorMethod, super.get(message)); } @Override public Object get(final GeneratedMessage.Builder builder) { + if (supportUnknownEnumValue) { + int value = (Integer) invokeOrDie(getValueMethodBuilder, builder); + return enumDescriptor.findValueByNumberCreatingIfUnknown(value); + } return invokeOrDie(getValueDescriptorMethod, super.get(builder)); } @Override public void set(final Builder builder, final Object value) { + if (supportUnknownEnumValue) { + invokeOrDie(setValueMethod, builder, + ((EnumValueDescriptor) value).getNumber()); + return; + } super.set(builder, invokeOrDie(valueOfMethod, null, value)); } } @@ -2059,22 +2291,44 @@ public abstract class GeneratedMessage extends AbstractMessage final Class<? extends GeneratedMessage> messageClass, final Class<? extends Builder> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); + + enumDescriptor = descriptor.getEnumType(); valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class); getValueDescriptorMethod = getMethodOrDie(type, "getValueDescriptor"); + + supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue(); + if (supportUnknownEnumValue) { + getRepeatedValueMethod = + getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class); + getRepeatedValueMethodBuilder = + getMethodOrDie(builderClass, "get" + camelCaseName + "Value", int.class); + setRepeatedValueMethod = + getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class, int.class); + addRepeatedValueMethod = + getMethodOrDie(builderClass, "add" + camelCaseName + "Value", int.class); + } } + private EnumDescriptor enumDescriptor; private final Method valueOfMethod; private final Method getValueDescriptorMethod; + + private boolean supportUnknownEnumValue; + private Method getRepeatedValueMethod; + private Method getRepeatedValueMethodBuilder; + private Method setRepeatedValueMethod; + private Method addRepeatedValueMethod; @Override @SuppressWarnings("unchecked") public Object get(final GeneratedMessage message) { final List newList = new ArrayList(); - for (final Object element : (List) super.get(message)) { - newList.add(invokeOrDie(getValueDescriptorMethod, element)); + final int size = getRepeatedCount(message); + for (int i = 0; i < size; i++) { + newList.add(getRepeated(message, i)); } return Collections.unmodifiableList(newList); } @@ -2083,8 +2337,9 @@ public abstract class GeneratedMessage extends AbstractMessage @SuppressWarnings("unchecked") public Object get(final GeneratedMessage.Builder builder) { final List newList = new ArrayList(); - for (final Object element : (List) super.get(builder)) { - newList.add(invokeOrDie(getValueDescriptorMethod, element)); + final int size = getRepeatedCount(builder); + for (int i = 0; i < size; i++) { + newList.add(getRepeated(builder, i)); } return Collections.unmodifiableList(newList); } @@ -2092,23 +2347,41 @@ public abstract class GeneratedMessage extends AbstractMessage @Override public Object getRepeated(final GeneratedMessage message, final int index) { + if (supportUnknownEnumValue) { + int value = (Integer) invokeOrDie(getRepeatedValueMethod, message, index); + return enumDescriptor.findValueByNumberCreatingIfUnknown(value); + } return invokeOrDie(getValueDescriptorMethod, super.getRepeated(message, index)); } @Override public Object getRepeated(final GeneratedMessage.Builder builder, final int index) { + if (supportUnknownEnumValue) { + int value = (Integer) invokeOrDie(getRepeatedValueMethodBuilder, builder, index); + return enumDescriptor.findValueByNumberCreatingIfUnknown(value); + } return invokeOrDie(getValueDescriptorMethod, super.getRepeated(builder, index)); } @Override public void setRepeated(final Builder builder, final int index, final Object value) { + if (supportUnknownEnumValue) { + invokeOrDie(setRepeatedValueMethod, builder, index, + ((EnumValueDescriptor) value).getNumber()); + return; + } super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); } @Override public void addRepeated(final Builder builder, final Object value) { + if (supportUnknownEnumValue) { + invokeOrDie(addRepeatedValueMethod, builder, + ((EnumValueDescriptor) value).getNumber()); + return; + } super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); } } @@ -2168,9 +2441,12 @@ public abstract class GeneratedMessage extends AbstractMessage super(descriptor, camelCaseName, messageClass, builderClass); newBuilderMethod = getMethodOrDie(type, "newBuilder"); + getBuilderMethodBuilder = getMethodOrDie(builderClass, + "get" + camelCaseName + "Builder", Integer.TYPE); } private final Method newBuilderMethod; + private final Method getBuilderMethodBuilder; private Object coerceType(final Object value) { if (type.isInstance(value)) { @@ -2198,6 +2474,12 @@ public abstract class GeneratedMessage extends AbstractMessage public Message.Builder newBuilder() { return (Message.Builder) invokeOrDie(newBuilderMethod, null); } + @Override + public Message.Builder getRepeatedBuilder( + final GeneratedMessage.Builder builder, final int index) { + return (Message.Builder) invokeOrDie( + getBuilderMethodBuilder, builder, index); + } } } @@ -2210,4 +2492,18 @@ public abstract class GeneratedMessage extends AbstractMessage protected Object writeReplace() throws ObjectStreamException { return new GeneratedMessageLite.SerializedForm(this); } + + /** + * Checks that the {@link Extension} is non-Lite and returns it as a + * {@link GeneratedExtension}. + */ + private static <MessageType extends ExtendableMessage<MessageType>, T> + Extension<MessageType, T> checkNotLite( + ExtensionLite<MessageType, T> extension) { + if (extension.isLite()) { + throw new IllegalArgumentException("Expected non-lite extension."); + } + + return (Extension<MessageType, T>) extension; + } } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 6c5136fd..8fe33bc6 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -30,6 +30,8 @@ package com.google.protobuf; +import com.google.protobuf.WireFormat.FieldType; + import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; @@ -50,10 +52,15 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite implements Serializable { private static final long serialVersionUID = 1L; + /** For use by generated code only. */ + protected UnknownFieldSetLite unknownFields; + protected GeneratedMessageLite() { + unknownFields = UnknownFieldSetLite.getDefaultInstance(); } protected GeneratedMessageLite(Builder builder) { + unknownFields = builder.unknownFields; } public Parser<? extends MessageLite> getParserForType() { @@ -62,15 +69,16 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite } /** - * Called by subclasses to parse an unknown field. + * Called by subclasses to parse an unknown field. For use by generated code + * only. * @return {@code true} unless the tag is an end-group tag. */ - protected boolean parseUnknownField( + protected static boolean parseUnknownField( CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, + UnknownFieldSetLite.Builder unknownFields, ExtensionRegistryLite extensionRegistry, int tag) throws IOException { - return input.skipField(tag, unknownFieldsCodedOutput); + return unknownFields.mergeFieldFrom(tag, input); } /** @@ -84,22 +92,28 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite public abstract static class Builder<MessageType extends GeneratedMessageLite, BuilderType extends Builder> extends AbstractMessageLite.Builder<BuilderType> { + + private UnknownFieldSetLite unknownFields = + UnknownFieldSetLite.getDefaultInstance(); + protected Builder() {} //@Override (Java 1.6 override semantics, but we must support 1.5) public BuilderType clear() { - unknownFields = ByteString.EMPTY; + unknownFields = UnknownFieldSetLite.getDefaultInstance(); return (BuilderType) this; } - // This is implemented here only to work around an apparent bug in the - // Java compiler and/or build system. See bug #1898463. The mere presence - // of this dummy clone() implementation makes it go away. - @Override + //@Override (Java 1.6 override semantics, but we must support 1.5) public BuilderType clone() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); + BuilderType builder = + (BuilderType) getDefaultInstanceForType().newBuilderForType(); + builder.mergeFrom(buildPartial()); + return builder; } + + /** All subclasses implement this. */ + public abstract MessageType buildPartial(); /** All subclasses implement this. */ public abstract BuilderType mergeFrom(MessageType message); @@ -113,22 +127,43 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite */ protected boolean parseUnknownField( CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, + UnknownFieldSetLite.Builder unknownFields, ExtensionRegistryLite extensionRegistry, int tag) throws IOException { - return input.skipField(tag, unknownFieldsCodedOutput); + return unknownFields.mergeFieldFrom(tag, input); } - public final ByteString getUnknownFields() { - return unknownFields; + /** + * Merge some unknown fields into the {@link UnknownFieldSetLite} for this + * message. + * + * <p>For use by generated code only. + */ + protected final BuilderType mergeUnknownFields( + final UnknownFieldSetLite unknownFields) { + this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); + return (BuilderType) this; } - - public final BuilderType setUnknownFields(final ByteString unknownFields) { - this.unknownFields = unknownFields; + + public BuilderType mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + MessageType parsedMessage = null; + try { + parsedMessage = + (MessageType) getDefaultInstanceForType().getParserForType().parsePartialFrom( + input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (MessageType) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } return (BuilderType) this; } - - private ByteString unknownFields = ByteString.EMPTY; } @@ -143,18 +178,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite /** Check if a singular extension is present. */ <Type> boolean hasExtension( - GeneratedExtension<MessageType, Type> extension); + ExtensionLite<MessageType, Type> extension); /** Get the number of elements in a repeated extension. */ <Type> int getExtensionCount( - GeneratedExtension<MessageType, List<Type>> extension); + ExtensionLite<MessageType, List<Type>> extension); /** Get the value of an extension. */ - <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); + <Type> Type getExtension(ExtensionLite<MessageType, Type> extension); /** Get one element of a repeated extension. */ <Type> Type getExtension( - GeneratedExtension<MessageType, List<Type>> extension, + ExtensionLite<MessageType, List<Type>> extension, int index); } @@ -166,7 +201,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite extends GeneratedMessageLite implements ExtendableMessageOrBuilder<MessageType> { - private final FieldSet<ExtensionDescriptor> extensions; + /** + * Represents the set of extensions on this message. For use by generated + * code only. + */ + protected final FieldSet<ExtensionDescriptor> extensions; protected ExtendableMessage() { this.extensions = FieldSet.newFieldSet(); @@ -190,30 +229,39 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite /** Check if a singular extension is present. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> boolean hasExtension( - final GeneratedExtension<MessageType, Type> extension) { - verifyExtensionContainingType(extension); - return extensions.hasField(extension.descriptor); + final ExtensionLite<MessageType, Type> extension) { + GeneratedExtension<MessageType, Type> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return extensions.hasField(extensionLite.descriptor); } /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( - final GeneratedExtension<MessageType, List<Type>> extension) { - verifyExtensionContainingType(extension); - return extensions.getRepeatedFieldCount(extension.descriptor); + final ExtensionLite<MessageType, List<Type>> extension) { + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return extensions.getRepeatedFieldCount(extensionLite.descriptor); } /** Get the value of an extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final GeneratedExtension<MessageType, Type> extension) { - verifyExtensionContainingType(extension); - final Object value = extensions.getField(extension.descriptor); + final ExtensionLite<MessageType, Type> extension) { + GeneratedExtension<MessageType, Type> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + final Object value = extensions.getField(extensionLite.descriptor); if (value == null) { - return extension.defaultValue; + return extensionLite.defaultValue; } else { - return (Type) extension.fromFieldSetType(value); + return (Type) extensionLite.fromFieldSetType(value); } } @@ -221,11 +269,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final GeneratedExtension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extension, final int index) { - verifyExtensionContainingType(extension); - return (Type) extension.singularFromFieldSetType( - extensions.getRepeatedField(extension.descriptor, index)); + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return (Type) extensionLite.singularFromFieldSetType( + extensions.getRepeatedField(extensionLite.descriptor, index)); } /** Called by subclasses to check if all extensions are initialized. */ @@ -233,25 +284,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite return extensions.isInitialized(); } - /** - * Called by subclasses to parse an unknown field or an extension. - * @return {@code true} unless the tag is an end-group tag. - */ - @Override - protected boolean parseUnknownField( - CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return GeneratedMessageLite.parseUnknownField( - extensions, - getDefaultInstanceForType(), - input, - unknownFieldsCodedOutput, - extensionRegistry, - tag); - } - /** * Used by parsing constructors in generated classes. @@ -377,30 +409,39 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite /** Check if a singular extension is present. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> boolean hasExtension( - final GeneratedExtension<MessageType, Type> extension) { - verifyExtensionContainingType(extension); - return extensions.hasField(extension.descriptor); + final ExtensionLite<MessageType, Type> extension) { + GeneratedExtension<MessageType, Type> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return extensions.hasField(extensionLite.descriptor); } /** Get the number of elements in a repeated extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> int getExtensionCount( - final GeneratedExtension<MessageType, List<Type>> extension) { - verifyExtensionContainingType(extension); - return extensions.getRepeatedFieldCount(extension.descriptor); + final ExtensionLite<MessageType, List<Type>> extension) { + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return extensions.getRepeatedFieldCount(extensionLite.descriptor); } /** Get the value of an extension. */ //@Override (Java 1.6 override semantics, but we must support 1.5) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final GeneratedExtension<MessageType, Type> extension) { - verifyExtensionContainingType(extension); - final Object value = extensions.getField(extension.descriptor); + final ExtensionLite<MessageType, Type> extension) { + GeneratedExtension<MessageType, Type> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + final Object value = extensions.getField(extensionLite.descriptor); if (value == null) { - return extension.defaultValue; + return extensionLite.defaultValue; } else { - return (Type) extension.fromFieldSetType(value); + return (Type) extensionLite.fromFieldSetType(value); } } @@ -408,11 +449,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite @SuppressWarnings("unchecked") //@Override (Java 1.6 override semantics, but we must support 1.5) public final <Type> Type getExtension( - final GeneratedExtension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extension, final int index) { - verifyExtensionContainingType(extension); - return (Type) extension.singularFromFieldSetType( - extensions.getRepeatedField(extension.descriptor, index)); + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); + return (Type) extensionLite.singularFromFieldSetType( + extensions.getRepeatedField(extensionLite.descriptor, index)); } // This is implemented here only to work around an apparent bug in the @@ -423,46 +467,57 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite throw new UnsupportedOperationException( "This is supposed to be overridden by subclasses."); } - + /** Set the value of an extension. */ public final <Type> BuilderType setExtension( - final GeneratedExtension<MessageType, Type> extension, + final ExtensionLite<MessageType, Type> extension, final Type value) { - verifyExtensionContainingType(extension); + GeneratedExtension<MessageType, Type> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); ensureExtensionsIsMutable(); - extensions.setField(extension.descriptor, - extension.toFieldSetType(value)); + extensions.setField(extensionLite.descriptor, + extensionLite.toFieldSetType(value)); return (BuilderType) this; } /** Set the value of one element of a repeated extension. */ public final <Type> BuilderType setExtension( - final GeneratedExtension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extension, final int index, final Type value) { - verifyExtensionContainingType(extension); + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); ensureExtensionsIsMutable(); - extensions.setRepeatedField(extension.descriptor, index, - extension.singularToFieldSetType(value)); + extensions.setRepeatedField(extensionLite.descriptor, index, + extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } /** Append a value to a repeated extension. */ public final <Type> BuilderType addExtension( - final GeneratedExtension<MessageType, List<Type>> extension, + final ExtensionLite<MessageType, List<Type>> extension, final Type value) { - verifyExtensionContainingType(extension); + GeneratedExtension<MessageType, List<Type>> extensionLite = + checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); ensureExtensionsIsMutable(); - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); + extensions.addRepeatedField(extensionLite.descriptor, + extensionLite.singularToFieldSetType(value)); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( - final GeneratedExtension<MessageType, ?> extension) { - verifyExtensionContainingType(extension); + final ExtensionLite<MessageType, ?> extension) { + GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension); + + verifyExtensionContainingType(extensionLite); ensureExtensionsIsMutable(); - extensions.clearField(extension.descriptor); + extensions.clearField(extensionLite.descriptor); return (BuilderType) this; } @@ -471,44 +526,24 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite return extensions.isInitialized(); } - /** - * Called by subclasses to parse an unknown field or an extension. - * @return {@code true} unless the tag is an end-group tag. - */ - @Override - protected boolean parseUnknownField( - CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - ensureExtensionsIsMutable(); - return GeneratedMessageLite.parseUnknownField( - extensions, - getDefaultInstanceForType(), - input, - unknownFieldsCodedOutput, - extensionRegistry, - tag); - } - protected final void mergeExtensionFields(final MessageType other) { ensureExtensionsIsMutable(); extensions.mergeFrom(((ExtendableMessage) other).extensions); } } - // ----------------------------------------------------------------- + //----------------------------------------------------------------- /** - * Parse an unknown field or an extension. + * Parse an unknown field or an extension. For use by generated code only. * @return {@code true} unless the tag is an end-group tag. */ - private static <MessageType extends MessageLite> + protected static <MessageType extends MessageLite> boolean parseUnknownField( FieldSet<ExtensionDescriptor> extensions, MessageType defaultInstance, CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, + UnknownFieldSetLite.Builder unknownFields, ExtensionRegistryLite extensionRegistry, int tag) throws IOException { int wireType = WireFormat.getTagWireType(tag); @@ -537,7 +572,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite } if (unknown) { // Unknown field or wrong wire type. Skip. - return input.skipField(tag, unknownFieldsCodedOutput); + return unknownFields.mergeFieldFrom(tag, input); } if (packed) { @@ -599,8 +634,7 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite // If the number isn't recognized as a valid value for this enum, // write it to unknown fields object. if (value == null) { - unknownFieldsCodedOutput.writeRawVarint32(tag); - unknownFieldsCodedOutput.writeUInt32NoTag(rawValue); + unknownFields.mergeVarintField(fieldNumber, rawValue); return true; } break; @@ -768,7 +802,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite * this type as parameters to extension accessors and ExtensionRegistry.add(). */ public static class GeneratedExtension< - ContainingType extends MessageLite, Type> { + ContainingType extends MessageLite, Type> + extends ExtensionLite<ContainingType, Type> { /** * Create a new isntance with the given parameters. @@ -888,6 +923,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite return value; } } + + public FieldType getLiteType() { + return descriptor.getLiteType(); + } + + public boolean isRepeated() { + return descriptor.isRepeated; + } + + public Type getDefaultValue() { + return defaultValue; + } } /** @@ -897,8 +944,8 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite static final class SerializedForm implements Serializable { private static final long serialVersionUID = 0L; - private String messageClassName; - private byte[] asBytes; + private final String messageClassName; + private final byte[] asBytes; /** * Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}. @@ -918,19 +965,17 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite protected Object readResolve() throws ObjectStreamException { try { Class messageClass = Class.forName(messageClassName); - Method newBuilder = messageClass.getMethod("newBuilder"); - MessageLite.Builder builder = - (MessageLite.Builder) newBuilder.invoke(null); - builder.mergeFrom(asBytes); - return builder.buildPartial(); + Parser<?> parser = + (Parser<?>) messageClass.getField("PARSER").get(null); + return parser.parsePartialFrom(asBytes); } catch (ClassNotFoundException e) { throw new RuntimeException("Unable to find proto buffer class", e); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Unable to find newBuilder method", e); + } catch (NoSuchFieldException e) { + throw new RuntimeException("Unable to find PARSER", e); + } catch (SecurityException e) { + throw new RuntimeException("Unable to call PARSER", e); } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to call newBuilder method", e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Error calling newBuilder", e.getCause()); + throw new RuntimeException("Unable to call parseFrom method", e); } catch (InvalidProtocolBufferException e) { throw new RuntimeException("Unable to understand proto buffer", e); } @@ -946,4 +991,18 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite protected Object writeReplace() throws ObjectStreamException { return new SerializedForm(this); } + + /** + * Checks that the {@link Extension} is Lite and returns it as a + * {@link GeneratedExtension}. + */ + private static <MessageType extends ExtendableMessage<MessageType>, T> + GeneratedExtension<MessageType, T> checkIsLite( + ExtensionLite<MessageType, T> extension) { + if (!extension.isLite()) { + throw new IllegalArgumentException("Expected a lite extension."); + } + + return (GeneratedExtension<MessageType, T>) extension; + } } diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 48d29e69..ba0f526b 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -33,8 +33,14 @@ package com.google.protobuf; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.AbstractSet; import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Set; /** * The classes contained within are used internally by the Protocol Buffer @@ -388,4 +394,164 @@ public class Internal { public static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.wrap(EMPTY_BYTE_ARRAY); + + /** + * Provides an immutable view of List<T> around a List<F>. + * + * Protobuf internal. Used in protobuf generated code only. + */ + public static class ListAdapter<F, T> extends AbstractList<T> { + /** + * Convert individual elements of the List from F to T. + */ + public interface Converter<F, T> { + T convert(F from); + } + + private final List<F> fromList; + private final Converter<F, T> converter; + + public ListAdapter(List<F> fromList, Converter<F, T> converter) { + this.fromList = fromList; + this.converter = converter; + } + + @Override + public T get(int index) { + return converter.convert(fromList.get(index)); + } + + @Override + public int size() { + return fromList.size(); + } + } + + /** + * Wrap around a Map<K, RealValue> and provide a Map<K, V> interface. + */ + public static class MapAdapter<K, V, RealValue> extends AbstractMap<K, V> { + /** + * An interface used to convert between two types. + */ + public interface Converter<A, B> { + B doForward(A object); + A doBackward(B object); + } + + public static <T extends EnumLite> Converter<Integer, T> newEnumConverter( + final EnumLiteMap<T> enumMap, final T unrecognizedValue) { + return new Converter<Integer, T>() { + public T doForward(Integer value) { + T result = enumMap.findValueByNumber(value); + return result == null ? unrecognizedValue : result; + } + public Integer doBackward(T value) { + return value.getNumber(); + } + }; + } + + private final Map<K, RealValue> realMap; + private final Converter<RealValue, V> valueConverter; + + public MapAdapter(Map<K, RealValue> realMap, + Converter<RealValue, V> valueConverter) { + this.realMap = realMap; + this.valueConverter = valueConverter; + } + + @SuppressWarnings("unchecked") + @Override + public V get(Object key) { + RealValue result = realMap.get(key); + if (result == null) { + return null; + } + return valueConverter.doForward(result); + } + + @Override + public V put(K key, V value) { + RealValue oldValue = realMap.put(key, valueConverter.doBackward(value)); + if (oldValue == null) { + return null; + } + return valueConverter.doForward(oldValue); + } + + @Override + public Set<java.util.Map.Entry<K, V>> entrySet() { + return new SetAdapter(realMap.entrySet()); + } + + private class SetAdapter extends AbstractSet<Map.Entry<K, V>> { + private final Set<Map.Entry<K, RealValue>> realSet; + public SetAdapter(Set<Map.Entry<K, RealValue>> realSet) { + this.realSet = realSet; + } + + @Override + public Iterator<java.util.Map.Entry<K, V>> iterator() { + return new IteratorAdapter(realSet.iterator()); + } + + @Override + public int size() { + return realSet.size(); + } + } + + private class IteratorAdapter implements Iterator<Map.Entry<K, V>> { + private final Iterator<Map.Entry<K, RealValue>> realIterator; + + public IteratorAdapter( + Iterator<Map.Entry<K, RealValue>> realIterator) { + this.realIterator = realIterator; + } + + @Override + public boolean hasNext() { + return realIterator.hasNext(); + } + + @Override + public java.util.Map.Entry<K, V> next() { + return new EntryAdapter(realIterator.next()); + } + + @Override + public void remove() { + realIterator.remove(); + } + } + + private class EntryAdapter implements Map.Entry<K, V> { + private final Map.Entry<K, RealValue> realEntry; + + public EntryAdapter(Map.Entry<K, RealValue> realEntry) { + this.realEntry = realEntry; + } + + @Override + public K getKey() { + return realEntry.getKey(); + } + + @Override + public V getValue() { + return valueConverter.doForward(realEntry.getValue()); + } + + @Override + public V setValue(V value) { + RealValue oldValue = realEntry.setValue( + valueConverter.doBackward(value)); + if (oldValue == null) { + return null; + } + return valueConverter.doForward(oldValue); + } + } + } } diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java index 61c7e1ea..2d40a51f 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -74,6 +74,10 @@ public class LazyStringArrayList extends AbstractList<String> list = new ArrayList<Object>(); } + public LazyStringArrayList(int intialCapacity) { + list = new ArrayList<Object>(intialCapacity); + } + public LazyStringArrayList(LazyStringList from) { list = new ArrayList<Object>(from.size()); addAll(from); diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java index 83e71e93..127c574d 100644 --- a/java/src/main/java/com/google/protobuf/LiteralByteString.java +++ b/java/src/main/java/com/google/protobuf/LiteralByteString.java @@ -190,6 +190,15 @@ class LiteralByteString extends ByteString { } if (other instanceof LiteralByteString) { + LiteralByteString otherAsLiteral = (LiteralByteString) other; + // If we know the hash codes and they are not equal, we know the byte + // strings are not equal. + if (hash != 0 + && otherAsLiteral.hash != 0 + && hash != otherAsLiteral.hash) { + return false; + } + return equalsRange((LiteralByteString) other, 0, size()); } else if (other instanceof RopeByteString) { return other.equals(this); diff --git a/java/src/main/java/com/google/protobuf/MapEntry.java b/java/src/main/java/com/google/protobuf/MapEntry.java new file mode 100644 index 00000000..8e3551b6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MapEntry.java @@ -0,0 +1,433 @@ +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * Implements MapEntry messages. + * + * In reflection API, map fields will be treated as repeated message fields and + * each map entry is accessed as a message. This MapEntry class is used to + * represent these map entry messages in reflection API. + * + * Protobuf internal. Users shouldn't use this class. + */ +public final class MapEntry<K, V> extends AbstractMessage { + private static class Metadata<K, V> { + public final Descriptor descriptor; + public final MapEntry<K, V> defaultInstance; + public final AbstractParser<MapEntry<K, V>> parser; + + public Metadata( + final Descriptor descriptor, final MapEntry<K, V> defaultInstance) { + this.descriptor = descriptor; + this.defaultInstance = defaultInstance; + final Metadata<K, V> thisMetadata = this; + this.parser = new AbstractParser<MapEntry<K, V>>() { + private final Parser<MapEntryLite<K, V>> dataParser = + defaultInstance.data.getParserForType(); + @Override + public MapEntry<K, V> parsePartialFrom( + CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + MapEntryLite<K, V> data = + dataParser.parsePartialFrom(input, extensionRegistry); + return new MapEntry<K, V>(thisMetadata, data); + } + + }; + } + } + + private final Metadata<K, V> metadata; + private final MapEntryLite<K, V> data; + + /** Create a default MapEntry instance. */ + private MapEntry(Descriptor descriptor, + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + this.data = MapEntryLite.newDefaultInstance( + keyType, defaultKey, valueType, defaultValue); + this.metadata = new Metadata<K, V>(descriptor, this); + } + + /** Create a new MapEntry message. */ + private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) { + this.metadata = metadata; + this.data = data; + } + + /** + * Create a default MapEntry instance. A default MapEntry instance should be + * created only once for each map entry message type. Generated code should + * store the created default instance and use it later to create new MapEntry + * messages of the same type. + */ + public static <K, V> MapEntry<K, V> newDefaultInstance( + Descriptor descriptor, + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + return new MapEntry<K, V>( + descriptor, keyType, defaultKey, valueType, defaultValue); + } + + public K getKey() { + return data.getKey(); + } + + public V getValue() { + return data.getValue(); + } + + @Override + public int getSerializedSize() { + return data.getSerializedSize(); + } + + @Override + public void writeTo(CodedOutputStream output) throws IOException { + data.writeTo(output); + } + + @Override + public boolean isInitialized() { + return data.isInitialized(); + } + + @Override + public Parser<MapEntry<K, V>> getParserForType() { + return metadata.parser; + } + + @Override + public Builder<K, V> newBuilderForType() { + return new Builder<K, V>(metadata); + } + + @Override + public Builder<K, V> toBuilder() { + return new Builder<K, V>(metadata, data); + } + + @Override + public MapEntry<K, V> getDefaultInstanceForType() { + return metadata.defaultInstance; + } + + @Override + public Descriptor getDescriptorForType() { + return metadata.descriptor; + } + + @Override + public Map<FieldDescriptor, Object> getAllFields() { + final TreeMap<FieldDescriptor, Object> result = + new TreeMap<FieldDescriptor, Object>(); + for (final FieldDescriptor field : metadata.descriptor.getFields()) { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + return Collections.unmodifiableMap(result); + } + + private void checkFieldDescriptor(FieldDescriptor field) { + if (field.getContainingType() != metadata.descriptor) { + throw new RuntimeException( + "Wrong FieldDescriptor \"" + field.getFullName() + + "\" used in message \"" + metadata.descriptor.getFullName()); + } + } + + @Override + public boolean hasField(FieldDescriptor field) { + checkFieldDescriptor(field);; + // A MapEntry always contains two fields. + return true; + } + + @Override + public Object getField(FieldDescriptor field) { + checkFieldDescriptor(field); + Object result = field.getNumber() == 1 ? getKey() : getValue(); + // Convert enums to EnumValueDescriptor. + if (field.getType() == FieldDescriptor.Type.ENUM) { + result = field.getEnumType().findValueByNumberCreatingIfUnknown( + (java.lang.Integer) result); + } + return result; + } + + @Override + public int getRepeatedFieldCount(FieldDescriptor field) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public Object getRepeatedField(FieldDescriptor field, int index) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public UnknownFieldSet getUnknownFields() { + return UnknownFieldSet.getDefaultInstance(); + } + + /** + * Builder to create {@link MapEntry} messages. + */ + public static class Builder<K, V> + extends AbstractMessage.Builder<Builder<K, V>> { + private final Metadata<K, V> metadata; + private MapEntryLite<K, V> data; + private MapEntryLite.Builder<K, V> dataBuilder; + + private Builder(Metadata<K, V> metadata) { + this.metadata = metadata; + this.data = metadata.defaultInstance.data; + this.dataBuilder = null; + } + + private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) { + this.metadata = metadata; + this.data = data; + this.dataBuilder = null; + } + + public K getKey() { + return dataBuilder == null ? data.getKey() : dataBuilder.getKey(); + } + + public V getValue() { + return dataBuilder == null ? data.getValue() : dataBuilder.getValue(); + } + + private void ensureMutable() { + if (dataBuilder == null) { + dataBuilder = data.toBuilder(); + } + } + + public Builder<K, V> setKey(K key) { + ensureMutable(); + dataBuilder.setKey(key); + return this; + } + + public Builder<K, V> clearKey() { + ensureMutable(); + dataBuilder.clearKey(); + return this; + } + + public Builder<K, V> setValue(V value) { + ensureMutable(); + dataBuilder.setValue(value); + return this; + } + + public Builder<K, V> clearValue() { + ensureMutable(); + dataBuilder.clearValue(); + return this; + } + + @Override + public MapEntry<K, V> build() { + MapEntry<K, V> result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public MapEntry<K, V> buildPartial() { + if (dataBuilder != null) { + data = dataBuilder.build(); + dataBuilder = null; + } + return new MapEntry<K, V>(metadata, data); + } + + @Override + public Descriptor getDescriptorForType() { + return metadata.descriptor; + } + + private void checkFieldDescriptor(FieldDescriptor field) { + if (field.getContainingType() != metadata.descriptor) { + throw new RuntimeException( + "Wrong FieldDescriptor \"" + field.getFullName() + + "\" used in message \"" + metadata.descriptor.getFullName()); + } + } + + @Override + public com.google.protobuf.Message.Builder newBuilderForField( + FieldDescriptor field) { + checkFieldDescriptor(field);; + // This method should be called for message fields and in a MapEntry + // message only the value field can possibly be a message field. + if (field.getNumber() != 2 + || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new RuntimeException( + "\"" + field.getFullName() + "\" is not a message value field."); + } + return ((Message) data.getValue()).newBuilderForType(); + } + + @SuppressWarnings("unchecked") + @Override + public Builder<K, V> setField(FieldDescriptor field, Object value) { + checkFieldDescriptor(field); + if (field.getNumber() == 1) { + setKey((K) value); + } else { + if (field.getType() == FieldDescriptor.Type.ENUM) { + value = ((EnumValueDescriptor) value).getNumber(); + } + setValue((V) value); + } + return this; + } + + @Override + public Builder<K, V> clearField(FieldDescriptor field) { + checkFieldDescriptor(field); + if (field.getNumber() == 1) { + clearKey(); + } else { + clearValue(); + } + return this; + } + + @Override + public Builder<K, V> setRepeatedField(FieldDescriptor field, int index, + Object value) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public Builder<K, V> addRepeatedField(FieldDescriptor field, Object value) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public Builder<K, V> setUnknownFields(UnknownFieldSet unknownFields) { + // Unknown fields are discarded for MapEntry message. + return this; + } + + @Override + public MapEntry<K, V> getDefaultInstanceForType() { + return metadata.defaultInstance; + } + + @Override + public boolean isInitialized() { + if (dataBuilder != null) { + return dataBuilder.isInitialized(); + } else { + return data.isInitialized(); + } + } + + @Override + public Map<FieldDescriptor, Object> getAllFields() { + final TreeMap<FieldDescriptor, Object> result = + new TreeMap<FieldDescriptor, Object>(); + for (final FieldDescriptor field : metadata.descriptor.getFields()) { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + return Collections.unmodifiableMap(result); + } + + @Override + public boolean hasField(FieldDescriptor field) { + checkFieldDescriptor(field); + return true; + } + + @Override + public Object getField(FieldDescriptor field) { + checkFieldDescriptor(field); + Object result = field.getNumber() == 1 ? getKey() : getValue(); + // Convert enums to EnumValueDescriptor. + if (field.getType() == FieldDescriptor.Type.ENUM) { + result = field.getEnumType().findValueByNumberCreatingIfUnknown( + (java.lang.Integer) result); + } + return result; + } + + @Override + public int getRepeatedFieldCount(FieldDescriptor field) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public Object getRepeatedField(FieldDescriptor field, int index) { + throw new RuntimeException( + "There is no repeated field in a map entry message."); + } + + @Override + public UnknownFieldSet getUnknownFields() { + return UnknownFieldSet.getDefaultInstance(); + } + + @Override + public Builder<K, V> clone() { + if (dataBuilder == null) { + return new Builder<K, V>(metadata, data); + } else { + return new Builder<K, V>(metadata, dataBuilder.build()); + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/MapEntryLite.java b/java/src/main/java/com/google/protobuf/MapEntryLite.java new file mode 100644 index 00000000..bcffa946 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MapEntryLite.java @@ -0,0 +1,331 @@ +// 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. + +package com.google.protobuf; + +import java.io.IOException; + +/** + * Implements the lite version of map entry messages. + * + * This class serves as an utility class to help do serialization/parsing of + * map entries. It's used in generated code and also in the full version + * MapEntry message. + * + * Protobuf internal. Users shouldn't use. + */ +public class MapEntryLite<K, V> extends AbstractMessageLite { + private static class Metadata<K, V> { + public final MapEntryLite<K, V> defaultInstance; + public final WireFormat.FieldType keyType; + public final WireFormat.FieldType valueType; + public final Parser<MapEntryLite<K, V>> parser; + public Metadata( + MapEntryLite<K, V> defaultInstance, + WireFormat.FieldType keyType, + WireFormat.FieldType valueType) { + this.defaultInstance = defaultInstance; + this.keyType = keyType; + this.valueType = valueType; + final Metadata<K, V> finalThis = this; + this.parser = new AbstractParser<MapEntryLite<K, V>>() { + @Override + public MapEntryLite<K, V> parsePartialFrom( + CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return new MapEntryLite<K, V>(finalThis, input, extensionRegistry); + } + }; + } + } + + private static final int KEY_FIELD_NUMBER = 1; + private static final int VALUE_FIELD_NUMBER = 2; + + private final Metadata<K, V> metadata; + private final K key; + private final V value; + + /** Creates a default MapEntryLite message instance. */ + private MapEntryLite( + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + this.metadata = new Metadata<K, V>(this, keyType, valueType); + this.key = defaultKey; + this.value = defaultValue; + } + + /** Creates a new MapEntryLite message. */ + private MapEntryLite(Metadata<K, V> metadata, K key, V value) { + this.metadata = metadata; + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + /** + * Creates a default MapEntryLite message instance. + * + * This method is used by generated code to create the default instance for + * a map entry message. The created default instance should be used to create + * new map entry messages of the same type. For each map entry message, only + * one default instance should be created. + */ + public static <K, V> MapEntryLite<K, V> newDefaultInstance( + WireFormat.FieldType keyType, K defaultKey, + WireFormat.FieldType valueType, V defaultValue) { + return new MapEntryLite<K, V>( + keyType, defaultKey, valueType, defaultValue); + } + + @Override + public void writeTo(CodedOutputStream output) throws IOException { + writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output); + writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output); + } + + private void writeField( + int number, WireFormat.FieldType type, Object value, + CodedOutputStream output) throws IOException { + output.writeTag(number, type.getWireType()); + FieldSet.writeElementNoTag(output, type, value); + } + + private volatile int cachedSerializedSize = -1; + @Override + public int getSerializedSize() { + if (cachedSerializedSize != -1) { + return cachedSerializedSize; + } + int size = 0; + size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key); + size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value); + cachedSerializedSize = size; + return size; + } + + private int getFieldSize( + int number, WireFormat.FieldType type, Object value) { + return CodedOutputStream.computeTagSize(number) + + FieldSet.computeElementSizeNoTag(type, value); + } + + /** Parsing constructor. */ + private MapEntryLite( + Metadata<K, V> metadata, + CodedInputStream input, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + K key = metadata.defaultInstance.key; + V value = metadata.defaultInstance.value; + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + if (tag == WireFormat.makeTag( + KEY_FIELD_NUMBER, metadata.keyType.getWireType())) { + key = mergeField( + input, extensionRegistry, metadata.keyType, key); + } else if (tag == WireFormat.makeTag( + VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) { + value = mergeField( + input, extensionRegistry, metadata.valueType, value); + } else { + if (!input.skipField(tag)) { + break; + } + } + } + this.metadata = metadata; + this.key = key; + this.value = value; + } catch (InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (IOException e) { + throw new InvalidProtocolBufferException(e.getMessage()) + .setUnfinishedMessage(this); + } + } + + @SuppressWarnings("unchecked") + private <T> T mergeField( + CodedInputStream input, ExtensionRegistryLite extensionRegistry, + WireFormat.FieldType type, T value) throws IOException { + switch (type) { + case MESSAGE: + MessageLite.Builder subBuilder = ((MessageLite) value).toBuilder(); + input.readMessage(subBuilder, extensionRegistry); + return (T) subBuilder.buildPartial(); + case ENUM: + return (T) (java.lang.Integer) input.readEnum(); + case GROUP: + throw new RuntimeException("Groups are not allowed in maps."); + default: + return (T) FieldSet.readPrimitiveField(input, type, true); + } + } + + @Override + public Parser<MapEntryLite<K, V>> getParserForType() { + return metadata.parser; + } + + @Override + public Builder<K, V> newBuilderForType() { + return new Builder<K, V>(metadata); + } + + @Override + public Builder<K, V> toBuilder() { + return new Builder<K, V>(metadata, key, value); + } + + @Override + public MapEntryLite<K, V> getDefaultInstanceForType() { + return metadata.defaultInstance; + } + + @Override + public boolean isInitialized() { + if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) { + return ((MessageLite) value).isInitialized(); + } + return true; + } + + /** + * Builder used to create {@link MapEntryLite} messages. + */ + public static class Builder<K, V> + extends AbstractMessageLite.Builder<Builder<K, V>> { + private final Metadata<K, V> metadata; + private K key; + private V value; + + private Builder(Metadata<K, V> metadata) { + this.metadata = metadata; + this.key = metadata.defaultInstance.key; + this.value = metadata.defaultInstance.value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public Builder<K, V> setKey(K key) { + this.key = key; + return this; + } + + public Builder<K, V> setValue(V value) { + this.value = value; + return this; + } + + public Builder<K, V> clearKey() { + this.key = metadata.defaultInstance.key; + return this; + } + + public Builder<K, V> clearValue() { + this.value = metadata.defaultInstance.value; + return this; + } + + @Override + public Builder<K, V> clear() { + this.key = metadata.defaultInstance.key; + this.value = metadata.defaultInstance.value; + return this; + } + + @Override + public MapEntryLite<K, V> build() { + MapEntryLite<K, V> result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public MapEntryLite<K, V> buildPartial() { + return new MapEntryLite<K, V>(metadata, key, value); + } + + @Override + public MessageLite getDefaultInstanceForType() { + return metadata.defaultInstance; + } + + @Override + public boolean isInitialized() { + if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) { + return ((MessageLite) value).isInitialized(); + } + return true; + } + + private Builder(Metadata<K, V> metadata, K key, V value) { + this.metadata = metadata; + this.key = key; + this.value = value; + } + + @Override + public Builder<K, V> clone() { + return new Builder<K, V>(metadata, key, value); + } + + @Override + public Builder<K, V> mergeFrom( + CodedInputStream input, ExtensionRegistryLite extensionRegistry) + throws IOException { + MapEntryLite<K, V> entry = + new MapEntryLite<K, V>(metadata, input, extensionRegistry); + this.key = entry.key; + this.value = entry.value; + return this; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/MapField.java b/java/src/main/java/com/google/protobuf/MapField.java new file mode 100644 index 00000000..5bd70dbb --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MapField.java @@ -0,0 +1,259 @@ +// 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. + +package com.google.protobuf; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Internal representation of map fields in generated messages. + * + * This class supports accessing the map field as a {@link Map} to be used in + * generated API and also supports accessing the field as a {@link List} to be + * used in reflection API. It keeps track of where the data is currently stored + * and do necessary conversions between map and list. + * + * This class is a protobuf implementation detail. Users shouldn't use this + * class directly. + * + * THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap() + * and getList() concurrently in multiple threads. If write-access is needed, + * all access must be synchronized. + */ +public class MapField<K, V> { + /** + * Indicates where the data of this map field is currently stored. + * + * MAP: Data is stored in mapData. + * LIST: Data is stored in listData. + * BOTH: mapData and listData have the same data. + * + * When the map field is accessed (through generated API or reflection API), + * it will shift between these 3 modes: + * + * getMap() getList() getMutableMap() getMutableList() + * MAP MAP BOTH MAP LIST + * LIST BOTH LIST MAP LIST + * BOTH BOTH BOTH MAP LIST + * + * As the map field changes its mode, the list/map reference returned in a + * previous method call may be invalidated. + */ + private enum StorageMode {MAP, LIST, BOTH} + + private volatile StorageMode mode; + private Map<K, V> mapData; + private List<Message> listData; + + // Convert between a map entry Message and a key-value pair. + private static interface Converter<K, V> { + Message convertKeyAndValueToMessage(K key, V value); + void convertMessageToKeyAndValue(Message message, Map<K, V> map); + + Message getMessageDefaultInstance(); + } + + private static class ImmutableMessageConverter<K, V> implements Converter<K, V> { + private final MapEntry<K, V> defaultEntry; + public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) { + this.defaultEntry = defaultEntry; + } + + public Message convertKeyAndValueToMessage(K key, V value) { + return defaultEntry.newBuilderForType().setKey(key).setValue(value).build(); + } + + public void convertMessageToKeyAndValue(Message message, Map<K, V> map) { + MapEntry<K, V> entry = (MapEntry<K, V>) message; + map.put(entry.getKey(), entry.getValue()); + } + + public Message getMessageDefaultInstance() { + return defaultEntry; + } + } + + + private final Converter<K, V> converter; + + private MapField( + Converter<K, V> converter, + StorageMode mode, + Map<K, V> mapData, + List<Message> listData) { + this.converter = converter; + this.mode = mode; + this.mapData = mapData; + this.listData = listData; + } + + private MapField( + MapEntry<K, V> defaultEntry, + StorageMode mode, + Map<K, V> mapData, + List<Message> listData) { + this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData, listData); + } + + + /** Returns an immutable empty MapField. */ + public static <K, V> MapField<K, V> emptyMapField( + MapEntry<K, V> defaultEntry) { + return new MapField<K, V>( + defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap(), null); + } + + + /** Creates a new mutable empty MapField. */ + public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) { + return new MapField<K, V>( + defaultEntry, StorageMode.MAP, new HashMap<K, V>(), null); + } + + + private Message convertKeyAndValueToMessage(K key, V value) { + return converter.convertKeyAndValueToMessage(key, value); + } + + @SuppressWarnings("unchecked") + private void convertMessageToKeyAndValue(Message message, Map<K, V> map) { + converter.convertMessageToKeyAndValue(message, map); + } + + private List<Message> convertMapToList(Map<K, V> mapData) { + List<Message> listData = new ArrayList<Message>(); + for (Map.Entry<K, V> entry : mapData.entrySet()) { + listData.add( + convertKeyAndValueToMessage( + entry.getKey(), entry.getValue())); + } + return listData; + } + + private Map<K, V> convertListToMap(List<Message> listData) { + Map<K, V> mapData = new HashMap<K, V>(); + for (Message item : listData) { + convertMessageToKeyAndValue(item, mapData); + } + return mapData; + } + + /** Returns the content of this MapField as a read-only Map. */ + public Map<K, V> getMap() { + if (mode == StorageMode.LIST) { + synchronized (this) { + if (mode == StorageMode.LIST) { + mapData = convertListToMap(listData); + mode = StorageMode.BOTH; + } + } + } + return Collections.unmodifiableMap(mapData); + } + + /** Gets a mutable Map view of this MapField. */ + public Map<K, V> getMutableMap() { + if (mode != StorageMode.MAP) { + if (mode == StorageMode.LIST) { + mapData = convertListToMap(listData); + } + listData = null; + mode = StorageMode.MAP; + } + return mapData; + } + + public void mergeFrom(MapField<K, V> other) { + getMutableMap().putAll(MapFieldLite.copy(other.getMap())); + } + + public void clear() { + mapData = new HashMap<K, V>(); + mode = StorageMode.MAP; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object object) { + if (!(object instanceof MapField)) { + return false; + } + MapField<K, V> other = (MapField<K, V>) object; + return MapFieldLite.<K, V>equals(getMap(), other.getMap()); + } + + @Override + public int hashCode() { + return MapFieldLite.<K, V>calculateHashCodeForMap(getMap()); + } + + /** Returns a deep copy of this MapField. */ + public MapField<K, V> copy() { + return new MapField<K, V>( + converter, StorageMode.MAP, MapFieldLite.copy(getMap()), null); + } + + /** Gets the content of this MapField as a read-only List. */ + List<Message> getList() { + if (mode == StorageMode.MAP) { + synchronized (this) { + if (mode == StorageMode.MAP) { + listData = convertMapToList(mapData); + mode = StorageMode.BOTH; + } + } + } + return Collections.unmodifiableList(listData); + } + + /** Gets a mutable List view of this MapField. */ + List<Message> getMutableList() { + if (mode != StorageMode.LIST) { + if (mode == StorageMode.MAP) { + listData = convertMapToList(mapData); + } + mapData = null; + mode = StorageMode.LIST; + } + return listData; + } + + /** + * Gets the default instance of the message stored in the list view of this + * map field. + */ + Message getMapEntryMessageDefaultInstance() { + return converter.getMessageDefaultInstance(); + } +} diff --git a/java/src/main/java/com/google/protobuf/MapFieldLite.java b/java/src/main/java/com/google/protobuf/MapFieldLite.java new file mode 100644 index 00000000..eea36d9e --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MapFieldLite.java @@ -0,0 +1,182 @@ +// 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. + +package com.google.protobuf; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Internal representation of map fields in generated lite-runtime messages. + * + * This class is a protobuf implementation detail. Users shouldn't use this + * class directly. + */ +public class MapFieldLite<K, V> { + private Map<K, V> mapData; + + private MapFieldLite(Map<K, V> mapData) { + this.mapData = mapData; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static final MapFieldLite EMPTY_MAP_FIELD = + new MapFieldLite(Collections.emptyMap()); + + /** Returns an singleton immutable empty MapFieldLite instance. */ + @SuppressWarnings({"unchecked", "cast"}) + public static <K, V> MapFieldLite<K, V> emptyMapField() { + return (MapFieldLite<K, V>) EMPTY_MAP_FIELD; + } + + /** Creates a new MapFieldLite instance. */ + public static <K, V> MapFieldLite<K, V> newMapField() { + return new MapFieldLite<K, V>(new HashMap<K, V>()); + } + + /** Gets the content of this MapField as a read-only Map. */ + public Map<K, V> getMap() { + return Collections.unmodifiableMap(mapData); + } + + /** Gets a mutable Map view of this MapField. */ + public Map<K, V> getMutableMap() { + return mapData; + } + + public void mergeFrom(MapFieldLite<K, V> other) { + mapData.putAll(copy(other.mapData)); + } + + public void clear() { + mapData.clear(); + } + + private static boolean equals(Object a, Object b) { + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + return a.equals(b); + } + + /** + * Checks whether two {@link Map}s are equal. We don't use the default equals + * method of {@link Map} because it compares by identity not by content for + * byte arrays. + */ + static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) { + if (a == b) { + return true; + } + if (a.size() != a.size()) { + return false; + } + for (Map.Entry<K, V> entry : a.entrySet()) { + if (!b.containsKey(entry.getKey())) { + return false; + } + if (!equals(entry.getValue(), b.get(entry.getKey()))) { + return false; + } + } + return true; + } + + /** + * Checks whether two map fields are equal. + */ + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object object) { + if (!(object instanceof MapFieldLite)) { + return false; + } + MapFieldLite<K, V> other = (MapFieldLite<K, V>) object; + return equals(mapData, other.mapData); + } + + private static int calculateHashCodeForObject(Object a) { + if (a instanceof byte[]) { + return LiteralByteString.hashCode((byte[]) a); + } + if (a instanceof Internal.EnumLite) { + return Internal.hashEnum((Internal.EnumLite) a); + } + return a.hashCode(); + } + + /** + * Calculates the hash code for a {@link Map}. We don't use the default hash + * code method of {@link Map} because for byte arrays and protobuf enums it + * use {@link Object#hashCode()}. + */ + static <K, V> int calculateHashCodeForMap(Map<K, V> a) { + int result = 0; + for (Map.Entry<K, V> entry : a.entrySet()) { + result += calculateHashCodeForObject(entry.getKey()) + ^ calculateHashCodeForObject(entry.getValue()); + } + return result; + } + + @Override + public int hashCode() { + return calculateHashCodeForMap(mapData); + } + + private static Object copy(Object object) { + if (object instanceof byte[]) { + byte[] data = (byte[]) object; + return Arrays.copyOf(data, data.length); + } + return object; + } + + /** + * Makes a deep copy of a {@link Map}. Immutable objects in the map will be + * shared (e.g., integers, strings, immutable messages) and mutable ones will + * have a copy (e.g., byte arrays, mutable messages). + */ + @SuppressWarnings("unchecked") + static <K, V> Map<K, V> copy(Map<K, V> map) { + Map<K, V> result = new HashMap<K, V>(); + for (Map.Entry<K, V> entry : map.entrySet()) { + result.put(entry.getKey(), (V) copy(entry.getValue())); + } + return result; + } + + /** Returns a deep copy of this map field. */ + public MapFieldLite<K, V> copy() { + return new MapFieldLite<K, V>(copy(mapData)); + } +} diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index 5673d3bb..5c75b58b 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -168,6 +168,25 @@ public interface Message extends MessageLite, MessageOrBuilder { Builder getFieldBuilder(Descriptors.FieldDescriptor field); /** + * Get a nested builder instance for the given repeated field instance. + * <p> + * Normally, we hold a reference to the immutable message object for the + * message type field. Some implementations(the generated message builders), + * however, can also hold a reference to the builder object (a nested + * builder) for the field. + * <p> + * If the field is already backed up by a nested builder, the nested builder + * will be returned. Otherwise, a new field builder will be created and + * returned. The original message field (if exist) will be merged into the + * field builder, which will then be nested into its parent builder. + * <p> + * NOTE: implementations that do not support nested builders will throw + * <code>UnsupportedException</code>. + */ + Builder getRepeatedFieldBuilder(Descriptors.FieldDescriptor field, + int index); + + /** * Sets a field to the given value. The value must be of the correct type * for this field, i.e. the same type that * {@link Message#getField(Descriptors.FieldDescriptor)} would return. diff --git a/java/src/main/java/com/google/protobuf/MessageReflection.java b/java/src/main/java/com/google/protobuf/MessageReflection.java index edb5b5c2..06e3c99b 100644 --- a/java/src/main/java/com/google/protobuf/MessageReflection.java +++ b/java/src/main/java/com/google/protobuf/MessageReflection.java @@ -752,13 +752,18 @@ class MessageReflection { if (field.getLiteType() == WireFormat.FieldType.ENUM) { while (input.getBytesUntilLimit() > 0) { final int rawValue = input.readEnum(); - final Object value = field.getEnumType().findValueByNumber(rawValue); - if (value == null) { - // If the number isn't recognized as a valid value for this - // enum, drop it (don't even add it to unknownFields). - return true; + if (field.getFile().supportsUnknownEnumValue()) { + target.addRepeatedField(field, + field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue)); + } else { + final Object value = field.getEnumType().findValueByNumber(rawValue); + if (value == null) { + // If the number isn't recognized as a valid value for this + // enum, drop it (don't even add it to unknownFields). + return true; + } + target.addRepeatedField(field, value); } - target.addRepeatedField(field, value); } } else { while (input.getBytesUntilLimit() > 0) { @@ -783,12 +788,16 @@ class MessageReflection { } case ENUM: final int rawValue = input.readEnum(); - value = field.getEnumType().findValueByNumber(rawValue); - // If the number isn't recognized as a valid value for this enum, - // drop it. - if (value == null) { - unknownFields.mergeVarintField(fieldNumber, rawValue); - return true; + if (field.getFile().supportsUnknownEnumValue()) { + value = field.getEnumType().findValueByNumberCreatingIfUnknown(rawValue); + } else { + value = field.getEnumType().findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + unknownFields.mergeVarintField(fieldNumber, rawValue); + return true; + } } break; default: diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java index 22760d3a..63535ac8 100644 --- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java +++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java @@ -367,22 +367,28 @@ public class RepeatedFieldBuilder throw new NullPointerException(); } } + + // If we can inspect the size, we can more efficiently add messages. + int size = -1; if (values instanceof Collection) { @SuppressWarnings("unchecked") final Collection<MType> collection = (Collection<MType>) values; if (collection.size() == 0) { return this; } - ensureMutableMessageList(); - for (MType value : values) { - addMessage(value); - } - } else { - ensureMutableMessageList(); - for (MType value : values) { - addMessage(value); - } + size = collection.size(); + } + ensureMutableMessageList(); + + if (size >= 0 && messages instanceof ArrayList) { + ((ArrayList<MType>) messages) + .ensureCapacity(messages.size() + size); } + + for (MType value : values) { + addMessage(value); + } + onChanged(); incrementModCounts(); return this; diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 57d0ca68..44674811 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -1211,6 +1211,7 @@ public final class TextFormat { private SingularOverwritePolicy singularOverwritePolicy = SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES; + /** * Sets parser behavior when a non-repeated field appears more than once. */ @@ -1418,6 +1419,12 @@ public final class TextFormat { } else { consumeFieldValue(tokenizer, extensionRegistry, target, field, extension); } + + // For historical reasons, fields may optionally be separated by commas or + // semicolons. + if (!tokenizer.tryConsume(";")) { + tokenizer.tryConsume(","); + } } /** @@ -1656,10 +1663,9 @@ public final class TextFormat { case '\'': builder.append("\\\'"); break; case '"' : builder.append("\\\""); break; default: - // Note: Bytes with the high-order bit set should be escaped. Since - // bytes are signed, such bytes will compare less than 0x20, hence - // the following line is correct. - if (b >= 0x20) { + // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are + // printable. Other byte values must be escaped. + if (b >= 0x20 && b <= 0x7e) { builder.append((char) b); } else { builder.append('\\'); diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java index 99de3732..7cd2250e 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -431,6 +431,21 @@ public final class UnknownFieldSet implements MessageLite { return this; } + + /** + * Convenience method for merging a length-delimited field. + * + * <p>For use by generated code only. + */ + public Builder mergeLengthDelimitedField( + final int number, final ByteString value) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + getFieldBuilder(number).addLengthDelimited(value); + return this; + } + /** Check if the given field number is present in the set. */ public boolean hasField(final int number) { if (number == 0) { diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java new file mode 100644 index 00000000..7ea84022 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java @@ -0,0 +1,297 @@ +// 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. + +package com.google.protobuf; + +import java.io.IOException; + +/** + * {@code UnknownFieldSetLite} is used to keep track of fields which were seen + * when parsing a protocol message but whose field numbers or types are + * unrecognized. This most frequently occurs when new fields are added to a + * message type and then messages containing those fields are read by old + * software that was compiled before the new types were added. + * + * <p>For use by generated code only. + * + * @author dweis@google.com (Daniel Weis) + */ +public final class UnknownFieldSetLite { + + private static final UnknownFieldSetLite DEFAULT_INSTANCE = + new UnknownFieldSetLite(ByteString.EMPTY); + + /** + * Get an empty {@code UnknownFieldSetLite}. + * + * <p>For use by generated code only. + */ + public static UnknownFieldSetLite getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + /** + * Create a new {@link Builder}. + * + * <p>For use by generated code only. + */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Returns an {@code UnknownFieldSetLite} that is the composite of {@code first} and + * {@code second}. + */ + static UnknownFieldSetLite concat(UnknownFieldSetLite first, UnknownFieldSetLite second) { + return new UnknownFieldSetLite(first.byteString.concat(second.byteString)); + } + + /** + * The internal representation of the unknown fields. + */ + private final ByteString byteString; + + /** + * Constructs the {@code UnknownFieldSetLite} as a thin wrapper around {@link ByteString}. + */ + private UnknownFieldSetLite(ByteString byteString) { + this.byteString = byteString; + } + + /** + * Serializes the set and writes it to {@code output}. + * + * <p>For use by generated code only. + */ + public void writeTo(CodedOutputStream output) throws IOException { + output.writeRawBytes(byteString); + } + + + /** + * Get the number of bytes required to encode this set. + * + * <p>For use by generated code only. + */ + public int getSerializedSize() { + return byteString.size(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof UnknownFieldSetLite) { + return byteString.equals(((UnknownFieldSetLite) obj).byteString); + } + + return false; + } + + @Override + public int hashCode() { + return byteString.hashCode(); + } + + /** + * Builder for {@link UnknownFieldSetLite}s. + * + * <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. + * + * <p>For use by generated code only. + */ + public static final class Builder { + + private ByteString.Output byteStringOutput; + private CodedOutputStream output; + private boolean built; + + /** + * Constructs a {@code Builder}. Lazily initialized by + * {@link #ensureInitializedButNotBuilt()}. + */ + private Builder() {} + + /** + * Ensures internal state is initialized for use. + */ + private void ensureInitializedButNotBuilt() { + if (built) { + throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders."); + } + + if (output == null && byteStringOutput == null) { + byteStringOutput = ByteString.newOutput(100 /* initialCapacity */); + output = CodedOutputStream.newInstance(byteStringOutput); + } + } + + /** + * Parse a single field from {@code input} and merge it into this set. + * + * <p>For use by generated code only. + * + * @param tag The field's tag number, which was already parsed. + * @return {@code false} if the tag is an end group tag. + */ + public boolean mergeFieldFrom(final int tag, final CodedInputStream input) + throws IOException { + ensureInitializedButNotBuilt(); + + final int fieldNumber = WireFormat.getTagFieldNumber(tag); + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + output.writeUInt64(fieldNumber, input.readInt64()); + return true; + case WireFormat.WIRETYPE_FIXED32: + output.writeFixed32(fieldNumber, input.readFixed32()); + return true; + case WireFormat.WIRETYPE_FIXED64: + output.writeFixed64(fieldNumber, input.readFixed64()); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + output.writeBytes(fieldNumber, input.readBytes()); + return true; + case WireFormat.WIRETYPE_START_GROUP: + final Builder subBuilder = newBuilder(); + subBuilder.mergeFrom(input); + input.checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + + output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + subBuilder.build().writeTo(output); + output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + return true; + case WireFormat.WIRETYPE_END_GROUP: + return false; + default: + throw InvalidProtocolBufferException.invalidWireType(); + } + } + + /** + * Convenience method for merging a new field containing a single varint + * value. This is used in particular when an unknown enum value is + * encountered. + * + * <p>For use by generated code only. + */ + public Builder mergeVarintField(int fieldNumber, int value) { + if (fieldNumber == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + ensureInitializedButNotBuilt(); + try { + output.writeUInt64(fieldNumber, value); + } catch (IOException e) { + // Should never happen. + } + return this; + } + + /** + * Convenience method for merging a length-delimited field. + * + * <p>For use by generated code only. + */ + public Builder mergeLengthDelimitedField( + final int fieldNumber, final ByteString value) { + if (fieldNumber == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + ensureInitializedButNotBuilt(); + try { + output.writeBytes(fieldNumber, value); + } catch (IOException e) { + // Should never happen. + } + return this; + } + + /** + * Build the {@link UnknownFieldSetLite} and return it. + * + * <p>Once {@code build()} has been called, the {@code Builder} will no + * longer be usable. Calling any method after {@code build()} will result + * in undefined behavior and can cause a {@code IllegalStateException} to be + * thrown. + * + * <p>For use by generated code only. + */ + public UnknownFieldSetLite build() { + if (built) { + throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders."); + } + + built = true; + + final UnknownFieldSetLite result; + // If we were never initialized, no data was written. + if (output == null) { + result = getDefaultInstance(); + } else { + try { + output.flush(); + } catch (IOException e) { + // Should never happen. + } + ByteString byteString = byteStringOutput.toByteString(); + if (byteString.isEmpty()) { + result = getDefaultInstance(); + } else { + result = new UnknownFieldSetLite(byteString); + } + } + + // Allow for garbage collection. + output = null; + byteStringOutput = null; + return result; + } + + /** + * Parse an entire message from {@code input} and merge its fields into + * this set. + */ + private Builder mergeFrom(final CodedInputStream input) throws IOException { + // Ensures initialization in mergeFieldFrom. + while (true) { + final int tag = input.readTag(); + if (tag == 0 || !mergeFieldFrom(tag, input)) { + break; + } + } + return this; + } + } +} |