diff options
Diffstat (limited to 'java/src/main')
58 files changed, 2016 insertions, 12807 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index ae9d5e3..b059bc9 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,13 +30,12 @@ package com.google.protobuf; +import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.OneofDescriptor; -import com.google.protobuf.Internal.EnumLite; -import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -48,30 +47,37 @@ import java.util.Map; */ public abstract class AbstractMessage extends AbstractMessageLite implements Message { + @SuppressWarnings("unchecked") public boolean isInitialized() { - return MessageReflection.isInitialized(this); - } - - - public List<String> findInitializationErrors() { - return MessageReflection.findMissingFields(this); - } - - public String getInitializationErrorString() { - return MessageReflection.delimitWithCommas(findInitializationErrors()); - } + // Check that all required fields are present. + for (final FieldDescriptor field : getDescriptorForType().getFields()) { + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + } - /** TODO(jieluo): Clear it when all subclasses have implemented this method. */ - @Override - public boolean hasOneof(OneofDescriptor oneof) { - throw new UnsupportedOperationException("hasOneof() is not implemented."); - } + // Check that embedded messages are initialized. + for (final Map.Entry<FieldDescriptor, Object> entry : + getAllFields().entrySet()) { + final FieldDescriptor field = entry.getKey(); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + for (final Message element : (List<Message>) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (!((Message) entry.getValue()).isInitialized()) { + return false; + } + } + } + } - /** TODO(jieluo): Clear it when all subclasses have implemented this method. */ - @Override - public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { - throw new UnsupportedOperationException( - "getOneofFieldDescriptor() is not implemented."); + return true; } @Override @@ -80,7 +86,28 @@ public abstract class AbstractMessage extends AbstractMessageLite } public void writeTo(final CodedOutputStream output) throws IOException { - MessageReflection.writeMessageTo(this, output, false); + final boolean isMessageSet = + getDescriptorForType().getOptions().getMessageSetWireFormat(); + + for (final Map.Entry<FieldDescriptor, Object> entry : + getAllFields().entrySet()) { + final FieldDescriptor field = entry.getKey(); + final Object value = entry.getValue(); + if (isMessageSet && field.isExtension() && + field.getType() == FieldDescriptor.Type.MESSAGE && + !field.isRepeated()) { + output.writeMessageSetExtension(field.getNumber(), (Message) value); + } else { + FieldSet.writeField(field, value, output); + } + } + + final UnknownFieldSet unknownFields = getUnknownFields(); + if (isMessageSet) { + unknownFields.writeAsMessageSetTo(output); + } else { + unknownFields.writeTo(output); + } } private int memoizedSize = -1; @@ -91,8 +118,33 @@ public abstract class AbstractMessage extends AbstractMessageLite return size; } - memoizedSize = MessageReflection.getSerializedSize(this); - return memoizedSize; + size = 0; + final boolean isMessageSet = + getDescriptorForType().getOptions().getMessageSetWireFormat(); + + for (final Map.Entry<FieldDescriptor, Object> entry : + getAllFields().entrySet()) { + final FieldDescriptor field = entry.getKey(); + final Object value = entry.getValue(); + if (isMessageSet && field.isExtension() && + field.getType() == FieldDescriptor.Type.MESSAGE && + !field.isRepeated()) { + size += CodedOutputStream.computeMessageSetExtensionSize( + field.getNumber(), (Message) value); + } else { + size += FieldSet.computeFieldSize(field, value); + } + } + + final UnknownFieldSet unknownFields = getUnknownFields(); + if (isMessageSet) { + size += unknownFields.getSerializedSizeAsMessageSet(); + } else { + size += unknownFields.getSerializedSize(); + } + + memoizedSize = size; + return size; } @Override @@ -107,117 +159,18 @@ public abstract class AbstractMessage extends AbstractMessageLite if (getDescriptorForType() != otherMessage.getDescriptorForType()) { return false; } - return compareFields(getAllFields(), otherMessage.getAllFields()) && + return getAllFields().equals(otherMessage.getAllFields()) && getUnknownFields().equals(otherMessage.getUnknownFields()); } @Override public int hashCode() { - int hash = memoizedHashCode; - if (hash == 0) { - hash = 41; - hash = (19 * hash) + getDescriptorForType().hashCode(); - hash = hashFields(hash, getAllFields()); - hash = (29 * hash) + getUnknownFields().hashCode(); - memoizedHashCode = hash; - } + int hash = 41; + hash = (19 * hash) + getDescriptorForType().hashCode(); + hash = (53 * hash) + getAllFields().hashCode(); + hash = (29 * hash) + getUnknownFields().hashCode(); return hash; } - - private static ByteString toByteString(Object value) { - if (value instanceof byte[]) { - return ByteString.copyFrom((byte[]) value); - } else { - return (ByteString) value; - } - } - - /** - * Compares two bytes fields. The parameters must be either a byte array or a - * ByteString object. They can be of different type though. - */ - private static boolean compareBytes(Object a, Object b) { - if (a instanceof byte[] && b instanceof byte[]) { - return Arrays.equals((byte[])a, (byte[])b); - } - return toByteString(a).equals(toByteString(b)); - } - - /** - * Compares two set of fields. - * This method is used to implement {@link AbstractMessage#equals(Object)} - * and {@link AbstractMutableMessage#equals(Object)}. It takes special care - * of bytes fields because immutable messages and mutable messages use - * different Java type to reprensent a bytes field and this method should be - * able to compare immutable messages, mutable messages and also an immutable - * message to a mutable message. - */ - static boolean compareFields(Map<FieldDescriptor, Object> a, - Map<FieldDescriptor, Object> b) { - if (a.size() != b.size()) { - return false; - } - for (FieldDescriptor descriptor : a.keySet()) { - if (!b.containsKey(descriptor)) { - return false; - } - Object value1 = a.get(descriptor); - Object value2 = b.get(descriptor); - if (descriptor.getType() == FieldDescriptor.Type.BYTES) { - if (descriptor.isRepeated()) { - List list1 = (List) value1; - List list2 = (List) value2; - if (list1.size() != list2.size()) { - return false; - } - for (int i = 0; i < list1.size(); i++) { - if (!compareBytes(list1.get(i), list2.get(i))) { - return false; - } - } - } else { - // Compares a singular bytes field. - if (!compareBytes(value1, value2)) { - return false; - } - } - } else { - // Compare non-bytes fields. - if (!value1.equals(value2)) { - return false; - } - } - } - return true; - } - - /** Get a hash code for given fields and values, using the given seed. */ - @SuppressWarnings("unchecked") - protected static int hashFields(int hash, Map<FieldDescriptor, Object> map) { - for (Map.Entry<FieldDescriptor, Object> entry : map.entrySet()) { - FieldDescriptor field = entry.getKey(); - Object value = entry.getValue(); - hash = (37 * hash) + field.getNumber(); - if (field.getType() != FieldDescriptor.Type.ENUM){ - hash = (53 * hash) + value.hashCode(); - } else if (field.isRepeated()) { - List<? extends EnumLite> list = (List<? extends EnumLite>) value; - hash = (53 * hash) + Internal.hashEnumList(list); - } else { - hash = (53 * hash) + Internal.hashEnum((EnumLite) value); - } - } - return hash; - } - - /** - * Package private helper method for AbstractParser to create - * UninitializedMessageException with missing field information. - */ - @Override - UninitializedMessageException newUninitializedMessageException() { - return Builder.newUninitializedMessageException(this); - } // ================================================================= @@ -234,25 +187,6 @@ public abstract class AbstractMessage extends AbstractMessageLite @Override public abstract BuilderType clone(); - /** TODO(jieluo): Clear it when all subclasses have implemented this method. */ - @Override - public boolean hasOneof(OneofDescriptor oneof) { - throw new UnsupportedOperationException("hasOneof() is not implemented."); - } - - /** TODO(jieluo): Clear it when all subclasses have implemented this method. */ - @Override - public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { - throw new UnsupportedOperationException( - "getOneofFieldDescriptor() is not implemented."); - } - - /** TODO(jieluo): Clear it when all subclasses have implemented this method. */ - @Override - public BuilderType clearOneof(OneofDescriptor oneof) { - throw new UnsupportedOperationException("clearOneof() is not implemented."); - } - public BuilderType clear() { for (final Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { @@ -261,14 +195,6 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } - public List<String> findInitializationErrors() { - return MessageReflection.findMissingFields(this); - } - - public String getInitializationErrorString() { - return MessageReflection.delimitWithCommas(findInitializationErrors()); - } - public BuilderType mergeFrom(final Message other) { if (other.getDescriptorForType() != getDescriptorForType()) { throw new IllegalArgumentException( @@ -331,13 +257,8 @@ public abstract class AbstractMessage extends AbstractMessageLite break; } - MessageReflection.BuilderAdapter builderAdapter = - new MessageReflection.BuilderAdapter(this); - if (!MessageReflection.mergeFieldFrom(input, unknownFields, - extensionRegistry, - getDescriptorForType(), - builderAdapter, - tag)) { + if (!mergeFieldFrom(input, unknownFields, extensionRegistry, + this, tag)) { // end group tag break; } @@ -346,6 +267,272 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } + /** + * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, + * ExtensionRegistryLite, Message.Builder)}, but parses a single field. + * Package-private because it is used by GeneratedMessage.ExtendableMessage. + * @param tag The tag, which should have already been read. + * @return {@code true} unless the tag is an end-group tag. + */ + @SuppressWarnings("unchecked") + static boolean mergeFieldFrom( + final CodedInputStream input, + final UnknownFieldSet.Builder unknownFields, + final ExtensionRegistryLite extensionRegistry, + final Message.Builder builder, + final int tag) throws IOException { + final Descriptor type = builder.getDescriptorForType(); + + if (type.getOptions().getMessageSetWireFormat() && + tag == WireFormat.MESSAGE_SET_ITEM_TAG) { + mergeMessageSetExtensionFromCodedStream( + input, unknownFields, extensionRegistry, builder); + return true; + } + + final int wireType = WireFormat.getTagWireType(tag); + final int fieldNumber = WireFormat.getTagFieldNumber(tag); + + final FieldDescriptor field; + Message defaultInstance = null; + + if (type.isExtensionNumber(fieldNumber)) { + // extensionRegistry may be either ExtensionRegistry or + // ExtensionRegistryLite. Since the type we are parsing is a full + // message, only a full ExtensionRegistry could possibly contain + // extensions of it. Otherwise we will treat the registry as if it + // were empty. + if (extensionRegistry instanceof ExtensionRegistry) { + final ExtensionRegistry.ExtensionInfo extension = + ((ExtensionRegistry) extensionRegistry) + .findExtensionByNumber(type, fieldNumber); + if (extension == null) { + field = null; + } else { + field = extension.descriptor; + defaultInstance = extension.defaultInstance; + if (defaultInstance == null && + field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalStateException( + "Message-typed extension lacked default instance: " + + field.getFullName()); + } + } + } else { + field = null; + } + } else { + field = type.findFieldByNumber(fieldNumber); + } + + boolean unknown = false; + boolean packed = false; + if (field == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldSet.getWireFormatForFieldType( + field.getLiteType(), + false /* isPacked */)) { + packed = false; + } else if (field.isPackable() && + wireType == FieldSet.getWireFormatForFieldType( + field.getLiteType(), + true /* isPacked */)) { + packed = true; + } else { + unknown = true; // Unknown wire type. + } + + if (unknown) { // Unknown field or wrong wire type. Skip. + return unknownFields.mergeFieldFrom(tag, input); + } + + if (packed) { + final int length = input.readRawVarint32(); + final int limit = input.pushLimit(length); + 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; + } + builder.addRepeatedField(field, value); + } + } else { + while (input.getBytesUntilLimit() > 0) { + final Object value = + FieldSet.readPrimitiveField(input, field.getLiteType()); + builder.addRepeatedField(field, value); + } + } + input.popLimit(limit); + } else { + final Object value; + switch (field.getType()) { + case GROUP: { + final Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readGroup(field.getNumber(), subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + case MESSAGE: { + final Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readMessage(subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + 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; + } + break; + default: + value = FieldSet.readPrimitiveField(input, field.getLiteType()); + break; + } + + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } + } + + return true; + } + + /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ + private static void mergeMessageSetExtensionFromCodedStream( + final CodedInputStream input, + final UnknownFieldSet.Builder unknownFields, + final ExtensionRegistryLite extensionRegistry, + final Message.Builder builder) throws IOException { + final Descriptor type = builder.getDescriptorForType(); + + // The wire format for MessageSet is: + // message MessageSet { + // repeated group Item = 1 { + // required int32 typeId = 2; + // required bytes message = 3; + // } + // } + // "typeId" is the extension's field number. The extension can only be + // a message type, where "message" contains the encoded bytes of that + // message. + // + // In practice, we will probably never see a MessageSet item in which + // the message appears before the type ID, or where either field does not + // appear exactly once. However, in theory such cases are valid, so we + // should be prepared to accept them. + + int typeId = 0; + ByteString rawBytes = null; // If we encounter "message" before "typeId" + Message.Builder subBuilder = null; + FieldDescriptor field = null; + + while (true) { + final int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { + typeId = input.readUInt32(); + // Zero is not a valid type ID. + if (typeId != 0) { + final ExtensionRegistry.ExtensionInfo extension; + + // extensionRegistry may be either ExtensionRegistry or + // ExtensionRegistryLite. Since the type we are parsing is a full + // message, only a full ExtensionRegistry could possibly contain + // extensions of it. Otherwise we will treat the registry as if it + // were empty. + if (extensionRegistry instanceof ExtensionRegistry) { + extension = ((ExtensionRegistry) extensionRegistry) + .findExtensionByNumber(type, typeId); + } else { + extension = null; + } + + if (extension != null) { + field = extension.descriptor; + subBuilder = extension.defaultInstance.newBuilderForType(); + final Message originalMessage = (Message)builder.getField(field); + if (originalMessage != null) { + subBuilder.mergeFrom(originalMessage); + } + if (rawBytes != null) { + // We already encountered the message. Parse it now. + subBuilder.mergeFrom( + CodedInputStream.newInstance(rawBytes.newInput())); + rawBytes = null; + } + } else { + // Unknown extension number. If we already saw data, put it + // in rawBytes. + if (rawBytes != null) { + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(rawBytes) + .build()); + rawBytes = null; + } + } + } + } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { + if (typeId == 0) { + // We haven't seen a type ID yet, so we have to store the raw bytes + // for now. + rawBytes = input.readBytes(); + } else if (subBuilder == null) { + // We don't know how to parse this. Ignore it. + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(input.readBytes()) + .build()); + } else { + // We already know the type, so we can parse directly from the input + // with no copying. Hooray! + input.readMessage(subBuilder, extensionRegistry); + } + } else { + // Unknown tag. Skip it. + if (!input.skipField(tag)) { + break; // end of group + } + } + } + + input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); + + if (subBuilder != null) { + builder.setField(field, subBuilder.build()); + } + } + public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { setUnknownFields( UnknownFieldSet.newBuilder(getUnknownFields()) @@ -354,23 +541,78 @@ public abstract class AbstractMessage extends AbstractMessageLite return (BuilderType) this; } - public Message.Builder getFieldBuilder(final FieldDescriptor field) { - throw new UnsupportedOperationException( - "getFieldBuilder() called on an unsupported message type."); - } - - public String toString() { - return TextFormat.printToString(this); - } - /** * Construct an UninitializedMessageException reporting missing fields in * the given message. */ protected static UninitializedMessageException newUninitializedMessageException(Message message) { - return new UninitializedMessageException( - MessageReflection.findMissingFields(message)); + return new UninitializedMessageException(findMissingFields(message)); + } + + /** + * Populates {@code this.missingFields} with the full "path" of each + * missing required field in the given message. + */ + private static List<String> findMissingFields(final Message message) { + final List<String> results = new ArrayList<String>(); + findMissingFields(message, "", results); + return results; + } + + /** Recursive helper implementing {@link #findMissingFields(Message)}. */ + private static void findMissingFields(final Message message, + final String prefix, + final List<String> results) { + for (final FieldDescriptor field : + message.getDescriptorForType().getFields()) { + if (field.isRequired() && !message.hasField(field)) { + results.add(prefix + field.getName()); + } + } + + for (final Map.Entry<FieldDescriptor, Object> entry : + message.getAllFields().entrySet()) { + final FieldDescriptor field = entry.getKey(); + final Object value = entry.getValue(); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + int i = 0; + for (final Object element : (List) value) { + findMissingFields((Message) element, + subMessagePrefix(prefix, field, i++), + results); + } + } else { + if (message.hasField(field)) { + findMissingFields((Message) value, + subMessagePrefix(prefix, field, -1), + results); + } + } + } + } + } + + private static String subMessagePrefix(final String prefix, + final FieldDescriptor field, + final int index) { + final StringBuilder result = new StringBuilder(prefix); + if (field.isExtension()) { + result.append('(') + .append(field.getFullName()) + .append(')'); + } else { + result.append(field.getName()); + } + if (index != -1) { + result.append('[') + .append(index) + .append(']'); + } + result.append('.'); + return result.toString(); } // =============================================================== @@ -462,5 +704,6 @@ public abstract class AbstractMessage extends AbstractMessageLite throws IOException { return super.mergeDelimitedFrom(input, extensionRegistry); } + } } diff --git a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java index aac4fa7..9210d85 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessageLite.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -44,8 +44,6 @@ import java.util.Collection; * @author kenton@google.com Kenton Varda */ public abstract class AbstractMessageLite implements MessageLite { - protected int memoizedHashCode = 0; - public ByteString toByteString() { try { final ByteString.CodedBuilder out = @@ -93,22 +91,6 @@ public abstract class AbstractMessageLite implements MessageLite { codedOutput.flush(); } - - /** - * Package private helper method for AbstractParser to create - * UninitializedMessageException. - */ - UninitializedMessageException newUninitializedMessageException() { - return new UninitializedMessageException(this); - } - - protected static void checkByteStringIsUtf8(ByteString byteString) - throws IllegalArgumentException { - if (!byteString.isValidUtf8()) { - throw new IllegalArgumentException("Byte string is not UTF-8."); - } - } - /** * A partial implementation of the {@link Message.Builder} interface which * implements as many methods of that interface as possible in terms of @@ -321,35 +303,24 @@ public abstract class AbstractMessageLite implements MessageLite { * used by generated code. Users should ignore it. * * @throws NullPointerException if any of the elements of {@code values} is - * null. When that happens, some elements of {@code values} may have already - * been added to the result {@code list}. + * null. */ protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) { - if (values instanceof LazyStringList) { - // For StringOrByteStringLists, check the underlying elements to avoid - // forcing conversions of ByteStrings to Strings. - checkForNullValues(((LazyStringList) values).getUnderlyingElements()); - list.addAll((Collection<T>) values); - } else if (values instanceof Collection) { - checkForNullValues(values); - list.addAll((Collection<T>) values); + for (final T value : values) { + if (value == null) { + throw new NullPointerException(); + } + } + if (values instanceof Collection) { + @SuppressWarnings("unsafe") final + Collection<T> collection = (Collection<T>) values; + list.addAll(collection); } else { for (final T value : values) { - if (value == null) { - throw new NullPointerException(); - } list.add(value); } } } - - private static void checkForNullValues(final Iterable<?> values) { - for (final Object value : values) { - if (value == null) { - throw new NullPointerException(); - } - } - } } } diff --git a/java/src/main/java/com/google/protobuf/AbstractParser.java b/java/src/main/java/com/google/protobuf/AbstractParser.java deleted file mode 100644 index 1a4c631..0000000 --- a/java/src/main/java/com/google/protobuf/AbstractParser.java +++ /dev/null @@ -1,253 +0,0 @@ -// 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.AbstractMessageLite.Builder.LimitedInputStream; - -import java.io.IOException; -import java.io.InputStream; - -/** - * A partial implementation of the {@link Parser} interface which implements - * as many methods of that interface as possible in terms of other methods. - * - * Note: This class implements all the convenience methods in the - * {@link Parser} interface. See {@link Parser} for related javadocs. - * Subclasses need to implement - * {@link Parser#parsePartialFrom(CodedInputStream, ExtensionRegistryLite)} - * - * @author liujisi@google.com (Pherl Liu) - */ -public abstract class AbstractParser<MessageType extends MessageLite> - implements Parser<MessageType> { - /** - * Creates an UninitializedMessageException for MessageType. - */ - private UninitializedMessageException - newUninitializedMessageException(MessageType message) { - if (message instanceof AbstractMessageLite) { - return ((AbstractMessageLite) message).newUninitializedMessageException(); - } - return new UninitializedMessageException(message); - } - - /** - * Helper method to check if message is initialized. - * - * @throws InvalidProtocolBufferException if it is not initialized. - * @return The message to check. - */ - private MessageType checkMessageInitialized(MessageType message) - throws InvalidProtocolBufferException { - if (message != null && !message.isInitialized()) { - throw newUninitializedMessageException(message) - .asInvalidProtocolBufferException() - .setUnfinishedMessage(message); - } - return message; - } - - private static final ExtensionRegistryLite EMPTY_REGISTRY - = ExtensionRegistryLite.getEmptyRegistry(); - - public MessageType parsePartialFrom(CodedInputStream input) - throws InvalidProtocolBufferException { - return parsePartialFrom(input, EMPTY_REGISTRY); - } - - public MessageType parseFrom(CodedInputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return checkMessageInitialized( - parsePartialFrom(input, extensionRegistry)); - } - - public MessageType parseFrom(CodedInputStream input) - throws InvalidProtocolBufferException { - return parseFrom(input, EMPTY_REGISTRY); - } - - public MessageType parsePartialFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - MessageType message; - try { - CodedInputStream input = data.newCodedInput(); - message = parsePartialFrom(input, extensionRegistry); - try { - input.checkLastTagWas(0); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(message); - } - return message; - } catch (InvalidProtocolBufferException e) { - throw e; - } - } - - public MessageType parsePartialFrom(ByteString data) - throws InvalidProtocolBufferException { - return parsePartialFrom(data, EMPTY_REGISTRY); - } - - public MessageType parseFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return checkMessageInitialized(parsePartialFrom(data, extensionRegistry)); - } - - public MessageType parseFrom(ByteString data) - throws InvalidProtocolBufferException { - return parseFrom(data, EMPTY_REGISTRY); - } - - public MessageType parsePartialFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - try { - CodedInputStream input = CodedInputStream.newInstance(data, off, len); - MessageType message = parsePartialFrom(input, extensionRegistry); - try { - input.checkLastTagWas(0); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(message); - } - return message; - } catch (InvalidProtocolBufferException e) { - throw e; - } - } - - public MessageType parsePartialFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException { - return parsePartialFrom(data, off, len, EMPTY_REGISTRY); - } - - public MessageType parsePartialFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return parsePartialFrom(data, 0, data.length, extensionRegistry); - } - - public MessageType parsePartialFrom(byte[] data) - throws InvalidProtocolBufferException { - return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY); - } - - public MessageType parseFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return checkMessageInitialized( - parsePartialFrom(data, off, len, extensionRegistry)); - } - - public MessageType parseFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException { - return parseFrom(data, off, len, EMPTY_REGISTRY); - } - - public MessageType parseFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return parseFrom(data, 0, data.length, extensionRegistry); - } - - public MessageType parseFrom(byte[] data) - throws InvalidProtocolBufferException { - return parseFrom(data, EMPTY_REGISTRY); - } - - public MessageType parsePartialFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - CodedInputStream codedInput = CodedInputStream.newInstance(input); - MessageType message = parsePartialFrom(codedInput, extensionRegistry); - try { - codedInput.checkLastTagWas(0); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(message); - } - return message; - } - - public MessageType parsePartialFrom(InputStream input) - throws InvalidProtocolBufferException { - return parsePartialFrom(input, EMPTY_REGISTRY); - } - - public MessageType parseFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return checkMessageInitialized( - parsePartialFrom(input, extensionRegistry)); - } - - public MessageType parseFrom(InputStream input) - throws InvalidProtocolBufferException { - return parseFrom(input, EMPTY_REGISTRY); - } - - public MessageType parsePartialDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - int size; - try { - int firstByte = input.read(); - if (firstByte == -1) { - return null; - } - size = CodedInputStream.readRawVarint32(firstByte, input); - } catch (IOException e) { - throw new InvalidProtocolBufferException(e.getMessage()); - } - InputStream limitedInput = new LimitedInputStream(input, size); - return parsePartialFrom(limitedInput, extensionRegistry); - } - - public MessageType parsePartialDelimitedFrom(InputStream input) - throws InvalidProtocolBufferException { - return parsePartialDelimitedFrom(input, EMPTY_REGISTRY); - } - - public MessageType parseDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - return checkMessageInitialized( - parsePartialDelimitedFrom(input, extensionRegistry)); - } - - public MessageType parseDelimitedFrom(InputStream input) - throws InvalidProtocolBufferException { - return parseDelimitedFrom(input, EMPTY_REGISTRY); - } -} diff --git a/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java b/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java index d535efb..1e81143 100644 --- a/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java +++ b/java/src/main/java/com/google/protobuf/BlockingRpcChannel.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/BlockingService.java b/java/src/main/java/com/google/protobuf/BlockingService.java index d01f0b8..ecc8009 100644 --- a/java/src/main/java/com/google/protobuf/BlockingService.java +++ b/java/src/main/java/com/google/protobuf/BlockingService.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/BoundedByteString.java b/java/src/main/java/com/google/protobuf/BoundedByteString.java deleted file mode 100644 index 2828e9c..0000000 --- a/java/src/main/java/com/google/protobuf/BoundedByteString.java +++ /dev/null @@ -1,163 +0,0 @@ -// 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.NoSuchElementException; - -/** - * This class is used to represent the substring of a {@link ByteString} over a - * single byte array. In terms of the public API of {@link ByteString}, you end - * up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link - * ByteString#substring(int, int)}. - * - * <p>This class contains most of the overhead involved in creating a substring - * from a {@link LiteralByteString}. The overhead involves some range-checking - * and two extra fields. - * - * @author carlanton@google.com (Carl Haverl) - */ -class BoundedByteString extends LiteralByteString { - - private final int bytesOffset; - private final int bytesLength; - - /** - * Creates a {@code BoundedByteString} backed by the sub-range of given array, - * without copying. - * - * @param bytes array to wrap - * @param offset index to first byte to use in bytes - * @param length number of bytes to use from bytes - * @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0}, - * or if {@code offset + length > - * bytes.length}. - */ - BoundedByteString(byte[] bytes, int offset, int length) { - super(bytes); - if (offset < 0) { - throw new IllegalArgumentException("Offset too small: " + offset); - } - if (length < 0) { - throw new IllegalArgumentException("Length too small: " + offset); - } - if ((long) offset + length > bytes.length) { - throw new IllegalArgumentException( - "Offset+Length too large: " + offset + "+" + length); - } - - this.bytesOffset = offset; - this.bytesLength = length; - } - - /** - * Gets the byte at the given index. - * Throws {@link ArrayIndexOutOfBoundsException} - * for backwards-compatibility reasons although it would more properly be - * {@link IndexOutOfBoundsException}. - * - * @param index index of byte - * @return the value - * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size - */ - @Override - public byte byteAt(int index) { - // We must check the index ourselves as we cannot rely on Java array index - // checking for substrings. - if (index < 0) { - throw new ArrayIndexOutOfBoundsException("Index too small: " + index); - } - if (index >= size()) { - throw new ArrayIndexOutOfBoundsException( - "Index too large: " + index + ", " + size()); - } - - return bytes[bytesOffset + index]; - } - - @Override - public int size() { - return bytesLength; - } - - @Override - protected int getOffsetIntoBytes() { - return bytesOffset; - } - - // ================================================================= - // ByteString -> byte[] - - @Override - protected void copyToInternal(byte[] target, int sourceOffset, - int targetOffset, int numberToCopy) { - System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target, - targetOffset, numberToCopy); - } - - // ================================================================= - // ByteIterator - - @Override - public ByteIterator iterator() { - return new BoundedByteIterator(); - } - - private class BoundedByteIterator implements ByteIterator { - - private int position; - private final int limit; - - private BoundedByteIterator() { - position = getOffsetIntoBytes(); - limit = position + size(); - } - - public boolean hasNext() { - return (position < limit); - } - - public Byte next() { - // Boxing calls Byte.valueOf(byte), which does not instantiate. - return nextByte(); - } - - public byte nextByte() { - if (position >= limit) { - throw new NoSuchElementException(); - } - return bytes[position++]; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - } -} diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index 7da5612..5fade03 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,426 +30,140 @@ package com.google.protobuf; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; /** - * Immutable sequence of bytes. Substring is supported by sharing the reference - * to the immutable underlying bytes, as with {@link String}. Concatenation is - * likewise supported without copying (long strings) by building a tree of - * pieces in {@link RopeByteString}. - * <p> - * Like {@link String}, the contents of a {@link ByteString} can never be - * observed to change, not even in the presence of a data race or incorrect - * API usage in the client code. + * Immutable array of bytes. * * @author crazybob@google.com Bob Lee * @author kenton@google.com Kenton Varda - * @author carlanton@google.com Carl Haverl - * @author martinrb@google.com Martin Buchholz */ -public abstract class ByteString implements Iterable<Byte> { - - /** - * When two strings to be concatenated have a combined length shorter than - * this, we just copy their bytes on {@link #concat(ByteString)}. - * The trade-off is copy size versus the overhead of creating tree nodes - * in {@link RopeByteString}. - */ - static final int CONCATENATE_BY_COPY_SIZE = 128; - - /** - * When copying an InputStream into a ByteString with .readFrom(), - * the chunks in the underlying rope start at 256 bytes, but double - * each iteration up to 8192 bytes. - */ - static final int MIN_READ_FROM_CHUNK_SIZE = 0x100; // 256b - static final int MAX_READ_FROM_CHUNK_SIZE = 0x2000; // 8k +public final class ByteString { + private final byte[] bytes; - /** - * Empty {@code ByteString}. - */ - public static final ByteString EMPTY = new LiteralByteString(new byte[0]); - - // This constructor is here to prevent subclassing outside of this package, - ByteString() {} + private ByteString(final byte[] bytes) { + this.bytes = bytes; + } /** - * Gets the byte at the given index. This method should be used only for - * random access to individual bytes. To access bytes sequentially, use the - * {@link ByteIterator} returned by {@link #iterator()}, and call {@link - * #substring(int, int)} first if necessary. + * Gets the byte at the given index. * - * @param index index of byte - * @return the value * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size */ - public abstract byte byteAt(int index); - - /** - * Return a {@link ByteString.ByteIterator} over the bytes in the ByteString. - * To avoid auto-boxing, you may get the iterator manually and call - * {@link ByteIterator#nextByte()}. - * - * @return the iterator - */ - public abstract ByteIterator iterator(); - - /** - * This interface extends {@code Iterator<Byte>}, so that we can return an - * unboxed {@code byte}. - */ - public interface ByteIterator extends Iterator<Byte> { - /** - * An alternative to {@link Iterator#next()} that returns an - * unboxed primitive {@code byte}. - * - * @return the next {@code byte} in the iteration - * @throws NoSuchElementException if the iteration has no more elements - */ - byte nextByte(); + public byte byteAt(final int index) { + return bytes[index]; } /** * Gets the number of bytes. - * - * @return size in bytes */ - public abstract int size(); + public int size() { + return bytes.length; + } /** * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. - * - * @return true if this is zero bytes long */ public boolean isEmpty() { - return size() == 0; + return bytes.length == 0; } // ================================================================= - // ByteString -> substring - - /** - * Return the substring from {@code beginIndex}, inclusive, to the end of the - * string. - * - * @param beginIndex start at this index - * @return substring sharing underlying data - * @throws IndexOutOfBoundsException if {@code beginIndex < 0} or - * {@code beginIndex > size()}. - */ - public ByteString substring(int beginIndex) { - return substring(beginIndex, size()); - } - - /** - * Return the substring from {@code beginIndex}, inclusive, to {@code - * endIndex}, exclusive. - * - * @param beginIndex start at this index - * @param endIndex the last character is the one before this index - * @return substring sharing underlying data - * @throws IndexOutOfBoundsException if {@code beginIndex < 0}, - * {@code endIndex > size()}, or {@code beginIndex > endIndex}. - */ - public abstract ByteString substring(int beginIndex, int endIndex); - - /** - * Tests if this bytestring starts with the specified prefix. - * Similar to {@link String#startsWith(String)} - * - * @param prefix the prefix. - * @return <code>true</code> if the byte sequence represented by the - * argument is a prefix of the byte sequence represented by - * this string; <code>false</code> otherwise. - */ - public boolean startsWith(ByteString prefix) { - return size() >= prefix.size() && - substring(0, prefix.size()).equals(prefix); - } + // byte[] -> ByteString /** - * Tests if this bytestring ends with the specified suffix. - * Similar to {@link String#endsWith(String)} - * - * @param suffix the suffix. - * @return <code>true</code> if the byte sequence represented by the - * argument is a suffix of the byte sequence represented by - * this string; <code>false</code> otherwise. + * Empty ByteString. */ - public boolean endsWith(ByteString suffix) { - return size() >= suffix.size() && - substring(size() - suffix.size()).equals(suffix); - } - - // ================================================================= - // byte[] -> ByteString + public static final ByteString EMPTY = new ByteString(new byte[0]); /** * Copies the given bytes into a {@code ByteString}. - * - * @param bytes source array - * @param offset offset in source array - * @param size number of bytes to copy - * @return new {@code ByteString} */ - public static ByteString copyFrom(byte[] bytes, int offset, int size) { - byte[] copy = new byte[size]; + public static ByteString copyFrom(final byte[] bytes, final int offset, + final int size) { + final byte[] copy = new byte[size]; System.arraycopy(bytes, offset, copy, 0, size); - return new LiteralByteString(copy); + return new ByteString(copy); } /** * Copies the given bytes into a {@code ByteString}. - * - * @param bytes to copy - * @return new {@code ByteString} */ - public static ByteString copyFrom(byte[] bytes) { + public static ByteString copyFrom(final byte[] bytes) { return copyFrom(bytes, 0, bytes.length); } /** - * Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into + * Copies {@code size} bytes from a {@code java.nio.ByteBuffer} into * a {@code ByteString}. - * - * @param bytes source buffer - * @param size number of bytes to copy - * @return new {@code ByteString} */ - public static ByteString copyFrom(ByteBuffer bytes, int size) { - byte[] copy = new byte[size]; + public static ByteString copyFrom(final ByteBuffer bytes, final int size) { + final byte[] copy = new byte[size]; bytes.get(copy); - return new LiteralByteString(copy); + return new ByteString(copy); } /** * Copies the remaining bytes from a {@code java.nio.ByteBuffer} into * a {@code ByteString}. - * - * @param bytes sourceBuffer - * @return new {@code ByteString} */ - public static ByteString copyFrom(ByteBuffer bytes) { + public static ByteString copyFrom(final ByteBuffer bytes) { return copyFrom(bytes, bytes.remaining()); } /** * Encodes {@code text} into a sequence of bytes using the named charset * and returns the result as a {@code ByteString}. - * - * @param text source string - * @param charsetName encoding to use - * @return new {@code ByteString} - * @throws UnsupportedEncodingException if the encoding isn't found */ - public static ByteString copyFrom(String text, String charsetName) + public static ByteString copyFrom(final String text, final String charsetName) throws UnsupportedEncodingException { - return new LiteralByteString(text.getBytes(charsetName)); + return new ByteString(text.getBytes(charsetName)); } /** * Encodes {@code text} into a sequence of UTF-8 bytes and returns the * result as a {@code ByteString}. - * - * @param text source string - * @return new {@code ByteString} */ - public static ByteString copyFromUtf8(String text) { + public static ByteString copyFromUtf8(final String text) { try { - return new LiteralByteString(text.getBytes("UTF-8")); + return new ByteString(text.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 not supported?", e); } } - // ================================================================= - // InputStream -> ByteString - - /** - * Completely reads the given stream's bytes into a - * {@code ByteString}, blocking if necessary until all bytes are - * read through to the end of the stream. - * - * <b>Performance notes:</b> The returned {@code ByteString} is an - * immutable tree of byte arrays ("chunks") of the stream data. The - * first chunk is small, with subsequent chunks each being double - * the size, up to 8K. If the caller knows the precise length of - * the stream and wishes to avoid all unnecessary copies and - * allocations, consider using the two-argument version of this - * method, below. - * - * @param streamToDrain The source stream, which is read completely - * but not closed. - * @return A new {@code ByteString} which is made up of chunks of - * various sizes, depending on the behavior of the underlying - * stream. - * @throws IOException IOException is thrown if there is a problem - * reading the underlying stream. - */ - public static ByteString readFrom(InputStream streamToDrain) - throws IOException { - return readFrom( - streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE); - } - - /** - * Completely reads the given stream's bytes into a - * {@code ByteString}, blocking if necessary until all bytes are - * read through to the end of the stream. - * - * <b>Performance notes:</b> The returned {@code ByteString} is an - * immutable tree of byte arrays ("chunks") of the stream data. The - * chunkSize parameter sets the size of these byte arrays. In - * particular, if the chunkSize is precisely the same as the length - * of the stream, unnecessary allocations and copies will be - * avoided. Otherwise, the chunks will be of the given size, except - * for the last chunk, which will be resized (via a reallocation and - * copy) to contain the remainder of the stream. - * - * @param streamToDrain The source stream, which is read completely - * but not closed. - * @param chunkSize The size of the chunks in which to read the - * stream. - * @return A new {@code ByteString} which is made up of chunks of - * the given size. - * @throws IOException IOException is thrown if there is a problem - * reading the underlying stream. - */ - public static ByteString readFrom(InputStream streamToDrain, int chunkSize) - throws IOException { - return readFrom(streamToDrain, chunkSize, chunkSize); - } - - // Helper method that takes the chunk size range as a parameter. - public static ByteString readFrom(InputStream streamToDrain, int minChunkSize, - int maxChunkSize) throws IOException { - Collection<ByteString> results = new ArrayList<ByteString>(); - - // copy the inbound bytes into a list of chunks; the chunk size - // grows exponentially to support both short and long streams. - int chunkSize = minChunkSize; - while (true) { - ByteString chunk = readChunk(streamToDrain, chunkSize); - if (chunk == null) { - break; - } - results.add(chunk); - chunkSize = Math.min(chunkSize * 2, maxChunkSize); - } - - return ByteString.copyFrom(results); - } - - /** - * Blocks until a chunk of the given size can be made from the - * stream, or EOF is reached. Calls read() repeatedly in case the - * given stream implementation doesn't completely fill the given - * buffer in one read() call. - * - * @return A chunk of the desired size, or else a chunk as large as - * was available when end of stream was reached. Returns null if the - * given stream had no more data in it. - */ - private static ByteString readChunk(InputStream in, final int chunkSize) - throws IOException { - final byte[] buf = new byte[chunkSize]; - int bytesRead = 0; - while (bytesRead < chunkSize) { - final int count = in.read(buf, bytesRead, chunkSize - bytesRead); - if (count == -1) { - break; - } - bytesRead += count; - } - - if (bytesRead == 0) { - return null; - } else { - return ByteString.copyFrom(buf, 0, bytesRead); - } - } - - // ================================================================= - // Multiple ByteStrings -> One ByteString - - /** - * Concatenate the given {@code ByteString} to this one. Short concatenations, - * of total size smaller than {@link ByteString#CONCATENATE_BY_COPY_SIZE}, are - * produced by copying the underlying bytes (as per Rope.java, <a - * href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf"> - * BAP95 </a>. In general, the concatenate involves no copying. - * - * @param other string to concatenate - * @return a new {@code ByteString} instance - */ - public ByteString concat(ByteString other) { - int thisSize = size(); - int otherSize = other.size(); - if ((long) thisSize + otherSize >= Integer.MAX_VALUE) { - throw new IllegalArgumentException("ByteString would be too long: " + - thisSize + "+" + otherSize); - } - - return RopeByteString.concatenate(this, other); - } - /** - * Concatenates all byte strings in the iterable and returns the result. - * This is designed to run in O(list size), not O(total bytes). + * Concatenates all byte strings in the list and returns the result. * * <p>The returned {@code ByteString} is not necessarily a unique object. * If the list is empty, the returned object is the singleton empty * {@code ByteString}. If the list has only one element, that * {@code ByteString} will be returned without copying. - * - * @param byteStrings strings to be concatenated - * @return new {@code ByteString} */ - public static ByteString copyFrom(Iterable<ByteString> byteStrings) { - Collection<ByteString> collection; - if (!(byteStrings instanceof Collection)) { - collection = new ArrayList<ByteString>(); - for (ByteString byteString : byteStrings) { - collection.add(byteString); - } - } else { - collection = (Collection<ByteString>) byteStrings; - } - ByteString result; - if (collection.isEmpty()) { - result = EMPTY; - } else { - result = balancedConcat(collection.iterator(), collection.size()); + public static ByteString copyFrom(List<ByteString> list) { + if (list.size() == 0) { + return EMPTY; + } else if (list.size() == 1) { + return list.get(0); } - return result; - } - // Internal function used by copyFrom(Iterable<ByteString>). - // Create a balanced concatenation of the next "length" elements from the - // iterable. - private static ByteString balancedConcat(Iterator<ByteString> iterator, - int length) { - assert length >= 1; - ByteString result; - if (length == 1) { - result = iterator.next(); - } else { - int halfLength = length >>> 1; - ByteString left = balancedConcat(iterator, halfLength); - ByteString right = balancedConcat(iterator, length - halfLength); - result = left.concat(right); + int size = 0; + for (ByteString str : list) { + size += str.size(); + } + byte[] bytes = new byte[size]; + int pos = 0; + for (ByteString str : list) { + System.arraycopy(str.bytes, 0, bytes, pos, str.size()); + pos += str.size(); } - return result; + return new ByteString(bytes); } // ================================================================= @@ -460,493 +174,194 @@ public abstract class ByteString implements Iterable<Byte> { * * @param target buffer to copy into * @param offset in the target buffer - * @throws IndexOutOfBoundsException if the offset is negative or too large */ - public void copyTo(byte[] target, int offset) { - copyTo(target, 0, offset, size()); + public void copyTo(final byte[] target, final int offset) { + System.arraycopy(bytes, 0, target, offset, bytes.length); } /** * Copies bytes into a buffer. * - * @param target buffer to copy into + * @param target buffer to copy into * @param sourceOffset offset within these bytes * @param targetOffset offset within the target buffer - * @param numberToCopy number of bytes to copy - * @throws IndexOutOfBoundsException if an offset or size is negative or too - * large + * @param size number of bytes to copy */ - public void copyTo(byte[] target, int sourceOffset, int targetOffset, - int numberToCopy) { - if (sourceOffset < 0) { - throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset); - } - if (targetOffset < 0) { - throw new IndexOutOfBoundsException("Target offset < 0: " + targetOffset); - } - if (numberToCopy < 0) { - throw new IndexOutOfBoundsException("Length < 0: " + numberToCopy); - } - if (sourceOffset + numberToCopy > size()) { - throw new IndexOutOfBoundsException( - "Source end offset < 0: " + (sourceOffset + numberToCopy)); - } - if (targetOffset + numberToCopy > target.length) { - throw new IndexOutOfBoundsException( - "Target end offset < 0: " + (targetOffset + numberToCopy)); - } - if (numberToCopy > 0) { - copyToInternal(target, sourceOffset, targetOffset, numberToCopy); - } + public void copyTo(final byte[] target, final int sourceOffset, + final int targetOffset, + final int size) { + System.arraycopy(bytes, sourceOffset, target, targetOffset, size); } /** - * Internal (package private) implementation of - * @link{#copyTo(byte[],int,int,int}. - * It assumes that all error checking has already been performed and that - * @code{numberToCopy > 0}. - */ - protected abstract void copyToInternal(byte[] target, int sourceOffset, - int targetOffset, int numberToCopy); - - /** - * Copies bytes into a ByteBuffer. - * - * @param target ByteBuffer to copy into. - * @throws java.nio.ReadOnlyBufferException if the {@code target} is read-only - * @throws java.nio.BufferOverflowException if the {@code target}'s - * remaining() space is not large enough to hold the data. - */ - public abstract void copyTo(ByteBuffer target); - - /** * Copies bytes to a {@code byte[]}. - * - * @return copied bytes */ public byte[] toByteArray() { - int size = size(); - if (size == 0) { - return Internal.EMPTY_BYTE_ARRAY; - } - byte[] result = new byte[size]; - copyToInternal(result, 0, 0, size); - return result; + final int size = bytes.length; + final byte[] copy = new byte[size]; + System.arraycopy(bytes, 0, copy, 0, size); + return copy; } /** - * Writes the complete contents of this byte string to - * the specified output stream argument. - * - * @param out the output stream to which to write the data. - * @throws IOException if an I/O error occurs. + * Constructs a new read-only {@code java.nio.ByteBuffer} with the + * same backing byte array. */ - public abstract void writeTo(OutputStream out) throws IOException; - - /** - * Writes a specified part of this byte string to an output stream. - * - * @param out the output stream to which to write the data. - * @param sourceOffset offset within these bytes - * @param numberToWrite number of bytes to write - * @throws IOException if an I/O error occurs. - * @throws IndexOutOfBoundsException if an offset or size is negative or too - * large - */ - void writeTo(OutputStream out, int sourceOffset, int numberToWrite) - throws IOException { - if (sourceOffset < 0) { - throw new IndexOutOfBoundsException("Source offset < 0: " + sourceOffset); - } - if (numberToWrite < 0) { - throw new IndexOutOfBoundsException("Length < 0: " + numberToWrite); - } - if (sourceOffset + numberToWrite > size()) { - throw new IndexOutOfBoundsException( - "Source end offset exceeded: " + (sourceOffset + numberToWrite)); - } - if (numberToWrite > 0) { - writeToInternal(out, sourceOffset, numberToWrite); - } - + public ByteBuffer asReadOnlyByteBuffer() { + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + return byteBuffer.asReadOnlyBuffer(); } /** - * Internal version of {@link #writeTo(OutputStream,int,int)} that assumes - * all error checking has already been done. - */ - abstract void writeToInternal(OutputStream out, int sourceOffset, - int numberToWrite) throws IOException; - - /** - * Constructs a read-only {@code java.nio.ByteBuffer} whose content - * is equal to the contents of this byte string. - * The result uses the same backing array as the byte string, if possible. - * - * @return wrapped bytes - */ - public abstract ByteBuffer asReadOnlyByteBuffer(); - - /** - * Constructs a list of read-only {@code java.nio.ByteBuffer} objects - * such that the concatenation of their contents is equal to the contents - * of this byte string. The result uses the same backing arrays as the - * byte string. - * <p> - * By returning a list, implementations of this method may be able to avoid - * copying even when there are multiple backing arrays. - * - * @return a list of wrapped bytes - */ - public abstract List<ByteBuffer> asReadOnlyByteBufferList(); - - /** * Constructs a new {@code String} by decoding the bytes using the * specified charset. - * - * @param charsetName encode using this charset - * @return new string - * @throws UnsupportedEncodingException if charset isn't recognized */ - public abstract String toString(String charsetName) - throws UnsupportedEncodingException; - - // ================================================================= - // UTF-8 decoding + public String toString(final String charsetName) + throws UnsupportedEncodingException { + return new String(bytes, charsetName); + } /** * Constructs a new {@code String} by decoding the bytes as UTF-8. - * - * @return new string using UTF-8 encoding */ public String toStringUtf8() { try { - return toString("UTF-8"); + return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 not supported?", e); } } - /** - * Tells whether this {@code ByteString} represents a well-formed UTF-8 - * byte sequence, such that the original bytes can be converted to a - * String object and then round tripped back to bytes without loss. - * - * <p>More precisely, returns {@code true} whenever: <pre> {@code - * Arrays.equals(byteString.toByteArray(), - * new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8")) - * }</pre> - * - * <p>This method returns {@code false} for "overlong" byte sequences, - * as well as for 3-byte sequences that would map to a surrogate - * character, in accordance with the restricted definition of UTF-8 - * introduced in Unicode 3.1. Note that the UTF-8 decoder included in - * Oracle's JDK has been modified to also reject "overlong" byte - * sequences, but (as of 2011) still accepts 3-byte surrogate - * character byte sequences. - * - * <p>See the Unicode Standard,</br> - * Table 3-6. <em>UTF-8 Bit Distribution</em>,</br> - * Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>. - * - * @return whether the bytes in this {@code ByteString} are a - * well-formed UTF-8 byte sequence - */ - public abstract boolean isValidUtf8(); - - /** - * Tells whether the given byte sequence is a well-formed, malformed, or - * incomplete UTF-8 byte sequence. This method accepts and returns a partial - * state result, allowing the bytes for a complete UTF-8 byte sequence to be - * composed from multiple {@code ByteString} segments. - * - * @param state either {@code 0} (if this is the initial decoding operation) - * or the value returned from a call to a partial decoding method for the - * previous bytes - * @param offset offset of the first byte to check - * @param length number of bytes to check - * - * @return {@code -1} if the partial byte sequence is definitely malformed, - * {@code 0} if it is well-formed (no additional input needed), or, if the - * byte sequence is "incomplete", i.e. apparently terminated in the middle of - * a character, an opaque integer "state" value containing enough information - * to decode the character when passed to a subsequent invocation of a - * partial decoding method. - */ - protected abstract int partialIsValidUtf8(int state, int offset, int length); - // ================================================================= // equals() and hashCode() @Override - public abstract boolean equals(Object o); + public boolean equals(final Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ByteString)) { + return false; + } + + final ByteString other = (ByteString) o; + final int size = bytes.length; + if (size != other.bytes.length) { + return false; + } + + final byte[] thisBytes = bytes; + final byte[] otherBytes = other.bytes; + for (int i = 0; i < size; i++) { + if (thisBytes[i] != otherBytes[i]) { + return false; + } + } + + return true; + } + + private volatile int hash = 0; - /** - * Return a non-zero hashCode depending only on the sequence of bytes - * in this ByteString. - * - * @return hashCode value for this object - */ @Override - public abstract int hashCode(); + public int hashCode() { + int h = hash; + + if (h == 0) { + final byte[] thisBytes = bytes; + final int size = bytes.length; + + h = size; + for (int i = 0; i < size; i++) { + h = h * 31 + thisBytes[i]; + } + if (h == 0) { + h = 1; + } + + hash = h; + } + + return h; + } // ================================================================= // Input stream /** * Creates an {@code InputStream} which can be used to read the bytes. - * <p> - * The {@link InputStream} returned by this method is guaranteed to be - * completely non-blocking. The method {@link InputStream#available()} - * returns the number of bytes remaining in the stream. The methods - * {@link InputStream#read(byte[]), {@link InputStream#read(byte[],int,int)} - * and {@link InputStream#skip(long)} will read/skip as many bytes as are - * available. - * <p> - * The methods in the returned {@link InputStream} might <b>not</b> be - * thread safe. - * - * @return an input stream that returns the bytes of this byte string. */ - public abstract InputStream newInput(); + public InputStream newInput() { + return new ByteArrayInputStream(bytes); + } /** * Creates a {@link CodedInputStream} which can be used to read the bytes. - * Using this is often more efficient than creating a {@link CodedInputStream} - * that wraps the result of {@link #newInput()}. - * - * @return stream based on wrapped data + * Using this is more efficient than creating a {@link CodedInputStream} + * wrapping the result of {@link #newInput()}. */ - public abstract CodedInputStream newCodedInput(); + public CodedInputStream newCodedInput() { + // We trust CodedInputStream not to modify the bytes, or to give anyone + // else access to them. + return CodedInputStream.newInstance(bytes); + } // ================================================================= // Output stream /** - * Creates a new {@link Output} with the given initial capacity. Call {@link - * Output#toByteString()} to create the {@code ByteString} instance. - * <p> - * A {@link ByteString.Output} offers the same functionality as a - * {@link ByteArrayOutputStream}, except that it returns a {@link ByteString} - * rather than a {@code byte} array. - * - * @param initialCapacity estimate of number of bytes to be written - * @return {@code OutputStream} for building a {@code ByteString} + * Creates a new {@link Output} with the given initial capacity. */ - public static Output newOutput(int initialCapacity) { - return new Output(initialCapacity); + public static Output newOutput(final int initialCapacity) { + return new Output(new ByteArrayOutputStream(initialCapacity)); } /** - * Creates a new {@link Output}. Call {@link Output#toByteString()} to create - * the {@code ByteString} instance. - * <p> - * A {@link ByteString.Output} offers the same functionality as a - * {@link ByteArrayOutputStream}, except that it returns a {@link ByteString} - * rather than a {@code byte array}. - * - * @return {@code OutputStream} for building a {@code ByteString} + * Creates a new {@link Output}. */ public static Output newOutput() { - return new Output(CONCATENATE_BY_COPY_SIZE); + return newOutput(32); } /** * Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to * create the {@code ByteString} instance. */ - public static final class Output extends OutputStream { - // Implementation note. - // The public methods of this class must be synchronized. ByteStrings - // are guaranteed to be immutable. Without some sort of locking, it could - // be possible for one thread to call toByteSring(), while another thread - // is still modifying the underlying byte array. - - private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - // argument passed by user, indicating initial capacity. - private final int initialCapacity; - // ByteStrings to be concatenated to create the result - private final ArrayList<ByteString> flushedBuffers; - // Total number of bytes in the ByteStrings of flushedBuffers - private int flushedBuffersTotalBytes; - // Current buffer to which we are writing - private byte[] buffer; - // Location in buffer[] to which we write the next byte. - private int bufferPos; + public static final class Output extends FilterOutputStream { + private final ByteArrayOutputStream bout; /** - * Creates a new ByteString output stream with the specified - * initial capacity. - * - * @param initialCapacity the initial capacity of the output stream. + * Constructs a new output with the given initial capacity. */ - Output(int initialCapacity) { - if (initialCapacity < 0) { - throw new IllegalArgumentException("Buffer size < 0"); - } - this.initialCapacity = initialCapacity; - this.flushedBuffers = new ArrayList<ByteString>(); - this.buffer = new byte[initialCapacity]; - } - - @Override - public synchronized void write(int b) { - if (bufferPos == buffer.length) { - flushFullBuffer(1); - } - buffer[bufferPos++] = (byte)b; - } - - @Override - public synchronized void write(byte[] b, int offset, int length) { - if (length <= buffer.length - bufferPos) { - // The bytes can fit into the current buffer. - System.arraycopy(b, offset, buffer, bufferPos, length); - bufferPos += length; - } else { - // Use up the current buffer - int copySize = buffer.length - bufferPos; - System.arraycopy(b, offset, buffer, bufferPos, copySize); - offset += copySize; - length -= copySize; - // Flush the buffer, and get a new buffer at least big enough to cover - // what we still need to output - flushFullBuffer(length); - System.arraycopy(b, offset, buffer, 0 /* count */, length); - bufferPos = length; - } + private Output(final ByteArrayOutputStream bout) { + super(bout); + this.bout = bout; } /** - * Creates a byte string. Its size is the current size of this output - * stream and its output has been copied to it. - * - * @return the current contents of this output stream, as a byte string. + * Creates a {@code ByteString} instance from this {@code Output}. */ - public synchronized ByteString toByteString() { - flushLastBuffer(); - return ByteString.copyFrom(flushedBuffers); - } - - /** - * Implement java.util.Arrays.copyOf() for jdk 1.5. - */ - private byte[] copyArray(byte[] buffer, int length) { - byte[] result = new byte[length]; - System.arraycopy(buffer, 0, result, 0, Math.min(buffer.length, length)); - return result; - } - - /** - * Writes the complete contents of this byte array output stream to - * the specified output stream argument. - * - * @param out the output stream to which to write the data. - * @throws IOException if an I/O error occurs. - */ - public void writeTo(OutputStream out) throws IOException { - ByteString[] cachedFlushBuffers; - byte[] cachedBuffer; - int cachedBufferPos; - synchronized (this) { - // Copy the information we need into local variables so as to hold - // the lock for as short a time as possible. - cachedFlushBuffers = - flushedBuffers.toArray(new ByteString[flushedBuffers.size()]); - cachedBuffer = buffer; - cachedBufferPos = bufferPos; - } - for (ByteString byteString : cachedFlushBuffers) { - byteString.writeTo(out); - } - - out.write(copyArray(cachedBuffer, cachedBufferPos)); - } - - /** - * Returns the current size of the output stream. - * - * @return the current size of the output stream - */ - public synchronized int size() { - return flushedBuffersTotalBytes + bufferPos; - } - - /** - * Resets this stream, so that all currently accumulated output in the - * output stream is discarded. The output stream can be used again, - * reusing the already allocated buffer space. - */ - public synchronized void reset() { - flushedBuffers.clear(); - flushedBuffersTotalBytes = 0; - bufferPos = 0; - } - - @Override - public String toString() { - return String.format("<ByteString.Output@%s size=%d>", - Integer.toHexString(System.identityHashCode(this)), size()); - } - - /** - * Internal function used by writers. The current buffer is full, and the - * writer needs a new buffer whose size is at least the specified minimum - * size. - */ - private void flushFullBuffer(int minSize) { - flushedBuffers.add(new LiteralByteString(buffer)); - flushedBuffersTotalBytes += buffer.length; - // We want to increase our total capacity by 50%, but as a minimum, - // the new buffer should also at least be >= minSize and - // >= initial Capacity. - int newSize = Math.max(initialCapacity, - Math.max(minSize, flushedBuffersTotalBytes >>> 1)); - buffer = new byte[newSize]; - bufferPos = 0; - } - - /** - * Internal function used by {@link #toByteString()}. The current buffer may - * or may not be full, but it needs to be flushed. - */ - private void flushLastBuffer() { - if (bufferPos < buffer.length) { - if (bufferPos > 0) { - byte[] bufferCopy = copyArray(buffer, bufferPos); - flushedBuffers.add(new LiteralByteString(bufferCopy)); - } - // We reuse this buffer for further writes. - } else { - // Buffer is completely full. Huzzah. - flushedBuffers.add(new LiteralByteString(buffer)); - // 99% of the time, we're not going to use this OutputStream again. - // We set buffer to an empty byte stream so that we're handling this - // case without wasting space. In the rare case that more writes - // *do* occur, this empty buffer will be flushed and an appropriately - // sized new buffer will be created. - buffer = EMPTY_BYTE_ARRAY; - } - flushedBuffersTotalBytes += bufferPos; - bufferPos = 0; + public ByteString toByteString() { + final byte[] byteArray = bout.toByteArray(); + return new ByteString(byteArray); } } /** - * Constructs a new {@code ByteString} builder, which allows you to - * efficiently construct a {@code ByteString} by writing to a {@link - * CodedOutputStream}. Using this is much more efficient than calling {@code - * newOutput()} and wrapping that in a {@code CodedOutputStream}. + * Constructs a new ByteString builder, which allows you to efficiently + * construct a {@code ByteString} by writing to a {@link CodedOutputStream}. + * Using this is much more efficient than calling {@code newOutput()} and + * wrapping that in a {@code CodedOutputStream}. * * <p>This is package-private because it's a somewhat confusing interface. * Users can call {@link Message#toByteString()} instead of calling this * directly. * - * @param size The target byte size of the {@code ByteString}. You must write - * exactly this many bytes before building the result. - * @return the builder + * @param size The target byte size of the {@code ByteString}. You must + * write exactly this many bytes before building the result. */ - static CodedBuilder newCodedBuilder(int size) { + static CodedBuilder newCodedBuilder(final int size) { return new CodedBuilder(size); } @@ -955,7 +370,7 @@ public abstract class ByteString implements Iterable<Byte> { private final CodedOutputStream output; private final byte[] buffer; - private CodedBuilder(int size) { + private CodedBuilder(final int size) { buffer = new byte[size]; output = CodedOutputStream.newInstance(buffer); } @@ -966,57 +381,11 @@ public abstract class ByteString implements Iterable<Byte> { // We can be confident that the CodedOutputStream will not modify the // underlying bytes anymore because it already wrote all of them. So, // no need to make a copy. - return new LiteralByteString(buffer); + return new ByteString(buffer); } public CodedOutputStream getCodedOutput() { return output; } } - - // ================================================================= - // Methods {@link RopeByteString} needs on instances, which aren't part of the - // public API. - - /** - * Return the depth of the tree representing this {@code ByteString}, if any, - * whose root is this node. If this is a leaf node, return 0. - * - * @return tree depth or zero - */ - protected abstract int getTreeDepth(); - - /** - * Return {@code true} if this ByteString is literal (a leaf node) or a - * flat-enough tree in the sense of {@link RopeByteString}. - * - * @return true if the tree is flat enough - */ - protected abstract boolean isBalanced(); - - /** - * Return the cached hash code if available. - * - * @return value of cached hash code or 0 if not computed yet - */ - protected abstract int peekCachedHashCode(); - - /** - * Compute the hash across the value bytes starting with the given hash, and - * return the result. This is used to compute the hash across strings - * represented as a set of pieces by allowing the hash computation to be - * continued from piece to piece. - * - * @param h starting hash value - * @param offset offset into this value to start looking at data values - * @param length number of data values to include in the hash computation - * @return ending hash value - */ - protected abstract int partialHash(int h, int offset, int length); - - @Override - public String toString() { - return String.format("<ByteString@%s size=%d>", - Integer.toHexString(System.identityHashCode(this)), size()); - } } diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index a00ae86..22995e9 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,12 +30,9 @@ package com.google.protobuf; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -70,72 +67,7 @@ public final class CodedInputStream { */ public static CodedInputStream newInstance(final byte[] buf, final int off, final int len) { - CodedInputStream result = new CodedInputStream(buf, off, len); - try { - // Some uses of CodedInputStream can be more efficient if they know - // exactly how many bytes are available. By pushing the end point of the - // buffer as a limit, we allow them to get this information via - // getBytesUntilLimit(). Pushing a limit that we know is at the end of - // the stream can never hurt, since we can never past that point anyway. - result.pushLimit(len); - } catch (InvalidProtocolBufferException ex) { - // The only reason pushLimit() might throw an exception here is if len - // is negative. Normally pushLimit()'s parameter comes directly off the - // wire, so it's important to catch exceptions in case of corrupt or - // malicious data. However, in this case, we expect that len is not a - // user-supplied value, so we can assume that it being negative indicates - // a programming error. Therefore, throwing an unchecked exception is - // appropriate. - throw new IllegalArgumentException(ex); - } - return result; - } - - /** - * Create a new CodedInputStream wrapping the given ByteBuffer. The data - * starting from the ByteBuffer's current position to its limit will be read. - * The returned CodedInputStream may or may not share the underlying data - * in the ByteBuffer, therefore the ByteBuffer cannot be changed while the - * CodedInputStream is in use. - * Note that the ByteBuffer's position won't be changed by this function. - * Concurrent calls with the same ByteBuffer object are safe if no other - * thread is trying to alter the ByteBuffer's status. - */ - public static CodedInputStream newInstance(ByteBuffer buf) { - if (buf.hasArray()) { - return newInstance(buf.array(), buf.arrayOffset() + buf.position(), - buf.remaining()); - } else { - ByteBuffer temp = buf.duplicate(); - byte[] buffer = new byte[temp.remaining()]; - temp.get(buffer); - return newInstance(buffer); - } - } - - /** - * Create a new CodedInputStream wrapping a LiteralByteString. - */ - static CodedInputStream newInstance(LiteralByteString byteString) { - CodedInputStream result = new CodedInputStream(byteString); - try { - // Some uses of CodedInputStream can be more efficient if they know - // exactly how many bytes are available. By pushing the end point of the - // buffer as a limit, we allow them to get this information via - // getBytesUntilLimit(). Pushing a limit that we know is at the end of - // the stream can never hurt, since we can never past that point anyway. - result.pushLimit(byteString.size()); - } catch (InvalidProtocolBufferException ex) { - // The only reason pushLimit() might throw an exception here is if len - // is negative. Normally pushLimit()'s parameter comes directly off the - // wire, so it's important to catch exceptions in case of corrupt or - // malicious data. However, in this case, we expect that len is not a - // user-supplied value, so we can assume that it being negative indicates - // a programming error. Therefore, throwing an unchecked exception is - // appropriate. - throw new IllegalArgumentException(ex); - } - return result; + return new CodedInputStream(buf, off, len); } // ----------------------------------------------------------------- @@ -175,10 +107,6 @@ public final class CodedInputStream { } } - public int getLastTag() { - return lastTag; - } - /** * Reads and discards a single field, given its tag value. * @@ -188,10 +116,10 @@ public final class CodedInputStream { public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: - skipRawVarint(); + readInt32(); return true; case WireFormat.WIRETYPE_FIXED64: - skipRawBytes(8); + readRawLittleEndian64(); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: skipRawBytes(readRawVarint32()); @@ -205,7 +133,7 @@ public final class CodedInputStream { case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: - skipRawBytes(4); + readRawLittleEndian32(); return true; default: throw InvalidProtocolBufferException.invalidWireType(); @@ -213,57 +141,6 @@ public final class CodedInputStream { } /** - * Reads a single field and writes it to output in wire format, - * given its tag value. - * - * @return {@code false} if the tag is an endgroup tag, in which case - * nothing is skipped. Otherwise, returns {@code true}. - */ - public boolean skipField(final int tag, final CodedOutputStream output) - throws IOException { - switch (WireFormat.getTagWireType(tag)) { - case WireFormat.WIRETYPE_VARINT: { - long value = readInt64(); - output.writeRawVarint32(tag); - output.writeUInt64NoTag(value); - return true; - } - case WireFormat.WIRETYPE_FIXED64: { - long value = readRawLittleEndian64(); - output.writeRawVarint32(tag); - output.writeFixed64NoTag(value); - return true; - } - case WireFormat.WIRETYPE_LENGTH_DELIMITED: { - ByteString value = readBytes(); - output.writeRawVarint32(tag); - output.writeBytesNoTag(value); - return true; - } - case WireFormat.WIRETYPE_START_GROUP: { - output.writeRawVarint32(tag); - skipMessage(output); - int endtag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), - WireFormat.WIRETYPE_END_GROUP); - checkLastTagWas(endtag); - output.writeRawVarint32(endtag); - return true; - } - case WireFormat.WIRETYPE_END_GROUP: { - return false; - } - case WireFormat.WIRETYPE_FIXED32: { - int value = readRawLittleEndian32(); - output.writeRawVarint32(tag); - output.writeFixed32NoTag(value); - return true; - } - default: - throw InvalidProtocolBufferException.invalidWireType(); - } - } - - /** * Reads and discards an entire message. This will read either until EOF * or until an endgroup tag, whichever comes first. */ @@ -276,51 +153,6 @@ public final class CodedInputStream { } } - /** - * Reads an entire message and writes it to output in wire format. - * This will read either until EOF or until an endgroup tag, - * whichever comes first. - */ - public void skipMessage(CodedOutputStream output) throws IOException { - while (true) { - final int tag = readTag(); - if (tag == 0 || !skipField(tag, output)) { - return; - } - } - } - - /** - * Collects the bytes skipped and returns the data in a ByteBuffer. - */ - private class SkippedDataSink implements RefillCallback { - private int lastPos = bufferPos; - private ByteArrayOutputStream byteArrayStream; - - @Override - public void onRefill() { - if (byteArrayStream == null) { - byteArrayStream = new ByteArrayOutputStream(); - } - byteArrayStream.write(buffer, lastPos, bufferPos - lastPos); - lastPos = 0; - } - - /** - * Gets skipped data in a ByteBuffer. This method should only be - * called once. - */ - ByteBuffer getSkippedData() { - if (byteArrayStream == null) { - return ByteBuffer.wrap(buffer, lastPos, bufferPos - lastPos); - } else { - byteArrayStream.write(buffer, lastPos, bufferPos); - return ByteBuffer.wrap(byteArrayStream.toByteArray()); - } - } - } - - // ----------------------------------------------------------------- /** Read a {@code double} field value from the stream. */ @@ -360,14 +192,10 @@ public final class CodedInputStream { /** Read a {@code bool} field value from the stream. */ public boolean readBool() throws IOException { - return readRawVarint64() != 0; + return readRawVarint32() != 0; } - /** - * Read a {@code string} field value from the stream. - * If the stream contains malformed UTF-8, - * replace the offending bytes with the standard UTF-8 replacement character. - */ + /** Read a {@code string} field value from the stream. */ public String readString() throws IOException { final int size = readRawVarint32(); if (size <= (bufferSize - bufferPos) && size > 0) { @@ -376,40 +204,10 @@ public final class CodedInputStream { final String result = new String(buffer, bufferPos, size, "UTF-8"); bufferPos += size; return result; - } else if (size == 0) { - return ""; } else { // Slow path: Build a byte array first then copy it. - return new String(readRawBytesSlowPath(size), "UTF-8"); - } - } - - /** - * Read a {@code string} field value from the stream. - * If the stream contains malformed UTF-8, - * throw exception {@link InvalidProtocolBufferException}. - */ - public String readStringRequireUtf8() throws IOException { - final int size = readRawVarint32(); - final byte[] bytes; - int pos = bufferPos; - if (size <= (bufferSize - pos) && size > 0) { - // Fast path: We already have the bytes in a contiguous buffer, so - // just copy directly from it. - bytes = buffer; - bufferPos = pos + size; - } else if (size == 0) { - return ""; - } else { - // Slow path: Build a byte array first then copy it. - bytes = readRawBytesSlowPath(size); - pos = 0; - } - // TODO(martinrb): We could save a pass by validating while decoding. - if (!Utf8.isValidUtf8(bytes, pos, pos + size)) { - throw InvalidProtocolBufferException.invalidUtf8(); + return new String(readRawBytes(size), "UTF-8"); } - return new String(bytes, pos, size, "UTF-8"); } /** Read a {@code group} field value from the stream. */ @@ -427,24 +225,6 @@ public final class CodedInputStream { --recursionDepth; } - - /** Read a {@code group} field value from the stream. */ - public <T extends MessageLite> T readGroup( - final int fieldNumber, - final Parser<T> parser, - final ExtensionRegistryLite extensionRegistry) - throws IOException { - if (recursionDepth >= recursionLimit) { - throw InvalidProtocolBufferException.recursionLimitExceeded(); - } - ++recursionDepth; - T result = parser.parsePartialFrom(this, extensionRegistry); - checkLastTagWas( - WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); - --recursionDepth; - return result; - } - /** * Reads a {@code group} field value from the stream and merges it into the * given {@link UnknownFieldSet}. @@ -480,80 +260,18 @@ public final class CodedInputStream { popLimit(oldLimit); } - - /** Read an embedded message field value from the stream. */ - public <T extends MessageLite> T readMessage( - final Parser<T> parser, - final ExtensionRegistryLite extensionRegistry) - throws IOException { - int length = readRawVarint32(); - if (recursionDepth >= recursionLimit) { - throw InvalidProtocolBufferException.recursionLimitExceeded(); - } - final int oldLimit = pushLimit(length); - ++recursionDepth; - T result = parser.parsePartialFrom(this, extensionRegistry); - checkLastTagWas(0); - --recursionDepth; - popLimit(oldLimit); - return result; - } - /** Read a {@code bytes} field value from the stream. */ public ByteString readBytes() throws IOException { final int size = readRawVarint32(); if (size <= (bufferSize - bufferPos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. - final ByteString result = bufferIsImmutable && enableAliasing - ? new BoundedByteString(buffer, bufferPos, size) - : ByteString.copyFrom(buffer, bufferPos, size); + final ByteString result = ByteString.copyFrom(buffer, bufferPos, size); bufferPos += size; return result; - } else if (size == 0) { - return ByteString.EMPTY; } else { // Slow path: Build a byte array first then copy it. - return new LiteralByteString(readRawBytesSlowPath(size)); - } - } - - /** Read a {@code bytes} field value from the stream. */ - public byte[] readByteArray() throws IOException { - final int size = readRawVarint32(); - if (size <= (bufferSize - bufferPos) && size > 0) { - // Fast path: We already have the bytes in a contiguous buffer, so - // just copy directly from it. - final byte[] result = - Arrays.copyOfRange(buffer, bufferPos, bufferPos + size); - bufferPos += size; - return result; - } else { - // Slow path: Build a byte array first then copy it. - return readRawBytesSlowPath(size); - } - } - - /** Read a {@code bytes} field value from the stream. */ - public ByteBuffer readByteBuffer() throws IOException { - final int size = readRawVarint32(); - if (size <= (bufferSize - bufferPos) && size > 0) { - // Fast path: We already have the bytes in a contiguous buffer. - // When aliasing is enabled, we can return a ByteBuffer pointing directly - // into the underlying byte array without copy if the CodedInputStream is - // constructed from a byte array. If aliasing is disabled or the input is - // from an InputStream or ByteString, we have to make a copy of the bytes. - ByteBuffer result = input == null && !bufferIsImmutable && enableAliasing - ? ByteBuffer.wrap(buffer, bufferPos, size).slice() - : ByteBuffer.wrap(Arrays.copyOfRange( - buffer, bufferPos, bufferPos + size)); - bufferPos += size; - return result; - } else if (size == 0) { - return Internal.EMPTY_BYTE_BUFFER; - } else { - // Slow path: Build a byte array first then copy it. - return ByteBuffer.wrap(readRawBytesSlowPath(size)); + return ByteString.copyFrom(readRawBytes(size)); } } @@ -597,67 +315,37 @@ public final class CodedInputStream { * upper bits. */ public int readRawVarint32() throws IOException { - // See implementation notes for readRawVarint64 - fastpath: { - int pos = bufferPos; - - if (bufferSize == pos) { - break fastpath; - } - - final byte[] buffer = this.buffer; - int x; - if ((x = buffer[pos++]) >= 0) { - bufferPos = pos; - 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 { - int y = buffer[pos++]; - x ^= y << 28; - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28); - if (y < 0 && - buffer[pos++] < 0 && - buffer[pos++] < 0 && - buffer[pos++] < 0 && - buffer[pos++] < 0 && - buffer[pos++] < 0) { - break fastpath; // Will throw malformedVarint() - } - } - bufferPos = pos; - return x; + byte tmp = readRawByte(); + if (tmp >= 0) { + return tmp; } - return (int) readRawVarint64SlowPath(); - } - - private void skipRawVarint() throws IOException { - if (bufferSize - bufferPos >= 10) { - final byte[] buffer = this.buffer; - int pos = bufferPos; - for (int i = 0; i < 10; i++) { - if (buffer[pos++] >= 0) { - bufferPos = pos; - return; + int result = tmp & 0x7f; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = readRawByte()) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (readRawByte() >= 0) { + return result; + } + } + throw InvalidProtocolBufferException.malformedVarint(); + } } } } - skipRawVarintSlowPath(); - } - - private void skipRawVarintSlowPath() throws IOException { - for (int i = 0; i < 10; i++) { - if (readRawByte() >= 0) { - return; - } - } - throw InvalidProtocolBufferException.malformedVarint(); + return result; } /** @@ -680,8 +368,8 @@ public final class CodedInputStream { * has already read one byte. This allows the caller to determine if EOF * has been reached before attempting to read. */ - public static int readRawVarint32( - final int firstByte, final InputStream input) throws IOException { + static int readRawVarint32(final int firstByte, + final InputStream input) throws IOException { if ((firstByte & 0x80) == 0) { return firstByte; } @@ -713,115 +401,49 @@ public final class CodedInputStream { /** Read a raw Varint from the stream. */ public long readRawVarint64() throws IOException { - // Implementation notes: - // - // Optimized for one-byte values, expected to be common. - // The particular code below was selected from various candidates - // empirically, by winning VarintBenchmark. - // - // Sign extension of (signed) Java bytes is usually a nuisance, but - // we exploit it here to more easily obtain the sign of bytes read. - // Instead of cleaning up the sign extension bits by masking eagerly, - // we delay until we find the final (positive) byte, when we clear all - // accumulated bits with one xor. We depend on javac to constant fold. - fastpath: { - int pos = bufferPos; - - if (bufferSize == pos) { - break fastpath; - } - - final byte[] buffer = this.buffer; - long x; - int y; - if ((y = buffer[pos++]) >= 0) { - bufferPos = pos; - 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) { - 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); - } else if ((x ^= ((long) buffer[pos++] << 42)) >= 0L) { - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42); - } else if ((x ^= ((long) buffer[pos++] << 49)) < 0L) { - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) - ^ (~0L << 49); - } else { - x ^= ((long) buffer[pos++] << 56); - x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42) - ^ (~0L << 49) ^ (~0L << 56); - if (x < 0L) { - if (buffer[pos++] < 0L) { - break fastpath; // Will throw malformedVarint() - } - } - } - bufferPos = pos; - return x; - } - return readRawVarint64SlowPath(); - } - - /** Variant of readRawVarint64 for when uncomfortably close to the limit. */ - /* Visible for testing */ - long readRawVarint64SlowPath() throws IOException { + int shift = 0; long result = 0; - for (int shift = 0; shift < 64; shift += 7) { + while (shift < 64) { final byte b = readRawByte(); - result |= (long) (b & 0x7F) << shift; + result |= (long)(b & 0x7F) << shift; if ((b & 0x80) == 0) { return result; } + shift += 7; } throw InvalidProtocolBufferException.malformedVarint(); } /** Read a 32-bit little-endian integer from the stream. */ public int readRawLittleEndian32() throws IOException { - int pos = bufferPos; - - // hand-inlined ensureAvailable(4); - if (bufferSize - pos < 4) { - refillBuffer(4); - pos = bufferPos; - } - - final byte[] buffer = this.buffer; - bufferPos = pos + 4; - return (((buffer[pos] & 0xff)) | - ((buffer[pos + 1] & 0xff) << 8) | - ((buffer[pos + 2] & 0xff) << 16) | - ((buffer[pos + 3] & 0xff) << 24)); + final byte b1 = readRawByte(); + final byte b2 = readRawByte(); + final byte b3 = readRawByte(); + final byte b4 = readRawByte(); + return (((int)b1 & 0xff) ) | + (((int)b2 & 0xff) << 8) | + (((int)b3 & 0xff) << 16) | + (((int)b4 & 0xff) << 24); } /** Read a 64-bit little-endian integer from the stream. */ public long readRawLittleEndian64() throws IOException { - int pos = bufferPos; - - // hand-inlined ensureAvailable(8); - if (bufferSize - pos < 8) { - refillBuffer(8); - pos = bufferPos; - } - - final byte[] buffer = this.buffer; - bufferPos = pos + 8; - return ((((long) buffer[pos] & 0xffL)) | - (((long) buffer[pos + 1] & 0xffL) << 8) | - (((long) buffer[pos + 2] & 0xffL) << 16) | - (((long) buffer[pos + 3] & 0xffL) << 24) | - (((long) buffer[pos + 4] & 0xffL) << 32) | - (((long) buffer[pos + 5] & 0xffL) << 40) | - (((long) buffer[pos + 6] & 0xffL) << 48) | - (((long) buffer[pos + 7] & 0xffL) << 56)); + final byte b1 = readRawByte(); + final byte b2 = readRawByte(); + final byte b3 = readRawByte(); + final byte b4 = readRawByte(); + final byte b5 = readRawByte(); + final byte b6 = readRawByte(); + final byte b7 = readRawByte(); + final byte b8 = readRawByte(); + return (((long)b1 & 0xff) ) | + (((long)b2 & 0xff) << 8) | + (((long)b3 & 0xff) << 16) | + (((long)b4 & 0xff) << 24) | + (((long)b5 & 0xff) << 32) | + (((long)b6 & 0xff) << 40) | + (((long)b7 & 0xff) << 48) | + (((long)b8 & 0xff) << 56); } /** @@ -855,13 +477,11 @@ public final class CodedInputStream { // ----------------------------------------------------------------- private final byte[] buffer; - private final boolean bufferIsImmutable; private int bufferSize; private int bufferSizeAfterLimit; private int bufferPos; private final InputStream input; private int lastTag; - private boolean enableAliasing = false; /** * The total number of bytes read before the current buffer. The total @@ -892,7 +512,6 @@ public final class CodedInputStream { bufferPos = off; totalBytesRetired = -off; input = null; - bufferIsImmutable = false; } private CodedInputStream(final InputStream input) { @@ -901,20 +520,6 @@ public final class CodedInputStream { bufferPos = 0; totalBytesRetired = 0; this.input = input; - bufferIsImmutable = false; - } - - private CodedInputStream(final LiteralByteString byteString) { - buffer = byteString.bytes; - bufferPos = byteString.getOffsetIntoBytes(); - bufferSize = bufferPos + byteString.size(); - totalBytesRetired = -bufferPos; - input = null; - bufferIsImmutable = true; - } - - public void enableAliasing(boolean enabled) { - this.enableAliasing = enabled; } /** @@ -976,7 +581,7 @@ public final class CodedInputStream { * refreshing its buffer. If you need to prevent reading past a certain * point in the underlying {@code InputStream} (e.g. because you expect it to * contain more data after the end of the message which you need to handle - * differently) then you must place a wrapper around your {@code InputStream} + * differently) then you must place a wrapper around you {@code InputStream} * which limits the amount of data that can be read from it. * * @return the old limit. @@ -1038,7 +643,7 @@ public final class CodedInputStream { * if the stream has reached a limit created using {@link #pushLimit(int)}. */ public boolean isAtEnd() throws IOException { - return bufferPos == bufferSize && !tryRefillBuffer(1); + return bufferPos == bufferSize && !refillBuffer(false); } /** @@ -1049,93 +654,53 @@ public final class CodedInputStream { return totalBytesRetired + bufferPos; } - private interface RefillCallback { - void onRefill(); - } - - private RefillCallback refillCallback = null; - - /** - * Ensures that at least {@code n} bytes are available in the buffer, reading - * more bytes from the input if necessary to make it so. Caller must ensure - * that the requested space is less than BUFFER_SIZE. - * - * @throws InvalidProtocolBufferException The end of the stream or the current - * limit was reached. - */ - private void ensureAvailable(int n) throws IOException { - if (bufferSize - bufferPos < n) { - refillBuffer(n); - } - } - - /** - * Reads more bytes from the input, making at least {@code n} bytes available - * in the buffer. Caller must ensure that the requested space is not yet - * available, and that the requested space is less than BUFFER_SIZE. - * - * @throws InvalidProtocolBufferException The end of the stream or the current - * limit was reached. - */ - private void refillBuffer(int n) throws IOException { - if (!tryRefillBuffer(n)) { - throw InvalidProtocolBufferException.truncatedMessage(); - } - } - /** - * Tries to read more bytes from the input, making at least {@code n} bytes - * available in the buffer. Caller must ensure that the requested space is - * not yet available, and that the requested space is less than BUFFER_SIZE. - * - * @return {@code true} if the bytes could be made available; {@code false} - * if the end of the stream or the current limit was reached. + * Called with {@code this.buffer} is empty to read more bytes from the + * input. If {@code mustSucceed} is true, refillBuffer() gurantees that + * either there will be at least one byte in the buffer when it returns + * or it will throw an exception. If {@code mustSucceed} is false, + * refillBuffer() returns false if no more bytes were available. */ - private boolean tryRefillBuffer(int n) throws IOException { - if (bufferPos + n <= bufferSize) { + private boolean refillBuffer(final boolean mustSucceed) throws IOException { + if (bufferPos < bufferSize) { throw new IllegalStateException( - "refillBuffer() called when " + n + - " bytes were already available in buffer"); + "refillBuffer() called when buffer wasn't empty."); } - if (totalBytesRetired + bufferPos + n > currentLimit) { + if (totalBytesRetired + bufferSize == currentLimit) { // Oops, we hit a limit. - return false; - } - - if (refillCallback != null) { - refillCallback.onRefill(); + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; + } } - if (input != null) { - int pos = bufferPos; - if (pos > 0) { - if (bufferSize > pos) { - System.arraycopy(buffer, pos, buffer, 0, bufferSize - pos); - } - totalBytesRetired += pos; - bufferSize -= pos; - bufferPos = 0; - } + totalBytesRetired += bufferSize; - int bytesRead = input.read(buffer, bufferSize, buffer.length - bufferSize); - if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) { - throw new IllegalStateException( - "InputStream#read(byte[]) returned invalid result: " + bytesRead + - "\nThe InputStream implementation is buggy."); + bufferPos = 0; + bufferSize = (input == null) ? -1 : input.read(buffer); + if (bufferSize == 0 || bufferSize < -1) { + throw new IllegalStateException( + "InputStream#read(byte[]) returned invalid result: " + bufferSize + + "\nThe InputStream implementation is buggy."); + } + if (bufferSize == -1) { + bufferSize = 0; + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; } - if (bytesRead > 0) { - bufferSize += bytesRead; - // Integer-overflow-conscious check against sizeLimit - if (totalBytesRetired + n - sizeLimit > 0) { - throw InvalidProtocolBufferException.sizeLimitExceeded(); - } - recomputeBufferSizeAfterLimit(); - return (bufferSize >= n) ? true : tryRefillBuffer(n); + } else { + recomputeBufferSizeAfterLimit(); + final int totalBytesRead = + totalBytesRetired + bufferSize + bufferSizeAfterLimit; + if (totalBytesRead > sizeLimit || totalBytesRead < 0) { + throw InvalidProtocolBufferException.sizeLimitExceeded(); } + return true; } - - return false; } /** @@ -1146,7 +711,7 @@ public final class CodedInputStream { */ public byte readRawByte() throws IOException { if (bufferPos == bufferSize) { - refillBuffer(1); + refillBuffer(true); } return buffer[bufferPos++]; } @@ -1158,26 +723,8 @@ public final class CodedInputStream { * limit was reached. */ public byte[] readRawBytes(final int size) throws IOException { - final int pos = bufferPos; - if (size <= (bufferSize - pos) && size > 0) { - bufferPos = pos + size; - return Arrays.copyOfRange(buffer, pos, pos + size); - } else { - return readRawBytesSlowPath(size); - } - } - - /** - * Exactly like readRawBytes, but caller must have already checked the fast - * path: (size <= (bufferSize - pos) && size > 0) - */ - private byte[] readRawBytesSlowPath(final int size) throws IOException { - if (size <= 0) { - if (size == 0) { - return Internal.EMPTY_BYTE_ARRAY; - } else { - throw InvalidProtocolBufferException.negativeSize(); - } + if (size < 0) { + throw InvalidProtocolBufferException.negativeSize(); } if (totalBytesRetired + bufferPos + size > currentLimit) { @@ -1187,7 +734,13 @@ public final class CodedInputStream { throw InvalidProtocolBufferException.truncatedMessage(); } - if (size < BUFFER_SIZE) { + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + final byte[] bytes = new byte[size]; + System.arraycopy(buffer, bufferPos, bytes, 0, size); + bufferPos += size; + return bytes; + } else if (size < BUFFER_SIZE) { // Reading more bytes than are in the buffer, but not an excessive number // of bytes. We can safely allocate the resulting array ahead of time. @@ -1197,10 +750,18 @@ public final class CodedInputStream { System.arraycopy(buffer, bufferPos, bytes, 0, pos); bufferPos = bufferSize; - // We want to refill the buffer and then copy from the buffer into our + // We want to use refillBuffer() and then copy from the buffer into our // byte array rather than reading directly into our byte array because // the input may be unbuffered. - ensureAvailable(size - pos); + refillBuffer(true); + + while (size - pos > bufferSize) { + System.arraycopy(buffer, 0, bytes, pos, bufferSize); + pos += bufferSize; + bufferPos = bufferSize; + refillBuffer(true); + } + System.arraycopy(buffer, 0, bytes, pos, size - pos); bufferPos = size - pos; @@ -1269,19 +830,6 @@ public final class CodedInputStream { * limit was reached. */ public void skipRawBytes(final int size) throws IOException { - if (size <= (bufferSize - bufferPos) && size >= 0) { - // We have all the bytes we need already. - bufferPos += size; - } else { - skipRawBytesSlowPath(size); - } - } - - /** - * Exactly like skipRawBytes, but caller must have already checked the fast - * path: (size <= (bufferSize - pos) && size >= 0) - */ - private void skipRawBytesSlowPath(final int size) throws IOException { if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } @@ -1293,19 +841,25 @@ public final class CodedInputStream { throw InvalidProtocolBufferException.truncatedMessage(); } - // Skipping more bytes than are in the buffer. First skip what we have. - int pos = bufferSize - bufferPos; - bufferPos = bufferSize; + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + bufferPos += size; + } else { + // Skipping more bytes than are in the buffer. First skip what we have. + int pos = bufferSize - bufferPos; + totalBytesRetired += bufferSize; + bufferPos = 0; + bufferSize = 0; - // Keep refilling the buffer until we get to the point we wanted to skip to. - // This has the side effect of ensuring the limits are updated correctly. - refillBuffer(1); - while (size - pos > bufferSize) { - pos += bufferSize; - bufferPos = bufferSize; - refillBuffer(1); + // Then skip directly from the InputStream for the rest. + while (pos < size) { + final int n = (input == null) ? -1 : (int) input.skip(size - pos); + if (n <= 0) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + pos += n; + totalBytesRetired += n; + } } - - bufferPos = size - pos; } } diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java index fafe035..58dd150 100644 --- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,10 +30,9 @@ package com.google.protobuf; -import java.io.IOException; import java.io.OutputStream; +import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; /** * Encodes and writes protocol message fields. @@ -53,7 +52,6 @@ public final class CodedOutputStream { private final byte[] buffer; private final int limit; private int position; - private int totalBytesWritten = 0; private final OutputStream output; @@ -130,38 +128,6 @@ public final class CodedOutputStream { return new CodedOutputStream(flatArray, offset, length); } - /** - * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer. - */ - public static CodedOutputStream newInstance(ByteBuffer byteBuffer) { - return newInstance(byteBuffer, DEFAULT_BUFFER_SIZE); - } - - /** - * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer. - */ - public static CodedOutputStream newInstance(ByteBuffer byteBuffer, - int bufferSize) { - return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize); - } - - private static class ByteBufferOutputStream extends OutputStream { - private final ByteBuffer byteBuffer; - public ByteBufferOutputStream(ByteBuffer byteBuffer) { - this.byteBuffer = byteBuffer; - } - - @Override - public void write(int b) throws IOException { - byteBuffer.put((byte) b); - } - - @Override - public void write(byte[] data, int offset, int length) throws IOException { - byteBuffer.put(data, offset, length); - } - } - // ----------------------------------------------------------------- /** Write a {@code double} field, including tag, to the stream. */ @@ -235,7 +201,6 @@ public final class CodedOutputStream { writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); } - /** * Write a group represented by an {@link UnknownFieldSet}. * @@ -256,7 +221,6 @@ public final class CodedOutputStream { writeMessageNoTag(value); } - /** Write a {@code bytes} field, including tag, to the stream. */ public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { @@ -264,39 +228,6 @@ public final class CodedOutputStream { writeBytesNoTag(value); } - /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, final byte[] value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteArrayNoTag(value); - } - - /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, - final byte[] value, - final int offset, - final int length) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteArrayNoTag(value, offset, length); - } - - /** - * Write a {@code bytes} field, including tag, to the stream. - * This method will write all content of the ByteBuffer regardless of the - * current position and limit (i.e., the number of bytes to be written is - * value.capacity(), not value.remaining()). Furthermore, this method doesn't - * alter the state of the passed-in ByteBuffer. Its position, limit, mark, - * etc. will remain unchanged. If you only want to write the remaining bytes - * of a ByteBuffer, you can call - * {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}. - */ - public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeByteBufferNoTag(value); - } - /** Write a {@code uint32} field, including tag, to the stream. */ public void writeUInt32(final int fieldNumber, final int value) throws IOException { @@ -430,7 +361,6 @@ public final class CodedOutputStream { value.writeTo(this); } - /** * Write a group represented by an {@link UnknownFieldSet}. * @@ -449,39 +379,11 @@ public final class CodedOutputStream { value.writeTo(this); } - /** Write a {@code bytes} field to the stream. */ public void writeBytesNoTag(final ByteString value) throws IOException { - writeRawVarint32(value.size()); - writeRawBytes(value); - } - - /** Write a {@code bytes} field to the stream. */ - public void writeByteArrayNoTag(final byte[] value) throws IOException { - writeRawVarint32(value.length); - writeRawBytes(value); - } - - /** Write a {@code bytes} field to the stream. */ - public void writeByteArrayNoTag(final byte[] value, - final int offset, - final int length) throws IOException { - writeRawVarint32(length); - writeRawBytes(value, offset, length); - } - - /** - * Write a {@code bytes} field to the stream. This method will write all - * content of the ByteBuffer regardless of the current position and limit - * (i.e., the number of bytes to be written is value.capacity(), not - * value.remaining()). Furthermore, this method doesn't alter the state of - * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain - * unchanged. If you only want to write the remaining bytes of a ByteBuffer, - * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}. - */ - public void writeByteBufferNoTag(final ByteBuffer value) throws IOException { - writeRawVarint32(value.capacity()); - writeRawBytes(value); + final byte[] bytes = value.toByteArray(); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); } /** Write a {@code uint32} field to the stream. */ @@ -494,7 +396,7 @@ public final class CodedOutputStream { * for converting the enum value to its numeric value. */ public void writeEnumNoTag(final int value) throws IOException { - writeInt32NoTag(value); + writeRawVarint32(value); } /** Write an {@code sfixed32} field to the stream. */ @@ -639,33 +541,6 @@ public final class CodedOutputStream { /** * Compute the number of bytes that would be needed to encode a - * {@code bytes} field, including tag. - */ - public static int computeByteArraySize(final int fieldNumber, - final byte[] value) { - return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bytes} field, including tag. - */ - public static int computeByteBufferSize(final int fieldNumber, - final ByteBuffer value) { - return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * embedded message in lazy field, including tag. - */ - public static int computeLazyFieldSize(final int fieldNumber, - final LazyFieldLite value) { - return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode a * {@code uint32} field, including tag. */ public static int computeUInt32Size(final int fieldNumber, final int value) { @@ -739,18 +614,6 @@ public final class CodedOutputStream { computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); } - /** - * Compute the number of bytes that would be needed to encode an - * lazily parsed MessageSet extension field to the stream. For - * historical reasons, the wire format differs from normal fields. - */ - public static int computeLazyFieldMessageSetExtensionSize( - final int fieldNumber, final LazyFieldLite value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); - } - // ----------------------------------------------------------------- /** @@ -867,15 +730,6 @@ public final class CodedOutputStream { } /** - * Compute the number of bytes that would be needed to encode an embedded - * message stored in lazy field. - */ - public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) { - final int size = value.getSerializedSize(); - return computeRawVarint32Size(size) + size; - } - - /** * Compute the number of bytes that would be needed to encode a * {@code bytes} field. */ @@ -886,22 +740,6 @@ public final class CodedOutputStream { /** * Compute the number of bytes that would be needed to encode a - * {@code bytes} field. - */ - public static int computeByteArraySizeNoTag(final byte[] value) { - return computeRawVarint32Size(value.length) + value.length; - } - - /** - * Compute the number of bytes that would be needed to encode a - * {@code bytes} field. - */ - public static int computeByteBufferSizeNoTag(final ByteBuffer value) { - return computeRawVarint32Size(value.capacity()) + value.capacity(); - } - - /** - * Compute the number of bytes that would be needed to encode a * {@code uint32} field. */ public static int computeUInt32SizeNoTag(final int value) { @@ -913,7 +751,7 @@ public final class CodedOutputStream { * Caller is responsible for converting the enum value to its numeric value. */ public static int computeEnumSizeNoTag(final int value) { - return computeInt32SizeNoTag(value); + return computeRawVarint32Size(value); } /** @@ -1018,15 +856,6 @@ public final class CodedOutputStream { } } - /** - * Get the total number of bytes successfully written to this stream. The - * returned value is not guaranteed to be accurate if exceptions have been - * found in the middle of writing. - */ - public int getTotalBytesWritten() { - return totalBytesWritten; - } - /** Write a single byte. */ public void writeRawByte(final byte value) throws IOException { if (position == limit) { @@ -1034,7 +863,6 @@ public final class CodedOutputStream { } buffer[position++] = value; - ++totalBytesWritten; } /** Write a single byte, represented by an integer value. */ @@ -1042,71 +870,11 @@ public final class CodedOutputStream { writeRawByte((byte) value); } - /** Write a byte string. */ - public void writeRawBytes(final ByteString value) throws IOException { - writeRawBytes(value, 0, value.size()); - } - /** Write an array of bytes. */ public void writeRawBytes(final byte[] value) throws IOException { writeRawBytes(value, 0, value.length); } - /** - * Write a ByteBuffer. This method will write all content of the ByteBuffer - * regardless of the current position and limit (i.e., the number of bytes - * to be written is value.capacity(), not value.remaining()). Furthermore, - * this method doesn't alter the state of the passed-in ByteBuffer. Its - * position, limit, mark, etc. will remain unchanged. If you only want to - * write the remaining bytes of a ByteBuffer, you can call - * {@code writeRawBytes(byteBuffer.slice())}. - */ - public void writeRawBytes(final ByteBuffer value) throws IOException { - if (value.hasArray()) { - writeRawBytes(value.array(), value.arrayOffset(), value.capacity()); - } else { - ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); - writeRawBytesInternal(duplicated); - } - } - - /** Write a ByteBuffer that isn't backed by an array. */ - private void writeRawBytesInternal(final ByteBuffer value) - throws IOException { - int length = value.remaining(); - if (limit - position >= length) { - // We have room in the current buffer. - value.get(buffer, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - value.get(buffer, position, bytesWritten); - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); - - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - while (length > limit) { - // Copy data into the buffer before writing it to OutputStream. - // TODO(xiaofeng): Introduce ZeroCopyOutputStream to avoid this copy. - value.get(buffer, 0, limit); - output.write(buffer, 0, limit); - length -= limit; - totalBytesWritten += limit; - } - value.get(buffer, 0, length); - position = length; - totalBytesWritten += length; - } - } - /** Write part of an array of bytes. */ public void writeRawBytes(final byte[] value, int offset, int length) throws IOException { @@ -1114,7 +882,6 @@ public final class CodedOutputStream { // We have room in the current buffer. System.arraycopy(value, offset, buffer, position, length); position += length; - totalBytesWritten += length; } else { // Write extends past current buffer. Fill the rest of this buffer and // flush. @@ -1123,7 +890,6 @@ public final class CodedOutputStream { offset += bytesWritten; length -= bytesWritten; position = limit; - totalBytesWritten += bytesWritten; refreshBuffer(); // Now deal with the rest. @@ -1137,40 +903,6 @@ public final class CodedOutputStream { // Write is very big. Let's do it all at once. output.write(value, offset, length); } - totalBytesWritten += length; - } - } - - /** Write part of a byte string. */ - public void writeRawBytes(final ByteString value, int offset, int length) - throws IOException { - if (limit - position >= length) { - // We have room in the current buffer. - value.copyTo(buffer, offset, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - value.copyTo(buffer, offset, position, bytesWritten); - offset += bytesWritten; - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); - - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - if (length <= limit) { - // Fits in new buffer. - value.copyTo(buffer, offset, 0, length); - position = length; - } else { - value.writeTo(output, offset, length); - } - totalBytesWritten += length; } } diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index caae0f7..c5e9a04 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -32,15 +32,11 @@ package com.google.protobuf; import com.google.protobuf.DescriptorProtos.*; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.logging.Logger; import java.io.UnsupportedEncodingException; /** @@ -50,11 +46,6 @@ import java.io.UnsupportedEncodingException; * its fields and other information about a type. You can get a message * type's descriptor by calling {@code MessageType.getDescriptor()}, or * (given a message object of the type) {@code message.getDescriptorForType()}. - * Furthermore, each message is associated with a {@link FileDescriptor} for - * a relevant {@code .proto} file. You can obtain it by calling - * {@code Descriptor.getFile()}. A {@link FileDescriptor} contains descriptors - * for all the messages defined in that file, and file descriptors for all the - * imported {@code .proto} files. * * Descriptors are built from DescriptorProtos, as defined in * {@code google/protobuf/descriptor.proto}. @@ -62,27 +53,16 @@ import java.io.UnsupportedEncodingException; * @author kenton@google.com Kenton Varda */ public final class Descriptors { - private static final Logger logger = - Logger.getLogger(Descriptors.class.getName()); /** * Describes a {@code .proto} file, including everything defined within. - * That includes, in particular, descriptors for all the messages and - * file descriptors for all other imported {@code .proto} files - * (dependencies). */ - public static final class FileDescriptor extends GenericDescriptor { + public static final class FileDescriptor { /** Convert the descriptor to its protocol message representation. */ public FileDescriptorProto toProto() { return proto; } /** Get the file name. */ public String getName() { return proto.getName(); } - /** Returns this object. */ - public FileDescriptor getFile() { return this; } - - /** Returns the same as getName(). */ - public String getFullName() { return proto.getName(); } - /** * Get the proto package name. This is the package name given by the * {@code package} statement in the {@code .proto} file, which differs @@ -118,11 +98,6 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(dependencies)); } - /** Get a list of this file's public dependencies (public imports). */ - public List<FileDescriptor> getPublicDependencies() { - return Collections.unmodifiableList(Arrays.asList(publicDependencies)); - } - /** * Find a message type in the file by name. Does not find nested types. * @@ -223,7 +198,8 @@ public final class Descriptors { * * @param proto The protocol message form of the FileDescriptor. * @param dependencies {@code FileDescriptor}s corresponding to all of - * the file's dependencies. + * the file's dependencies, in the exact order listed + * in {@code proto}. * @throws DescriptorValidationException {@code proto} is not a valid * descriptor. This can occur for a number of reasons, e.g. * because a field has an undefined type or because two messages @@ -232,29 +208,7 @@ public final class Descriptors { public static FileDescriptor buildFrom(final FileDescriptorProto proto, final FileDescriptor[] dependencies) throws DescriptorValidationException { - return buildFrom(proto, dependencies, false); - } - - - /** - * Construct a {@code FileDescriptor}. - * - * @param proto The protocol message form of the FileDescriptor. - * @param dependencies {@code FileDescriptor}s corresponding to all of - * the file's dependencies. - * @param allowUnknownDependencies If true, non-exist dependenncies will be - * ignored and undefined message types will be replaced with a - * placeholder type. - * @throws DescriptorValidationException {@code proto} is not a valid - * descriptor. This can occur for a number of reasons, e.g. - * because a field has an undefined type or because two messages - * were defined with the same name. - */ - private static FileDescriptor buildFrom( - final FileDescriptorProto proto, final FileDescriptor[] dependencies, - final boolean allowUnknownDependencies) - throws DescriptorValidationException { - // Building descriptors involves two steps: translating and linking. + // Building decsriptors involves two steps: translating and linking. // In the translation step (implemented by FileDescriptor's // constructor), we build an object tree mirroring the // FileDescriptorProto's tree and put all of the descriptors into the @@ -263,10 +217,23 @@ public final class Descriptors { // FieldDescriptor for an embedded message contains a pointer directly // to the Descriptor for that message's type. We also detect undefined // types in the linking step. - final DescriptorPool pool = new DescriptorPool( - dependencies, allowUnknownDependencies); - final FileDescriptor result = new FileDescriptor( - proto, dependencies, pool, allowUnknownDependencies); + final DescriptorPool pool = new DescriptorPool(dependencies); + final FileDescriptor result = + new FileDescriptor(proto, dependencies, pool); + + if (dependencies.length != proto.getDependencyCount()) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + for (int i = 0; i < proto.getDependencyCount(); i++) { + if (!dependencies[i].getName().equals(proto.getDependency(i))) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + } + result.crossLink(); return result; } @@ -314,9 +281,7 @@ public final class Descriptors { final FileDescriptor result; try { - // When building descriptors for generated code, we allow unknown - // dependencies by default. - result = buildFrom(proto, dependencies, true); + result = buildFrom(proto, dependencies); } catch (DescriptorValidationException e) { throw new IllegalArgumentException( "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); @@ -340,66 +305,16 @@ public final class Descriptors { } /** - * This method is to be called by generated code only. It uses Java - * reflection to load the dependencies' descriptors. - */ - public static void internalBuildGeneratedFileFrom( - final String[] descriptorDataParts, - final Class<?> descriptorOuterClass, - final String[] dependencies, - final String[] dependencyFileNames, - final InternalDescriptorAssigner descriptorAssigner) { - List<FileDescriptor> descriptors = new ArrayList<FileDescriptor>(); - for (int i = 0; i < dependencies.length; i++) { - try { - Class<?> clazz = - descriptorOuterClass.getClassLoader().loadClass(dependencies[i]); - descriptors.add( - (FileDescriptor) clazz.getField("descriptor").get(null)); - } catch (Exception e) { - // We allow unknown dependencies by default. If a dependency cannot - // be found we only generate a warning. - logger.warning("Descriptors for \"" + dependencyFileNames[i] + - "\" can not be found."); - } - } - FileDescriptor[] descriptorArray = new FileDescriptor[descriptors.size()]; - descriptors.toArray(descriptorArray); - internalBuildGeneratedFileFrom( - descriptorDataParts, descriptorArray, descriptorAssigner); - } - - /** - * This method is to be called by generated code only. It is used to - * update the FileDescriptorProto associated with the descriptor by - * parsing it again with the given ExtensionRegistry. This is needed to - * recognize custom options. - */ - public static void internalUpdateFileDescriptor( - final FileDescriptor descriptor, - final ExtensionRegistry registry) { - ByteString bytes = descriptor.proto.toByteString(); - FileDescriptorProto proto; - try { - proto = FileDescriptorProto.parseFrom(bytes, registry); - } catch (InvalidProtocolBufferException e) { - throw new IllegalArgumentException( - "Failed to parse protocol buffer descriptor for generated code.", e); - } - descriptor.setProto(proto); - } - - /** * This class should be used by generated code only. When calling * {@link FileDescriptor#internalBuildGeneratedFileFrom}, the caller * provides a callback implementing this interface. The callback is called * after the FileDescriptor has been constructed, in order to assign all - * the global variables defined in the generated code which point at parts + * the global variales defined in the generated code which point at parts * of the FileDescriptor. The callback returns an ExtensionRegistry which * contains any extensions which might be used in the descriptor -- that * is, extensions of the various "Options" messages defined in * descriptor.proto. The callback may also return null to indicate that - * no extensions are used in the descriptor. + * no extensions are used in the decsriptor. */ public interface InternalDescriptorAssigner { ExtensionRegistry assignDescriptors(FileDescriptor root); @@ -411,43 +326,15 @@ public final class Descriptors { private final ServiceDescriptor[] services; private final FieldDescriptor[] extensions; private final FileDescriptor[] dependencies; - private final FileDescriptor[] publicDependencies; private final DescriptorPool pool; private FileDescriptor(final FileDescriptorProto proto, final FileDescriptor[] dependencies, - final DescriptorPool pool, - boolean allowUnknownDependencies) + final DescriptorPool pool) throws DescriptorValidationException { this.pool = pool; this.proto = proto; this.dependencies = dependencies.clone(); - HashMap<String, FileDescriptor> nameToFileMap = - new HashMap<String, FileDescriptor>(); - for (FileDescriptor file : dependencies) { - nameToFileMap.put(file.getName(), file); - } - List<FileDescriptor> publicDependencies = new ArrayList<FileDescriptor>(); - for (int i = 0; i < proto.getPublicDependencyCount(); i++) { - int index = proto.getPublicDependency(i); - if (index < 0 || index >= proto.getDependencyCount()) { - throw new DescriptorValidationException(this, - "Invalid public dependency index."); - } - String name = proto.getDependency(index); - FileDescriptor file = nameToFileMap.get(name); - if (file == null) { - if (!allowUnknownDependencies) { - throw new DescriptorValidationException(this, - "Invalid public dependency: " + name); - } - // Ignore unknown dependencies. - } else { - publicDependencies.add(file); - } - } - this.publicDependencies = new FileDescriptor[publicDependencies.size()]; - publicDependencies.toArray(this.publicDependencies); pool.addPackage(getPackage(), this); @@ -473,27 +360,6 @@ public final class Descriptors { proto.getExtension(i), this, null, i, true); } } - - /** - * Create a placeholder FileDescriptor for a message Descriptor. - */ - FileDescriptor(String packageName, Descriptor message) - throws DescriptorValidationException { - this.pool = new DescriptorPool(new FileDescriptor[0], true); - this.proto = FileDescriptorProto.newBuilder() - .setName(message.getFullName() + ".placeholder.proto") - .setPackage(packageName).addMessageType(message.toProto()).build(); - this.dependencies = new FileDescriptor[0]; - this.publicDependencies = new FileDescriptor[0]; - - messageTypes = new Descriptor[] {message}; - enumTypes = new EnumDescriptor[0]; - services = new ServiceDescriptor[0]; - extensions = new FieldDescriptor[0]; - - pool.addPackage(packageName, this); - pool.addSymbol(message); - } /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { @@ -516,7 +382,7 @@ public final class Descriptors { * in the original. This method is needed for bootstrapping when a file * defines custom options. The options may be defined in the file itself, * so we can't actually parse them until we've constructed the descriptors, - * but to construct the descriptors we have to have parsed the descriptor + * but to construct the decsriptors we have to have parsed the descriptor * protos. So, we have to parse the descriptor protos a second time after * constructing the descriptors. */ @@ -544,7 +410,7 @@ public final class Descriptors { // ================================================================= /** Describes a message type. */ - public static final class Descriptor extends GenericDescriptor { + public static final class Descriptor implements GenericDescriptor { /** * Get the index of this descriptor within its parent. In other words, * given a {@link FileDescriptor} {@code file}, the following is true: @@ -593,11 +459,6 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(fields)); } - /** Get a list of this message type's oneofs. */ - public List<OneofDescriptor> getOneofs() { - return Collections.unmodifiableList(Arrays.asList(oneofs)); - } - /** Get a list of this message type's extensions. */ public List<FieldDescriptor> getExtensions() { return Collections.unmodifiableList(Arrays.asList(extensions)); @@ -625,14 +486,6 @@ public final class Descriptors { } /** - * Indicates whether the message can be extended. That is, whether it has - * any "extensions x to y" ranges declared on it. - */ - public boolean isExtendable() { - return proto.getExtensionRangeList().size() != 0; - } - - /** * Finds a field by name. * @param name The unqualified name of the field (e.g. "foo"). * @return The field's descriptor, or {@code null} if not found. @@ -696,33 +549,6 @@ public final class Descriptors { private final EnumDescriptor[] enumTypes; private final FieldDescriptor[] fields; private final FieldDescriptor[] extensions; - private final OneofDescriptor[] oneofs; - - // Used to create a placeholder when the type cannot be found. - Descriptor(final String fullname) throws DescriptorValidationException { - String name = fullname; - String packageName = ""; - int pos = fullname.lastIndexOf('.'); - if (pos != -1) { - name = fullname.substring(pos + 1); - packageName = fullname.substring(0, pos); - } - this.index = 0; - this.proto = DescriptorProto.newBuilder().setName(name).addExtensionRange( - DescriptorProto.ExtensionRange.newBuilder().setStart(1) - .setEnd(536870912).build()).build(); - this.fullName = fullname; - this.containingType = null; - - this.nestedTypes = new Descriptor[0]; - this.enumTypes = new EnumDescriptor[0]; - this.fields = new FieldDescriptor[0]; - this.extensions = new FieldDescriptor[0]; - this.oneofs = new OneofDescriptor[0]; - - // Create a placeholder FileDescriptor to hold this message. - this.file = new FileDescriptor(packageName, this); - } private Descriptor(final DescriptorProto proto, final FileDescriptor file, @@ -735,12 +561,6 @@ public final class Descriptors { this.file = file; containingType = parent; - oneofs = new OneofDescriptor[proto.getOneofDeclCount()]; - for (int i = 0; i < proto.getOneofDeclCount(); i++) { - oneofs[i] = new OneofDescriptor( - proto.getOneofDecl(i), file, this, i); - } - nestedTypes = new Descriptor[proto.getNestedTypeCount()]; for (int i = 0; i < proto.getNestedTypeCount(); i++) { nestedTypes[i] = new Descriptor( @@ -765,17 +585,6 @@ public final class Descriptors { proto.getExtension(i), file, this, i, true); } - for (int i = 0; i < proto.getOneofDeclCount(); i++) { - oneofs[i].fields = new FieldDescriptor[oneofs[i].getFieldCount()]; - oneofs[i].fieldCount = 0; - } - for (int i = 0; i < proto.getFieldCount(); i++) { - OneofDescriptor oneofDescriptor = fields[i].getContainingOneof(); - if (oneofDescriptor != null) { - oneofDescriptor.fields[oneofDescriptor.fieldCount++] = fields[i]; - } - } - file.pool.addSymbol(this); } @@ -820,12 +629,11 @@ public final class Descriptors { /** Describes a field of a message type. */ public static final class FieldDescriptor - extends GenericDescriptor - implements Comparable<FieldDescriptor>, + implements GenericDescriptor, Comparable<FieldDescriptor>, FieldSet.FieldDescriptorLite<FieldDescriptor> { /** * Get the index of this descriptor within its parent. - * @see Descriptors.Descriptor#getIndex() + * @see Descriptor#getIndex() */ public int getIndex() { return index; } @@ -840,7 +648,7 @@ public final class Descriptors { /** * Get the field's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -865,12 +673,6 @@ public final class Descriptors { public WireFormat.FieldType getLiteType() { return table[type.ordinal()]; } - - /** For internal use only. */ - public boolean needsUtf8Check() { - return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8()); - } - // 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. @@ -932,9 +734,6 @@ public final class Descriptors { */ public Descriptor getContainingType() { return containingType; } - /** Get the field's containing oneof. */ - public OneofDescriptor getContainingOneof() { return containingOneof; } - /** * For extensions defined nested within message types, gets the outer * type. Not valid for non-extension fields. For example, consider @@ -1012,7 +811,6 @@ public final class Descriptors { private Type type; private Descriptor containingType; private Descriptor messageType; - private OneofDescriptor containingOneof; private EnumDescriptor enumType; private Object defaultValue; @@ -1103,6 +901,13 @@ public final class Descriptors { "Field numbers must be positive integers."); } + // Only repeated primitive fields may be packed. + if (proto.getOptions().getPacked() && !isPackable()) { + throw new DescriptorValidationException(this, + "[packed = true] can only be specified for repeated primitive " + + "fields."); + } + if (isExtension) { if (!proto.hasExtendee()) { throw new DescriptorValidationException(this, @@ -1114,31 +919,12 @@ public final class Descriptors { } else { extensionScope = null; } - - if (proto.hasOneofIndex()) { - throw new DescriptorValidationException(this, - "FieldDescriptorProto.oneof_index set for extension field."); - } - containingOneof = null; } else { if (proto.hasExtendee()) { throw new DescriptorValidationException(this, "FieldDescriptorProto.extendee set for non-extension field."); } containingType = parent; - - if (proto.hasOneofIndex()) { - if (proto.getOneofIndex() < 0 || - proto.getOneofIndex() >= parent.toProto().getOneofDeclCount()) { - throw new DescriptorValidationException(this, - "FieldDescriptorProto.oneof_index is out of range for type " - + parent.getName()); - } - containingOneof = parent.getOneofs().get(proto.getOneofIndex()); - containingOneof.fieldCount++; - } else { - containingOneof = null; - } extensionScope = null; } @@ -1149,8 +935,7 @@ public final class Descriptors { private void crossLink() throws DescriptorValidationException { if (proto.hasExtendee()) { final GenericDescriptor extendee = - file.pool.lookupSymbol(proto.getExtendee(), this, - DescriptorPool.SearchFilter.TYPES_ONLY); + file.pool.lookupSymbol(proto.getExtendee(), this); if (!(extendee instanceof Descriptor)) { throw new DescriptorValidationException(this, '\"' + proto.getExtendee() + "\" is not a message type."); @@ -1167,8 +952,7 @@ public final class Descriptors { if (proto.hasTypeName()) { final GenericDescriptor typeDescriptor = - file.pool.lookupSymbol(proto.getTypeName(), this, - DescriptorPool.SearchFilter.TYPES_ONLY); + file.pool.lookupSymbol(proto.getTypeName(), this); if (!proto.hasType()) { // Choose field type based on symbol. @@ -1211,13 +995,6 @@ public final class Descriptors { } } - // Only repeated primitive fields may be packed. - if (proto.getOptions().getPacked() && !isPackable()) { - throw new DescriptorValidationException(this, - "[packed = true] can only be specified for repeated primitive " + - "fields."); - } - // We don't attempt to parse the default value until here because for // enums we need the enum type's descriptor. if (proto.hasDefaultValue()) { @@ -1355,17 +1132,16 @@ public final class Descriptors { // down-cast and call mergeFrom directly. return ((Message.Builder) to).mergeFrom((Message) from); } - } // ================================================================= /** Describes an enum type. */ - public static final class EnumDescriptor extends GenericDescriptor - implements Internal.EnumLiteMap<EnumValueDescriptor> { + public static final class EnumDescriptor + implements GenericDescriptor, Internal.EnumLiteMap<EnumValueDescriptor> { /** * Get the index of this descriptor within its parent. - * @see Descriptors.Descriptor#getIndex() + * @see Descriptor#getIndex() */ public int getIndex() { return index; } @@ -1377,7 +1153,7 @@ public final class Descriptors { /** * Get the type's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1398,7 +1174,7 @@ public final class Descriptors { /** * Find an enum value by name. * @param name The unqualified name of the value (e.g. "FOO"). - * @return the value's descriptor, or {@code null} if not found. + * @return the value's decsriptor, or {@code null} if not found. */ public EnumValueDescriptor findValueByName(final String name) { final GenericDescriptor result = @@ -1414,7 +1190,7 @@ public final class Descriptors { * Find an enum value by number. If multiple enum values have the same * number, this returns the first defined value with that number. * @param number The value's number. - * @return the value's descriptor, or {@code null} if not found. + * @return the value's decsriptor, or {@code null} if not found. */ public EnumValueDescriptor findValueByNumber(final int number) { return file.pool.enumValuesByNumber.get( @@ -1473,11 +1249,11 @@ public final class Descriptors { * with the same number after the first become aliases of the first. * However, they still have independent EnumValueDescriptors. */ - public static final class EnumValueDescriptor extends GenericDescriptor - implements Internal.EnumLite { + public static final class EnumValueDescriptor + implements GenericDescriptor, Internal.EnumLite { /** * Get the index of this descriptor within its parent. - * @see Descriptors.Descriptor#getIndex() + * @see Descriptor#getIndex() */ public int getIndex() { return index; } @@ -1489,13 +1265,10 @@ public final class Descriptors { /** Get the value's number. */ public int getNumber() { return proto.getNumber(); } - - @Override - public String toString() { return proto.getName(); } /** * Get the value's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1541,7 +1314,7 @@ public final class Descriptors { // ================================================================= /** Describes a service type. */ - public static final class ServiceDescriptor extends GenericDescriptor { + public static final class ServiceDescriptor implements GenericDescriptor { /** * Get the index of this descriptor within its parent. * * @see Descriptors.Descriptor#getIndex() @@ -1556,7 +1329,7 @@ public final class Descriptors { /** * Get the type's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1574,7 +1347,7 @@ public final class Descriptors { /** * Find a method by name. * @param name The unqualified name of the method (e.g. "Foo"). - * @return the method's descriptor, or {@code null} if not found. + * @return the method's decsriptor, or {@code null} if not found. */ public MethodDescriptor findMethodByName(final String name) { final GenericDescriptor result = @@ -1631,7 +1404,7 @@ public final class Descriptors { /** * Describes one method within a service type. */ - public static final class MethodDescriptor extends GenericDescriptor { + public static final class MethodDescriptor implements GenericDescriptor { /** * Get the index of this descriptor within its parent. * * @see Descriptors.Descriptor#getIndex() @@ -1646,7 +1419,7 @@ public final class Descriptors { /** * Get the method's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1694,8 +1467,7 @@ public final class Descriptors { private void crossLink() throws DescriptorValidationException { final GenericDescriptor input = - file.pool.lookupSymbol(proto.getInputType(), this, - DescriptorPool.SearchFilter.TYPES_ONLY); + file.pool.lookupSymbol(proto.getInputType(), this); if (!(input instanceof Descriptor)) { throw new DescriptorValidationException(this, '\"' + proto.getInputType() + "\" is not a message type."); @@ -1703,8 +1475,7 @@ public final class Descriptors { inputType = (Descriptor)input; final GenericDescriptor output = - file.pool.lookupSymbol(proto.getOutputType(), this, - DescriptorPool.SearchFilter.TYPES_ONLY); + file.pool.lookupSymbol(proto.getOutputType(), this); if (!(output instanceof Descriptor)) { throw new DescriptorValidationException(this, '\"' + proto.getOutputType() + "\" is not a message type."); @@ -1735,18 +1506,14 @@ public final class Descriptors { // ================================================================= /** - * All descriptors implement this to make it easier to implement tools like - * {@code DescriptorPool}.<p> - * - * This class is public so that the methods it exposes can be called from - * outside of this package. However, it should only be subclassed from - * nested classes of Descriptors. + * All descriptors except {@code FileDescriptor} implement this to make + * {@code DescriptorPool}'s life easier. */ - public abstract static class GenericDescriptor { - public abstract Message toProto(); - public abstract String getName(); - public abstract String getFullName(); - public abstract FileDescriptor getFile(); + private interface GenericDescriptor { + Message toProto(); + String getName(); + String getFullName(); + FileDescriptor getFile(); } /** @@ -1760,7 +1527,7 @@ public final class Descriptors { public String getProblemSymbolName() { return name; } /** - * Gets the protocol message representation of the invalid descriptor. + * Gets the the protocol message representation of the invalid descriptor. */ public Message getProblemProto() { return proto; } @@ -1815,24 +1582,14 @@ public final class Descriptors { * descriptors defined in a particular file. */ private static final class DescriptorPool { - - /** Defines what subclass of descriptors to search in the descriptor pool. - */ - enum SearchFilter { - TYPES_ONLY, AGGREGATES_ONLY, ALL_SYMBOLS - } - - DescriptorPool(final FileDescriptor[] dependencies, - boolean allowUnknownDependencies) { - this.dependencies = new HashSet<FileDescriptor>(); - this.allowUnknownDependencies = allowUnknownDependencies; + DescriptorPool(final FileDescriptor[] dependencies) { + this.dependencies = new DescriptorPool[dependencies.length]; - for (int i = 0; i < dependencies.length; i++) { - this.dependencies.add(dependencies[i]); - importPublicDependencies(dependencies[i]); + for (int i = 0; i < dependencies.length; i++) { + this.dependencies[i] = dependencies[i].pool; } - for (final FileDescriptor dependency : this.dependencies) { + for (final FileDescriptor dependency : dependencies) { try { addPackage(dependency.getPackage(), dependency); } catch (DescriptorValidationException e) { @@ -1844,17 +1601,7 @@ public final class Descriptors { } } - /** Find and put public dependencies of the file into dependencies set.*/ - private void importPublicDependencies(final FileDescriptor file) { - for (FileDescriptor dependency : file.getPublicDependencies()) { - if (dependencies.add(dependency)) { - importPublicDependencies(dependency); - } - } - } - - private final Set<FileDescriptor> dependencies; - private boolean allowUnknownDependencies; + private final DescriptorPool[] dependencies; private final Map<String, GenericDescriptor> descriptorsByName = new HashMap<String, GenericDescriptor>(); @@ -1865,83 +1612,39 @@ public final class Descriptors { /** Find a generic descriptor by fully-qualified name. */ GenericDescriptor findSymbol(final String fullName) { - return findSymbol(fullName, SearchFilter.ALL_SYMBOLS); - } - - /** Find a descriptor by fully-qualified name and given option to only - * search valid field type descriptors. - */ - GenericDescriptor findSymbol(final String fullName, - final SearchFilter filter) { GenericDescriptor result = descriptorsByName.get(fullName); if (result != null) { - if ((filter==SearchFilter.ALL_SYMBOLS) || - ((filter==SearchFilter.TYPES_ONLY) && isType(result)) || - ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { - return result; - } + return result; } - for (final FileDescriptor dependency : dependencies) { - result = dependency.pool.descriptorsByName.get(fullName); + for (final DescriptorPool dependency : dependencies) { + result = dependency.descriptorsByName.get(fullName); if (result != null) { - if ((filter==SearchFilter.ALL_SYMBOLS) || - ((filter==SearchFilter.TYPES_ONLY) && isType(result)) || - ((filter==SearchFilter.AGGREGATES_ONLY) && isAggregate(result))) { - return result; - } + return result; } } return null; } - /** Checks if the descriptor is a valid type for a message field. */ - boolean isType(GenericDescriptor descriptor) { - return (descriptor instanceof Descriptor) || - (descriptor instanceof EnumDescriptor); - } - - /** Checks if the descriptor is a valid namespace type. */ - boolean isAggregate(GenericDescriptor descriptor) { - return (descriptor instanceof Descriptor) || - (descriptor instanceof EnumDescriptor) || - (descriptor instanceof PackageDescriptor) || - (descriptor instanceof ServiceDescriptor); - } - /** - * Look up a type descriptor by name, relative to some other descriptor. + * Look up a descriptor by name, relative to some other descriptor. * The name may be fully-qualified (with a leading '.'), * partially-qualified, or unqualified. C++-like name lookup semantics * are used to search for the matching descriptor. */ GenericDescriptor lookupSymbol(final String name, - final GenericDescriptor relativeTo, - final DescriptorPool.SearchFilter filter) + final GenericDescriptor relativeTo) throws DescriptorValidationException { // TODO(kenton): This could be optimized in a number of ways. GenericDescriptor result; - String fullname; if (name.startsWith(".")) { // Fully-qualified name. - fullname = name.substring(1); - result = findSymbol(fullname, filter); + result = findSymbol(name.substring(1)); } else { // If "name" is a compound identifier, we want to search for the // first component of it, then search within it for the rest. - // If name is something like "Foo.Bar.baz", and symbols named "Foo" are - // defined in multiple parent scopes, we only want to find "Bar.baz" in - // the innermost one. E.g., the following should produce an error: - // message Bar { message Baz {} } - // message Foo { - // message Bar { - // } - // optional Bar.Baz baz = 1; - // } - // So, we look for just "Foo" first, then look for "Bar.baz" within it - // if found. final int firstPartLength = name.indexOf('.'); final String firstPart; if (firstPartLength == -1) { @@ -1959,16 +1662,14 @@ public final class Descriptors { // Chop off the last component of the scope. final int dotpos = scopeToTry.lastIndexOf("."); if (dotpos == -1) { - fullname = name; - result = findSymbol(name, filter); + result = findSymbol(name); break; } else { scopeToTry.setLength(dotpos + 1); - // Append firstPart and try to find + // Append firstPart and try to find. scopeToTry.append(firstPart); - result = findSymbol(scopeToTry.toString(), - DescriptorPool.SearchFilter.AGGREGATES_ONLY); + result = findSymbol(scopeToTry.toString()); if (result != null) { if (firstPartLength != -1) { @@ -1977,9 +1678,8 @@ public final class Descriptors { // searching parent scopes. scopeToTry.setLength(dotpos + 1); scopeToTry.append(name); - result = findSymbol(scopeToTry.toString(), filter); + result = findSymbol(scopeToTry.toString()); } - fullname = scopeToTry.toString(); break; } @@ -1990,24 +1690,8 @@ public final class Descriptors { } if (result == null) { - if (allowUnknownDependencies && filter == SearchFilter.TYPES_ONLY) { - logger.warning("The descriptor for message type \"" + name + - "\" can not be found and a placeholder is created for it"); - // We create a dummy message descriptor here regardless of the - // expected type. If the type should be message, this dummy - // descriptor will work well and if the type should be enum, a - // DescriptorValidationException will be thrown latter. In either - // case, the code works as expected: we allow unknown message types - // but not unknwon enum types. - result = new Descriptor(fullname); - // Add the placeholder file as a dependency so we can find the - // placeholder symbol when resolving other references. - this.dependencies.add(result.getFile()); - return result; - } else { - throw new DescriptorValidationException(relativeTo, - '\"' + name + "\" is not defined."); - } + throw new DescriptorValidationException(relativeTo, + '\"' + name + "\" is not defined."); } else { return result; } @@ -2051,7 +1735,7 @@ public final class Descriptors { * just as placeholders so that someone cannot define, say, a message type * that has the same name as an existing package. */ - private static final class PackageDescriptor extends GenericDescriptor { + private static final class PackageDescriptor implements GenericDescriptor { public Message toProto() { return file.toProto(); } public String getName() { return name; } public String getFullName() { return fullName; } @@ -2125,7 +1809,7 @@ public final class Descriptors { /** * Adds a field to the fieldsByNumber table. Throws an exception if a - * field with the same containing type and number already exists. + * field with hte same containing type and number already exists. */ void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException { @@ -2136,7 +1820,7 @@ public final class Descriptors { fieldsByNumber.put(key, old); throw new DescriptorValidationException(field, "Field number " + field.getNumber() + - " has already been used in \"" + + "has already been used in \"" + field.getContainingType().getFullName() + "\" by field \"" + old.getName() + "\"."); } @@ -2192,47 +1876,4 @@ public final class Descriptors { } } } - - /** Describes an oneof of a message type. */ - public static final class OneofDescriptor { - /** Get the index of this descriptor within its parent. */ - public int getIndex() { return index; } - - public String getName() { return proto.getName(); } - - public FileDescriptor getFile() { return file; } - - public String getFullName() { return fullName; } - - public Descriptor getContainingType() { return containingType; } - - public int getFieldCount() { return fieldCount; } - - public FieldDescriptor getField(int index) { - return fields[index]; - } - - private OneofDescriptor(final OneofDescriptorProto proto, - final FileDescriptor file, - final Descriptor parent, - final int index) - throws DescriptorValidationException { - this.proto = proto; - fullName = computeFullName(file, parent, proto.getName()); - this.file = file; - this.index = index; - - containingType = parent; - fieldCount = 0; - } - - private final int index; - private OneofDescriptorProto proto; - private final String fullName; - private final FileDescriptor file; - - private Descriptor containingType; - private int fieldCount; - private FieldDescriptor[] fields; - } } diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java index c9ce667..c106b66 100644 --- a/java/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -31,14 +31,10 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.OneofDescriptor; import java.io.InputStream; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Map; /** @@ -50,25 +46,16 @@ import java.util.Map; public final class DynamicMessage extends AbstractMessage { private final Descriptor type; private final FieldSet<FieldDescriptor> fields; - private final FieldDescriptor[] oneofCases; private final UnknownFieldSet unknownFields; private int memoizedSize = -1; /** * Construct a {@code DynamicMessage} using the given {@code FieldSet}. - * oneofCases stores the FieldDescriptor for each oneof to indicate - * which field is set. Caller should make sure the array is immutable. - * - * This contructor is package private and will be used in - * {@code DynamicMutableMessage} to convert a mutable message to an immutable - * message. */ - DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields, - FieldDescriptor[] oneofCases, - UnknownFieldSet unknownFields) { + private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields, + UnknownFieldSet unknownFields) { this.type = type; this.fields = fields; - this.oneofCases = oneofCases; this.unknownFields = unknownFields; } @@ -77,14 +64,10 @@ public final class DynamicMessage extends AbstractMessage { * given type. */ public static DynamicMessage getDefaultInstance(Descriptor type) { - int oneofDeclCount = type.toProto().getOneofDeclCount(); - FieldDescriptor[] oneofCases = new FieldDescriptor[oneofDeclCount]; return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(), - oneofCases, UnknownFieldSet.getDefaultInstance()); } - /** Parse a message of the given type from the given input stream. */ public static DynamicMessage parseFrom(Descriptor type, CodedInputStream input) @@ -168,20 +151,6 @@ public final class DynamicMessage extends AbstractMessage { return fields.getAllFields(); } - public boolean hasOneof(OneofDescriptor oneof) { - verifyOneofContainingType(oneof); - FieldDescriptor field = oneofCases[oneof.getIndex()]; - if (field == null) { - return false; - } - return true; - } - - public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { - verifyOneofContainingType(oneof); - return oneofCases[oneof.getIndex()]; - } - public boolean hasField(FieldDescriptor field) { verifyContainingType(field); return fields.hasField(field); @@ -191,9 +160,7 @@ public final class DynamicMessage extends AbstractMessage { verifyContainingType(field); Object result = fields.getField(field); if (result == null) { - if (field.isRepeated()) { - result = Collections.emptyList(); - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { result = getDefaultInstance(field.getMessageType()); } else { result = field.getDefaultValue(); @@ -216,8 +183,8 @@ public final class DynamicMessage extends AbstractMessage { return unknownFields; } - static boolean isInitialized(Descriptor type, - FieldSet<FieldDescriptor> fields) { + private static boolean isInitialized(Descriptor type, + FieldSet<FieldDescriptor> fields) { // Check that all required fields are present. for (final FieldDescriptor field : type.getFields()) { if (field.isRequired()) { @@ -231,12 +198,10 @@ public final class DynamicMessage extends AbstractMessage { return fields.isInitialized(); } - @Override public boolean isInitialized() { return isInitialized(type, fields); } - @Override public void writeTo(CodedOutputStream output) throws IOException { if (type.getOptions().getMessageSetWireFormat()) { fields.writeMessageSetTo(output); @@ -247,7 +212,6 @@ public final class DynamicMessage extends AbstractMessage { } } - @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; @@ -272,26 +236,6 @@ public final class DynamicMessage extends AbstractMessage { return newBuilderForType().mergeFrom(this); } - public Parser<DynamicMessage> getParserForType() { - return new AbstractParser<DynamicMessage>() { - public DynamicMessage parsePartialFrom( - CodedInputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - Builder builder = newBuilder(type); - try { - builder.mergeFrom(input, extensionRegistry); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (IOException e) { - throw new InvalidProtocolBufferException(e.getMessage()) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - }; - } - /** Verifies that the field is a field of this message. */ private void verifyContainingType(FieldDescriptor field) { if (field.getContainingType() != type) { @@ -300,14 +244,6 @@ public final class DynamicMessage extends AbstractMessage { } } - /** Verifies that the oneof is an oneof of this message. */ - private void verifyOneofContainingType(OneofDescriptor oneof) { - if (oneof.getContainingType() != type) { - throw new IllegalArgumentException( - "OneofDescriptor does not match message type."); - } - } - // ================================================================= /** @@ -316,7 +252,6 @@ public final class DynamicMessage extends AbstractMessage { public static final class Builder extends AbstractMessage.Builder<Builder> { private final Descriptor type; private FieldSet<FieldDescriptor> fields; - private final FieldDescriptor[] oneofCases; private UnknownFieldSet unknownFields; /** Construct a {@code Builder} for the given type. */ @@ -324,24 +259,19 @@ public final class DynamicMessage extends AbstractMessage { this.type = type; this.fields = FieldSet.newFieldSet(); this.unknownFields = UnknownFieldSet.getDefaultInstance(); - this.oneofCases = new FieldDescriptor[type.toProto().getOneofDeclCount()]; } // --------------------------------------------------------------- // Implementation of Message.Builder interface. - @Override public Builder clear() { - if (fields.isImmutable()) { - fields = FieldSet.newFieldSet(); - } else { - fields.clear(); + if (fields == null) { + throw new IllegalStateException("Cannot call clear() after build()."); } - unknownFields = UnknownFieldSet.getDefaultInstance(); + fields.clear(); return this; } - @Override public Builder mergeFrom(Message other) { if (other instanceof DynamicMessage) { // This should be somewhat faster than calling super.mergeFrom(). @@ -350,20 +280,8 @@ public final class DynamicMessage extends AbstractMessage { throw new IllegalArgumentException( "mergeFrom(Message) can only merge messages of the same type."); } - ensureIsMutable(); fields.mergeFrom(otherDynamicMessage.fields); mergeUnknownFields(otherDynamicMessage.unknownFields); - for (int i = 0; i < oneofCases.length; i++) { - if (oneofCases[i] == null) { - oneofCases[i] = otherDynamicMessage.oneofCases[i]; - } else { - if ((otherDynamicMessage.oneofCases[i] != null) - && (oneofCases[i] != otherDynamicMessage.oneofCases[i])) { - fields.clearField(oneofCases[i]); - oneofCases[i] = otherDynamicMessage.oneofCases[i]; - } - } - } return this; } else { return super.mergeFrom(other); @@ -371,10 +289,10 @@ public final class DynamicMessage extends AbstractMessage { } public DynamicMessage build() { - if (!isInitialized()) { + // If fields == null, we'll throw an appropriate exception later. + if (fields != null && !isInitialized()) { throw newUninitializedMessageException( - new DynamicMessage(type, fields, - java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields)); + new DynamicMessage(type, fields, unknownFields)); } return buildPartial(); } @@ -387,27 +305,28 @@ public final class DynamicMessage extends AbstractMessage { private DynamicMessage buildParsed() throws InvalidProtocolBufferException { if (!isInitialized()) { throw newUninitializedMessageException( - new DynamicMessage(type, fields, - java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields)) + new DynamicMessage(type, fields, unknownFields)) .asInvalidProtocolBufferException(); } return buildPartial(); } public DynamicMessage buildPartial() { + if (fields == null) { + throw new IllegalStateException( + "build() has already been called on this Builder."); + } fields.makeImmutable(); DynamicMessage result = - new DynamicMessage(type, fields, - java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields); + new DynamicMessage(type, fields, unknownFields); + fields = null; + unknownFields = null; return result; } - @Override public Builder clone() { Builder result = new Builder(type); result.fields.mergeFrom(fields); - result.mergeUnknownFields(unknownFields); - System.arraycopy(oneofCases, 0, result.oneofCases, 0 , oneofCases.length); return result; } @@ -438,29 +357,6 @@ public final class DynamicMessage extends AbstractMessage { return new Builder(field.getMessageType()); } - public boolean hasOneof(OneofDescriptor oneof) { - verifyOneofContainingType(oneof); - FieldDescriptor field = oneofCases[oneof.getIndex()]; - if (field == null) { - return false; - } - return true; - } - - public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) { - verifyOneofContainingType(oneof); - return oneofCases[oneof.getIndex()]; - } - - public Builder clearOneof(OneofDescriptor oneof) { - verifyOneofContainingType(oneof); - FieldDescriptor field = oneofCases[oneof.getIndex()]; - if (field != null) { - clearField(field); - } - return this; - } - public boolean hasField(FieldDescriptor field) { verifyContainingType(field); return fields.hasField(field); @@ -470,9 +366,7 @@ public final class DynamicMessage extends AbstractMessage { verifyContainingType(field); Object result = fields.getField(field); if (result == null) { - if (field.isRepeated()) { - result = Collections.emptyList(); - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { result = getDefaultInstance(field.getMessageType()); } else { result = field.getDefaultValue(); @@ -483,38 +377,12 @@ public final class DynamicMessage extends AbstractMessage { public Builder setField(FieldDescriptor field, Object value) { verifyContainingType(field); - ensureIsMutable(); - // TODO(xiaofeng): This check should really be put in FieldSet.setField() - // where all other such checks are done. However, currently - // FieldSet.setField() permits Integer value for enum fields probably - // because of some internal features we support. Should figure it out - // and move this check to a more appropriate place. - if (field.getType() == FieldDescriptor.Type.ENUM) { - ensureEnumValueDescriptor(field, value); - } - OneofDescriptor oneofDescriptor = field.getContainingOneof(); - if (oneofDescriptor != null) { - int index = oneofDescriptor.getIndex(); - FieldDescriptor oldField = oneofCases[index]; - if ((oldField != null) && (oldField != field)) { - fields.clearField(oldField); - } - oneofCases[index] = field; - } fields.setField(field, value); return this; } public Builder clearField(FieldDescriptor field) { verifyContainingType(field); - ensureIsMutable(); - OneofDescriptor oneofDescriptor = field.getContainingOneof(); - if (oneofDescriptor != null) { - int index = oneofDescriptor.getIndex(); - if (oneofCases[index] == field) { - oneofCases[index] = null; - } - } fields.clearField(field); return this; } @@ -532,14 +400,12 @@ public final class DynamicMessage extends AbstractMessage { public Builder setRepeatedField(FieldDescriptor field, int index, Object value) { verifyContainingType(field); - ensureIsMutable(); fields.setRepeatedField(field, index, value); return this; } public Builder addRepeatedField(FieldDescriptor field, Object value) { verifyContainingType(field); - ensureIsMutable(); fields.addRepeatedField(field, value); return this; } @@ -553,7 +419,6 @@ public final class DynamicMessage extends AbstractMessage { return this; } - @Override public Builder mergeUnknownFields(UnknownFieldSet unknownFields) { this.unknownFields = UnknownFieldSet.newBuilder(this.unknownFields) @@ -569,54 +434,5 @@ public final class DynamicMessage extends AbstractMessage { "FieldDescriptor does not match message type."); } } - - /** Verifies that the oneof is an oneof of this message. */ - private void verifyOneofContainingType(OneofDescriptor oneof) { - if (oneof.getContainingType() != type) { - throw new IllegalArgumentException( - "OneofDescriptor does not match message type."); - } - } - - /** Verifies that the value is EnumValueDescriptor and matches Enum Type. */ - private void ensureSingularEnumValueDescriptor( - FieldDescriptor field, Object value) { - if (value == null) { - throw new NullPointerException(); - } - if (!(value instanceof EnumValueDescriptor)) { - 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."); - } - } - - /** Verifies the value for an enum field. */ - private void ensureEnumValueDescriptor( - FieldDescriptor field, Object value) { - if (field.isRepeated()) { - for (Object item : (List) value) { - ensureSingularEnumValueDescriptor(field, item); - } - } else { - ensureSingularEnumValueDescriptor(field, value); - } - } - - private void ensureIsMutable() { - if (fields.isImmutable()) { - fields = fields.clone(); - } - } - - @Override - public com.google.protobuf.Message.Builder getFieldBuilder(FieldDescriptor field) { - // TODO(xiangl): need implementation for dynamic message - throw new UnsupportedOperationException( - "getFieldBuilder() 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 deleted file mode 100644 index 0baa22b..0000000 --- a/java/src/main/java/com/google/protobuf/Extension.java +++ /dev/null @@ -1,96 +0,0 @@ -// 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; - -/** - * Interface that generated extensions implement. - * - * @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(); - - /** 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(); - - // All the methods below are extension implementation details. - - /** - * The API type that the extension is used for. - */ - protected enum ExtensionType { - IMMUTABLE, - MUTABLE, - PROTO1, - } - - protected ExtensionType getExtensionType() { - // TODO(liujisi): make this abstract after we fix proto1. - return ExtensionType.IMMUTABLE; - } - - /** - * Type of a message extension. - */ - public enum MessageType { - PROTO1, - PROTO2, - } - - /** - * If the extension is a message extension (i.e., getLiteType() == MESSAGE), - * returns the type of the message, otherwise undefined. - */ - public MessageType getMessageType() { - return MessageType.PROTO2; - } - - protected abstract Object fromReflectionType(Object value); - protected abstract Object singularFromReflectionType(Object value); - protected abstract Object toReflectionType(Object value); - protected abstract Object singularToReflectionType(Object value); -} diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java index 0067392..d4f6ba9 100644 --- a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -33,12 +33,9 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; /** * A table of known extensions, searchable by name or field number. When @@ -93,7 +90,7 @@ import java.util.Set; * * @author kenton@google.com Kenton Varda */ -public class ExtensionRegistry extends ExtensionRegistryLite { +public final class ExtensionRegistry extends ExtensionRegistryLite { /** Construct a new, empty instance. */ public static ExtensionRegistry newInstance() { return new ExtensionRegistry(); @@ -104,7 +101,6 @@ public class ExtensionRegistry extends ExtensionRegistryLite { return EMPTY; } - /** Returns an unmodifiable view of the registry. */ @Override public ExtensionRegistry getUnmodifiable() { @@ -134,127 +130,42 @@ public class ExtensionRegistry extends ExtensionRegistryLite { } /** - * Deprecated. Use {@link #findImmutableExtensionByName(String)} instead. - */ - public ExtensionInfo findExtensionByName(final String fullName) { - return findImmutableExtensionByName(fullName); - } - - /** - * Find an extension for immutable APIs by fully-qualified field name, - * in the proto namespace. i.e. {@code result.descriptor.fullName()} will - * match {@code fullName} if a match is found. - * - * @return Information about the extension if found, or {@code null} - * otherwise. - */ - public ExtensionInfo findImmutableExtensionByName(final String fullName) { - return immutableExtensionsByName.get(fullName); - } - - /** - * Find an extension for mutable APIs by fully-qualified field name, - * in the proto namespace. i.e. {@code result.descriptor.fullName()} will - * match {@code fullName} if a match is found. + * Find an extension by fully-qualified field name, in the proto namespace. + * I.e. {@code result.descriptor.fullName()} will match {@code fullName} if + * a match is found. * * @return Information about the extension if found, or {@code null} * otherwise. */ - public ExtensionInfo findMutableExtensionByName(final String fullName) { - return mutableExtensionsByName.get(fullName); - } - - /** - * Deprecated. Use {@link #findImmutableExtensionByNumber( - * Descriptors.Descriptor, int)} - */ - public ExtensionInfo findExtensionByNumber( - final Descriptor containingType, final int fieldNumber) { - return findImmutableExtensionByNumber(containingType, fieldNumber); + public ExtensionInfo findExtensionByName(final String fullName) { + return extensionsByName.get(fullName); } /** - * Find an extension by containing type and field number for immutable APIs. + * Find an extension by containing type and field number. * * @return Information about the extension if found, or {@code null} * otherwise. */ - public ExtensionInfo findImmutableExtensionByNumber( - final Descriptor containingType, final int fieldNumber) { - return immutableExtensionsByNumber.get( + public ExtensionInfo findExtensionByNumber(final Descriptor containingType, + final int fieldNumber) { + return extensionsByNumber.get( new DescriptorIntPair(containingType, fieldNumber)); } - /** - * Find an extension by containing type and field number for mutable APIs. - * - * @return Information about the extension if found, or {@code null} - * otherwise. - */ - public ExtensionInfo findMutableExtensionByNumber( - final Descriptor containingType, final int fieldNumber) { - return mutableExtensionsByNumber.get( - new DescriptorIntPair(containingType, fieldNumber)); - } - - /** - * Find all extensions for mutable APIs by fully-qualified name of - * extended class. Note that this method is more computationally expensive - * than getting a single extension by name or number. - * - * @return Information about the extensions found, or {@code null} if there - * are none. - */ - public Set<ExtensionInfo> getAllMutableExtensionsByExtendedType(final String fullName) { - HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>(); - for (DescriptorIntPair pair : mutableExtensionsByNumber.keySet()) { - if (pair.descriptor.getFullName().equals(fullName)) { - extensions.add(mutableExtensionsByNumber.get(pair)); - } - } - return extensions; - } - - /** - * Find all extensions for immutable APIs by fully-qualified name of - * extended class. Note that this method is more computationally expensive - * than getting a single extension by name or number. - * - * @return Information about the extensions found, or {@code null} if there - * are none. - */ - public Set<ExtensionInfo> getAllImmutableExtensionsByExtendedType(final String fullName) { - HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>(); - for (DescriptorIntPair pair : immutableExtensionsByNumber.keySet()) { - if (pair.descriptor.getFullName().equals(fullName)) { - extensions.add(immutableExtensionsByNumber.get(pair)); - } - } - return extensions; - } - /** Add an extension from a generated file to the registry. */ - public void add(final Extension<?, ?> extension) { - if (extension.getExtensionType() != Extension.ExtensionType.IMMUTABLE && - extension.getExtensionType() != Extension.ExtensionType.MUTABLE) { - // do not support other extension types. ignore - return; - } - add(newExtensionInfo(extension), extension.getExtensionType()); - } - - static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) { + public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { if (extension.getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { if (extension.getMessageDefaultInstance() == null) { throw new IllegalStateException( "Registered message-type extension had null default instance: " + - extension.getDescriptor().getFullName()); + extension.getDescriptor().getFullName()); } - return new ExtensionInfo(extension.getDescriptor(), - (Message) extension.getMessageDefaultInstance()); + add(new ExtensionInfo(extension.getDescriptor(), + extension.getMessageDefaultInstance())); } else { - return new ExtensionInfo(extension.getDescriptor(), null); + add(new ExtensionInfo(extension.getDescriptor(), null)); } } @@ -265,9 +176,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite { "ExtensionRegistry.add() must be provided a default instance when " + "adding an embedded message extension."); } - ExtensionInfo info = new ExtensionInfo(type, null); - add(info, Extension.ExtensionType.IMMUTABLE); - add(info, Extension.ExtensionType.MUTABLE); + add(new ExtensionInfo(type, null)); } /** Add a message-type extension to the registry by descriptor. */ @@ -277,75 +186,40 @@ public class ExtensionRegistry extends ExtensionRegistryLite { "ExtensionRegistry.add() provided a default instance for a " + "non-message extension."); } - add(new ExtensionInfo(type, defaultInstance), - Extension.ExtensionType.IMMUTABLE); + add(new ExtensionInfo(type, defaultInstance)); } // ================================================================= // Private stuff. private ExtensionRegistry() { - this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>(); - this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>(); - this.immutableExtensionsByNumber = - new HashMap<DescriptorIntPair, ExtensionInfo>(); - this.mutableExtensionsByNumber = - new HashMap<DescriptorIntPair, ExtensionInfo>(); + this.extensionsByName = new HashMap<String, ExtensionInfo>(); + this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); } private ExtensionRegistry(ExtensionRegistry other) { super(other); - this.immutableExtensionsByName = - Collections.unmodifiableMap(other.immutableExtensionsByName); - this.mutableExtensionsByName = - Collections.unmodifiableMap(other.mutableExtensionsByName); - this.immutableExtensionsByNumber = - Collections.unmodifiableMap(other.immutableExtensionsByNumber); - this.mutableExtensionsByNumber = - Collections.unmodifiableMap(other.mutableExtensionsByNumber); + this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName); + this.extensionsByNumber = + Collections.unmodifiableMap(other.extensionsByNumber); } - private final Map<String, ExtensionInfo> immutableExtensionsByName; - private final Map<String, ExtensionInfo> mutableExtensionsByName; - private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber; - private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber; + private final Map<String, ExtensionInfo> extensionsByName; + private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; - ExtensionRegistry(boolean empty) { + private ExtensionRegistry(boolean empty) { super(ExtensionRegistryLite.getEmptyRegistry()); - this.immutableExtensionsByName = - Collections.<String, ExtensionInfo>emptyMap(); - this.mutableExtensionsByName = - Collections.<String, ExtensionInfo>emptyMap(); - this.immutableExtensionsByNumber = + this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap(); + this.extensionsByNumber = Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); - this.mutableExtensionsByNumber = - Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); } private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true); - private void add( - final ExtensionInfo extension, - final Extension.ExtensionType extensionType) { + private void add(final ExtensionInfo extension) { if (!extension.descriptor.isExtension()) { throw new IllegalArgumentException( - "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + - "(non-extension) field."); - } - - Map<String, ExtensionInfo> extensionsByName; - Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; - switch (extensionType) { - case IMMUTABLE: - extensionsByName = immutableExtensionsByName; - extensionsByNumber = immutableExtensionsByNumber; - break; - case MUTABLE: - extensionsByName = mutableExtensionsByName; - extensionsByNumber = mutableExtensionsByNumber; - break; - default: - // Ignore the unknown supported type. - return; + "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + + "(non-extension) field."); } extensionsByName.put(extension.descriptor.getFullName(), extension); diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java index 65cf738..d5288dd 100644 --- a/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -43,7 +43,7 @@ import java.util.Map; * make sense to mix the two, since if you have any regular types in your * program, you then require the full runtime and lose all the benefits of * the lite runtime, so you might as well make all your types be regular types. - * However, in some cases (e.g. when depending on multiple third-party libraries + * However, in some cases (e.g. when depending on multiple third-patry libraries * where one uses lite types and one uses regular), you may find yourself * wanting to mix the two. In this case things get more complicated. * <p> @@ -71,22 +71,6 @@ import java.util.Map; * @author kenton@google.com Kenton Varda */ public class ExtensionRegistryLite { - - // Set true to enable lazy parsing feature for MessageSet. - // - // TODO(xiangl): Now we use a global flag to control whether enable lazy - // parsing feature for MessageSet, which may be too crude for some - // applications. Need to support this feature on smaller granularity. - private static volatile boolean eagerlyParseMessageSets = false; - - public static boolean isEagerlyParseMessageSets() { - return eagerlyParseMessageSets; - } - - public static void setEagerlyParseMessageSets(boolean isEagerlyParse) { - eagerlyParseMessageSets = isEagerlyParse; - } - /** Construct a new, empty instance. */ public static ExtensionRegistryLite newInstance() { return new ExtensionRegistryLite(); diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java index 392f4ef..93e55f2 100644 --- a/java/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,14 +30,13 @@ package com.google.protobuf; -import com.google.protobuf.LazyField.LazyIterator; - -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; +import java.util.TreeMap; import java.util.List; import java.util.Map; +import java.io.IOException; /** * A class which represents an arbitrary set of fields of some message type. @@ -68,13 +67,16 @@ final class FieldSet<FieldDescriptorType extends MessageLite.Builder to, MessageLite from); } - private final SmallSortedMap<FieldDescriptorType, Object> fields; - private boolean isImmutable; - private boolean hasLazyField = false; + private Map<FieldDescriptorType, Object> fields; /** Construct a new FieldSet. */ private FieldSet() { - this.fields = SmallSortedMap.newFieldMap(16); + // Use a TreeMap because fields need to be in canonical order when + // serializing. + // TODO(kenton): Maybe use some sort of sparse array instead? It would + // even make sense to store the first 16 or so tags in a flat array + // to make DynamicMessage faster. + fields = new TreeMap<FieldDescriptorType, Object>(); } /** @@ -82,8 +84,7 @@ final class FieldSet<FieldDescriptorType extends * DEFAULT_INSTANCE. */ private FieldSet(final boolean dummy) { - this.fields = SmallSortedMap.newFieldMap(0); - makeImmutable(); + this.fields = Collections.emptyMap(); } /** Construct a new FieldSet. */ @@ -98,106 +99,41 @@ final class FieldSet<FieldDescriptorType extends FieldSet<T> emptySet() { return DEFAULT_INSTANCE; } - @SuppressWarnings("rawtypes") + @SuppressWarnings("unchecked") private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); /** Make this FieldSet immutable from this point forward. */ @SuppressWarnings("unchecked") public void makeImmutable() { - if (isImmutable) { - return; - } - fields.makeImmutable(); - isImmutable = true; - } - - /** - * Returns whether the FieldSet is immutable. This is true if it is the - * {@link #emptySet} or if {@link #makeImmutable} were called. - * - * @return whether the FieldSet is immutable. - */ - public boolean isImmutable() { - return isImmutable; - } - - /** - * Clones the FieldSet. The returned FieldSet will be mutable even if the - * original FieldSet was immutable. - * - * @return the newly cloned FieldSet - */ - @Override - public FieldSet<FieldDescriptorType> clone() { - // We can't just call fields.clone because List objects in the map - // should not be shared. - FieldSet<FieldDescriptorType> clone = FieldSet.newFieldSet(); - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - Map.Entry<FieldDescriptorType, Object> entry = fields.getArrayEntryAt(i); - FieldDescriptorType descriptor = entry.getKey(); - clone.setField(descriptor, entry.getValue()); - } - for (Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { - FieldDescriptorType descriptor = entry.getKey(); - clone.setField(descriptor, entry.getValue()); + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + if (entry.getKey().isRepeated()) { + final List value = (List)entry.getValue(); + fields.put(entry.getKey(), Collections.unmodifiableList(value)); + } } - clone.hasLazyField = hasLazyField; - return clone; + fields = Collections.unmodifiableMap(fields); } - // ================================================================= /** See {@link Message.Builder#clear()}. */ public void clear() { fields.clear(); - hasLazyField = false; } /** * Get a simple map containing all the fields. */ public Map<FieldDescriptorType, Object> getAllFields() { - if (hasLazyField) { - SmallSortedMap<FieldDescriptorType, Object> result = - SmallSortedMap.newFieldMap(16); - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - cloneFieldEntry(result, fields.getArrayEntryAt(i)); - } - for (Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { - cloneFieldEntry(result, entry); - } - if (fields.isImmutable()) { - result.makeImmutable(); - } - return result; - } - return fields.isImmutable() ? fields : Collections.unmodifiableMap(fields); - } - - private void cloneFieldEntry(Map<FieldDescriptorType, Object> map, - Map.Entry<FieldDescriptorType, Object> entry) { - FieldDescriptorType key = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof LazyField) { - map.put(key, ((LazyField) value).getValue()); - } else { - map.put(key, value); - } + return Collections.unmodifiableMap(fields); } /** - * Get an iterator to the field map. This iterator should not be leaked out - * of the protobuf library as it is not protected from mutation when fields - * is not immutable. + * Get an iterator to the field map. This iterator should not be leaked + * out of the protobuf library as it is not protected from mutation. */ public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { - if (hasLazyField) { - return new LazyIterator<FieldDescriptorType>( - fields.entrySet().iterator()); - } return fields.entrySet().iterator(); } @@ -221,18 +157,14 @@ final class FieldSet<FieldDescriptorType extends * to the caller to fetch the field's default value. */ public Object getField(final FieldDescriptorType descriptor) { - Object o = fields.get(descriptor); - if (o instanceof LazyField) { - return ((LazyField) o).getValue(); - } - return o; + return fields.get(descriptor); } /** * Useful for implementing * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */ - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings("unchecked") public void setField(final FieldDescriptorType descriptor, Object value) { if (descriptor.isRepeated()) { @@ -244,7 +176,7 @@ final class FieldSet<FieldDescriptorType extends // Wrap the contents in a new list so that the caller cannot change // the list's contents after setting it. final List newList = new ArrayList(); - newList.addAll((List) value); + newList.addAll((List)value); for (final Object element : newList) { verifyType(descriptor.getLiteType(), element); } @@ -253,9 +185,6 @@ final class FieldSet<FieldDescriptorType extends verifyType(descriptor.getLiteType(), value); } - if (value instanceof LazyField) { - hasLazyField = true; - } fields.put(descriptor, value); } @@ -265,9 +194,6 @@ final class FieldSet<FieldDescriptorType extends */ public void clearField(final FieldDescriptorType descriptor) { fields.remove(descriptor); - if (fields.isEmpty()) { - hasLazyField = false; - } } /** @@ -280,11 +206,11 @@ final class FieldSet<FieldDescriptorType extends "getRepeatedField() can only be called on repeated fields."); } - final Object value = getField(descriptor); + final Object value = fields.get(descriptor); if (value == null) { return 0; } else { - return ((List<?>) value).size(); + return ((List) value).size(); } } @@ -299,12 +225,12 @@ final class FieldSet<FieldDescriptorType extends "getRepeatedField() can only be called on repeated fields."); } - final Object value = getField(descriptor); + final Object value = fields.get(descriptor); if (value == null) { throw new IndexOutOfBoundsException(); } else { - return ((List<?>) value).get(index); + return ((List) value).get(index); } } @@ -321,13 +247,13 @@ final class FieldSet<FieldDescriptorType extends "getRepeatedField() can only be called on repeated fields."); } - final Object list = getField(descriptor); + final Object list = fields.get(descriptor); if (list == null) { throw new IndexOutOfBoundsException(); } verifyType(descriptor.getLiteType(), value); - ((List<Object>) list).set(index, value); + ((List) list).set(index, value); } /** @@ -344,13 +270,13 @@ final class FieldSet<FieldDescriptorType extends verifyType(descriptor.getLiteType(), value); - final Object existingValue = getField(descriptor); - List<Object> list; + final Object existingValue = fields.get(descriptor); + List list; if (existingValue == null) { - list = new ArrayList<Object>(); + list = new ArrayList(); fields.put(descriptor, list); } else { - list = (List<Object>) existingValue; + list = (List) existingValue; } list.add(value); @@ -377,18 +303,14 @@ final class FieldSet<FieldDescriptorType extends case DOUBLE: isValid = value instanceof Double ; break; case BOOLEAN: isValid = value instanceof Boolean ; break; case STRING: isValid = value instanceof String ; break; - case BYTE_STRING: - isValid = value instanceof ByteString || value instanceof byte[]; - break; + case BYTE_STRING: isValid = value instanceof ByteString; break; case ENUM: // TODO(kenton): Caller must do type checking here, I guess. - isValid = - (value instanceof Integer || value instanceof Internal.EnumLite); + isValid = value instanceof Internal.EnumLite; break; case MESSAGE: // TODO(kenton): Caller must do type checking here, I guess. - isValid = - (value instanceof MessageLite) || (value instanceof LazyField); + isValid = value instanceof MessageLite; break; } @@ -414,47 +336,27 @@ final class FieldSet<FieldDescriptorType extends * aren't actually present in the set, it is up to the caller to check * that all required fields are present. */ - public boolean isInitialized() { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - if (!isInitialized(fields.getArrayEntryAt(i))) { - return false; - } - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { - if (!isInitialized(entry)) { - return false; - } - } - return true; - } - @SuppressWarnings("unchecked") - private boolean isInitialized( - final Map.Entry<FieldDescriptorType, Object> entry) { - final FieldDescriptorType descriptor = entry.getKey(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { - if (descriptor.isRepeated()) { - for (final MessageLite element: - (List<MessageLite>) entry.getValue()) { - if (!element.isInitialized()) { - return false; + public boolean isInitialized() { + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { + if (descriptor.isRepeated()) { + for (final MessageLite element: + (List<MessageLite>) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } } - } - } else { - Object value = entry.getValue(); - if (value instanceof MessageLite) { - if (!((MessageLite) value).isInitialized()) { + } else { + if (!((MessageLite) entry.getValue()).isInitialized()) { return false; } - } else if (value instanceof LazyField) { - return true; - } else { - throw new IllegalArgumentException( - "Wrong object type used with protocol message reflection."); } } } + return true; } @@ -474,62 +376,41 @@ final class FieldSet<FieldDescriptorType extends } /** - * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another - * {@link FieldSet}. + * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. */ + @SuppressWarnings("unchecked") public void mergeFrom(final FieldSet<FieldDescriptorType> other) { - for (int i = 0; i < other.fields.getNumArrayEntries(); i++) { - mergeFromField(other.fields.getArrayEntryAt(i)); - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - other.fields.getOverflowEntries()) { - mergeFromField(entry); - } - } + for (final Map.Entry<FieldDescriptorType, Object> entry: + other.fields.entrySet()) { + final FieldDescriptorType descriptor = entry.getKey(); + final Object otherValue = entry.getValue(); - private Object cloneIfMutable(Object value) { - if (value instanceof byte[]) { - byte[] bytes = (byte[]) value; - byte[] copy = new byte[bytes.length]; - System.arraycopy(bytes, 0, copy, 0, bytes.length); - return copy; - } else { - return value; - } - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private void mergeFromField( - final Map.Entry<FieldDescriptorType, Object> entry) { - final FieldDescriptorType descriptor = entry.getKey(); - Object otherValue = entry.getValue(); - if (otherValue instanceof LazyField) { - otherValue = ((LazyField) otherValue).getValue(); - } - - if (descriptor.isRepeated()) { - Object value = getField(descriptor); - if (value == null) { - value = new ArrayList(); - } - for (Object element : (List) otherValue) { - ((List) value).add(cloneIfMutable(element)); - } - fields.put(descriptor, value); - } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { - Object value = getField(descriptor); - if (value == null) { - fields.put(descriptor, cloneIfMutable(otherValue)); - } else { - // Merge the messages. - value = descriptor.internalMergeFrom( + if (descriptor.isRepeated()) { + Object value = fields.get(descriptor); + if (value == null) { + // Our list is empty, but we still need to make a defensive copy of + // the other list since we don't know if the other FieldSet is still + // mutable. + fields.put(descriptor, new ArrayList((List) otherValue)); + } else { + // Concatenate the lists. + ((List) value).addAll((List) otherValue); + } + } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { + Object value = fields.get(descriptor); + if (value == null) { + fields.put(descriptor, otherValue); + } else { + // Merge the messages. + fields.put(descriptor, + descriptor.internalMergeFrom( ((MessageLite) value).toBuilder(), (MessageLite) otherValue) - .build(); + .build()); + } - fields.put(descriptor, value); + } else { + fields.put(descriptor, otherValue); } - } else { - fields.put(descriptor, cloneIfMutable(otherValue)); } } @@ -537,13 +418,11 @@ final class FieldSet<FieldDescriptorType extends // other class. Probably WireFormat. /** - * Read a field of any primitive type for immutable messages from a - * CodedInputStream. Enums, groups, and embedded messages are not handled by - * this method. + * Read a field of any primitive type from a CodedInputStream. Enums, + * groups, and embedded messages are not handled by this method. * * @param input The stream from which to read. * @param type Declared type of the field. - * @param checkUtf8 When true, check that the input is valid utf8. * @return An object representing the field's value, of the exact * type which would be returned by * {@link Message#getField(Descriptors.FieldDescriptor)} for @@ -551,8 +430,7 @@ final class FieldSet<FieldDescriptorType extends */ public static Object readPrimitiveField( CodedInputStream input, - final WireFormat.FieldType type, - boolean checkUtf8) throws IOException { + final WireFormat.FieldType type) throws IOException { switch (type) { case DOUBLE : return input.readDouble (); case FLOAT : return input.readFloat (); @@ -562,11 +440,7 @@ final class FieldSet<FieldDescriptorType extends case FIXED64 : return input.readFixed64 (); case FIXED32 : return input.readFixed32 (); case BOOL : return input.readBool (); - case STRING : if (checkUtf8) { - return input.readStringRequireUtf8(); - } else { - return input.readString(); - } + case STRING : return input.readString (); case BYTES : return input.readBytes (); case UINT32 : return input.readUInt32 (); case SFIXED32: return input.readSFixed32(); @@ -591,17 +465,11 @@ final class FieldSet<FieldDescriptorType extends "There is no way to get here, but the compiler thinks otherwise."); } - /** See {@link Message#writeTo(CodedOutputStream)}. */ public void writeTo(final CodedOutputStream output) throws IOException { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - final Map.Entry<FieldDescriptorType, Object> entry = - fields.getArrayEntryAt(i); - writeField(entry.getKey(), entry.getValue(), output); - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { writeField(entry.getKey(), entry.getValue(), output); } } @@ -611,29 +479,16 @@ final class FieldSet<FieldDescriptorType extends */ public void writeMessageSetTo(final CodedOutputStream output) throws IOException { - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - writeMessageSetTo(fields.getArrayEntryAt(i), output); - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { - writeMessageSetTo(entry, output); - } - } - - private void writeMessageSetTo( - final Map.Entry<FieldDescriptorType, Object> entry, - final CodedOutputStream output) throws IOException { - final FieldDescriptorType descriptor = entry.getKey(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && - !descriptor.isRepeated() && !descriptor.isPacked()) { - Object value = entry.getValue(); - if (value instanceof LazyField) { - value = ((LazyField) value).getValue(); + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && + !descriptor.isRepeated() && !descriptor.isPacked()) { + output.writeMessageSetExtension(entry.getKey().getNumber(), + (MessageLite) entry.getValue()); + } else { + writeField(descriptor, entry.getValue(), output); } - output.writeMessageSetExtension(entry.getKey().getNumber(), - (MessageLite) value); - } else { - writeField(descriptor, entry.getValue(), output); } } @@ -655,7 +510,7 @@ final class FieldSet<FieldDescriptorType extends // Special case for groups, which need a start and end tag; other fields // can just use writeTag() and writeFieldNoTag(). if (type == WireFormat.FieldType.GROUP) { - output.writeGroup(number, (MessageLite) value); + output.writeGroup(number, (MessageLite) value); } else { output.writeTag(number, getWireFormatForFieldType(type, false)); writeElementNoTag(output, type, value); @@ -688,13 +543,7 @@ final class FieldSet<FieldDescriptorType extends case STRING : output.writeStringNoTag ((String ) value); break; case GROUP : output.writeGroupNoTag ((MessageLite) value); break; case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break; - case BYTES: - if (value instanceof ByteString) { - output.writeBytesNoTag((ByteString) value); - } else { - output.writeByteArrayNoTag((byte[]) value); - } - break; + case BYTES : output.writeBytesNoTag ((ByteString ) value); break; case UINT32 : output.writeUInt32NoTag ((Integer ) value); break; case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break; case SFIXED64: output.writeSFixed64NoTag((Long ) value); break; @@ -702,11 +551,7 @@ final class FieldSet<FieldDescriptorType extends case SINT64 : output.writeSInt64NoTag ((Long ) value); break; case ENUM: - if (value instanceof Internal.EnumLite) { - output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); - } else { - output.writeEnumNoTag(((Integer) value).intValue()); - } + output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); break; } } @@ -719,7 +564,7 @@ final class FieldSet<FieldDescriptorType extends WireFormat.FieldType type = descriptor.getLiteType(); int number = descriptor.getNumber(); if (descriptor.isRepeated()) { - final List<?> valueList = (List<?>)value; + final List valueList = (List)value; if (descriptor.isPacked()) { output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED); // Compute the total data size so the length can be written. @@ -738,11 +583,7 @@ final class FieldSet<FieldDescriptorType extends } } } else { - if (value instanceof LazyField) { - writeElement(output, type, number, ((LazyField) value).getValue()); - } else { - writeElement(output, type, number, value); - } + writeElement(output, type, number, value); } } @@ -752,13 +593,8 @@ final class FieldSet<FieldDescriptorType extends */ public int getSerializedSize() { int size = 0; - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - final Map.Entry<FieldDescriptorType, Object> entry = - fields.getArrayEntryAt(i); - size += computeFieldSize(entry.getKey(), entry.getValue()); - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { size += computeFieldSize(entry.getKey(), entry.getValue()); } return size; @@ -769,32 +605,18 @@ final class FieldSet<FieldDescriptorType extends */ public int getMessageSetSerializedSize() { int size = 0; - for (int i = 0; i < fields.getNumArrayEntries(); i++) { - size += getMessageSetSerializedSize(fields.getArrayEntryAt(i)); - } - for (final Map.Entry<FieldDescriptorType, Object> entry : - fields.getOverflowEntries()) { - size += getMessageSetSerializedSize(entry); - } - return size; - } - - private int getMessageSetSerializedSize( - final Map.Entry<FieldDescriptorType, Object> entry) { - final FieldDescriptorType descriptor = entry.getKey(); - Object value = entry.getValue(); - if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE - && !descriptor.isRepeated() && !descriptor.isPacked()) { - if (value instanceof LazyField) { - return CodedOutputStream.computeLazyFieldMessageSetExtensionSize( - entry.getKey().getNumber(), (LazyField) value); + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + final FieldDescriptorType descriptor = entry.getKey(); + if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && + !descriptor.isRepeated() && !descriptor.isPacked()) { + size += CodedOutputStream.computeMessageSetExtensionSize( + entry.getKey().getNumber(), (MessageLite) entry.getValue()); } else { - return CodedOutputStream.computeMessageSetExtensionSize( - entry.getKey().getNumber(), (MessageLite) value); + size += computeFieldSize(descriptor, entry.getValue()); } - } else { - return computeFieldSize(descriptor, value); } + return size; } /** @@ -813,9 +635,7 @@ final class FieldSet<FieldDescriptorType extends final int number, final Object value) { int tagSize = CodedOutputStream.computeTagSize(number); if (type == WireFormat.FieldType.GROUP) { - // Only count the end group tag for proto2 messages as for proto1 the end - // group tag will be counted as a part of getSerializedSize(). - tagSize *= 2; + tagSize *= 2; } return tagSize + computeElementSizeNoTag(type, value); } @@ -845,32 +665,17 @@ final class FieldSet<FieldDescriptorType extends case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value); case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value); case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value); - case BYTES : - if (value instanceof ByteString) { - return CodedOutputStream.computeBytesSizeNoTag((ByteString) value); - } else { - return CodedOutputStream.computeByteArraySizeNoTag((byte[]) value); - } + case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value); + case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value); case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value); case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value); case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value); case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value); case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value); - case MESSAGE: - if (value instanceof LazyField) { - return CodedOutputStream.computeLazyFieldSizeNoTag((LazyField) value); - } else { - return CodedOutputStream.computeMessageSizeNoTag((MessageLite) value); - } - case ENUM: - if (value instanceof Internal.EnumLite) { - return CodedOutputStream.computeEnumSizeNoTag( - ((Internal.EnumLite) value).getNumber()); - } else { - return CodedOutputStream.computeEnumSizeNoTag((Integer) value); - } + return CodedOutputStream.computeEnumSizeNoTag( + ((Internal.EnumLite) value).getNumber()); } throw new RuntimeException( @@ -887,7 +692,7 @@ final class FieldSet<FieldDescriptorType extends if (descriptor.isRepeated()) { if (descriptor.isPacked()) { int dataSize = 0; - for (final Object element : (List<?>)value) { + for (final Object element : (List)value) { dataSize += computeElementSizeNoTag(type, element); } return dataSize + @@ -895,7 +700,7 @@ final class FieldSet<FieldDescriptorType extends CodedOutputStream.computeRawVarint32Size(dataSize); } else { int size = 0; - for (final Object element : (List<?>)value) { + for (final Object element : (List)value) { size += computeElementSize(type, number, element); } return size; diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index a6101cb..dba0ec8 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -33,14 +33,10 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; 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 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.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -56,37 +52,10 @@ import java.util.TreeMap; * * @author kenton@google.com Kenton Varda */ -public abstract class GeneratedMessage extends AbstractMessage - implements Serializable { - private static final long serialVersionUID = 1L; - - /** - * For testing. Allows a test to disable the optimization that avoids using - * field builders for nested messages until they are requested. By disabling - * this optimization, existing tests can be reused to test the field builders. - */ - protected static boolean alwaysUseFieldBuilders = false; - - protected GeneratedMessage() { - } +public abstract class GeneratedMessage extends AbstractMessage { + protected GeneratedMessage() {} - protected GeneratedMessage(Builder<?> builder) { - } - - public Parser<? extends GeneratedMessage> getParserForType() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); - } - - /** - * For testing. Allows a test to disable the optimization that avoids using - * field builders for nested messages until they are requested. By disabling - * this optimization, existing tests can be reused to test the field builders. - * See {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder}. - */ - static void enableAlwaysUseFieldBuildersForTesting() { - alwaysUseFieldBuilders = true; - } + private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); /** * Get the FieldAccessorTable for this type. We can't have the message @@ -95,7 +64,6 @@ public abstract class GeneratedMessage extends AbstractMessage */ protected abstract FieldAccessorTable internalGetFieldAccessorTable(); - //@Override (Java 1.6 override semantics, but we must support 1.5) public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } @@ -107,7 +75,7 @@ public abstract class GeneratedMessage extends AbstractMessage final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; for (final FieldDescriptor field : descriptor.getFields()) { if (field.isRepeated()) { - final List<?> value = (List<?>) getField(field); + final List value = (List) getField(field); if (!value.isEmpty()) { result.put(field, value); } @@ -150,146 +118,36 @@ public abstract class GeneratedMessage extends AbstractMessage return true; } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Map<FieldDescriptor, Object> getAllFields() { return Collections.unmodifiableMap(getAllFieldsMutable()); } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasOneof(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).has(this); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).get(this); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean hasField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).has(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).get(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field) .getRepeatedCount(this); } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getRepeatedField(final FieldDescriptor field, final int index) { return internalGetFieldAccessorTable().getField(field) .getRepeated(this, index); } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public UnknownFieldSet getUnknownFields() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); - } - - /** - * Called by subclasses to parse an unknown field. - * @return {@code true} unless the tag is an end-group tag. - */ - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return unknownFields.mergeFieldFrom(tag, input); - } - - - /** - * Used by parsing constructors in generated classes. - */ - protected void makeExtensionsImmutable() { - // Noop for messages without extensions. - } - - protected abstract Message.Builder newBuilderForType(BuilderParent parent); - - /** - * Interface for the parent of a Builder that allows the builder to - * communicate invalidations back to the parent for use when using nested - * builders. - */ - protected interface BuilderParent { - - /** - * A builder becomes dirty whenever a field is modified -- including fields - * in nested builders -- and becomes clean when build() is called. Thus, - * when a builder becomes dirty, all its parents become dirty as well, and - * when it becomes clean, all its children become clean. The dirtiness - * state is used to invalidate certain cached values. - * <br> - * To this end, a builder calls markAsDirty() on its parent whenever it - * transitions from clean to dirty. The parent must propagate this call to - * its own parent, unless it was already dirty, in which case the - * grandparent must necessarily already be dirty as well. The parent can - * only transition back to "clean" after calling build() on all children. - */ - void markDirty(); + public final UnknownFieldSet getUnknownFields() { + return unknownFields; } @SuppressWarnings("unchecked") public abstract static class Builder <BuilderType extends Builder> extends AbstractMessage.Builder<BuilderType> { - - private BuilderParent builderParent; - - private BuilderParentImpl meAsParent; - - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. - private boolean isClean; - - private UnknownFieldSet unknownFields = - UnknownFieldSet.getDefaultInstance(); - - protected Builder() { - this(null); - } - - protected Builder(BuilderParent builderParent) { - this.builderParent = builderParent; - } - - void dispose() { - builderParent = null; - } - - /** - * Called by the subclass when a message is built. - */ - protected void onBuilt() { - if (builderParent != null) { - markClean(); - } - } - - /** - * Called by the subclass or a builder to notify us that a message was - * built and may be cached and therefore invalidations are needed. - */ - protected void markClean() { - this.isClean = true; - } - - /** - * Gets whether invalidations are needed - * - * @return whether invalidations are needed - */ - protected boolean isClean() { - return isClean; - } + protected Builder() {} // 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 @@ -301,50 +159,26 @@ public abstract class GeneratedMessage extends AbstractMessage } /** - * Called by the initialization and clear code paths to allow subclasses to - * reset any of their builtin fields back to the initial values. + * Get the message being built. We don't just pass this to the + * constructor because it becomes null when build() is called. */ - public BuilderType clear() { - unknownFields = UnknownFieldSet.getDefaultInstance(); - onChanged(); - return (BuilderType) this; - } + protected abstract GeneratedMessage internalGetResult(); /** * Get the FieldAccessorTable for this type. We can't have the message * class pass this in to the constructor because of bootstrapping trouble * with DescriptorProtos. */ - protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + private FieldAccessorTable internalGetFieldAccessorTable() { + return internalGetResult().internalGetFieldAccessorTable(); + } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Map<FieldDescriptor, Object> getAllFields() { - return Collections.unmodifiableMap(getAllFieldsMutable()); - } - - /** Internal helper which returns a mutable map. */ - private Map<FieldDescriptor, Object> getAllFieldsMutable() { - final TreeMap<FieldDescriptor, Object> result = - new TreeMap<FieldDescriptor, Object>(); - final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; - for (final FieldDescriptor field : descriptor.getFields()) { - if (field.isRepeated()) { - final List value = (List) getField(field); - if (!value.isEmpty()) { - result.put(field, value); - } - } else { - if (hasField(field)) { - result.put(field, getField(field)); - } - } - } - return result; + return internalGetResult().getAllFields(); } public Message.Builder newBuilderForField( @@ -352,35 +186,18 @@ public abstract class GeneratedMessage extends AbstractMessage return internalGetFieldAccessorTable().getField(field).newBuilder(); } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Message.Builder getFieldBuilder(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).getBuilder(this); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasOneof(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).has(this); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) { - return internalGetFieldAccessorTable().getOneof(oneof).get(this); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean hasField(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field).has(this); + return internalGetResult().hasField(field); } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getField(final FieldDescriptor field) { - Object object = internalGetFieldAccessorTable().getField(field).get(this); if (field.isRepeated()) { // The underlying list object is still modifiable at this point. // Make sure not to expose the modifiable list to the caller. - return Collections.unmodifiableList((List) object); + return Collections.unmodifiableList( + (List) internalGetResult().getField(field)); } else { - return object; + return internalGetResult().getField(field); } } @@ -390,29 +207,18 @@ public abstract class GeneratedMessage extends AbstractMessage return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) public BuilderType clearField(final FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public BuilderType clearOneof(final OneofDescriptor oneof) { - internalGetFieldAccessorTable().getOneof(oneof).clear(this); - return (BuilderType) this; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) public int getRepeatedFieldCount(final FieldDescriptor field) { - return internalGetFieldAccessorTable().getField(field) - .getRepeatedCount(this); + return internalGetResult().getRepeatedFieldCount(field); } - //@Override (Java 1.6 override semantics, but we must support 1.5) public Object getRepeatedField(final FieldDescriptor field, final int index) { - return internalGetFieldAccessorTable().getField(field) - .getRepeated(this, index); + return internalGetResult().getRepeatedField(field, index); } public BuilderType setRepeatedField(final FieldDescriptor field, @@ -428,57 +234,29 @@ public abstract class GeneratedMessage extends AbstractMessage return (BuilderType) this; } + public final UnknownFieldSet getUnknownFields() { + return internalGetResult().unknownFields; + } + public final BuilderType setUnknownFields( final UnknownFieldSet unknownFields) { - this.unknownFields = unknownFields; - onChanged(); + internalGetResult().unknownFields = unknownFields; return (BuilderType) this; } @Override public final BuilderType mergeUnknownFields( final UnknownFieldSet unknownFields) { - this.unknownFields = - UnknownFieldSet.newBuilder(this.unknownFields) + final GeneratedMessage result = internalGetResult(); + result.unknownFields = + UnknownFieldSet.newBuilder(result.unknownFields) .mergeFrom(unknownFields) .build(); - onChanged(); return (BuilderType) this; } - //@Override (Java 1.6 override semantics, but we must support 1.5) public boolean isInitialized() { - for (final FieldDescriptor field : getDescriptorForType().getFields()) { - // Check that all required fields are present. - if (field.isRequired()) { - if (!hasField(field)) { - return false; - } - } - // Check that embedded messages are initialized. - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - @SuppressWarnings("unchecked") final - List<Message> messageList = (List<Message>) getField(field); - for (final Message element : messageList) { - if (!element.isInitialized()) { - return false; - } - } - } else { - if (hasField(field) && - !((Message) getField(field)).isInitialized()) { - return false; - } - } - } - } - return true; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public final UnknownFieldSet getUnknownFields() { - return unknownFields; + return internalGetResult().isInitialized(); } /** @@ -492,71 +270,11 @@ public abstract class GeneratedMessage extends AbstractMessage final int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } - - /** - * Implementation of {@link BuilderParent} for giving to our children. This - * small inner class makes it so we don't publicly expose the BuilderParent - * methods. - */ - private class BuilderParentImpl implements BuilderParent { - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void markDirty() { - onChanged(); - } - } - - /** - * Gets the {@link BuilderParent} for giving to our children. - * @return The builder parent for our children. - */ - protected BuilderParent getParentForChildren() { - if (meAsParent == null) { - meAsParent = new BuilderParentImpl(); - } - return meAsParent; - } - - /** - * Called when a the builder or one of its nested children has changed - * and any parent should be notified of its invalidation. - */ - protected final void onChanged() { - if (isClean && builderParent != null) { - builderParent.markDirty(); - - // Don't keep dispatching invalidations until build is called again. - isClean = false; - } - } } // ================================================================= // Extensions-related stuff - public interface ExtendableMessageOrBuilder< - MessageType extends ExtendableMessage> extends MessageOrBuilder { - // Re-define for return type covariance. - Message getDefaultInstanceForType(); - - /** Check if a singular extension is present. */ - <Type> boolean hasExtension( - Extension<MessageType, Type> extension); - - /** Get the number of elements in a repeated extension. */ - <Type> int getExtensionCount( - Extension<MessageType, List<Type>> extension); - - /** Get the value of an extension. */ - <Type> Type getExtension( - Extension<MessageType, Type> extension); - - /** Get one element of a repeated extension. */ - <Type> Type getExtension( - Extension<MessageType, List<Type>> extension, - int index); - } - /** * Generated message classes for message types that contain extension ranges * subclass this. @@ -594,23 +312,12 @@ public abstract class GeneratedMessage extends AbstractMessage */ public abstract static class ExtendableMessage< MessageType extends ExtendableMessage> - extends GeneratedMessage - implements ExtendableMessageOrBuilder<MessageType> { - - private final FieldSet<FieldDescriptor> extensions; - - protected ExtendableMessage() { - this.extensions = FieldSet.newFieldSet(); - } - - protected ExtendableMessage( - ExtendableBuilder<MessageType, ?> builder) { - super(builder); - this.extensions = builder.buildExtensions(); - } + extends GeneratedMessage { + protected ExtendableMessage() {} + private final FieldSet<FieldDescriptor> extensions = FieldSet.newFieldSet(); private void verifyExtensionContainingType( - final Extension<MessageType, ?> extension) { + final GeneratedExtension<MessageType, ?> extension) { if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { // This can only happen if someone uses unchecked operations. @@ -623,27 +330,24 @@ 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) { + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); } /** 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 GeneratedExtension<MessageType, List<Type>> extension) { verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); return extensions.getRepeatedFieldCount(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 Extension<MessageType, Type> extension) { + final GeneratedExtension<MessageType, Type> extension) { verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); final Object value = extensions.getField(descriptor); @@ -663,10 +367,9 @@ 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) @SuppressWarnings("unchecked") public final <Type> Type getExtension( - final Extension<MessageType, List<Type>> extension, + final GeneratedExtension<MessageType, List<Type>> extension, final int index) { verifyExtensionContainingType(extension); FieldDescriptor descriptor = extension.getDescriptor(); @@ -684,26 +387,6 @@ public abstract class GeneratedMessage extends AbstractMessage return super.isInitialized() && extensionsAreInitialized(); } - @Override - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return MessageReflection.mergeFieldFrom( - input, unknownFields, extensionRegistry, getDescriptorForType(), - new MessageReflection.ExtensionAdapter(extensions), tag); - } - - - /** - * Used by parsing constructors in generated classes. - */ - @Override - protected void makeExtensionsImmutable() { - extensions.makeImmutable(); - } - /** * Used by subclasses to serialize extensions. Extension ranges may be * interleaved with field numbers, but we must write them in canonical @@ -733,21 +416,9 @@ public abstract class GeneratedMessage extends AbstractMessage if (messageSetWireFormat && descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE && !descriptor.isRepeated()) { - if (next instanceof LazyField.LazyEntry<?>) { - output.writeRawMessageSetExtension(descriptor.getNumber(), - ((LazyField.LazyEntry<?>) next).getField().toByteString()); - } else { - output.writeMessageSetExtension(descriptor.getNumber(), - (Message) next.getValue()); - } + output.writeMessageSetExtension(descriptor.getNumber(), + (Message) next.getValue()); } else { - // TODO(xiangl): Taken care of following code, it may cause - // problem when we use LazyField for normal fields/extensions. - // Due to the optional field can be duplicated at the end of - // serialized bytes, which will make the serialized size change - // after lazy field parsed. So when we use LazyField globally, - // we need to change the following write method to write cached - // bytes directly rather than write the parsed message. FieldSet.writeField(descriptor, next.getValue(), output); } if (iter.hasNext()) { @@ -777,14 +448,10 @@ public abstract class GeneratedMessage extends AbstractMessage // --------------------------------------------------------------- // Reflection - protected Map<FieldDescriptor, Object> getExtensionFields() { - return extensions.getAllFields(); - } - @Override public Map<FieldDescriptor, Object> getAllFields() { final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); - result.putAll(getExtensionFields()); + result.putAll(extensions.getAllFields()); return Collections.unmodifiableMap(result); } @@ -889,29 +556,9 @@ public abstract class GeneratedMessage extends AbstractMessage public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage, BuilderType extends ExtendableBuilder> - extends Builder<BuilderType> - implements ExtendableMessageOrBuilder<MessageType> { - - private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet(); - + extends Builder<BuilderType> { protected ExtendableBuilder() {} - protected ExtendableBuilder( - BuilderParent parent) { - super(parent); - } - - // For immutable message conversion. - void internalSetExtensionSet(FieldSet<FieldDescriptor> extensions) { - this.extensions = extensions; - } - - @Override - public BuilderType clear() { - extensions = FieldSet.emptySet(); - return super.clear(); - } - // 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. @@ -921,143 +568,80 @@ public abstract class GeneratedMessage extends AbstractMessage "This is supposed to be overridden by subclasses."); } - private void ensureExtensionsIsMutable() { - if (extensions.isImmutable()) { - extensions = extensions.clone(); - } - } - - private void verifyExtensionContainingType( - final Extension<MessageType, ?> extension) { - if (extension.getDescriptor().getContainingType() != - getDescriptorForType()) { - // This can only happen if someone uses unchecked operations. - throw new IllegalArgumentException( - "Extension is for type \"" + - extension.getDescriptor().getContainingType().getFullName() + - "\" which does not match message type \"" + - getDescriptorForType().getFullName() + "\"."); - } - } + @Override + protected abstract ExtendableMessage<MessageType> internalGetResult(); /** 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) { - verifyExtensionContainingType(extension); - return extensions.hasField(extension.getDescriptor()); + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { + return internalGetResult().hasExtension(extension); } /** 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) { - verifyExtensionContainingType(extension); - final FieldDescriptor descriptor = extension.getDescriptor(); - return extensions.getRepeatedFieldCount(descriptor); + final GeneratedExtension<MessageType, List<Type>> extension) { + return internalGetResult().getExtensionCount(extension); } /** 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) { - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - final Object value = extensions.getField(descriptor); - if (value == null) { - if (descriptor.isRepeated()) { - return (Type) Collections.emptyList(); - } else if (descriptor.getJavaType() == - FieldDescriptor.JavaType.MESSAGE) { - return (Type) extension.getMessageDefaultInstance(); - } else { - return (Type) extension.fromReflectionType( - descriptor.getDefaultValue()); - } - } else { - return (Type) extension.fromReflectionType(value); - } + final GeneratedExtension<MessageType, Type> extension) { + return internalGetResult().getExtension(extension); } /** 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 GeneratedExtension<MessageType, List<Type>> extension, final int index) { - verifyExtensionContainingType(extension); - FieldDescriptor descriptor = extension.getDescriptor(); - return (Type) extension.singularFromReflectionType( - extensions.getRepeatedField(descriptor, index)); + return internalGetResult().getExtension(extension, index); } /** Set the value of an extension. */ public final <Type> BuilderType setExtension( - final Extension<MessageType, Type> extension, + final GeneratedExtension<MessageType, Type> extension, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.setField(descriptor, extension.toReflectionType(value)); - onChanged(); + message.extensions.setField(descriptor, + extension.toReflectionType(value)); return (BuilderType) this; } /** Set the value of one element of a repeated extension. */ public final <Type> BuilderType setExtension( - final Extension<MessageType, List<Type>> extension, + final GeneratedExtension<MessageType, List<Type>> extension, final int index, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.setRepeatedField( + message.extensions.setRepeatedField( descriptor, index, extension.singularToReflectionType(value)); - onChanged(); return (BuilderType) this; } /** Append a value to a repeated extension. */ public final <Type> BuilderType addExtension( - final Extension<MessageType, List<Type>> extension, + final GeneratedExtension<MessageType, List<Type>> extension, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); final FieldDescriptor descriptor = extension.getDescriptor(); - extensions.addRepeatedField( + message.extensions.addRepeatedField( descriptor, extension.singularToReflectionType(value)); - onChanged(); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( - final Extension<MessageType, ?> extension) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.clearField(extension.getDescriptor()); - onChanged(); + final GeneratedExtension<MessageType, ?> extension) { + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.getDescriptor()); return (BuilderType) this; } - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - return extensions.isInitialized(); - } - - /** - * Called by the build code path to create a copy of the extensions for - * building the message. - */ - private FieldSet<FieldDescriptor> buildExtensions() { - extensions.makeImmutable(); - return extensions; - } - - @Override - public boolean isInitialized() { - return super.isInitialized() && extensionsAreInitialized(); - } - /** * Called by subclasses to parse an unknown field or an extension. * @return {@code true} unless the tag is an end-group tag. @@ -1068,81 +652,24 @@ public abstract class GeneratedMessage extends AbstractMessage final UnknownFieldSet.Builder unknownFields, final ExtensionRegistryLite extensionRegistry, final int tag) throws IOException { - return MessageReflection.mergeFieldFrom( - input, unknownFields, extensionRegistry, getDescriptorForType(), - new MessageReflection.BuilderAdapter(this), tag); + final ExtendableMessage<MessageType> message = internalGetResult(); + return AbstractMessage.Builder.mergeFieldFrom( + input, unknownFields, extensionRegistry, this, tag); } // --------------------------------------------------------------- // Reflection - @Override - public Map<FieldDescriptor, Object> getAllFields() { - final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); - result.putAll(extensions.getAllFields()); - return Collections.unmodifiableMap(result); - } - - @Override - public Object getField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - final Object value = extensions.getField(field); - if (value == null) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - // Lacking an ExtensionRegistry, we have no way to determine the - // extension's real type, so we return a DynamicMessage. - return DynamicMessage.getDefaultInstance(field.getMessageType()); - } else { - return field.getDefaultValue(); - } - } else { - return value; - } - } else { - return super.getField(field); - } - } - - @Override - public int getRepeatedFieldCount(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.getRepeatedFieldCount(field); - } else { - return super.getRepeatedFieldCount(field); - } - } - - @Override - public Object getRepeatedField(final FieldDescriptor field, - final int index) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.getRepeatedField(field, index); - } else { - return super.getRepeatedField(field, index); - } - } - - @Override - public boolean hasField(final FieldDescriptor field) { - if (field.isExtension()) { - verifyContainingType(field); - return extensions.hasField(field); - } else { - return super.hasField(field); - } - } + // We don't have to override the get*() methods here because they already + // just forward to the underlying message. @Override public BuilderType setField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.setField(field, value); - onChanged(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setField(field, value); return (BuilderType) this; } else { return super.setField(field, value); @@ -1152,10 +679,9 @@ public abstract class GeneratedMessage extends AbstractMessage @Override public BuilderType clearField(final FieldDescriptor field) { if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.clearField(field); - onChanged(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.clearField(field); return (BuilderType) this; } else { return super.clearField(field); @@ -1166,10 +692,9 @@ public abstract class GeneratedMessage extends AbstractMessage public BuilderType setRepeatedField(final FieldDescriptor field, final int index, final Object value) { if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(field, index, value); - onChanged(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setRepeatedField(field, index, value); return (BuilderType) this; } else { return super.setRepeatedField(field, index, value); @@ -1180,10 +705,9 @@ public abstract class GeneratedMessage extends AbstractMessage public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) { if (field.isExtension()) { - verifyContainingType(field); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(field, value); - onChanged(); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.addRepeatedField(field, value); return (BuilderType) this; } else { return super.addRepeatedField(field, value); @@ -1191,144 +715,17 @@ public abstract class GeneratedMessage extends AbstractMessage } protected final void mergeExtensionFields(final ExtendableMessage other) { - ensureExtensionsIsMutable(); - extensions.mergeFrom(other.extensions); - onChanged(); - } - - private void verifyContainingType(final FieldDescriptor field) { - if (field.getContainingType() != getDescriptorForType()) { - throw new IllegalArgumentException( - "FieldDescriptor does not match message type."); - } + internalGetResult().extensions.mergeFrom(other.extensions); } } // ----------------------------------------------------------------- - /** - * Gets the descriptor for an extension. The implementation depends on whether - * the extension is scoped in the top level of a file or scoped in a Message. - */ - static interface ExtensionDescriptorRetriever { - FieldDescriptor getDescriptor(); - } - - /** For use by generated code only. */ - public static <ContainingType extends Message, Type> - GeneratedExtension<ContainingType, Type> - newMessageScopedGeneratedExtension(final Message scope, - final int descriptorIndex, - final Class singularType, - final Message defaultInstance) { - // For extensions scoped within a Message, we use the Message to resolve - // the outer class's descriptor, from which the extension descriptor is - // obtained. - return new GeneratedExtension<ContainingType, Type>( - new CachedDescriptorRetriever() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public FieldDescriptor loadDescriptor() { - return scope.getDescriptorForType().getExtensions() - .get(descriptorIndex); - } - }, - singularType, - defaultInstance, - Extension.ExtensionType.IMMUTABLE); - } - /** For use by generated code only. */ public static <ContainingType extends Message, Type> - GeneratedExtension<ContainingType, Type> - newFileScopedGeneratedExtension(final Class singularType, - final Message defaultInstance) { - // For extensions scoped within a file, we rely on the outer class's - // static initializer to call internalInit() on the extension when the - // descriptor is available. - return new GeneratedExtension<ContainingType, Type>( - null, // ExtensionDescriptorRetriever is initialized in internalInit(); - singularType, - defaultInstance, - Extension.ExtensionType.IMMUTABLE); - } - - private abstract static class CachedDescriptorRetriever - implements ExtensionDescriptorRetriever { - private volatile FieldDescriptor descriptor; - protected abstract FieldDescriptor loadDescriptor(); - - public FieldDescriptor getDescriptor() { - if (descriptor == null) { - synchronized (this) { - if (descriptor == null) { - descriptor = loadDescriptor(); - } - } - } - return descriptor; - } - } - - /** - * Used in proto1 generated code only. - * - * After enabling bridge, we can define proto2 extensions (the extended type - * is a proto2 mutable message) in a proto1 .proto file. For these extensions - * we should generate proto2 GeneratedExtensions. - */ - public static <ContainingType extends Message, Type> GeneratedExtension<ContainingType, Type> - newMessageScopedGeneratedExtension( - final Message scope, final String name, - final Class singularType, final Message defaultInstance) { - // For extensions scoped within a Message, we use the Message to resolve - // the outer class's descriptor, from which the extension descriptor is - // obtained. - return new GeneratedExtension<ContainingType, Type>( - new CachedDescriptorRetriever() { - protected FieldDescriptor loadDescriptor() { - return scope.getDescriptorForType().findFieldByName(name); - } - }, - singularType, - defaultInstance, - Extension.ExtensionType.MUTABLE); - } - - /** - * Used in proto1 generated code only. - * - * After enabling bridge, we can define proto2 extensions (the extended type - * is a proto2 mutable message) in a proto1 .proto file. For these extensions - * we should generate proto2 GeneratedExtensions. - */ - public static <ContainingType extends Message, Type> - GeneratedExtension<ContainingType, Type> - newFileScopedGeneratedExtension( - final Class singularType, final Message defaultInstance, - final String descriptorOuterClass, final String extensionName) { - // For extensions scoped within a file, we load the descriptor outer - // class and rely on it to get the FileDescriptor which then can be - // used to obtain the extension's FieldDescriptor. - return new GeneratedExtension<ContainingType, Type>( - new CachedDescriptorRetriever() { - protected FieldDescriptor loadDescriptor() { - try { - Class clazz = - singularType.getClassLoader().loadClass(descriptorOuterClass); - FileDescriptor file = - (FileDescriptor) clazz.getField("descriptor").get(null); - return file.findExtensionByName(extensionName); - } catch (Exception e) { - throw new RuntimeException( - "Cannot load descriptors: " + descriptorOuterClass + - " is not a valid descriptor class name", e); - } - } - }, - singularType, - defaultInstance, - Extension.ExtensionType.MUTABLE); + newGeneratedExtension() { + return new GeneratedExtension<ContainingType, Type>(); } /** @@ -1356,99 +753,87 @@ public abstract class GeneratedMessage extends AbstractMessage * these static singletons as parameters to the extension accessors defined * in {@link ExtendableMessage} and {@link ExtendableBuilder}. */ - public static class GeneratedExtension< - ContainingType extends Message, Type> extends - Extension<ContainingType, Type> { + public static final class GeneratedExtension< + ContainingType extends Message, Type> { // TODO(kenton): Find ways to avoid using Java reflection within this // class. Also try to avoid suppressing unchecked warnings. - // We can't always initialize the descriptor of a GeneratedExtension when - // we first construct it due to initialization order difficulties (namely, - // the descriptor may not have been constructed yet, since it is often - // constructed by the initializer of a separate module). - // - // In the case of nested extensions, we initialize the - // ExtensionDescriptorRetriever with an instance that uses the scoping - // Message's default instance to retrieve the extension's descriptor. - // - // In the case of non-nested extensions, we initialize the - // ExtensionDescriptorRetriever to null and rely on the outer class's static - // initializer to call internalInit() after the descriptor has been parsed. - GeneratedExtension(ExtensionDescriptorRetriever descriptorRetriever, - Class singularType, - Message messageDefaultInstance, - ExtensionType extensionType) { - if (Message.class.isAssignableFrom(singularType) && - !singularType.isInstance(messageDefaultInstance)) { - throw new IllegalArgumentException( - "Bad messageDefaultInstance for " + singularType.getName()); - } - this.descriptorRetriever = descriptorRetriever; - this.singularType = singularType; - this.messageDefaultInstance = messageDefaultInstance; - - if (ProtocolMessageEnum.class.isAssignableFrom(singularType)) { - this.enumValueOf = getMethodOrDie(singularType, "valueOf", - EnumValueDescriptor.class); - this.enumGetValueDescriptor = - getMethodOrDie(singularType, "getValueDescriptor"); - } else { - this.enumValueOf = null; - this.enumGetValueDescriptor = null; - } - this.extensionType = extensionType; - } + // We can't always initialize a GeneratedExtension when we first construct + // it due to initialization order difficulties (namely, the descriptor may + // not have been constructed yet, since it is often constructed by the + // initializer of a separate module). So, we construct an uninitialized + // GeneratedExtension once, then call internalInit() on it later. Generated + // code will always call internalInit() on all extensions as part of the + // static initialization code, and internalInit() throws an exception if + // called more than once, so this method is useless to users. + private GeneratedExtension() {} /** For use by generated code only. */ - public void internalInit(final FieldDescriptor descriptor) { - if (descriptorRetriever != null) { + public void internalInit(final FieldDescriptor descriptor, + final Class type) { + if (this.descriptor != null) { throw new IllegalStateException("Already initialized."); } - descriptorRetriever = new ExtensionDescriptorRetriever() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public FieldDescriptor getDescriptor() { - return descriptor; - } - }; - } - private ExtensionDescriptorRetriever descriptorRetriever; - private final Class singularType; - private final Message messageDefaultInstance; - private final Method enumValueOf; - private final Method enumGetValueDescriptor; - private final ExtensionType extensionType; + if (!descriptor.isExtension()) { + throw new IllegalArgumentException( + "GeneratedExtension given a regular (non-extension) field."); + } - public FieldDescriptor getDescriptor() { - if (descriptorRetriever == null) { - throw new IllegalStateException( - "getDescriptor() called before internalInit()"); + this.descriptor = descriptor; + this.type = type; + + switch (descriptor.getJavaType()) { + case MESSAGE: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = + (Message) invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), + null); + if (messageDefaultInstance == null) { + throw new IllegalStateException( + type.getName() + ".getDefaultInstance() returned null."); + } + break; + case ENUM: + enumValueOf = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor"); + messageDefaultInstance = null; + break; + default: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = null; + break; } - return descriptorRetriever.getDescriptor(); } + private FieldDescriptor descriptor; + private Class type; + private Method enumValueOf; + private Method enumGetValueDescriptor; + private Message messageDefaultInstance; + + public FieldDescriptor getDescriptor() { return descriptor; } + /** * If the extension is an embedded message or group, returns the default * instance of the message. */ + @SuppressWarnings("unchecked") public Message getMessageDefaultInstance() { return messageDefaultInstance; } - protected ExtensionType getExtensionType() { - return extensionType; - } - /** * Convert from the type used by the reflection accessors to the type used * by native accessors. E.g., for enums, the reflection accessors use * EnumValueDescriptors but the native accessors use the generated enum * type. */ - // @Override @SuppressWarnings("unchecked") - protected Object fromReflectionType(final Object value) { - FieldDescriptor descriptor = getDescriptor(); + private Object fromReflectionType(final Object value) { if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { @@ -1470,16 +855,20 @@ public abstract class GeneratedMessage extends AbstractMessage * Like {@link #fromReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - // @Override - protected Object singularFromReflectionType(final Object value) { - FieldDescriptor descriptor = getDescriptor(); + private Object singularFromReflectionType(final Object value) { switch (descriptor.getJavaType()) { case MESSAGE: - if (singularType.isInstance(value)) { + if (type.isInstance(value)) { return value; } else { + // It seems the copy of the embedded message stored inside the + // extended message is not of the exact type the user was + // expecting. This can happen if a user defines a + // GeneratedExtension manually and gives it a different type. + // This should not happen in normal use. But, to be nice, we'll + // copy the message to whatever type the caller was expecting. return messageDefaultInstance.newBuilderForType() - .mergeFrom((Message) value).build(); + .mergeFrom((Message) value).build(); } case ENUM: return invokeOrDie(enumValueOf, null, (EnumValueDescriptor) value); @@ -1494,10 +883,8 @@ public abstract class GeneratedMessage extends AbstractMessage * EnumValueDescriptors but the native accessors use the generated enum * type. */ - // @Override @SuppressWarnings("unchecked") - protected Object toReflectionType(final Object value) { - FieldDescriptor descriptor = getDescriptor(); + private Object toReflectionType(final Object value) { if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. @@ -1518,9 +905,7 @@ public abstract class GeneratedMessage extends AbstractMessage * Like {@link #toReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - // @Override - protected Object singularToReflectionType(final Object value) { - FieldDescriptor descriptor = getDescriptor(); + private Object singularToReflectionType(final Object value) { switch (descriptor.getJavaType()) { case ENUM: return invokeOrDie(enumGetValueDescriptor, value); @@ -1528,34 +913,6 @@ public abstract class GeneratedMessage extends AbstractMessage return value; } } - - // @Override - public int getNumber() { - return getDescriptor().getNumber(); - } - - // @Override - public WireFormat.FieldType getLiteType() { - return getDescriptor().getLiteType(); - } - - // @Override - public boolean isRepeated() { - return getDescriptor().isRepeated(); - } - - // @Override - @SuppressWarnings("unchecked") - public Type getDefaultValue() { - if (isRepeated()) { - return (Type) Collections.emptyList(); - } - if (getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return (Type) messageDefaultInstance; - } - return (Type) singularFromReflectionType( - getDescriptor().getDefaultValue()); - } } // ================================================================= @@ -1616,90 +973,39 @@ public abstract class GeneratedMessage extends AbstractMessage final String[] camelCaseNames, final Class<? extends GeneratedMessage> messageClass, final Class<? extends Builder> builderClass) { - this(descriptor, camelCaseNames); - ensureFieldAccessorsInitialized(messageClass, builderClass); - } - - /** - * Construct a FieldAccessorTable for a particular message class without - * initializing FieldAccessors. - */ - public FieldAccessorTable( - final Descriptor descriptor, - final String[] camelCaseNames) { this.descriptor = descriptor; - this.camelCaseNames = camelCaseNames; fields = new FieldAccessor[descriptor.getFields().size()]; - oneofs = new OneofAccessor[descriptor.getOneofs().size()]; - initialized = false; - } - /** - * Ensures the field accessors are initialized. This method is thread-safe. - * - * @param messageClass The message type. - * @param builderClass The builder type. - * @return this - */ - public FieldAccessorTable ensureFieldAccessorsInitialized( - Class<? extends GeneratedMessage> messageClass, - Class<? extends Builder> builderClass) { - if (initialized) { return this; } - synchronized (this) { - if (initialized) { return this; } - int fieldsSize = fields.length; - for (int i = 0; i < fieldsSize; i++) { - FieldDescriptor field = descriptor.getFields().get(i); - String containingOneofCamelCaseName = null; - if (field.getContainingOneof() != null) { - containingOneofCamelCaseName = - camelCaseNames[fieldsSize + field.getContainingOneof().getIndex()]; + for (int i = 0; i < fields.length; i++) { + final FieldDescriptor field = descriptor.getFields().get(i); + if (field.isRepeated()) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + 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); + } else { + fields[i] = new RepeatedFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); } - if (field.isRepeated()) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - 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); - } else { - fields[i] = new RepeatedFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass); - } + } else { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new SingularMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new SingularEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); } else { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - fields[i] = new SingularMessageFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass, - containingOneofCamelCaseName); - } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { - fields[i] = new SingularEnumFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass, - containingOneofCamelCaseName); - } else { - fields[i] = new SingularFieldAccessor( - field, camelCaseNames[i], messageClass, builderClass, - containingOneofCamelCaseName); - } + fields[i] = new SingularFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); } } - - int oneofsSize = oneofs.length; - for (int i = 0; i < oneofsSize; i++) { - oneofs[i] = new OneofAccessor( - descriptor, camelCaseNames[i + fieldsSize], - messageClass, builderClass); - } - initialized = true; - camelCaseNames = null; - return this; } } private final Descriptor descriptor; private final FieldAccessor[] fields; - private String[] camelCaseNames; - private final OneofAccessor[] oneofs; - private volatile boolean initialized; /** Get the FieldAccessor for a particular field. */ private FieldAccessor getField(final FieldDescriptor field) { @@ -1715,93 +1021,21 @@ public abstract class GeneratedMessage extends AbstractMessage return fields[field.getIndex()]; } - /** Get the OneofAccessor for a particular oneof. */ - private OneofAccessor getOneof(final OneofDescriptor oneof) { - if (oneof.getContainingType() != descriptor) { - throw new IllegalArgumentException( - "OneofDescriptor does not match message type."); - } - return oneofs[oneof.getIndex()]; - } - /** * Abstract interface that provides access to a single field. This is * implemented differently depending on the field type and cardinality. */ private interface FieldAccessor { Object get(GeneratedMessage message); - Object get(GeneratedMessage.Builder builder); void set(Builder builder, Object value); Object getRepeated(GeneratedMessage message, int index); - Object getRepeated(GeneratedMessage.Builder builder, int index); void setRepeated(Builder builder, int index, Object value); void addRepeated(Builder builder, Object value); boolean has(GeneratedMessage message); - boolean has(GeneratedMessage.Builder builder); int getRepeatedCount(GeneratedMessage message); - int getRepeatedCount(GeneratedMessage.Builder builder); void clear(Builder builder); Message.Builder newBuilder(); - Message.Builder getBuilder(GeneratedMessage.Builder builder); - } - - /** OneofAccessor provides access to a single oneof. */ - private static class OneofAccessor { - OneofAccessor( - final Descriptor descriptor, final String camelCaseName, - final Class<? extends GeneratedMessage> messageClass, - final Class<? extends Builder> builderClass) { - this.descriptor = descriptor; - caseMethod = - getMethodOrDie(messageClass, "get" + camelCaseName + "Case"); - caseMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName + "Case"); - clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - } - - private final Descriptor descriptor; - private final Method caseMethod; - private final Method caseMethodBuilder; - private final Method clearMethod; - - public boolean has(final GeneratedMessage message) { - if (((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() == 0) { - return false; - } - return true; - } - - public boolean has(GeneratedMessage.Builder builder) { - if (((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() == 0) { - return false; - } - return true; - } - - public FieldDescriptor get(final GeneratedMessage message) { - int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); - if (fieldNumber > 0) { - return descriptor.findFieldByNumber(fieldNumber); - } - return null; - } - - public FieldDescriptor get(GeneratedMessage.Builder builder) { - int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); - if (fieldNumber > 0) { - return descriptor.findFieldByNumber(fieldNumber); - } - return null; - } - - public void clear(final Builder builder) { - invokeOrDie(clearMethod, builder); - } - } - - private static boolean supportFieldPresence(FileDescriptor file) { - return true; } // --------------------------------------------------------------- @@ -1810,57 +1044,27 @@ public abstract class GeneratedMessage extends AbstractMessage SingularFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class<? extends GeneratedMessage> messageClass, - final Class<? extends Builder> builderClass, - final String containingOneofCamelCaseName) { - field = descriptor; - isOneofField = descriptor.getContainingOneof() != null; - hasHasMethod = supportFieldPresence(descriptor.getFile()) - || (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE); + final Class<? extends Builder> builderClass) { getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); - getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName); type = getMethod.getReturnType(); setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); hasMethod = - hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null; - hasMethodBuilder = - hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null; + getMethodOrDie(messageClass, "has" + camelCaseName); clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); - caseMethod = isOneofField ? getMethodOrDie( - messageClass, "get" + containingOneofCamelCaseName + "Case") : null; - caseMethodBuilder = isOneofField ? getMethodOrDie( - builderClass, "get" + containingOneofCamelCaseName + "Case") : null; } // Note: We use Java reflection to call public methods rather than // access private fields directly as this avoids runtime security // checks. - protected final Class<?> type; + protected final Class type; protected final Method getMethod; - protected final Method getMethodBuilder; protected final Method setMethod; protected final Method hasMethod; - protected final Method hasMethodBuilder; protected final Method clearMethod; - protected final Method caseMethod; - protected final Method caseMethodBuilder; - protected final FieldDescriptor field; - protected final boolean isOneofField; - protected final boolean hasHasMethod; - - private int getOneofFieldNumber(final GeneratedMessage message) { - return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber(); - } - - private int getOneofFieldNumber(final GeneratedMessage.Builder builder) { - return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber(); - } public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } - public Object get(GeneratedMessage.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); - } public void set(final Builder builder, final Object value) { invokeOrDie(setMethod, builder, value); } @@ -1869,10 +1073,6 @@ public abstract class GeneratedMessage extends AbstractMessage throw new UnsupportedOperationException( "getRepeatedField() called on a singular field."); } - public Object getRepeated(GeneratedMessage.Builder builder, int index) { - throw new UnsupportedOperationException( - "getRepeatedField() called on a singular field."); - } public void setRepeated(final Builder builder, final int index, final Object value) { throw new UnsupportedOperationException( @@ -1883,31 +1083,12 @@ public abstract class GeneratedMessage extends AbstractMessage "addRepeatedField() called on a singular field."); } public boolean has(final GeneratedMessage message) { - if (!hasHasMethod) { - if (isOneofField) { - return getOneofFieldNumber(message) == field.getNumber(); - } - return !get(message).equals(field.getDefaultValue()); - } return (Boolean) invokeOrDie(hasMethod, message); } - public boolean has(GeneratedMessage.Builder builder) { - if (!hasHasMethod) { - if (isOneofField) { - return getOneofFieldNumber(builder) == field.getNumber(); - } - return !get(builder).equals(field.getDefaultValue()); - } - return (Boolean) invokeOrDie(hasMethodBuilder, builder); - } public int getRepeatedCount(final GeneratedMessage message) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } - public int getRepeatedCount(GeneratedMessage.Builder builder) { - throw new UnsupportedOperationException( - "getRepeatedFieldSize() called on a singular field."); - } public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } @@ -1915,63 +1096,48 @@ public abstract class GeneratedMessage extends AbstractMessage throw new UnsupportedOperationException( "newBuilderForField() called on a non-Message type."); } - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { - throw new UnsupportedOperationException( - "getFieldBuilder() called on a non-Message type."); - } } private static class RepeatedFieldAccessor implements FieldAccessor { - protected final Class type; - protected final Method getMethod; - protected final Method getMethodBuilder; - protected final Method getRepeatedMethod; - protected final Method getRepeatedMethodBuilder; - protected final Method setRepeatedMethod; - protected final Method addRepeatedMethod; - protected final Method getCountMethod; - protected final Method getCountMethodBuilder; - protected final Method clearMethod; - RepeatedFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class<? extends GeneratedMessage> messageClass, final Class<? extends Builder> builderClass) { getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); - getMethodBuilder = getMethodOrDie(builderClass, - "get" + camelCaseName + "List"); + getRepeatedMethod = - getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); - getRepeatedMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE); + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); type = getRepeatedMethod.getReturnType(); setRepeatedMethod = - getMethodOrDie(builderClass, "set" + camelCaseName, - Integer.TYPE, type); + getMethodOrDie(builderClass, "set" + camelCaseName, + Integer.TYPE, type); addRepeatedMethod = - getMethodOrDie(builderClass, "add" + camelCaseName, type); + getMethodOrDie(builderClass, "add" + camelCaseName, type); getCountMethod = - getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); - getCountMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName + "Count"); + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); } + protected final Class type; + protected final Method getMethod; + protected final Method getRepeatedMethod; + protected final Method setRepeatedMethod; + protected final Method addRepeatedMethod; + protected final Method getCountMethod; + protected final Method clearMethod; + public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } - public Object get(GeneratedMessage.Builder builder) { - return invokeOrDie(getMethodBuilder, builder); - } public void set(final Builder builder, final Object value) { // Add all the elements individually. This serves two purposes: // 1) Verifies that each element has the correct type. // 2) Insures that the caller cannot modify the list later on and // have the modifications be reflected in the message. clear(builder); - for (final Object element : (List<?>) value) { + for (final Object element : (List) value) { addRepeated(builder, element); } } @@ -1979,9 +1145,6 @@ public abstract class GeneratedMessage extends AbstractMessage final int index) { return invokeOrDie(getRepeatedMethod, message, index); } - public Object getRepeated(GeneratedMessage.Builder builder, int index) { - return invokeOrDie(getRepeatedMethodBuilder, builder, index); - } public void setRepeated(final Builder builder, final int index, final Object value) { invokeOrDie(setRepeatedMethod, builder, index, value); @@ -1991,18 +1154,11 @@ public abstract class GeneratedMessage extends AbstractMessage } public boolean has(final GeneratedMessage message) { throw new UnsupportedOperationException( - "hasField() called on a repeated field."); - } - public boolean has(GeneratedMessage.Builder builder) { - throw new UnsupportedOperationException( - "hasField() called on a repeated field."); + "hasField() called on a singular field."); } public int getRepeatedCount(final GeneratedMessage message) { return (Integer) invokeOrDie(getCountMethod, message); } - public int getRepeatedCount(GeneratedMessage.Builder builder) { - return (Integer) invokeOrDie(getCountMethodBuilder, builder); - } public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } @@ -2010,10 +1166,6 @@ public abstract class GeneratedMessage extends AbstractMessage throw new UnsupportedOperationException( "newBuilderForField() called on a non-Message type."); } - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { - throw new UnsupportedOperationException( - "getFieldBuilder() called on a non-Message type."); - } } // --------------------------------------------------------------- @@ -2023,9 +1175,8 @@ public abstract class GeneratedMessage extends AbstractMessage SingularEnumFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class<? extends GeneratedMessage> messageClass, - final Class<? extends Builder> builderClass, - final String containingOneofCamelCaseName) { - super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); + final Class<? extends Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); valueOfMethod = getMethodOrDie(type, "valueOf", EnumValueDescriptor.class); @@ -2040,12 +1191,6 @@ public abstract class GeneratedMessage extends AbstractMessage public Object get(final GeneratedMessage message) { return invokeOrDie(getValueDescriptorMethod, super.get(message)); } - - @Override - public Object get(final GeneratedMessage.Builder builder) { - return invokeOrDie(getValueDescriptorMethod, super.get(builder)); - } - @Override public void set(final Builder builder, final Object value) { super.set(builder, invokeOrDie(valueOfMethod, null, value)); @@ -2078,17 +1223,6 @@ public abstract class GeneratedMessage extends AbstractMessage } return Collections.unmodifiableList(newList); } - - @Override - @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)); - } - return Collections.unmodifiableList(newList); - } - @Override public Object getRepeated(final GeneratedMessage message, final int index) { @@ -2096,12 +1230,6 @@ public abstract class GeneratedMessage extends AbstractMessage super.getRepeated(message, index)); } @Override - public Object getRepeated(final GeneratedMessage.Builder builder, - final int index) { - return invokeOrDie(getValueDescriptorMethod, - super.getRepeated(builder, index)); - } - @Override public void setRepeated(final Builder builder, final int index, final Object value) { super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, @@ -2120,17 +1248,13 @@ public abstract class GeneratedMessage extends AbstractMessage SingularMessageFieldAccessor( final FieldDescriptor descriptor, final String camelCaseName, final Class<? extends GeneratedMessage> messageClass, - final Class<? extends Builder> builderClass, - final String containingOneofCamelCaseName) { - super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName); + final Class<? extends Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); newBuilderMethod = getMethodOrDie(type, "newBuilder"); - getBuilderMethodBuilder = - getMethodOrDie(builderClass, "get" + camelCaseName + "Builder"); } private final Method newBuilderMethod; - private final Method getBuilderMethodBuilder; private Object coerceType(final Object value) { if (type.isInstance(value)) { @@ -2141,7 +1265,7 @@ public abstract class GeneratedMessage extends AbstractMessage // DynamicMessage -- we should accept it. In this case we can make // a copy of the message. return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) - .mergeFrom((Message) value).buildPartial(); + .mergeFrom((Message) value).build(); } } @@ -2153,10 +1277,6 @@ public abstract class GeneratedMessage extends AbstractMessage public Message.Builder newBuilder() { return (Message.Builder) invokeOrDie(newBuilderMethod, null); } - @Override - public Message.Builder getBuilder(GeneratedMessage.Builder builder) { - return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder); - } } private static final class RepeatedMessageFieldAccessor @@ -2200,14 +1320,4 @@ public abstract class GeneratedMessage extends AbstractMessage } } } - - /** - * Replaces this object in the output stream with a serialized form. - * Part of Java's serialization magic. Generated sub-classes must override - * this method by calling {@code return super.writeReplace();} - * @return a SerializedForm of this message - */ - protected Object writeReplace() throws ObjectStreamException { - return new GeneratedMessageLite.SerializedForm(this); - } } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index 6c5136f..9cdd4e9 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -31,11 +31,6 @@ package com.google.protobuf; import java.io.IOException; -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -46,39 +41,8 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public abstract class GeneratedMessageLite extends AbstractMessageLite - implements Serializable { - private static final long serialVersionUID = 1L; - - protected GeneratedMessageLite() { - } - - protected GeneratedMessageLite(Builder builder) { - } - - public Parser<? extends MessageLite> getParserForType() { - throw new UnsupportedOperationException( - "This is supposed to be overridden by subclasses."); - } - - /** - * Called by subclasses to parse an unknown field. - * @return {@code true} unless the tag is an end-group tag. - */ - protected boolean parseUnknownField( - CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return input.skipField(tag, unknownFieldsCodedOutput); - } - - /** - * Used by parsing constructors in generated classes. - */ - protected void makeExtensionsImmutable() { - // Noop for messages without extensions. - } +public abstract class GeneratedMessageLite extends AbstractMessageLite { + protected GeneratedMessageLite() {} @SuppressWarnings("unchecked") public abstract static class Builder<MessageType extends GeneratedMessageLite, @@ -86,12 +50,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite extends AbstractMessageLite.Builder<BuilderType> { protected Builder() {} - //@Override (Java 1.6 override semantics, but we must support 1.5) - public BuilderType clear() { - unknownFields = ByteString.EMPTY; - 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. @@ -108,73 +66,35 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite public abstract MessageType getDefaultInstanceForType(); /** + * Get the message being built. We don't just pass this to the + * constructor because it becomes null when build() is called. + */ + protected abstract MessageType internalGetResult(); + + /** * Called by subclasses to parse an unknown field. * @return {@code true} unless the tag is an end-group tag. */ protected boolean parseUnknownField( - CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return input.skipField(tag, unknownFieldsCodedOutput); + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { + return input.skipField(tag); } - - public final ByteString getUnknownFields() { - return unknownFields; - } - - public final BuilderType setUnknownFields(final ByteString unknownFields) { - this.unknownFields = unknownFields; - return (BuilderType) this; - } - - private ByteString unknownFields = ByteString.EMPTY; } - // ================================================================= // Extensions-related stuff /** - * Lite equivalent of {@link com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}. - */ - public interface ExtendableMessageOrBuilder< - MessageType extends ExtendableMessage> extends MessageLiteOrBuilder { - - /** Check if a singular extension is present. */ - <Type> boolean hasExtension( - GeneratedExtension<MessageType, Type> extension); - - /** Get the number of elements in a repeated extension. */ - <Type> int getExtensionCount( - GeneratedExtension<MessageType, List<Type>> extension); - - /** Get the value of an extension. */ - <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension); - - /** Get one element of a repeated extension. */ - <Type> Type getExtension( - GeneratedExtension<MessageType, List<Type>> extension, - int index); - } - - /** * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}. */ public abstract static class ExtendableMessage< MessageType extends ExtendableMessage<MessageType>> - extends GeneratedMessageLite - implements ExtendableMessageOrBuilder<MessageType> { - - private final FieldSet<ExtensionDescriptor> extensions; - - protected ExtendableMessage() { - this.extensions = FieldSet.newFieldSet(); - } - - protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) { - this.extensions = builder.buildExtensions(); - } + extends GeneratedMessageLite { + protected ExtendableMessage() {} + private final FieldSet<ExtensionDescriptor> extensions = + FieldSet.newFieldSet(); private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { @@ -188,15 +108,13 @@ 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) { + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { verifyExtensionContainingType(extension); return extensions.hasField(extension.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); @@ -204,7 +122,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite } /** 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) { @@ -213,19 +130,17 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite if (value == null) { return extension.defaultValue; } else { - return (Type) extension.fromFieldSetType(value); + return (Type) value; } } /** Get one element of a repeated extension. */ - //@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 int index) { verifyExtensionContainingType(extension); - return (Type) extension.singularFromFieldSetType( - extensions.getRepeatedField(extension.descriptor, index)); + return (Type) extensions.getRepeatedField(extension.descriptor, index); } /** Called by subclasses to check if all extensions are initialized. */ @@ -234,34 +149,6 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite } /** - * 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. - */ - @Override - protected void makeExtensionsImmutable() { - extensions.makeImmutable(); - } - - /** * Used by subclasses to serialize extensions. Extension ranges may be * interleaved with field numbers, but we must write them in canonical * (sorted by field number) order. ExtensionWriter helps us write @@ -327,111 +214,53 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage<MessageType>, BuilderType extends ExtendableBuilder<MessageType, BuilderType>> - extends Builder<MessageType, BuilderType> - implements ExtendableMessageOrBuilder<MessageType> { + extends Builder<MessageType, BuilderType> { protected ExtendableBuilder() {} - private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet(); - private boolean extensionsIsMutable; - - // For immutable message conversion. - void internalSetExtensionSet(FieldSet<ExtensionDescriptor> extensions) { - this.extensions = extensions; - } - + // 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 clear() { - extensions.clear(); - extensionsIsMutable = false; - return super.clear(); - } - - private void ensureExtensionsIsMutable() { - if (!extensionsIsMutable) { - extensions = extensions.clone(); - extensionsIsMutable = true; - } - } - - /** - * Called by the build code path to create a copy of the extensions for - * building the message. - */ - private FieldSet<ExtensionDescriptor> buildExtensions() { - extensions.makeImmutable(); - extensionsIsMutable = false; - return extensions; + public BuilderType clone() { + throw new UnsupportedOperationException( + "This is supposed to be overridden by subclasses."); } - private void verifyExtensionContainingType( - final GeneratedExtension<MessageType, ?> extension) { - if (extension.getContainingTypeDefaultInstance() != - getDefaultInstanceForType()) { - // This can only happen if someone uses unchecked operations. - throw new IllegalArgumentException( - "This extension is for a different message type. Please make " + - "sure that you are not suppressing any generics type warnings."); - } - } + @Override + protected abstract MessageType internalGetResult(); /** 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); + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { + return internalGetResult().hasExtension(extension); } /** 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); + return internalGetResult().getExtensionCount(extension); } /** 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); - if (value == null) { - return extension.defaultValue; - } else { - return (Type) extension.fromFieldSetType(value); - } + return internalGetResult().getExtension(extension); } /** Get one element of a repeated extension. */ - @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 int index) { - verifyExtensionContainingType(extension); - return (Type) extension.singularFromFieldSetType( - extensions.getRepeatedField(extension.descriptor, index)); - } - - // 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."); + return internalGetResult().getExtension(extension, index); } /** Set the value of an extension. */ public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, Type> extension, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.setField(extension.descriptor, - extension.toFieldSetType(value)); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setField(extension.descriptor, value); return (BuilderType) this; } @@ -439,10 +268,9 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite public final <Type> BuilderType setExtension( final GeneratedExtension<MessageType, List<Type>> extension, final int index, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.setRepeatedField(extension.descriptor, index, - extension.singularToFieldSetType(value)); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setRepeatedField(extension.descriptor, index, value); return (BuilderType) this; } @@ -450,177 +278,141 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite public final <Type> BuilderType addExtension( final GeneratedExtension<MessageType, List<Type>> extension, final Type value) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.addRepeatedField(extension.descriptor, value); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( final GeneratedExtension<MessageType, ?> extension) { - verifyExtensionContainingType(extension); - ensureExtensionsIsMutable(); - extensions.clearField(extension.descriptor); + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.descriptor); return (BuilderType) this; } - /** Called by subclasses to check if all extensions are initialized. */ - protected boolean extensionsAreInitialized() { - 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); - } - } + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { + final FieldSet<ExtensionDescriptor> extensions = + ((ExtendableMessage) internalGetResult()).extensions; + + final int wireType = WireFormat.getTagWireType(tag); + final int fieldNumber = WireFormat.getTagFieldNumber(tag); + + final GeneratedExtension<MessageType, ?> extension = + extensionRegistry.findLiteExtensionByNumber( + getDefaultInstanceForType(), fieldNumber); + + boolean unknown = false; + boolean packed = false; + if (extension == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + false /* isPacked */)) { + packed = false; // Normal, unpacked value. + } else if (extension.descriptor.isRepeated && + extension.descriptor.type.isPackable() && + wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + true /* isPacked */)) { + packed = true; // Packed value. + } else { + unknown = true; // Wrong wire type. + } - // ----------------------------------------------------------------- + if (unknown) { // Unknown field or wrong wire type. Skip. + return input.skipField(tag); + } - /** - * Parse an unknown field or an extension. - * @return {@code true} unless the tag is an end-group tag. - */ - private static <MessageType extends MessageLite> - boolean parseUnknownField( - FieldSet<ExtensionDescriptor> extensions, - MessageType defaultInstance, - CodedInputStream input, - CodedOutputStream unknownFieldsCodedOutput, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - int wireType = WireFormat.getTagWireType(tag); - int fieldNumber = WireFormat.getTagFieldNumber(tag); - - GeneratedExtension<MessageType, ?> extension = - extensionRegistry.findLiteExtensionByNumber( - defaultInstance, fieldNumber); - - boolean unknown = false; - boolean packed = false; - if (extension == null) { - unknown = true; // Unknown field. - } else if (wireType == FieldSet.getWireFormatForFieldType( - extension.descriptor.getLiteType(), - false /* isPacked */)) { - packed = false; // Normal, unpacked value. - } else if (extension.descriptor.isRepeated && - extension.descriptor.type.isPackable() && - wireType == FieldSet.getWireFormatForFieldType( - extension.descriptor.getLiteType(), - true /* isPacked */)) { - packed = true; // Packed value. - } else { - unknown = true; // Wrong wire type. - } - - if (unknown) { // Unknown field or wrong wire type. Skip. - return input.skipField(tag, unknownFieldsCodedOutput); - } - - if (packed) { - int length = input.readRawVarint32(); - int limit = input.pushLimit(length); - if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { - while (input.getBytesUntilLimit() > 0) { - int rawValue = input.readEnum(); - Object value = - extension.descriptor.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 (packed) { + final int length = input.readRawVarint32(); + final int limit = input.pushLimit(length); + if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { + while (input.getBytesUntilLimit() > 0) { + final int rawValue = input.readEnum(); + final Object value = + extension.descriptor.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; + } + extensions.addRepeatedField(extension.descriptor, value); } - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); - } - } else { - while (input.getBytesUntilLimit() > 0) { - Object value = + } else { + while (input.getBytesUntilLimit() > 0) { + final Object value = FieldSet.readPrimitiveField(input, - extension.descriptor.getLiteType(), - /*checkUtf8=*/ false); - extensions.addRepeatedField(extension.descriptor, value); + extension.descriptor.getLiteType()); + extensions.addRepeatedField(extension.descriptor, value); + } } - } - input.popLimit(limit); - } else { - Object value; - switch (extension.descriptor.getLiteJavaType()) { - case MESSAGE: { - MessageLite.Builder subBuilder = null; - if (!extension.descriptor.isRepeated()) { - MessageLite existingValue = - (MessageLite) extensions.getField(extension.descriptor); - if (existingValue != null) { - subBuilder = existingValue.toBuilder(); + input.popLimit(limit); + } else { + final Object value; + switch (extension.descriptor.getLiteJavaType()) { + case MESSAGE: { + MessageLite.Builder subBuilder = null; + if (!extension.descriptor.isRepeated()) { + MessageLite existingValue = + (MessageLite) extensions.getField(extension.descriptor); + if (existingValue != null) { + subBuilder = existingValue.toBuilder(); + } } + if (subBuilder == null) { + subBuilder = extension.messageDefaultInstance.newBuilderForType(); + } + if (extension.descriptor.getLiteType() == + WireFormat.FieldType.GROUP) { + input.readGroup(extension.getNumber(), + subBuilder, extensionRegistry); + } else { + input.readMessage(subBuilder, extensionRegistry); + } + value = subBuilder.build(); + break; } - if (subBuilder == null) { - subBuilder = extension.getMessageDefaultInstance() - .newBuilderForType(); - } - if (extension.descriptor.getLiteType() == - WireFormat.FieldType.GROUP) { - input.readGroup(extension.getNumber(), - subBuilder, extensionRegistry); - } else { - input.readMessage(subBuilder, extensionRegistry); - } - value = subBuilder.build(); - break; + case ENUM: + final int rawValue = input.readEnum(); + value = extension.descriptor.getEnumType() + .findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + return true; + } + break; + default: + value = FieldSet.readPrimitiveField(input, + extension.descriptor.getLiteType()); + break; } - case ENUM: - int rawValue = input.readEnum(); - value = extension.descriptor.getEnumType() - .findValueByNumber(rawValue); - // 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); - return true; - } - break; - default: - value = FieldSet.readPrimitiveField(input, - extension.descriptor.getLiteType(), - /*checkUtf8=*/ false); - break; - } - if (extension.descriptor.isRepeated()) { - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); - } else { - extensions.setField(extension.descriptor, - extension.singularToFieldSetType(value)); + if (extension.descriptor.isRepeated()) { + extensions.addRepeatedField(extension.descriptor, value); + } else { + extensions.setField(extension.descriptor, value); + } } + + return true; } - return true; + protected final void mergeExtensionFields(final MessageType other) { + ((ExtendableMessage) internalGetResult()).extensions.mergeFrom( + ((ExtendableMessage) other).extensions); + } } // ----------------------------------------------------------------- @@ -628,50 +420,14 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite /** For use by generated code only. */ public static <ContainingType extends MessageLite, Type> GeneratedExtension<ContainingType, Type> - newSingularGeneratedExtension( - final ContainingType containingTypeDefaultInstance, - final Type defaultValue, - final MessageLite messageDefaultInstance, - final Internal.EnumLiteMap<?> enumTypeMap, - final int number, - final WireFormat.FieldType type, - final Class singularType) { - return new GeneratedExtension<ContainingType, Type>( - containingTypeDefaultInstance, - defaultValue, - messageDefaultInstance, - new ExtensionDescriptor(enumTypeMap, number, type, - false /* isRepeated */, - false /* isPacked */), - singularType); + newGeneratedExtension() { + return new GeneratedExtension<ContainingType, Type>(); } - /** For use by generated code only. */ - public static <ContainingType extends MessageLite, Type> - GeneratedExtension<ContainingType, Type> - newRepeatedGeneratedExtension( - final ContainingType containingTypeDefaultInstance, - final MessageLite messageDefaultInstance, - final Internal.EnumLiteMap<?> enumTypeMap, - final int number, - final WireFormat.FieldType type, - final boolean isPacked, - final Class singularType) { - @SuppressWarnings("unchecked") // Subclasses ensure Type is a List - Type emptyList = (Type) Collections.emptyList(); - return new GeneratedExtension<ContainingType, Type>( - containingTypeDefaultInstance, - emptyList, - messageDefaultInstance, - new ExtensionDescriptor( - enumTypeMap, number, type, true /* isRepeated */, isPacked), - singularType); - } - - static final class ExtensionDescriptor + private static final class ExtensionDescriptor implements FieldSet.FieldDescriptorLite< ExtensionDescriptor> { - ExtensionDescriptor( + private ExtensionDescriptor( final Internal.EnumLiteMap<?> enumTypeMap, final int number, final WireFormat.FieldType type, @@ -684,11 +440,11 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite this.isPacked = isPacked; } - final Internal.EnumLiteMap<?> enumTypeMap; - final int number; - final WireFormat.FieldType type; - final boolean isRepeated; - final boolean isPacked; + private final Internal.EnumLiteMap<?> enumTypeMap; + private final int number; + private final WireFormat.FieldType type; + private final boolean isRepeated; + private final boolean isPacked; public int getNumber() { return number; @@ -720,103 +476,72 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite return ((Builder) to).mergeFrom((GeneratedMessageLite) from); } - public int compareTo(ExtensionDescriptor other) { return number - other.number; } } - // ================================================================= - - /** Calls Class.getMethod and throws a RuntimeException if it fails. */ - @SuppressWarnings("unchecked") - static Method getMethodOrDie(Class clazz, String name, Class... params) { - try { - return clazz.getMethod(name, params); - } catch (NoSuchMethodException e) { - throw new RuntimeException( - "Generated message class \"" + clazz.getName() + - "\" missing method \"" + name + "\".", e); - } - } - - /** Calls invoke and throws a RuntimeException if it fails. */ - static Object invokeOrDie(Method method, Object object, Object... params) { - try { - return method.invoke(object, params); - } catch (IllegalAccessException e) { - throw new RuntimeException( - "Couldn't use Java reflection to implement protocol message " + - "reflection.", e); - } catch (InvocationTargetException e) { - final Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new RuntimeException( - "Unexpected exception thrown by generated accessor method.", cause); - } - } - } - /** * Lite equivalent to {@link GeneratedMessage.GeneratedExtension}. * * Users should ignore the contents of this class and only use objects of * this type as parameters to extension accessors and ExtensionRegistry.add(). */ - public static class GeneratedExtension< + public static final class GeneratedExtension< ContainingType extends MessageLite, Type> { - - /** - * Create a new isntance with the given parameters. - * - * The last parameter {@code singularType} is only needed for enum types. - * We store integer values for enum types in a {@link ExtendableMessage} - * and use Java reflection to convert an integer value back into a concrete - * enum object. - */ - GeneratedExtension( + // We can't always initialize a GeneratedExtension when we first construct + // it due to initialization order difficulties (namely, the default + // instances may not have been constructed yet). So, we construct an + // uninitialized GeneratedExtension once, then call internalInit() on it + // later. Generated code will always call internalInit() on all extensions + // as part of the static initialization code, and internalInit() throws an + // exception if called more than once, so this method is useless to users. + private GeneratedExtension() {} + + private void internalInit( final ContainingType containingTypeDefaultInstance, final Type defaultValue, final MessageLite messageDefaultInstance, - final ExtensionDescriptor descriptor, - Class singularType) { - // Defensive checks to verify the correct initialization order of - // GeneratedExtensions and their related GeneratedMessages. - if (containingTypeDefaultInstance == null) { - throw new IllegalArgumentException( - "Null containingTypeDefaultInstance"); - } - if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE && - messageDefaultInstance == null) { - throw new IllegalArgumentException( - "Null messageDefaultInstance"); - } + final ExtensionDescriptor descriptor) { this.containingTypeDefaultInstance = containingTypeDefaultInstance; this.defaultValue = defaultValue; this.messageDefaultInstance = messageDefaultInstance; this.descriptor = descriptor; + } - // Use Java reflection to invoke the static method {@code valueOf} of - // enum types in order to convert integers to concrete enum objects. - this.singularType = singularType; - if (Internal.EnumLite.class.isAssignableFrom(singularType)) { - this.enumValueOf = getMethodOrDie( - singularType, "valueOf", int.class); - } else { - this.enumValueOf = null; - } + /** For use by generated code only. */ + public void internalInitSingular( + final ContainingType containingTypeDefaultInstance, + final Type defaultValue, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type) { + internalInit( + containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptor(enumTypeMap, number, type, + false /* isRepeated */, false /* isPacked */)); } - final ContainingType containingTypeDefaultInstance; - final Type defaultValue; - final MessageLite messageDefaultInstance; - final ExtensionDescriptor descriptor; - final Class singularType; - final Method enumValueOf; + /** For use by generated code only. */ + public void internalInitRepeated( + final ContainingType containingTypeDefaultInstance, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type, + final boolean isPacked) { + internalInit( + containingTypeDefaultInstance, (Type) Collections.emptyList(), + messageDefaultInstance, + new ExtensionDescriptor( + enumTypeMap, number, type, true /* isRepeated */, isPacked)); + } + + private ContainingType containingTypeDefaultInstance; + private Type defaultValue; + private MessageLite messageDefaultInstance; + private ExtensionDescriptor descriptor; /** * Default instance of the type being extended, used to identify that type. @@ -830,120 +555,12 @@ public abstract class GeneratedMessageLite extends AbstractMessageLite return descriptor.getNumber(); } - /** - * If the extension is an embedded message or group, returns the default - * instance of the message. + * If the extension is an embedded message, this is the default instance of + * that type. */ public MessageLite getMessageDefaultInstance() { return messageDefaultInstance; } - - @SuppressWarnings("unchecked") - Object fromFieldSetType(final Object value) { - if (descriptor.isRepeated()) { - if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) { - final List result = new ArrayList(); - for (final Object element : (List) value) { - result.add(singularFromFieldSetType(element)); - } - return result; - } else { - return value; - } - } else { - return singularFromFieldSetType(value); - } - } - - Object singularFromFieldSetType(final Object value) { - if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) { - return invokeOrDie(enumValueOf, null, (Integer) value); - } else { - return value; - } - } - - @SuppressWarnings("unchecked") - Object toFieldSetType(final Object value) { - if (descriptor.isRepeated()) { - if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) { - final List result = new ArrayList(); - for (final Object element : (List) value) { - result.add(singularToFieldSetType(element)); - } - return result; - } else { - return value; - } - } else { - return singularToFieldSetType(value); - } - } - - Object singularToFieldSetType(final Object value) { - if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) { - return ((Internal.EnumLite) value).getNumber(); - } else { - return value; - } - } - } - - /** - * A serialized (serializable) form of the generated message. Stores the - * message as a class name and a byte array. - */ - static final class SerializedForm implements Serializable { - private static final long serialVersionUID = 0L; - - private String messageClassName; - private byte[] asBytes; - - /** - * Creates the serialized form by calling {@link com.google.protobuf.MessageLite#toByteArray}. - * @param regularForm the message to serialize - */ - SerializedForm(MessageLite regularForm) { - messageClassName = regularForm.getClass().getName(); - asBytes = regularForm.toByteArray(); - } - - /** - * When read from an ObjectInputStream, this method converts this object - * back to the regular form. Part of Java's serialization magic. - * @return a GeneratedMessage of the type that was serialized - */ - @SuppressWarnings("unchecked") - 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(); - } 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 (IllegalAccessException e) { - throw new RuntimeException("Unable to call newBuilder method", e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Error calling newBuilder", e.getCause()); - } catch (InvalidProtocolBufferException e) { - throw new RuntimeException("Unable to understand proto buffer", e); - } - } - } - - /** - * Replaces this object in the output stream with a serialized form. - * Part of Java's serialization magic. Generated sub-classes must override - * this method by calling {@code return super.writeReplace();} - * @return a SerializedForm of this message - */ - protected Object writeReplace() throws ObjectStreamException { - return new SerializedForm(this); } } diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 48d29e6..965465e 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,11 +30,7 @@ package com.google.protobuf; -import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; /** * The classes contained within are used internally by the Protocol Buffer @@ -102,112 +98,6 @@ public class Internal { "Java VM does not support a standard character set.", e); } } - /** - * Helper called by generated code to construct default values for bytes - * fields. - * <p> - * This is like {@link #bytesDefaultValue}, but returns a byte array. - */ - public static byte[] byteArrayDefaultValue(String bytes) { - try { - return bytes.getBytes("ISO-8859-1"); - } catch (UnsupportedEncodingException e) { - // This should never happen since all JVMs are required to implement - // ISO-8859-1. - throw new IllegalStateException( - "Java VM does not support a standard character set.", e); - } - } - - /** - * Helper called by generated code to construct default values for bytes - * fields. - * <p> - * This is like {@link #bytesDefaultValue}, but returns a ByteBuffer. - */ - public static ByteBuffer byteBufferDefaultValue(String bytes) { - return ByteBuffer.wrap(byteArrayDefaultValue(bytes)); - } - - /** - * Create a new ByteBuffer and copy all the content of {@code source} - * ByteBuffer to the new ByteBuffer. The new ByteBuffer's limit and - * capacity will be source.capacity(), and its position will be 0. - * Note that the state of {@code source} ByteBuffer won't be changed. - */ - public static ByteBuffer copyByteBuffer(ByteBuffer source) { - // Make a duplicate of the source ByteBuffer and read data from the - // duplicate. This is to avoid affecting the source ByteBuffer's state. - ByteBuffer temp = source.duplicate(); - // We want to copy all the data in the source ByteBuffer, not just the - // remaining bytes. - temp.clear(); - ByteBuffer result = ByteBuffer.allocate(temp.capacity()); - result.put(temp); - result.clear(); - return result; - } - - /** - * Helper called by generated code to determine if a byte array is a valid - * UTF-8 encoded string such that the original bytes can be converted to - * a String object and then back to a byte array round tripping the bytes - * without loss. More precisely, returns {@code true} whenever: - * <pre> {@code - * Arrays.equals(byteString.toByteArray(), - * new String(byteString.toByteArray(), "UTF-8").getBytes("UTF-8")) - * }</pre> - * - * <p>This method rejects "overlong" byte sequences, as well as - * 3-byte sequences that would map to a surrogate character, in - * accordance with the restricted definition of UTF-8 introduced in - * Unicode 3.1. Note that the UTF-8 decoder included in Oracle's - * JDK has been modified to also reject "overlong" byte sequences, - * but currently (2011) still accepts 3-byte surrogate character - * byte sequences. - * - * <p>See the Unicode Standard,</br> - * Table 3-6. <em>UTF-8 Bit Distribution</em>,</br> - * Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>. - * - * <p>As of 2011-02, this method simply returns the result of {@link - * ByteString#isValidUtf8()}. Calling that method directly is preferred. - * - * @param byteString the string to check - * @return whether the byte array is round trippable - */ - public static boolean isValidUtf8(ByteString byteString) { - return byteString.isValidUtf8(); - } - - /** - * Like {@link #isValidUtf8(ByteString)} but for byte arrays. - */ - public static boolean isValidUtf8(byte[] byteArray) { - return Utf8.isValidUtf8(byteArray); - } - - /** - * Helper method to get the UTF-8 bytes of a string. - */ - public static byte[] toByteArray(String value) { - try { - return value.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("UTF-8 not supported?", e); - } - } - - /** - * Helper method to convert a byte array to a string using UTF-8 encoding. - */ - public static String toStringUtf8(byte[] bytes) { - try { - return new String(bytes, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("UTF-8 not supported?", e); - } - } /** * Interface for an enum value or value descriptor, to be used in FieldSet. @@ -228,164 +118,4 @@ public class Internal { public interface EnumLiteMap<T extends EnumLite> { T findValueByNumber(int number); } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for longs. - * @see Long#hashCode() - */ - public static int hashLong(long n) { - return (int) (n ^ (n >>> 32)); - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for - * booleans. - * @see Boolean#hashCode() - */ - public static int hashBoolean(boolean b) { - return b ? 1231 : 1237; - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for enums. - * <p> - * This is needed because {@link java.lang.Enum#hashCode()} is final, but we - * need to use the field number as the hash code to ensure compatibility - * between statically and dynamically generated enum objects. - */ - public static int hashEnum(EnumLite e) { - return e.getNumber(); - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for - * enum lists. - */ - public static int hashEnumList(List<? extends EnumLite> list) { - int hash = 1; - for (EnumLite e : list) { - hash = 31 * hash + hashEnum(e); - } - return hash; - } - - /** - * Helper method for implementing {@link MessageLite#equals()} for bytes field. - */ - public static boolean equals(List<byte[]> a, List<byte[]> b) { - if (a.size() != b.size()) return false; - for (int i = 0; i < a.size(); ++i) { - if (!Arrays.equals(a.get(i), b.get(i))) { - return false; - } - } - return true; - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes field. - */ - public static int hashCode(List<byte[]> list) { - int hash = 1; - for (byte[] bytes : list) { - hash = 31 * hash + hashCode(bytes); - } - return hash; - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes field. - */ - public static int hashCode(byte[] bytes) { - // The hash code for a byte array should be the same as the hash code for a - // ByteString with the same content. This is to ensure that the generated - // hashCode() method will return the same value as the pure reflection - // based hashCode() method. - return LiteralByteString.hashCode(bytes); - } - - /** - * Helper method for implementing {@link MessageLite#equals()} for bytes - * field. - */ - public static boolean equalsByteBuffer(ByteBuffer a, ByteBuffer b) { - if (a.capacity() != b.capacity()) { - return false; - } - // ByteBuffer.equals() will only compare the remaining bytes, but we want to - // compare all the content. - return a.duplicate().clear().equals(b.duplicate().clear()); - } - - /** - * Helper method for implementing {@link MessageLite#equals()} for bytes - * field. - */ - public static boolean equalsByteBuffer( - List<ByteBuffer> a, List<ByteBuffer> b) { - if (a.size() != b.size()) { - return false; - } - for (int i = 0; i < a.size(); ++i) { - if (!equalsByteBuffer(a.get(i), b.get(i))) { - return false; - } - } - return true; - } - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes - * field. - */ - public static int hashCodeByteBuffer(List<ByteBuffer> list) { - int hash = 1; - for (ByteBuffer bytes : list) { - hash = 31 * hash + hashCodeByteBuffer(bytes); - } - return hash; - } - - private static final int DEFAULT_BUFFER_SIZE = 4096; - - /** - * Helper method for implementing {@link MessageLite#hashCode()} for bytes - * field. - */ - public static int hashCodeByteBuffer(ByteBuffer bytes) { - if (bytes.hasArray()) { - // Fast path. - int h = LiteralByteString.hashCode(bytes.capacity(), bytes.array(), - bytes.arrayOffset(), bytes.capacity()); - return h == 0 ? 1 : h; - } else { - // Read the data into a temporary byte array before calculating the - // hash value. - final int bufferSize = bytes.capacity() > DEFAULT_BUFFER_SIZE - ? DEFAULT_BUFFER_SIZE : bytes.capacity(); - final byte[] buffer = new byte[bufferSize]; - final ByteBuffer duplicated = bytes.duplicate(); - duplicated.clear(); - int h = bytes.capacity(); - while (duplicated.remaining() > 0) { - final int length = duplicated.remaining() <= bufferSize ? - duplicated.remaining() : bufferSize; - duplicated.get(buffer, 0, length); - h = LiteralByteString.hashCode(h, buffer, 0, length); - } - return h == 0 ? 1 : h; - } - } - - /** - * An empty byte array constant used in generated code. - */ - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - - /** - * An empty byte array constant used in generated code. - */ - public static final ByteBuffer EMPTY_BYTE_BUFFER = - ByteBuffer.wrap(EMPTY_BYTE_ARRAY); - } diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java index 367fa23..90f7ffb 100644 --- a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java +++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -40,32 +40,11 @@ import java.io.IOException; */ public class InvalidProtocolBufferException extends IOException { private static final long serialVersionUID = -1616151763072450476L; - private MessageLite unfinishedMessage = null; public InvalidProtocolBufferException(final String description) { super(description); } - /** - * Attaches an unfinished message to the exception to support best-effort - * parsing in {@code Parser} interface. - * - * @return this - */ - public InvalidProtocolBufferException setUnfinishedMessage( - MessageLite unfinishedMessage) { - this.unfinishedMessage = unfinishedMessage; - return this; - } - - /** - * Returns the unfinished message attached to the exception, or null if - * no message is attached. - */ - public MessageLite getUnfinishedMessage() { - return unfinishedMessage; - } - static InvalidProtocolBufferException truncatedMessage() { return new InvalidProtocolBufferException( "While parsing a protocol message, the input ended unexpectedly " + @@ -111,12 +90,4 @@ public class InvalidProtocolBufferException extends IOException { "Protocol message was too large. May be malicious. " + "Use CodedInputStream.setSizeLimit() to increase the size limit."); } - - static InvalidProtocolBufferException parseFailure() { - return new InvalidProtocolBufferException("Failed to parse the message."); - } - - static InvalidProtocolBufferException invalidUtf8() { - return new InvalidProtocolBufferException("Protocol message had invalid UTF-8."); - } } diff --git a/java/src/main/java/com/google/protobuf/LazyField.java b/java/src/main/java/com/google/protobuf/LazyField.java deleted file mode 100644 index 3da8b90..0000000 --- a/java/src/main/java/com/google/protobuf/LazyField.java +++ /dev/null @@ -1,154 +0,0 @@ -// 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.Iterator; -import java.util.Map.Entry; - -/** - * LazyField encapsulates the logic of lazily parsing message fields. It stores - * the message in a ByteString initially and then parse it on-demand. - * - * Most of key methods are implemented in {@link LazyFieldLite} but this class - * can contain default instance of the message to provide {@code hashCode()}, - * {@code euqals()} and {@code toString()}. - * - * @author xiangl@google.com (Xiang Li) - */ -public class LazyField extends LazyFieldLite { - - /** - * Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and - * {@code toString()}. - */ - private final MessageLite defaultInstance; - - public LazyField(MessageLite defaultInstance, - ExtensionRegistryLite extensionRegistry, ByteString bytes) { - super(extensionRegistry, bytes); - - this.defaultInstance = defaultInstance; - } - - @Override - public boolean containsDefaultInstance() { - return super.containsDefaultInstance() || value == defaultInstance; - } - - public MessageLite getValue() { - return getValue(defaultInstance); - } - - @Override - public int hashCode() { - return getValue().hashCode(); - } - - @Override - public boolean equals(Object obj) { - return getValue().equals(obj); - } - - @Override - public String toString() { - return getValue().toString(); - } - - // ==================================================== - - /** - * LazyEntry and LazyIterator are used to encapsulate the LazyField, when - * users iterate all fields from FieldSet. - */ - static class LazyEntry<K> implements Entry<K, Object> { - private Entry<K, LazyField> entry; - - private LazyEntry(Entry<K, LazyField> entry) { - this.entry = entry; - } - - // @Override - public K getKey() { - return entry.getKey(); - } - - // @Override - public Object getValue() { - LazyField field = entry.getValue(); - if (field == null) { - return null; - } - return field.getValue(); - } - - public LazyField getField() { - return entry.getValue(); - } - - // @Override - public Object setValue(Object value) { - if (!(value instanceof MessageLite)) { - throw new IllegalArgumentException( - "LazyField now only used for MessageSet, " - + "and the value of MessageSet must be an instance of MessageLite"); - } - return entry.getValue().setValue((MessageLite) value); - } - } - - static class LazyIterator<K> implements Iterator<Entry<K, Object>> { - private Iterator<Entry<K, Object>> iterator; - - public LazyIterator(Iterator<Entry<K, Object>> iterator) { - this.iterator = iterator; - } - - // @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @SuppressWarnings("unchecked") - // @Override - public Entry<K, Object> next() { - Entry<K, ?> entry = iterator.next(); - if (entry.getValue() instanceof LazyField) { - return new LazyEntry<K>((Entry<K, LazyField>) entry); - } - return (Entry<K, Object>) entry; - } - - // @Override - public void remove() { - iterator.remove(); - } - } -} diff --git a/java/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/src/main/java/com/google/protobuf/LazyFieldLite.java deleted file mode 100644 index 1fc80e8..0000000 --- a/java/src/main/java/com/google/protobuf/LazyFieldLite.java +++ /dev/null @@ -1,176 +0,0 @@ -// 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; - -/** - * LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores - * the message in a ByteString initially and then parse it on-demand. - * - * LazyField is thread-compatible e.g. concurrent read are safe, however, - * synchronizations are needed under read/write situations. - * - * This class is internal implementation detail, so you don't need to use it directly. - * - * @author xiangl@google.com (Xiang Li) - */ -public class LazyFieldLite { - private ByteString bytes; - private ExtensionRegistryLite extensionRegistry; - private volatile boolean isDirty = false; - - protected volatile MessageLite value; - - public LazyFieldLite(ExtensionRegistryLite extensionRegistry, ByteString bytes) { - this.extensionRegistry = extensionRegistry; - this.bytes = bytes; - } - - public LazyFieldLite() { - } - - public static LazyFieldLite fromValue(MessageLite value) { - LazyFieldLite lf = new LazyFieldLite(); - lf.setValue(value); - return lf; - } - - public boolean containsDefaultInstance() { - return value == null && bytes == null; - } - - public void clear() { - bytes = null; - value = null; - extensionRegistry = null; - isDirty = true; - } - - /** - * Returns message instance. At first time, serialized data is parsed by - * {@code defaultInstance.getParserForType()}. - * - * @param defaultInstance its message's default instance. It's also used to get parser for the - * message type. - */ - public MessageLite getValue(MessageLite defaultInstance) { - ensureInitialized(defaultInstance); - return value; - } - - /** - * LazyField is not thread-safe for write access. Synchronizations are needed - * under read/write situations. - */ - public MessageLite setValue(MessageLite value) { - MessageLite originalValue = this.value; - this.value = value; - bytes = null; - isDirty = true; - return originalValue; - } - - public void merge(LazyFieldLite value) { - if (value.containsDefaultInstance()) { - return; - } - - if (bytes == null) { - this.bytes = value.bytes; - } else { - this.bytes.concat(value.toByteString()); - } - isDirty = false; - } - - public void setByteString(ByteString bytes, ExtensionRegistryLite extensionRegistry) { - this.bytes = bytes; - this.extensionRegistry = extensionRegistry; - isDirty = false; - } - - public ExtensionRegistryLite getExtensionRegistry() { - return extensionRegistry; - } - - /** - * Due to the optional field can be duplicated at the end of serialized - * bytes, which will make the serialized size changed after LazyField - * parsed. Be careful when using this method. - */ - public int getSerializedSize() { - if (isDirty) { - return value.getSerializedSize(); - } - return bytes.size(); - } - - public ByteString toByteString() { - if (!isDirty) { - return bytes; - } - synchronized (this) { - if (!isDirty) { - return bytes; - } - if (value == null) { - bytes = ByteString.EMPTY; - } else { - bytes = value.toByteString(); - } - isDirty = false; - return bytes; - } - } - - protected void ensureInitialized(MessageLite defaultInstance) { - if (value != null) { - return; - } - synchronized (this) { - if (value != null) { - return; - } - try { - if (bytes != null) { - value = defaultInstance.getParserForType() - .parseFrom(bytes, extensionRegistry); - } else { - value = defaultInstance; - } - } catch (IOException e) { - // TODO(xiangl): Refactory the API to support the exception thrown from - // lazily load messages. - } - } - } -} diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java deleted file mode 100644 index 61c7e1e..0000000 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ /dev/null @@ -1,367 +0,0 @@ -// 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.List; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.RandomAccess; - -/** - * An implementation of {@link LazyStringList} that wraps an ArrayList. Each - * element is one of String, ByteString, or byte[]. It caches the last one - * requested which is most likely the one needed next. This minimizes memory - * usage while satisfying the most common use cases. - * <p> - * <strong>Note that this implementation is not synchronized.</strong> - * If multiple threads access an <tt>ArrayList</tt> instance concurrently, - * and at least one of the threads modifies the list structurally, it - * <i>must</i> be synchronized externally. (A structural modification is - * any operation that adds or deletes one or more elements, or explicitly - * resizes the backing array; merely setting the value of an element is not - * a structural modification.) This is typically accomplished by - * synchronizing on some object that naturally encapsulates the list. - * <p> - * If the implementation is accessed via concurrent reads, this is thread safe. - * Conversions are done in a thread safe manner. It's possible that the - * conversion may happen more than once if two threads attempt to access the - * same element and the modifications were not visible to each other, but this - * will not result in any corruption of the list or change in behavior other - * than performance. - * - * @author jonp@google.com (Jon Perlow) - */ -public class LazyStringArrayList extends AbstractList<String> - implements LazyStringList, RandomAccess { - - public static final LazyStringList EMPTY = - new LazyStringArrayList().getUnmodifiableView(); - - private final List<Object> list; - - public LazyStringArrayList() { - list = new ArrayList<Object>(); - } - - public LazyStringArrayList(LazyStringList from) { - list = new ArrayList<Object>(from.size()); - addAll(from); - } - - public LazyStringArrayList(List<String> from) { - list = new ArrayList<Object>(from); - } - - @Override - public String get(int index) { - Object o = list.get(index); - if (o instanceof String) { - return (String) o; - } else if (o instanceof ByteString) { - ByteString bs = (ByteString) o; - String s = bs.toStringUtf8(); - if (bs.isValidUtf8()) { - list.set(index, s); - } - return s; - } else { - byte[] ba = (byte[]) o; - String s = Internal.toStringUtf8(ba); - if (Internal.isValidUtf8(ba)) { - list.set(index, s); - } - return s; - } - } - - @Override - public int size() { - return list.size(); - } - - @Override - public String set(int index, String s) { - Object o = list.set(index, s); - return asString(o); - } - - @Override - public void add(int index, String element) { - list.add(index, element); - modCount++; - } - - @Override - public boolean addAll(Collection<? extends String> c) { - // The default implementation of AbstractCollection.addAll(Collection) - // delegates to add(Object). This implementation instead delegates to - // addAll(int, Collection), which makes a special case for Collections - // which are instances of LazyStringList. - return addAll(size(), c); - } - - @Override - public boolean addAll(int index, Collection<? extends String> c) { - // When copying from another LazyStringList, directly copy the underlying - // elements rather than forcing each element to be decoded to a String. - Collection<?> collection = c instanceof LazyStringList - ? ((LazyStringList) c).getUnderlyingElements() : c; - boolean ret = list.addAll(index, collection); - modCount++; - return ret; - } - - // @Override - public boolean addAllByteString(Collection<? extends ByteString> values) { - boolean ret = list.addAll(values); - modCount++; - return ret; - } - - // @Override - public boolean addAllByteArray(Collection<byte[]> c) { - boolean ret = list.addAll(c); - modCount++; - return ret; - } - - @Override - public String remove(int index) { - Object o = list.remove(index); - modCount++; - return asString(o); - } - - @Override - public void clear() { - list.clear(); - modCount++; - } - - // @Override - public void add(ByteString element) { - list.add(element); - modCount++; - } - - // @Override - public void add(byte[] element) { - list.add(element); - modCount++; - } - - // @Override - public ByteString getByteString(int index) { - Object o = list.get(index); - ByteString b = asByteString(o); - if (b != o) { - list.set(index, b); - } - return b; - } - - // @Override - public byte[] getByteArray(int index) { - Object o = list.get(index); - byte[] b = asByteArray(o); - if (b != o) { - list.set(index, b); - } - return b; - } - - // @Override - public void set(int index, ByteString s) { - list.set(index, s); - } - - // @Override - public void set(int index, byte[] s) { - list.set(index, s); - } - - - private static String asString(Object o) { - if (o instanceof String) { - return (String) o; - } else if (o instanceof ByteString) { - return ((ByteString) o).toStringUtf8(); - } else { - return Internal.toStringUtf8((byte[]) o); - } - } - - private static ByteString asByteString(Object o) { - if (o instanceof ByteString) { - return (ByteString) o; - } else if (o instanceof String) { - return ByteString.copyFromUtf8((String) o); - } else { - return ByteString.copyFrom((byte[]) o); - } - } - - private static byte[] asByteArray(Object o) { - if (o instanceof byte[]) { - return (byte[]) o; - } else if (o instanceof String) { - return Internal.toByteArray((String) o); - } else { - return ((ByteString) o).toByteArray(); - } - } - - // @Override - public List<?> getUnderlyingElements() { - return Collections.unmodifiableList(list); - } - - // @Override - public void mergeFrom(LazyStringList other) { - for (Object o : other.getUnderlyingElements()) { - if (o instanceof byte[]) { - byte[] b = (byte[]) o; - // Byte array's content is mutable so they should be copied rather than - // shared when merging from one message to another. - list.add(Arrays.copyOf(b, b.length)); - } else { - list.add(o); - } - } - } - - private static class ByteArrayListView extends AbstractList<byte[]> - implements RandomAccess { - private final List<Object> list; - - ByteArrayListView(List<Object> list) { - this.list = list; - } - - @Override - public byte[] get(int index) { - Object o = list.get(index); - byte[] b = asByteArray(o); - if (b != o) { - list.set(index, b); - } - return b; - } - - @Override - public int size() { - return list.size(); - } - - @Override - public byte[] set(int index, byte[] s) { - Object o = list.set(index, s); - modCount++; - return asByteArray(o); - } - - @Override - public void add(int index, byte[] s) { - list.add(index, s); - modCount++; - } - - @Override - public byte[] remove(int index) { - Object o = list.remove(index); - modCount++; - return asByteArray(o); - } - } - - // @Override - public List<byte[]> asByteArrayList() { - return new ByteArrayListView(list); - } - - private static class ByteStringListView extends AbstractList<ByteString> - implements RandomAccess { - private final List<Object> list; - - ByteStringListView(List<Object> list) { - this.list = list; - } - - @Override - public ByteString get(int index) { - Object o = list.get(index); - ByteString b = asByteString(o); - if (b != o) { - list.set(index, b); - } - return b; - } - - @Override - public int size() { - return list.size(); - } - - @Override - public ByteString set(int index, ByteString s) { - Object o = list.set(index, s); - modCount++; - return asByteString(o); - } - - @Override - public void add(int index, ByteString s) { - list.add(index, s); - modCount++; - } - - @Override - public ByteString remove(int index) { - Object o = list.remove(index); - modCount++; - return asByteString(o); - } - } - - // @Override - public List<ByteString> asByteStringList() { - return new ByteStringListView(list); - } - - // @Override - public LazyStringList getUnmodifiableView() { - return new UnmodifiableLazyStringList(this); - } - -} diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/src/main/java/com/google/protobuf/LazyStringList.java deleted file mode 100644 index 235126b..0000000 --- a/java/src/main/java/com/google/protobuf/LazyStringList.java +++ /dev/null @@ -1,163 +0,0 @@ -// 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.Collection; -import java.util.List; - -/** - * An interface extending {@code List<String>} that also provides access to the - * items of the list as UTF8-encoded ByteString or byte[] objects. This is - * used by the protocol buffer implementation to support lazily converting bytes - * parsed over the wire to String objects until needed and also increases the - * efficiency of serialization if the String was never requested as the - * ByteString or byte[] is already cached. The ByteString methods are used in - * immutable API only and byte[] methods used in mutable API only for they use - * different representations for string/bytes fields. - * - * @author jonp@google.com (Jon Perlow) - */ -public interface LazyStringList extends ProtocolStringList { - - /** - * Returns the element at the specified position in this list as a ByteString. - * - * @param index index of the element to return - * @return the element at the specified position in this list - * @throws IndexOutOfBoundsException if the index is out of range - * ({@code index < 0 || index >= size()}) - */ - ByteString getByteString(int index); - - /** - * Returns the element at the specified position in this list as byte[]. - * - * @param index index of the element to return - * @return the element at the specified position in this list - * @throws IndexOutOfBoundsException if the index is out of range - * ({@code index < 0 || index >= size()}) - */ - byte[] getByteArray(int index); - - /** - * Appends the specified element to the end of this list (optional - * operation). - * - * @param element element to be appended to this list - * @throws UnsupportedOperationException if the <tt>add</tt> operation - * is not supported by this list - */ - void add(ByteString element); - - /** - * Appends the specified element to the end of this list (optional - * operation). - * - * @param element element to be appended to this list - * @throws UnsupportedOperationException if the <tt>add</tt> operation - * is not supported by this list - */ - void add(byte[] element); - - /** - * Replaces the element at the specified position in this list with the - * specified element (optional operation). - * - * @param index index of the element to replace - * @param element the element to be stored at the specified position - * @throws UnsupportedOperationException if the <tt>set</tt> operation - * is not supported by this list - * IndexOutOfBoundsException if the index is out of range - * ({@code index < 0 || index >= size()}) - */ - void set(int index, ByteString element); - - /** - * Replaces the element at the specified position in this list with the - * specified element (optional operation). - * - * @param index index of the element to replace - * @param element the element to be stored at the specified position - * @throws UnsupportedOperationException if the <tt>set</tt> operation - * is not supported by this list - * IndexOutOfBoundsException if the index is out of range - * ({@code index < 0 || index >= size()}) - */ - void set(int index, byte[] element); - - /** - * Appends all elements in the specified ByteString collection to the end of - * this list. - * - * @param c collection whose elements are to be added to this list - * @return true if this list changed as a result of the call - * @throws UnsupportedOperationException if the <tt>addAllByteString</tt> - * operation is not supported by this list - */ - boolean addAllByteString(Collection<? extends ByteString> c); - - /** - * Appends all elements in the specified byte[] collection to the end of - * this list. - * - * @param c collection whose elements are to be added to this list - * @return true if this list changed as a result of the call - * @throws UnsupportedOperationException if the <tt>addAllByteArray</tt> - * operation is not supported by this list - */ - boolean addAllByteArray(Collection<byte[]> c); - - /** - * Returns an unmodifiable List of the underlying elements, each of which is - * either a {@code String} or its equivalent UTF-8 encoded {@code ByteString} - * or byte[]. It is an error for the caller to modify the returned - * List, and attempting to do so will result in an - * {@link UnsupportedOperationException}. - */ - List<?> getUnderlyingElements(); - - /** - * Merges all elements from another LazyStringList into this one. This method - * differs from {@link #addAll(Collection)} on that underlying byte arrays are - * copied instead of reference shared. Immutable API doesn't need to use this - * method as byte[] is not used there at all. - */ - void mergeFrom(LazyStringList other); - - /** - * Returns a mutable view of this list. Changes to the view will be made into - * the original list. This method is used in mutable API only. - */ - List<byte[]> asByteArrayList(); - - /** Returns an unmodifiable view of the list. */ - LazyStringList getUnmodifiableView(); -} diff --git a/java/src/main/java/com/google/protobuf/LiteralByteString.java b/java/src/main/java/com/google/protobuf/LiteralByteString.java deleted file mode 100644 index 83e71e9..0000000 --- a/java/src/main/java/com/google/protobuf/LiteralByteString.java +++ /dev/null @@ -1,362 +0,0 @@ -// 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.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * This class implements a {@link com.google.protobuf.ByteString} backed by a - * single array of bytes, contiguous in memory. It supports substring by - * pointing to only a sub-range of the underlying byte array, meaning that a - * substring will reference the full byte-array of the string it's made from, - * exactly as with {@link String}. - * - * @author carlanton@google.com (Carl Haverl) - */ -class LiteralByteString extends ByteString { - - protected final byte[] bytes; - - /** - * Creates a {@code LiteralByteString} backed by the given array, without - * copying. - * - * @param bytes array to wrap - */ - LiteralByteString(byte[] bytes) { - this.bytes = bytes; - } - - @Override - public byte byteAt(int index) { - // Unlike most methods in this class, this one is a direct implementation - // ignoring the potential offset because we need to do range-checking in the - // substring case anyway. - return bytes[index]; - } - - @Override - public int size() { - return bytes.length; - } - - // ================================================================= - // ByteString -> substring - - @Override - public ByteString substring(int beginIndex, int endIndex) { - if (beginIndex < 0) { - throw new IndexOutOfBoundsException( - "Beginning index: " + beginIndex + " < 0"); - } - if (endIndex > size()) { - throw new IndexOutOfBoundsException("End index: " + endIndex + " > " + - size()); - } - int substringLength = endIndex - beginIndex; - if (substringLength < 0) { - throw new IndexOutOfBoundsException( - "Beginning index larger than ending index: " + beginIndex + ", " - + endIndex); - } - - ByteString result; - if (substringLength == 0) { - result = ByteString.EMPTY; - } else { - result = new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex, - substringLength); - } - return result; - } - - // ================================================================= - // ByteString -> byte[] - - @Override - protected void copyToInternal(byte[] target, int sourceOffset, - int targetOffset, int numberToCopy) { - // Optimized form, not for subclasses, since we don't call - // getOffsetIntoBytes() or check the 'numberToCopy' parameter. - System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy); - } - - @Override - public void copyTo(ByteBuffer target) { - target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes - } - - @Override - public ByteBuffer asReadOnlyByteBuffer() { - ByteBuffer byteBuffer = - ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size()); - return byteBuffer.asReadOnlyBuffer(); - } - - @Override - public List<ByteBuffer> asReadOnlyByteBufferList() { - // Return the ByteBuffer generated by asReadOnlyByteBuffer() as a singleton - List<ByteBuffer> result = new ArrayList<ByteBuffer>(1); - result.add(asReadOnlyByteBuffer()); - return result; - } - - @Override - public void writeTo(OutputStream outputStream) throws IOException { - outputStream.write(toByteArray()); - } - - @Override - void writeToInternal(OutputStream outputStream, int sourceOffset, - int numberToWrite) throws IOException { - outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset, - numberToWrite); - } - - @Override - public String toString(String charsetName) - throws UnsupportedEncodingException { - return new String(bytes, getOffsetIntoBytes(), size(), charsetName); - } - - // ================================================================= - // UTF-8 decoding - - @Override - public boolean isValidUtf8() { - int offset = getOffsetIntoBytes(); - return Utf8.isValidUtf8(bytes, offset, offset + size()); - } - - @Override - protected int partialIsValidUtf8(int state, int offset, int length) { - int index = getOffsetIntoBytes() + offset; - return Utf8.partialIsValidUtf8(state, bytes, index, index + length); - } - - // ================================================================= - // equals() and hashCode() - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof ByteString)) { - return false; - } - - if (size() != ((ByteString) other).size()) { - return false; - } - if (size() == 0) { - return true; - } - - if (other instanceof LiteralByteString) { - return equalsRange((LiteralByteString) other, 0, size()); - } else if (other instanceof RopeByteString) { - return other.equals(this); - } else { - throw new IllegalArgumentException( - "Has a new type of ByteString been created? Found " - + other.getClass()); - } - } - - /** - * Check equality of the substring of given length of this object starting at - * zero with another {@code LiteralByteString} substring starting at offset. - * - * @param other what to compare a substring in - * @param offset offset into other - * @param length number of bytes to compare - * @return true for equality of substrings, else false. - */ - boolean equalsRange(LiteralByteString other, int offset, int length) { - if (length > other.size()) { - throw new IllegalArgumentException( - "Length too large: " + length + size()); - } - if (offset + length > other.size()) { - throw new IllegalArgumentException( - "Ran off end of other: " + offset + ", " + length + ", " + - other.size()); - } - - byte[] thisBytes = bytes; - byte[] otherBytes = other.bytes; - int thisLimit = getOffsetIntoBytes() + length; - for (int thisIndex = getOffsetIntoBytes(), otherIndex = - other.getOffsetIntoBytes() + offset; - (thisIndex < thisLimit); ++thisIndex, ++otherIndex) { - if (thisBytes[thisIndex] != otherBytes[otherIndex]) { - return false; - } - } - return true; - } - - /** - * Cached hash value. Intentionally accessed via a data race, which - * is safe because of the Java Memory Model's "no out-of-thin-air values" - * guarantees for ints. - */ - private int hash = 0; - - /** - * Compute the hashCode using the traditional algorithm from {@link - * ByteString}. - * - * @return hashCode value - */ - @Override - public int hashCode() { - int h = hash; - - if (h == 0) { - int size = size(); - h = partialHash(size, 0, size); - if (h == 0) { - h = 1; - } - hash = h; - } - return h; - } - - @Override - protected int peekCachedHashCode() { - return hash; - } - - @Override - protected int partialHash(int h, int offset, int length) { - return hashCode(h, bytes, getOffsetIntoBytes() + offset, length); - } - - static int hashCode(int h, byte[] bytes, int offset, int length) { - for (int i = offset; i < offset + length; i++) { - h = h * 31 + bytes[i]; - } - return h; - } - - static int hashCode(byte[] bytes) { - int h = hashCode(bytes.length, bytes, 0, bytes.length); - return h == 0 ? 1 : h; - } - - // ================================================================= - // Input stream - - @Override - public InputStream newInput() { - return new ByteArrayInputStream(bytes, getOffsetIntoBytes(), - size()); // No copy - } - - @Override - public CodedInputStream newCodedInput() { - // We trust CodedInputStream not to modify the bytes, or to give anyone - // else access to them. - return CodedInputStream.newInstance(this); - } - - // ================================================================= - // ByteIterator - - @Override - public ByteIterator iterator() { - return new LiteralByteIterator(); - } - - private class LiteralByteIterator implements ByteIterator { - private int position; - private final int limit; - - private LiteralByteIterator() { - position = 0; - limit = size(); - } - - public boolean hasNext() { - return (position < limit); - } - - public Byte next() { - // Boxing calls Byte.valueOf(byte), which does not instantiate. - return nextByte(); - } - - public byte nextByte() { - try { - return bytes[position++]; - } catch (ArrayIndexOutOfBoundsException e) { - throw new NoSuchElementException(e.getMessage()); - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } - } - - // ================================================================= - // Internal methods - - @Override - protected int getTreeDepth() { - return 0; - } - - @Override - protected boolean isBalanced() { - return true; - } - - /** - * Offset into {@code bytes[]} to use, non-zero for substrings. - * - * @return always 0 for this class - */ - protected int getOffsetIntoBytes() { - return 0; - } -} diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index 5673d3b..8c29e21 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -48,33 +48,89 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public interface Message extends MessageLite, MessageOrBuilder { +public interface Message extends MessageLite { + /** + * Get the message's type's descriptor. This differs from the + * {@code getDescriptor()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDescriptor()} is a static method of a specific class. + * They return the same thing. + */ + Descriptors.Descriptor getDescriptorForType(); // (From MessageLite, re-declared here only for return type covariance.) - Parser<? extends Message> getParserForType(); + Message getDefaultInstanceForType(); + + /** + * Returns a collection of all the fields in this message which are set + * and their corresponding values. A singular ("required" or "optional") + * field is set iff hasField() returns true for that field. A "repeated" + * field is set iff getRepeatedFieldSize() is greater than zero. The + * values are exactly what would be returned by calling + * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map + * is guaranteed to be a sorted map, so iterating over it will return fields + * in order by field number. + */ + Map<Descriptors.FieldDescriptor, Object> getAllFields(); + + /** + * Returns true if the given field is set. This is exactly equivalent to + * calling the generated "has" accessor method corresponding to the field. + * @throws IllegalArgumentException The field is a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** + * Obtains the value of the given field, or the default value if it is + * not set. For primitive fields, the boxed primitive value is returned. + * For enum fields, the EnumValueDescriptor for the value is returend. For + * embedded message fields, the sub-message is returned. For repeated + * fields, a java.util.List is returned. + */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Gets the number of elements of a repeated field. This is exactly + * equivalent to calling the generated "Count" accessor method corresponding + * to the field. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Gets an element of a repeated field. For primitive fields, the boxed + * primitive value is returned. For enum fields, the EnumValueDescriptor + * for the value is returend. For embedded message fields, the sub-message + * is returned. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); // ----------------------------------------------------------------- // Comparison and hashing /** * Compares the specified object with this message for equality. Returns - * {@code true} if the given object is a message of the same type (as + * <tt>true</tt> if the given object is a message of the same type (as * defined by {@code getDescriptorForType()}) and has identical values for - * all of its fields. Subclasses must implement this; inheriting - * {@code Object.equals()} is incorrect. + * all of its fields. * * @param other object to be compared for equality with this message - * @return {@code true} if the specified object is equal to this message + * @return <tt>true</tt> if the specified object is equal to this message */ @Override boolean equals(Object other); /** * Returns the hash code value for this message. The hash code of a message - * should mix the message's type (object identity of the descriptor) with its - * contents (known and unknown field values). Subclasses must implement this; - * inheriting {@code Object.hashCode()} is incorrect. + * is defined to be <tt>getDescriptor().hashCode() ^ map.hashCode()</tt>, + * where <tt>map</tt> is a map of field numbers to field values. * * @return the hash code value for this message * @see Map#hashCode() @@ -87,8 +143,7 @@ public interface Message extends MessageLite, MessageOrBuilder { /** * Converts the message to a string in protocol buffer text format. This is - * just a trivial wrapper around {@link - * TextFormat#printToString(MessageOrBuilder)}. + * just a trivial wrapper around {@link TextFormat#printToString(Message)}. */ @Override String toString(); @@ -103,7 +158,7 @@ public interface Message extends MessageLite, MessageOrBuilder { /** * Abstract interface implemented by Protocol Message builders. */ - interface Builder extends MessageLite.Builder, MessageOrBuilder { + interface Builder extends MessageLite.Builder { // (From MessageLite.Builder, re-declared here only for return type // covariance.) Builder clear(); @@ -142,6 +197,17 @@ public interface Message extends MessageLite, MessageOrBuilder { */ Descriptors.Descriptor getDescriptorForType(); + // (From MessageLite.Builder, re-declared here only for return type + // covariance.) + Message getDefaultInstanceForType(); + + /** + * Like {@link Message#getAllFields()}. The returned map may or may not + * reflect future changes to the builder. Either way, the returned map is + * itself unmodifiable. + */ + Map<Descriptors.FieldDescriptor, Object> getAllFields(); + /** * Create a Builder for messages of the appropriate type for the given * field. Messages built with this can then be passed to setField(), @@ -149,23 +215,11 @@ public interface Message extends MessageLite, MessageOrBuilder { */ Builder newBuilderForField(Descriptors.FieldDescriptor field); - /** - * Get a nested builder instance for the given field. - * <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 getFieldBuilder(Descriptors.FieldDescriptor field); + /** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** Like {@link Message#getField(Descriptors.FieldDescriptor)} */ + Object getField(Descriptors.FieldDescriptor field); /** * Sets a field to the given value. The value must be of the correct type @@ -181,10 +235,14 @@ public interface Message extends MessageLite, MessageOrBuilder { Builder clearField(Descriptors.FieldDescriptor field); /** - * Clears the oneof. This is exactly equivalent to calling the generated - * "clear" accessor method corresponding to the oneof. + * Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)} */ - Builder clearOneof(Descriptors.OneofDescriptor oneof); + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); /** * Sets an element of a repeated field to the given value. The value must @@ -204,6 +262,9 @@ public interface Message extends MessageLite, MessageOrBuilder { */ Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); + /** Set the {@link UnknownFieldSet} for this message. */ Builder setUnknownFields(UnknownFieldSet unknownFields); diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/src/main/java/com/google/protobuf/MessageLite.java index 798b794..cf7f39e 100644 --- a/java/src/main/java/com/google/protobuf/MessageLite.java +++ b/java/src/main/java/com/google/protobuf/MessageLite.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -64,8 +64,22 @@ import java.io.OutputStream; * * @author kenton@google.com Kenton Varda */ -public interface MessageLite extends MessageLiteOrBuilder { +public interface MessageLite { + /** + * Get an instance of the type with all fields set to their default values. + * This may or may not be a singleton. This differs from the + * {@code getDefaultInstance()} method of generated message classes in that + * this method is an abstract method of the {@code MessageLite} interface + * whereas {@code getDefaultInstance()} is a static method of a specific + * class. They return the same thing. + */ + MessageLite getDefaultInstanceForType(); + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); /** * Serializes the message and writes it to {@code output}. This does not @@ -79,12 +93,6 @@ public interface MessageLite extends MessageLiteOrBuilder { */ int getSerializedSize(); - - /** - * Gets the parser for a message of the same type as this message. - */ - Parser<? extends MessageLite> getParserForType(); - // ----------------------------------------------------------------- // Convenience methods. @@ -128,7 +136,6 @@ public interface MessageLite extends MessageLiteOrBuilder { */ void writeDelimitedTo(OutputStream output) throws IOException; - // ================================================================= // Builders @@ -146,13 +153,16 @@ public interface MessageLite extends MessageLiteOrBuilder { /** * Abstract interface implemented by Protocol Message builders. */ - interface Builder extends MessageLiteOrBuilder, Cloneable { + interface Builder extends Cloneable { /** Resets all fields to their default values. */ Builder clear(); /** - * Constructs the message based on the state of the Builder. Subsequent - * changes to the Builder will not affect the returned message. + * Construct the final message. Once this is called, the Builder is no + * longer valid, and calling any other method will result in undefined + * behavior and may throw a NullPointerException. If you need to continue + * working with the builder after calling {@code build()}, {@code clone()} + * it first. * @throws UninitializedMessageException The message is missing one or more * required fields (i.e. {@link #isInitialized()} returns false). * Use {@link #buildPartial()} to bypass this check. @@ -162,7 +172,11 @@ public interface MessageLite extends MessageLiteOrBuilder { /** * Like {@link #build()}, but does not throw an exception if the message * is missing required fields. Instead, a partial message is returned. - * Subsequent changes to the Builder will not affect the returned message. + * Once this is called, the Builder is no longer valid, and calling any + * will result in undefined behavior and may throw a NullPointerException. + * + * If you need to continue working with the builder after calling + * {@code buildPartial()}, {@code clone()} it first. */ MessageLite buildPartial(); @@ -173,8 +187,14 @@ public interface MessageLite extends MessageLiteOrBuilder { Builder clone(); /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + + /** * Parses a message of this type from the input and merges it with this - * message. + * message, as if using {@link Builder#mergeFrom(MessageLite)}. * * <p>Warning: This does not verify that all required fields are present in * the input message. If you call {@link #build()} without setting all @@ -184,6 +204,11 @@ public interface MessageLite extends MessageLiteOrBuilder { * <ul> * <li>Call {@link #isInitialized()} to verify that all required fields * are set before building. + * <li>Parse the message separately using one of the static + * {@code parseFrom} methods, then use {@link #mergeFrom(MessageLite)} + * to merge it with this one. {@code parseFrom} will throw an + * {@link InvalidProtocolBufferException} (an {@code IOException}) + * if some required fields are missing. * <li>Use {@code buildPartial()} to build, which ignores missing * required fields. * </ul> @@ -205,6 +230,12 @@ public interface MessageLite extends MessageLiteOrBuilder { ExtensionRegistryLite extensionRegistry) throws IOException; + /** + * Get the message's type's default instance. + * See {@link MessageLite#getDefaultInstanceForType()}. + */ + MessageLite getDefaultInstanceForType(); + // --------------------------------------------------------------- // Convenience methods. @@ -212,17 +243,13 @@ public interface MessageLite extends MessageLiteOrBuilder { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. - * - * @return this */ Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; /** * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around - * {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}. - * - * @return this + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. */ Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry) @@ -232,8 +259,6 @@ public interface MessageLite extends MessageLiteOrBuilder { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. - * - * @return this */ Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; @@ -241,8 +266,6 @@ public interface MessageLite extends MessageLiteOrBuilder { * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. - * - * @return this */ Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException; @@ -250,9 +273,7 @@ public interface MessageLite extends MessageLiteOrBuilder { /** * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around - * {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}. - * - * @return this + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. */ Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry) @@ -261,9 +282,7 @@ public interface MessageLite extends MessageLiteOrBuilder { /** * Parse {@code data} as a message of this type and merge it with the * message being built. This is just a small wrapper around - * {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}. - * - * @return this + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. */ Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) @@ -280,17 +299,13 @@ public interface MessageLite extends MessageLiteOrBuilder { * and {@link #mergeDelimitedFrom(InputStream)} to read it. * <p> * Despite usually reading the entire input, this does not close the stream. - * - * @return this */ Builder mergeFrom(InputStream input) throws IOException; /** * Parse a message of this type from {@code input} and merge it with the * message being built. This is just a small wrapper around - * {@link #mergeFrom(CodedInputStream,ExtensionRegistryLite)}. - * - * @return this + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. */ Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry) @@ -303,9 +318,9 @@ public interface MessageLite extends MessageLiteOrBuilder { * {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in * this format. * - * @return True if successful, or false if the stream is at EOF when the - * method starts. Any other error (including reaching EOF during - * parsing) will cause an exception to be thrown. + * @returns True if successful, or false if the stream is at EOF when the + * method starts. Any other error (including reaching EOF during + * parsing) will cause an exception to be thrown. */ boolean mergeDelimitedFrom(InputStream input) throws IOException; diff --git a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java deleted file mode 100644 index 818386c..0000000 --- a/java/src/main/java/com/google/protobuf/MessageLiteOrBuilder.java +++ /dev/null @@ -1,60 +0,0 @@ -// 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; - -/** - * Base interface for methods common to {@link MessageLite} - * and {@link MessageLite.Builder} to provide type equivalency. - * - * @author jonp@google.com (Jon Perlow) - */ -public interface MessageLiteOrBuilder { - /** - * Get an instance of the type with no fields set. Because no fields are set, - * all getters for singular fields will return default values and repeated - * fields will appear empty. - * This may or may not be a singleton. This differs from the - * {@code getDefaultInstance()} method of generated message classes in that - * this method is an abstract method of the {@code MessageLite} interface - * whereas {@code getDefaultInstance()} is a static method of a specific - * class. They return the same thing. - */ - MessageLite getDefaultInstanceForType(); - - /** - * Returns true if all required fields in the message and all embedded - * messages are set, false otherwise. - * - * <p>See also: {@link MessageOrBuilder#getInitializationErrorString()} - */ - boolean isInitialized(); - -} diff --git a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/src/main/java/com/google/protobuf/MessageOrBuilder.java deleted file mode 100644 index f0fc485..0000000 --- a/java/src/main/java/com/google/protobuf/MessageOrBuilder.java +++ /dev/null @@ -1,143 +0,0 @@ -// 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.List; -import java.util.Map; - -/** - * Base interface for methods common to {@link Message} and - * {@link Message.Builder} to provide type equivalency. - * - * @author jonp@google.com (Jon Perlow) - */ -public interface MessageOrBuilder extends MessageLiteOrBuilder { - - // (From MessageLite, re-declared here only for return type covariance.) - //@Override (Java 1.6 override semantics, but we must support 1.5) - Message getDefaultInstanceForType(); - - /** - * Returns a list of field paths (e.g. "foo.bar.baz") of required fields - * which are not set in this message. You should call - * {@link MessageLiteOrBuilder#isInitialized()} first to check if there - * are any missing fields, as that method is likely to be much faster - * than this one even when the message is fully-initialized. - */ - List<String> findInitializationErrors(); - - /** - * Returns a comma-delimited list of required fields which are not set - * in this message object. You should call - * {@link MessageLiteOrBuilder#isInitialized()} first to check if there - * are any missing fields, as that method is likely to be much faster - * than this one even when the message is fully-initialized. - */ - String getInitializationErrorString(); - - /** - * Get the message's type's descriptor. This differs from the - * {@code getDescriptor()} method of generated message classes in that - * this method is an abstract method of the {@code Message} interface - * whereas {@code getDescriptor()} is a static method of a specific class. - * They return the same thing. - */ - Descriptors.Descriptor getDescriptorForType(); - - /** - * Returns a collection of all the fields in this message which are set - * and their corresponding values. A singular ("required" or "optional") - * field is set iff hasField() returns true for that field. A "repeated" - * field is set iff getRepeatedFieldCount() is greater than zero. The - * values are exactly what would be returned by calling - * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map - * is guaranteed to be a sorted map, so iterating over it will return fields - * in order by field number. - * <br> - * If this is for a builder, the returned map may or may not reflect future - * changes to the builder. Either way, the returned map is itself - * unmodifiable. - */ - Map<Descriptors.FieldDescriptor, Object> getAllFields(); - - /** - * Returns true if the given oneof is set. - * @throws IllegalArgumentException if - * {@code oneof.getContainingType() != getDescriptorForType()}. - */ - boolean hasOneof(Descriptors.OneofDescriptor oneof); - - /** - * Obtains the FieldDescriptor if the given oneof is set. Returns null - * if no field is set. - */ - Descriptors.FieldDescriptor getOneofFieldDescriptor( - Descriptors.OneofDescriptor oneof); - - /** - * Returns true if the given field is set. This is exactly equivalent to - * calling the generated "has" accessor method corresponding to the field. - * @throws IllegalArgumentException The field is a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - boolean hasField(Descriptors.FieldDescriptor field); - - /** - * Obtains the value of the given field, or the default value if it is - * not set. For primitive fields, the boxed primitive value is returned. - * For enum fields, the EnumValueDescriptor for the value is returned. For - * embedded message fields, the sub-message is returned. For repeated - * fields, a java.util.List is returned. - */ - Object getField(Descriptors.FieldDescriptor field); - - /** - * Gets the number of elements of a repeated field. This is exactly - * equivalent to calling the generated "Count" accessor method corresponding - * to the field. - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - int getRepeatedFieldCount(Descriptors.FieldDescriptor field); - - /** - * Gets an element of a repeated field. For primitive fields, the boxed - * primitive value is returned. For enum fields, the EnumValueDescriptor - * for the value is returned. For embedded message fields, the sub-message - * is returned. - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != getDescriptorForType()}. - */ - Object getRepeatedField(Descriptors.FieldDescriptor field, int index); - - /** Get the {@link UnknownFieldSet} for this message. */ - UnknownFieldSet getUnknownFields(); -} diff --git a/java/src/main/java/com/google/protobuf/MessageReflection.java b/java/src/main/java/com/google/protobuf/MessageReflection.java deleted file mode 100644 index edb5b5c..0000000 --- a/java/src/main/java/com/google/protobuf/MessageReflection.java +++ /dev/null @@ -1,931 +0,0 @@ -// 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.FieldDescriptor; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * Reflection utility methods shared by both mutable and immutable messages. - * - * @author liujisi@google.com (Pherl Liu) - */ -class MessageReflection { - - static void writeMessageTo(Message message, CodedOutputStream output, - boolean alwaysWriteRequiredFields) - throws IOException { - final boolean isMessageSet = - message.getDescriptorForType().getOptions().getMessageSetWireFormat(); - - Map<FieldDescriptor, Object> fields = message.getAllFields(); - if (alwaysWriteRequiredFields) { - fields = new TreeMap<FieldDescriptor, Object>(fields); - for (final FieldDescriptor field : - message.getDescriptorForType().getFields()) { - if (field.isRequired() && !fields.containsKey(field)) { - fields.put(field, message.getField(field)); - } - } - } - for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry : - fields.entrySet()) { - final Descriptors.FieldDescriptor field = entry.getKey(); - final Object value = entry.getValue(); - if (isMessageSet && field.isExtension() && - field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE && - !field.isRepeated()) { - output.writeMessageSetExtension(field.getNumber(), (Message) value); - } else { - FieldSet.writeField(field, value, output); - } - } - - final UnknownFieldSet unknownFields = message.getUnknownFields(); - if (isMessageSet) { - unknownFields.writeAsMessageSetTo(output); - } else { - unknownFields.writeTo(output); - } - } - - static int getSerializedSize(Message message) { - int size = 0; - final boolean isMessageSet = - message.getDescriptorForType().getOptions().getMessageSetWireFormat(); - - for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry : - message.getAllFields().entrySet()) { - final Descriptors.FieldDescriptor field = entry.getKey(); - final Object value = entry.getValue(); - if (isMessageSet && field.isExtension() && - field.getType() == Descriptors.FieldDescriptor.Type.MESSAGE && - !field.isRepeated()) { - size += CodedOutputStream.computeMessageSetExtensionSize( - field.getNumber(), (Message) value); - } else { - size += FieldSet.computeFieldSize(field, value); - } - } - - final UnknownFieldSet unknownFields = message.getUnknownFields(); - if (isMessageSet) { - size += unknownFields.getSerializedSizeAsMessageSet(); - } else { - size += unknownFields.getSerializedSize(); - } - return size; - } - - static String delimitWithCommas(List<String> parts) { - StringBuilder result = new StringBuilder(); - for (String part : parts) { - if (result.length() > 0) { - result.append(", "); - } - result.append(part); - } - return result.toString(); - } - - @SuppressWarnings("unchecked") - static boolean isInitialized(MessageOrBuilder message) { - // Check that all required fields are present. - for (final Descriptors.FieldDescriptor field : message - .getDescriptorForType() - .getFields()) { - if (field.isRequired()) { - if (!message.hasField(field)) { - return false; - } - } - } - - // Check that embedded messages are initialized. - for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry : - message.getAllFields().entrySet()) { - final Descriptors.FieldDescriptor field = entry.getKey(); - if (field.getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - for (final Message element - : (List<Message>) entry.getValue()) { - if (!element.isInitialized()) { - return false; - } - } - } else { - if (!((Message) entry.getValue()).isInitialized()) { - return false; - } - } - } - } - - return true; - } - - private static String subMessagePrefix(final String prefix, - final Descriptors.FieldDescriptor field, - final int index) { - final StringBuilder result = new StringBuilder(prefix); - if (field.isExtension()) { - result.append('(') - .append(field.getFullName()) - .append(')'); - } else { - result.append(field.getName()); - } - if (index != -1) { - result.append('[') - .append(index) - .append(']'); - } - result.append('.'); - return result.toString(); - } - - private static void findMissingFields(final MessageOrBuilder message, - final String prefix, - final List<String> results) { - for (final Descriptors.FieldDescriptor field : - message.getDescriptorForType().getFields()) { - if (field.isRequired() && !message.hasField(field)) { - results.add(prefix + field.getName()); - } - } - - for (final Map.Entry<Descriptors.FieldDescriptor, Object> entry : - message.getAllFields().entrySet()) { - final Descriptors.FieldDescriptor field = entry.getKey(); - final Object value = entry.getValue(); - - if (field.getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - int i = 0; - for (final Object element : (List) value) { - findMissingFields((MessageOrBuilder) element, - subMessagePrefix(prefix, field, i++), - results); - } - } else { - if (message.hasField(field)) { - findMissingFields((MessageOrBuilder) value, - subMessagePrefix(prefix, field, -1), - results); - } - } - } - } - } - - /** - * Populates {@code this.missingFields} with the full "path" of each missing - * required field in the given message. - */ - static List<String> findMissingFields( - final MessageOrBuilder message) { - final List<String> results = new ArrayList<String>(); - findMissingFields(message, "", results); - return results; - } - - static interface MergeTarget { - enum ContainerType { - MESSAGE, EXTENSION_SET - } - - /** - * Returns the descriptor for the target. - */ - public Descriptors.Descriptor getDescriptorForType(); - - public ContainerType getContainerType(); - - public ExtensionRegistry.ExtensionInfo findExtensionByName( - ExtensionRegistry registry, String name); - - public ExtensionRegistry.ExtensionInfo findExtensionByNumber( - ExtensionRegistry registry, Descriptors.Descriptor containingType, - int fieldNumber); - - /** - * Obtains the value of the given field, or the default value if it is not - * set. For primitive fields, the boxed primitive value is returned. For - * enum fields, the EnumValueDescriptor for the value is returned. For - * embedded message fields, the sub-message is returned. For repeated - * fields, a java.util.List is returned. - */ - public Object getField(Descriptors.FieldDescriptor field); - - /** - * Returns true if the given field is set. This is exactly equivalent to - * calling the generated "has" accessor method corresponding to the field. - * - * @throws IllegalArgumentException The field is a repeated field, or {@code - * field.getContainingType() != getDescriptorForType()}. - */ - boolean hasField(Descriptors.FieldDescriptor field); - - /** - * 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. - */ - MergeTarget setField(Descriptors.FieldDescriptor field, Object value); - - /** - * Clears the field. This is exactly equivalent to calling the generated - * "clear" accessor method corresponding to the field. - */ - MergeTarget clearField(Descriptors.FieldDescriptor field); - - /** - * Sets an element of a repeated field to the given value. The value must - * be of the correct type for this field, i.e. the same type that {@link - * Message#getRepeatedField(Descriptors.FieldDescriptor, int)} would return. - * - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != - * getDescriptorForType()}. - */ - MergeTarget setRepeatedField(Descriptors.FieldDescriptor field, - int index, Object value); - - /** - * Like {@code setRepeatedField}, but appends the value as a new element. - * - * @throws IllegalArgumentException The field is not a repeated field, or - * {@code field.getContainingType() != - * getDescriptorForType()}. - */ - MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, - Object value); - - /** - * Returns true if the given oneof is set. - * - * @throws IllegalArgumentException if - * {@code oneof.getContainingType() != getDescriptorForType()}. - */ - boolean hasOneof(Descriptors.OneofDescriptor oneof); - - /** - * Clears the oneof. This is exactly equivalent to calling the generated - * "clear" accessor method corresponding to the oneof. - */ - MergeTarget clearOneof(Descriptors.OneofDescriptor oneof); - - /** - * Obtains the FieldDescriptor if the given oneof is set. Returns null - * if no field is set. - */ - Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof); - - /** - * Parse the input stream into a sub field group defined based on either - * FieldDescriptor or the default instance. - */ - Object parseGroup(CodedInputStream input, ExtensionRegistryLite registry, - Descriptors.FieldDescriptor descriptor, Message defaultInstance) - throws IOException; - - /** - * Parse the input stream into a sub field message defined based on either - * FieldDescriptor or the default instance. - */ - Object parseMessage(CodedInputStream input, ExtensionRegistryLite registry, - Descriptors.FieldDescriptor descriptor, Message defaultInstance) - throws IOException; - - /** - * Parse from a ByteString into a sub field message defined based on either - * FieldDescriptor or the default instance. There isn't a varint indicating - * the length of the message at the beginning of the input ByteString. - */ - Object parseMessageFromBytes( - ByteString bytes, ExtensionRegistryLite registry, - Descriptors.FieldDescriptor descriptor, Message defaultInstance) - throws IOException; - - /** - * Read a primitive field from input. Note that builders and mutable - * messages may use different Java types to represent a primtive field. - */ - Object readPrimitiveField( - CodedInputStream input, WireFormat.FieldType type, - boolean checkUtf8) throws IOException; - - /** - * Returns a new merge target for a sub-field. When defaultInstance is - * provided, it indicates the descriptor is for an extension type, and - * implementations should create a new instance from the defaultInstance - * prototype directly. - */ - MergeTarget newMergeTargetForField( - Descriptors.FieldDescriptor descriptor, - Message defaultInstance); - - /** - * Finishes the merge and returns the underlying object. - */ - Object finish(); - } - - static class BuilderAdapter implements MergeTarget { - - private final Message.Builder builder; - - public Descriptors.Descriptor getDescriptorForType() { - return builder.getDescriptorForType(); - } - - public BuilderAdapter(Message.Builder builder) { - this.builder = builder; - } - - public Object getField(Descriptors.FieldDescriptor field) { - return builder.getField(field); - } - - @Override - public boolean hasField(Descriptors.FieldDescriptor field) { - return builder.hasField(field); - } - - public MergeTarget setField(Descriptors.FieldDescriptor field, - Object value) { - builder.setField(field, value); - return this; - } - - public MergeTarget clearField(Descriptors.FieldDescriptor field) { - builder.clearField(field); - return this; - } - - public MergeTarget setRepeatedField( - Descriptors.FieldDescriptor field, int index, Object value) { - builder.setRepeatedField(field, index, value); - return this; - } - - public MergeTarget addRepeatedField( - Descriptors.FieldDescriptor field, Object value) { - builder.addRepeatedField(field, value); - return this; - } - - @Override - public boolean hasOneof(Descriptors.OneofDescriptor oneof) { - return builder.hasOneof(oneof); - } - - @Override - public MergeTarget clearOneof(Descriptors.OneofDescriptor oneof) { - builder.clearOneof(oneof); - return this; - } - - @Override - public Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof) { - return builder.getOneofFieldDescriptor(oneof); - } - - public ContainerType getContainerType() { - return ContainerType.MESSAGE; - } - - public ExtensionRegistry.ExtensionInfo findExtensionByName( - ExtensionRegistry registry, String name) { - return registry.findImmutableExtensionByName(name); - } - - public ExtensionRegistry.ExtensionInfo findExtensionByNumber( - ExtensionRegistry registry, Descriptors.Descriptor containingType, - int fieldNumber) { - return registry.findImmutableExtensionByNumber(containingType, - fieldNumber); - } - - public Object parseGroup(CodedInputStream input, - ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) - throws IOException { - Message.Builder subBuilder; - // When default instance is not null. The field is an extension field. - if (defaultInstance != null) { - subBuilder = defaultInstance.newBuilderForType(); - } else { - subBuilder = builder.newBuilderForField(field); - } - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - input.readGroup(field.getNumber(), subBuilder, extensionRegistry); - return subBuilder.buildPartial(); - } - - public Object parseMessage(CodedInputStream input, - ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) - throws IOException { - Message.Builder subBuilder; - // When default instance is not null. The field is an extension field. - if (defaultInstance != null) { - subBuilder = defaultInstance.newBuilderForType(); - } else { - subBuilder = builder.newBuilderForField(field); - } - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - input.readMessage(subBuilder, extensionRegistry); - return subBuilder.buildPartial(); - } - - public Object parseMessageFromBytes(ByteString bytes, - ExtensionRegistryLite extensionRegistry, - Descriptors.FieldDescriptor field, Message defaultInstance) - throws IOException { - Message.Builder subBuilder; - // When default instance is not null. The field is an extension field. - if (defaultInstance != null) { - subBuilder = defaultInstance.newBuilderForType(); - } else { - subBuilder = builder.newBuilderForField(field); - } - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - subBuilder.mergeFrom(bytes, extensionRegistry); - return subBuilder.buildPartial(); - } - - public MergeTarget newMergeTargetForField(Descriptors.FieldDescriptor field, - Message defaultInstance) { - if (defaultInstance != null) { - return new BuilderAdapter( - defaultInstance.newBuilderForType()); - } else { - return new BuilderAdapter(builder.newBuilderForField(field)); - } - } - - public Object readPrimitiveField( - CodedInputStream input, WireFormat.FieldType type, - boolean checkUtf8) throws IOException { - return FieldSet.readPrimitiveField(input, type, checkUtf8); - } - - public Object finish() { - return builder.buildPartial(); - } - } - - - static class ExtensionAdapter implements MergeTarget { - - private final FieldSet<Descriptors.FieldDescriptor> extensions; - - ExtensionAdapter(FieldSet<Descriptors.FieldDescriptor> extensions) { - this.extensions = extensions; - } - - public Descriptors.Descriptor getDescriptorForType() { - throw new UnsupportedOperationException( - "getDescriptorForType() called on FieldSet object"); - } - - public Object getField(Descriptors.FieldDescriptor field) { - return extensions.getField(field); - } - - public boolean hasField(Descriptors.FieldDescriptor field) { - return extensions.hasField(field); - } - - public MergeTarget setField(Descriptors.FieldDescriptor field, - Object value) { - extensions.setField(field, value); - return this; - } - - public MergeTarget clearField(Descriptors.FieldDescriptor field) { - extensions.clearField(field); - return this; - } - - public MergeTarget setRepeatedField( - Descriptors.FieldDescriptor field, int index, Object value) { - extensions.setRepeatedField(field, index, value); - return this; - } - - public MergeTarget addRepeatedField( - Descriptors.FieldDescriptor field, Object value) { - extensions.addRepeatedField(field, value); - return this; - } - - @Override - public boolean hasOneof(Descriptors.OneofDescriptor oneof) { - return false; - } - - @Override - public MergeTarget clearOneof(Descriptors.OneofDescriptor oneof) { - // Nothing to clear. - return this; - } - - @Override - public Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof) { - return null; - } - - public ContainerType getContainerType() { - return ContainerType.EXTENSION_SET; - } - - public ExtensionRegistry.ExtensionInfo findExtensionByName( - ExtensionRegistry registry, String name) { - return registry.findImmutableExtensionByName(name); - } - - public ExtensionRegistry.ExtensionInfo findExtensionByNumber( - ExtensionRegistry registry, Descriptors.Descriptor containingType, - int fieldNumber) { - return registry.findImmutableExtensionByNumber(containingType, - fieldNumber); - } - - public Object parseGroup(CodedInputStream input, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { - Message.Builder subBuilder = - defaultInstance.newBuilderForType(); - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - input.readGroup(field.getNumber(), subBuilder, registry); - return subBuilder.buildPartial(); - } - - public Object parseMessage(CodedInputStream input, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { - Message.Builder subBuilder = - defaultInstance.newBuilderForType(); - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - input.readMessage(subBuilder, registry); - return subBuilder.buildPartial(); - } - - public Object parseMessageFromBytes(ByteString bytes, - ExtensionRegistryLite registry, Descriptors.FieldDescriptor field, - Message defaultInstance) throws IOException { - Message.Builder subBuilder = defaultInstance.newBuilderForType(); - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { - subBuilder.mergeFrom(originalMessage); - } - } - subBuilder.mergeFrom(bytes, registry); - return subBuilder.buildPartial(); - } - - public MergeTarget newMergeTargetForField( - Descriptors.FieldDescriptor descriptor, Message defaultInstance) { - throw new UnsupportedOperationException( - "newMergeTargetForField() called on FieldSet object"); - } - - public Object readPrimitiveField( - CodedInputStream input, WireFormat.FieldType type, - boolean checkUtf8) throws IOException { - return FieldSet.readPrimitiveField(input, type, checkUtf8); - } - - public Object finish() { - throw new UnsupportedOperationException( - "finish() called on FieldSet object"); - } - } - - /** - * Parses a single field into MergeTarget. The target can be Message.Builder, - * FieldSet or MutableMessage. - * - * Package-private because it is used by GeneratedMessage.ExtendableMessage. - * - * @param tag The tag, which should have already been read. - * @return {@code true} unless the tag is an end-group tag. - */ - static boolean mergeFieldFrom( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - Descriptors.Descriptor type, - MergeTarget target, - int tag) throws IOException { - if (type.getOptions().getMessageSetWireFormat() && - tag == WireFormat.MESSAGE_SET_ITEM_TAG) { - mergeMessageSetExtensionFromCodedStream( - input, unknownFields, extensionRegistry, type, target); - return true; - } - - final int wireType = WireFormat.getTagWireType(tag); - final int fieldNumber = WireFormat.getTagFieldNumber(tag); - - final Descriptors.FieldDescriptor field; - Message defaultInstance = null; - - if (type.isExtensionNumber(fieldNumber)) { - // extensionRegistry may be either ExtensionRegistry or - // ExtensionRegistryLite. Since the type we are parsing is a full - // message, only a full ExtensionRegistry could possibly contain - // extensions of it. Otherwise we will treat the registry as if it - // were empty. - if (extensionRegistry instanceof ExtensionRegistry) { - final ExtensionRegistry.ExtensionInfo extension = - target.findExtensionByNumber((ExtensionRegistry) extensionRegistry, - type, fieldNumber); - if (extension == null) { - field = null; - } else { - field = extension.descriptor; - defaultInstance = extension.defaultInstance; - if (defaultInstance == null && - field.getJavaType() - == Descriptors.FieldDescriptor.JavaType.MESSAGE) { - throw new IllegalStateException( - "Message-typed extension lacked default instance: " + - field.getFullName()); - } - } - } else { - field = null; - } - } else if (target.getContainerType() == MergeTarget.ContainerType.MESSAGE) { - field = type.findFieldByNumber(fieldNumber); - } else { - field = null; - } - - boolean unknown = false; - boolean packed = false; - if (field == null) { - unknown = true; // Unknown field. - } else if (wireType == FieldSet.getWireFormatForFieldType( - field.getLiteType(), - false /* isPacked */)) { - packed = false; - } else if (field.isPackable() && - wireType == FieldSet.getWireFormatForFieldType( - field.getLiteType(), - true /* isPacked */)) { - packed = true; - } else { - unknown = true; // Unknown wire type. - } - - if (unknown) { // Unknown field or wrong wire type. Skip. - return unknownFields.mergeFieldFrom(tag, input); - } - - if (packed) { - final int length = input.readRawVarint32(); - final int limit = input.pushLimit(length); - 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; - } - target.addRepeatedField(field, value); - } - } else { - while (input.getBytesUntilLimit() > 0) { - final Object value = - target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check()); - target.addRepeatedField(field, value); - } - } - input.popLimit(limit); - } else { - final Object value; - switch (field.getType()) { - case GROUP: { - value = target - .parseGroup(input, extensionRegistry, field, defaultInstance); - break; - } - case MESSAGE: { - value = target - .parseMessage(input, extensionRegistry, field, defaultInstance); - break; - } - 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; - } - break; - default: - value = target.readPrimitiveField(input, field.getLiteType(), field.needsUtf8Check()); - break; - } - - if (field.isRepeated()) { - target.addRepeatedField(field, value); - } else { - target.setField(field, value); - } - } - - return true; - } - - /** - * Called by {@code #mergeFieldFrom()} to parse a MessageSet extension into - * MergeTarget. - */ - private static void mergeMessageSetExtensionFromCodedStream( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - Descriptors.Descriptor type, - MergeTarget target) throws IOException { - - // The wire format for MessageSet is: - // message MessageSet { - // repeated group Item = 1 { - // required int32 typeId = 2; - // required bytes message = 3; - // } - // } - // "typeId" is the extension's field number. The extension can only be - // a message type, where "message" contains the encoded bytes of that - // message. - // - // In practice, we will probably never see a MessageSet item in which - // the message appears before the type ID, or where either field does not - // appear exactly once. However, in theory such cases are valid, so we - // should be prepared to accept them. - - int typeId = 0; - ByteString rawBytes = null; // If we encounter "message" before "typeId" - ExtensionRegistry.ExtensionInfo extension = null; - - // Read bytes from input, if we get it's type first then parse it eagerly, - // otherwise we store the raw bytes in a local variable. - while (true) { - final int tag = input.readTag(); - if (tag == 0) { - break; - } - - if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { - typeId = input.readUInt32(); - if (typeId != 0) { - // extensionRegistry may be either ExtensionRegistry or - // ExtensionRegistryLite. Since the type we are parsing is a full - // message, only a full ExtensionRegistry could possibly contain - // extensions of it. Otherwise we will treat the registry as if it - // were empty. - if (extensionRegistry instanceof ExtensionRegistry) { - extension = target.findExtensionByNumber( - (ExtensionRegistry) extensionRegistry, type, typeId); - } - } - - } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { - if (typeId != 0) { - if (extension != null && - ExtensionRegistryLite.isEagerlyParseMessageSets()) { - // We already know the type, so we can parse directly from the - // input with no copying. Hooray! - eagerlyMergeMessageSetExtension( - input, extension, extensionRegistry, target); - rawBytes = null; - continue; - } - } - // We haven't seen a type ID yet or we want parse message lazily. - rawBytes = input.readBytes(); - - } else { // Unknown tag. Skip it. - if (!input.skipField(tag)) { - break; // End of group - } - } - } - input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); - - // Process the raw bytes. - if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID. - if (extension != null) { // We known the type - mergeMessageSetExtensionFromBytes( - rawBytes, extension, extensionRegistry, target); - } else { // We don't know how to parse this. Ignore it. - if (rawBytes != null) { - unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder() - .addLengthDelimited(rawBytes).build()); - } - } - } - } - - private static void mergeMessageSetExtensionFromBytes( - ByteString rawBytes, - ExtensionRegistry.ExtensionInfo extension, - ExtensionRegistryLite extensionRegistry, - MergeTarget target) throws IOException { - - Descriptors.FieldDescriptor field = extension.descriptor; - boolean hasOriginalValue = target.hasField(field); - - if (hasOriginalValue || ExtensionRegistryLite.isEagerlyParseMessageSets()) { - // If the field already exists, we just parse the field. - Object value = target.parseMessageFromBytes( - rawBytes, extensionRegistry,field, extension.defaultInstance); - target.setField(field, value); - } else { - // Use LazyField to load MessageSet lazily. - LazyField lazyField = new LazyField( - extension.defaultInstance, extensionRegistry, rawBytes); - target.setField(field, lazyField); - } - } - - private static void eagerlyMergeMessageSetExtension( - CodedInputStream input, - ExtensionRegistry.ExtensionInfo extension, - ExtensionRegistryLite extensionRegistry, - MergeTarget target) throws IOException { - Descriptors.FieldDescriptor field = extension.descriptor; - Object value = target.parseMessage(input, extensionRegistry, field, - extension.defaultInstance); - target.setField(field, value); - } -} diff --git a/java/src/main/java/com/google/protobuf/Parser.java b/java/src/main/java/com/google/protobuf/Parser.java deleted file mode 100644 index f636014..0000000 --- a/java/src/main/java/com/google/protobuf/Parser.java +++ /dev/null @@ -1,261 +0,0 @@ -// 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.InputStream; - -/** - * Abstract interface for parsing Protocol Messages. - * - * The implementation should be stateless and thread-safe. - * - * @author liujisi@google.com (Pherl Liu) - */ -public interface Parser<MessageType> { - /** - * Parses a message of {@code MessageType} from the input. - * - * <p>Note: The caller should call - * {@link CodedInputStream#checkLastTagWas(int)} after calling this to - * verify that the last tag seen was the appropriate end-group tag, - * or zero for EOF. - */ - public MessageType parseFrom(CodedInputStream input) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(CodedInputStream)}, but also parses extensions. - * The extensions that you want to be able to parse must be registered in - * {@code extensionRegistry}. Extensions not in the registry will be treated - * as unknown fields. - */ - public MessageType parseFrom(CodedInputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(CodedInputStream)}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialFrom(CodedInputStream input) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(CodedInputStream input, ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialFrom(CodedInputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - // --------------------------------------------------------------- - // Convenience methods. - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around {@link #parseFrom(CodedInputStream)}. - */ - public MessageType parseFrom(ByteString data) - throws InvalidProtocolBufferException; - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around - * {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}. - */ - public MessageType parseFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(ByteString)}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialFrom(ByteString data) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(ByteString, ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialFrom(ByteString data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around {@link #parseFrom(CodedInputStream)}. - */ - public MessageType parseFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException; - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around - * {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}. - */ - public MessageType parseFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around {@link #parseFrom(CodedInputStream)}. - */ - public MessageType parseFrom(byte[] data) - throws InvalidProtocolBufferException; - - /** - * Parses {@code data} as a message of {@code MessageType}. - * This is just a small wrapper around - * {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}. - */ - public MessageType parseFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(byte[], int, int)}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialFrom(byte[] data, int off, int len) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(ByteString, ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialFrom(byte[] data, int off, int len, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(byte[])}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialFrom(byte[] data) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(byte[], ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialFrom(byte[] data, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Parse a message of {@code MessageType} from {@code input}. - * This is just a small wrapper around {@link #parseFrom(CodedInputStream)}. - * Note that this method always reads the <i>entire</i> input (unless it - * throws an exception). If you want it to stop earlier, you will need to - * wrap your input in some wrapper stream that limits reading. Or, use - * {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write your - * message and {@link #parseDelimitedFrom(InputStream)} to read it. - * <p> - * Despite usually reading the entire input, this does not close the stream. - */ - public MessageType parseFrom(InputStream input) - throws InvalidProtocolBufferException; - - /** - * Parses a message of {@code MessageType} from {@code input}. - * This is just a small wrapper around - * {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}. - */ - public MessageType parseFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(InputStream)}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialFrom(InputStream input) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(InputStream, ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseFrom(InputStream)}, but does not read util EOF. - * Instead, the size of message (encoded as a varint) is read first, - * then the message data. Use - * {@link MessageLite#writeDelimitedTo(java.io.OutputStream)} to write - * messages in this format. - * - * @return True if successful, or false if the stream is at EOF when the - * method starts. Any other error (including reaching EOF during - * parsing) will cause an exception to be thrown. - */ - public MessageType parseDelimitedFrom(InputStream input) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseDelimitedFrom(InputStream)} but supporting extensions. - */ - public MessageType parseDelimitedFrom(InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseDelimitedFrom(InputStream)}, but does not throw an - * exception if the message is missing required fields. Instead, a partial - * message is returned. - */ - public MessageType parsePartialDelimitedFrom(InputStream input) - throws InvalidProtocolBufferException; - - /** - * Like {@link #parseDelimitedFrom(InputStream, ExtensionRegistryLite)}, - * but does not throw an exception if the message is missing required fields. - * Instead, a partial message is returned. - */ - public MessageType parsePartialDelimitedFrom( - InputStream input, - ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException; -} diff --git a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java b/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java index 0c8df98..112400f 100644 --- a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java +++ b/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/ProtocolStringList.java b/java/src/main/java/com/google/protobuf/ProtocolStringList.java deleted file mode 100644 index d553b41..0000000 --- a/java/src/main/java/com/google/protobuf/ProtocolStringList.java +++ /dev/null @@ -1,48 +0,0 @@ -// 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.List; - -/** - * An interface extending {@code List<String>} used for repeated string fields - * to provide optional access to the data as a list of ByteStrings. The - * underlying implementation stores values as either ByteStrings or Strings - * (see {@link LazyStringArrayList}) depending on how the value was initialized - * or last read, and it is often more efficient to deal with lists of - * ByteStrings when handling protos that have been deserialized from bytes. - */ -public interface ProtocolStringList extends List<String> { - - /** Returns a view of the data as a list of ByteStrings. */ - List<ByteString> asByteStringList(); - -} diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java deleted file mode 100644 index 22760d3..0000000 --- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java +++ /dev/null @@ -1,696 +0,0 @@ -// 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.AbstractList; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -/** - * {@code RepeatedFieldBuilder} implements a structure that a protocol - * message uses to hold a repeated field of other protocol messages. It supports - * the classical use case of adding immutable {@link Message}'s to the - * repeated field and is highly optimized around this (no extra memory - * allocations and sharing of immutable arrays). - * <br> - * It also supports the additional use case of adding a {@link Message.Builder} - * to the repeated field and deferring conversion of that {@code Builder} - * to an immutable {@code Message}. In this way, it's possible to maintain - * a tree of {@code Builder}'s that acts as a fully read/write data - * structure. - * <br> - * Logically, one can think of a tree of builders as converting the entire tree - * to messages when build is called on the root or when any method is called - * that desires a Message instead of a Builder. In terms of the implementation, - * the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder} - * classes cache messages that were created so that messages only need to be - * created when some change occured in its builder or a builder for one of its - * descendants. - * - * @param <MType> the type of message for the field - * @param <BType> the type of builder for the field - * @param <IType> the common interface for the message and the builder - * - * @author jonp@google.com (Jon Perlow) - */ -public class RepeatedFieldBuilder - <MType extends GeneratedMessage, - BType extends GeneratedMessage.Builder, - IType extends MessageOrBuilder> - implements GeneratedMessage.BuilderParent { - - // Parent to send changes to. - private GeneratedMessage.BuilderParent parent; - - // List of messages. Never null. It may be immutable, in which case - // isMessagesListImmutable will be true. See note below. - private List<MType> messages; - - // Whether messages is an mutable array that can be modified. - private boolean isMessagesListMutable; - - // List of builders. May be null, in which case, no nested builders were - // created. If not null, entries represent the builder for that index. - private List<SingleFieldBuilder<MType, BType, IType>> builders; - - // Here are the invariants for messages and builders: - // 1. messages is never null and its count corresponds to the number of items - // in the repeated field. - // 2. If builders is non-null, messages and builders MUST always - // contain the same number of items. - // 3. Entries in either array can be null, but for any index, there MUST be - // either a Message in messages or a builder in builders. - // 4. If the builder at an index is non-null, the builder is - // authoritative. This is the case where a Builder was set on the index. - // Any message in the messages array MUST be ignored. - // t. If the builder at an index is null, the message in the messages - // list is authoritative. This is the case where a Message (not a Builder) - // was set directly for an index. - - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. - private boolean isClean; - - // A view of this builder that exposes a List interface of messages. This is - // initialized on demand. This is fully backed by this object and all changes - // are reflected in it. Access to any item converts it to a message if it - // was a builder. - private MessageExternalList<MType, BType, IType> externalMessageList; - - // A view of this builder that exposes a List interface of builders. This is - // initialized on demand. This is fully backed by this object and all changes - // are reflected in it. Access to any item converts it to a builder if it - // was a message. - private BuilderExternalList<MType, BType, IType> externalBuilderList; - - // A view of this builder that exposes a List interface of the interface - // implemented by messages and builders. This is initialized on demand. This - // is fully backed by this object and all changes are reflected in it. - // Access to any item returns either a builder or message depending on - // what is most efficient. - private MessageOrBuilderExternalList<MType, BType, IType> - externalMessageOrBuilderList; - - /** - * Constructs a new builder with an empty list of messages. - * - * @param messages the current list of messages - * @param isMessagesListMutable Whether the messages list is mutable - * @param parent a listener to notify of changes - * @param isClean whether the builder is initially marked clean - */ - public RepeatedFieldBuilder( - List<MType> messages, - boolean isMessagesListMutable, - GeneratedMessage.BuilderParent parent, - boolean isClean) { - this.messages = messages; - this.isMessagesListMutable = isMessagesListMutable; - this.parent = parent; - this.isClean = isClean; - } - - public void dispose() { - // Null out parent so we stop sending it invalidations. - parent = null; - } - - /** - * Ensures that the list of messages is mutable so it can be updated. If it's - * immutable, a copy is made. - */ - private void ensureMutableMessageList() { - if (!isMessagesListMutable) { - messages = new ArrayList<MType>(messages); - isMessagesListMutable = true; - } - } - - /** - * Ensures that the list of builders is not null. If it's null, the list is - * created and initialized to be the same size as the messages list with - * null entries. - */ - private void ensureBuilders() { - if (this.builders == null) { - this.builders = - new ArrayList<SingleFieldBuilder<MType, BType, IType>>( - messages.size()); - for (int i = 0; i < messages.size(); i++) { - builders.add(null); - } - } - } - - /** - * Gets the count of items in the list. - * - * @return the count of items in the list. - */ - public int getCount() { - return messages.size(); - } - - /** - * Gets whether the list is empty. - * - * @return whether the list is empty - */ - public boolean isEmpty() { - return messages.isEmpty(); - } - - /** - * Get the message at the specified index. If the message is currently stored - * as a {@code Builder}, it is converted to a {@code Message} by - * calling {@link Message.Builder#buildPartial} on it. - * - * @param index the index of the message to get - * @return the message for the specified index - */ - public MType getMessage(int index) { - return getMessage(index, false); - } - - /** - * Get the message at the specified index. If the message is currently stored - * as a {@code Builder}, it is converted to a {@code Message} by - * calling {@link Message.Builder#buildPartial} on it. - * - * @param index the index of the message to get - * @param forBuild this is being called for build so we want to make sure - * we SingleFieldBuilder.build to send dirty invalidations - * @return the message for the specified index - */ - private MType getMessage(int index, boolean forBuild) { - if (this.builders == null) { - // We don't have any builders -- return the current Message. - // This is the case where no builder was created, so we MUST have a - // Message. - return messages.get(index); - } - - SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); - if (builder == null) { - // We don't have a builder -- return the current message. - // This is the case where no builder was created for the entry at index, - // so we MUST have a message. - return messages.get(index); - - } else { - return forBuild ? builder.build() : builder.getMessage(); - } - } - - /** - * Gets a builder for the specified index. If no builder has been created for - * that index, a builder is created on demand by calling - * {@link Message#toBuilder}. - * - * @param index the index of the message to get - * @return The builder for that index - */ - public BType getBuilder(int index) { - ensureBuilders(); - SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); - if (builder == null) { - MType message = messages.get(index); - builder = new SingleFieldBuilder<MType, BType, IType>( - message, this, isClean); - builders.set(index, builder); - } - return builder.getBuilder(); - } - - /** - * Gets the base class interface for the specified index. This may either be - * a builder or a message. It will return whatever is more efficient. - * - * @param index the index of the message to get - * @return the message or builder for the index as the base class interface - */ - @SuppressWarnings("unchecked") - public IType getMessageOrBuilder(int index) { - if (this.builders == null) { - // We don't have any builders -- return the current Message. - // This is the case where no builder was created, so we MUST have a - // Message. - return (IType) messages.get(index); - } - - SingleFieldBuilder<MType, BType, IType> builder = builders.get(index); - if (builder == null) { - // We don't have a builder -- return the current message. - // This is the case where no builder was created for the entry at index, - // so we MUST have a message. - return (IType) messages.get(index); - - } else { - return builder.getMessageOrBuilder(); - } - } - - /** - * Sets a message at the specified index replacing the existing item at - * that index. - * - * @param index the index to set. - * @param message the message to set - * @return the builder - */ - public RepeatedFieldBuilder<MType, BType, IType> setMessage( - int index, MType message) { - if (message == null) { - throw new NullPointerException(); - } - ensureMutableMessageList(); - messages.set(index, message); - if (builders != null) { - SingleFieldBuilder<MType, BType, IType> entry = - builders.set(index, null); - if (entry != null) { - entry.dispose(); - } - } - onChanged(); - incrementModCounts(); - return this; - } - - /** - * Appends the specified element to the end of this list. - * - * @param message the message to add - * @return the builder - */ - public RepeatedFieldBuilder<MType, BType, IType> addMessage( - MType message) { - if (message == null) { - throw new NullPointerException(); - } - ensureMutableMessageList(); - messages.add(message); - if (builders != null) { - builders.add(null); - } - onChanged(); - incrementModCounts(); - return this; - } - - /** - * Inserts the specified message at the specified position in this list. - * Shifts the element currently at that position (if any) and any subsequent - * elements to the right (adds one to their indices). - * - * @param index the index at which to insert the message - * @param message the message to add - * @return the builder - */ - public RepeatedFieldBuilder<MType, BType, IType> addMessage( - int index, MType message) { - if (message == null) { - throw new NullPointerException(); - } - ensureMutableMessageList(); - messages.add(index, message); - if (builders != null) { - builders.add(index, null); - } - onChanged(); - incrementModCounts(); - return this; - } - - /** - * Appends all of the messages in the specified collection to the end of - * this list, in the order that they are returned by the specified - * collection's iterator. - * - * @param values the messages to add - * @return the builder - */ - public RepeatedFieldBuilder<MType, BType, IType> addAllMessages( - Iterable<? extends MType> values) { - for (final MType value : values) { - if (value == null) { - throw new NullPointerException(); - } - } - 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); - } - } - onChanged(); - incrementModCounts(); - return this; - } - - /** - * Appends a new builder to the end of this list and returns the builder. - * - * @param message the message to add which is the basis of the builder - * @return the new builder - */ - public BType addBuilder(MType message) { - ensureMutableMessageList(); - ensureBuilders(); - SingleFieldBuilder<MType, BType, IType> builder = - new SingleFieldBuilder<MType, BType, IType>( - message, this, isClean); - messages.add(null); - builders.add(builder); - onChanged(); - incrementModCounts(); - return builder.getBuilder(); - } - - /** - * Inserts a new builder at the specified position in this list. - * Shifts the element currently at that position (if any) and any subsequent - * elements to the right (adds one to their indices). - * - * @param index the index at which to insert the builder - * @param message the message to add which is the basis of the builder - * @return the builder - */ - public BType addBuilder(int index, MType message) { - ensureMutableMessageList(); - ensureBuilders(); - SingleFieldBuilder<MType, BType, IType> builder = - new SingleFieldBuilder<MType, BType, IType>( - message, this, isClean); - messages.add(index, null); - builders.add(index, builder); - onChanged(); - incrementModCounts(); - return builder.getBuilder(); - } - - /** - * Removes the element at the specified position in this list. Shifts any - * subsequent elements to the left (subtracts one from their indices). - * Returns the element that was removed from the list. - * - * @param index the index at which to remove the message - */ - public void remove(int index) { - ensureMutableMessageList(); - messages.remove(index); - if (builders != null) { - SingleFieldBuilder<MType, BType, IType> entry = - builders.remove(index); - if (entry != null) { - entry.dispose(); - } - } - onChanged(); - incrementModCounts(); - } - - /** - * Removes all of the elements from this list. - * The list will be empty after this call returns. - */ - public void clear() { - messages = Collections.emptyList(); - isMessagesListMutable = false; - if (builders != null) { - for (SingleFieldBuilder<MType, BType, IType> entry : - builders) { - if (entry != null) { - entry.dispose(); - } - } - builders = null; - } - onChanged(); - incrementModCounts(); - } - - /** - * Builds the list of messages from the builder and returns them. - * - * @return an immutable list of messages - */ - public List<MType> build() { - // Now that build has been called, we are required to dispatch - // invalidations. - isClean = true; - - if (!isMessagesListMutable && builders == null) { - // We still have an immutable list and we never created a builder. - return messages; - } - - boolean allMessagesInSync = true; - if (!isMessagesListMutable) { - // We still have an immutable list. Let's see if any of them are out - // of sync with their builders. - for (int i = 0; i < messages.size(); i++) { - Message message = messages.get(i); - SingleFieldBuilder<MType, BType, IType> builder = builders.get(i); - if (builder != null) { - if (builder.build() != message) { - allMessagesInSync = false; - break; - } - } - } - if (allMessagesInSync) { - // Immutable list is still in sync. - return messages; - } - } - - // Need to make sure messages is up to date - ensureMutableMessageList(); - for (int i = 0; i < messages.size(); i++) { - messages.set(i, getMessage(i, true)); - } - - // We're going to return our list as immutable so we mark that we can - // no longer update it. - messages = Collections.unmodifiableList(messages); - isMessagesListMutable = false; - return messages; - } - - /** - * Gets a view of the builder as a list of messages. The returned list is live - * and will reflect any changes to the underlying builder. - * - * @return the messages in the list - */ - public List<MType> getMessageList() { - if (externalMessageList == null) { - externalMessageList = - new MessageExternalList<MType, BType, IType>(this); - } - return externalMessageList; - } - - /** - * Gets a view of the builder as a list of builders. This returned list is - * live and will reflect any changes to the underlying builder. - * - * @return the builders in the list - */ - public List<BType> getBuilderList() { - if (externalBuilderList == null) { - externalBuilderList = - new BuilderExternalList<MType, BType, IType>(this); - } - return externalBuilderList; - } - - /** - * Gets a view of the builder as a list of MessageOrBuilders. This returned - * list is live and will reflect any changes to the underlying builder. - * - * @return the builders in the list - */ - public List<IType> getMessageOrBuilderList() { - if (externalMessageOrBuilderList == null) { - externalMessageOrBuilderList = - new MessageOrBuilderExternalList<MType, BType, IType>(this); - } - return externalMessageOrBuilderList; - } - - /** - * Called when a the builder or one of its nested children has changed - * and any parent should be notified of its invalidation. - */ - private void onChanged() { - if (isClean && parent != null) { - parent.markDirty(); - - // Don't keep dispatching invalidations until build is called again. - isClean = false; - } - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void markDirty() { - onChanged(); - } - - /** - * Increments the mod counts so that an ConcurrentModificationException can - * be thrown if calling code tries to modify the builder while its iterating - * the list. - */ - private void incrementModCounts() { - if (externalMessageList != null) { - externalMessageList.incrementModCount(); - } - if (externalBuilderList != null) { - externalBuilderList.incrementModCount(); - } - if (externalMessageOrBuilderList != null) { - externalMessageOrBuilderList.incrementModCount(); - } - } - - /** - * Provides a live view of the builder as a list of messages. - * - * @param <MType> the type of message for the field - * @param <BType> the type of builder for the field - * @param <IType> the common interface for the message and the builder - */ - private static class MessageExternalList< - MType extends GeneratedMessage, - BType extends GeneratedMessage.Builder, - IType extends MessageOrBuilder> - extends AbstractList<MType> implements List<MType> { - - RepeatedFieldBuilder<MType, BType, IType> builder; - - MessageExternalList( - RepeatedFieldBuilder<MType, BType, IType> builder) { - this.builder = builder; - } - - public int size() { - return this.builder.getCount(); - } - - public MType get(int index) { - return builder.getMessage(index); - } - - void incrementModCount() { - modCount++; - } - } - - /** - * Provides a live view of the builder as a list of builders. - * - * @param <MType> the type of message for the field - * @param <BType> the type of builder for the field - * @param <IType> the common interface for the message and the builder - */ - private static class BuilderExternalList< - MType extends GeneratedMessage, - BType extends GeneratedMessage.Builder, - IType extends MessageOrBuilder> - extends AbstractList<BType> implements List<BType> { - - RepeatedFieldBuilder<MType, BType, IType> builder; - - BuilderExternalList( - RepeatedFieldBuilder<MType, BType, IType> builder) { - this.builder = builder; - } - - public int size() { - return this.builder.getCount(); - } - - public BType get(int index) { - return builder.getBuilder(index); - } - - void incrementModCount() { - modCount++; - } - } - - /** - * Provides a live view of the builder as a list of builders. - * - * @param <MType> the type of message for the field - * @param <BType> the type of builder for the field - * @param <IType> the common interface for the message and the builder - */ - private static class MessageOrBuilderExternalList< - MType extends GeneratedMessage, - BType extends GeneratedMessage.Builder, - IType extends MessageOrBuilder> - extends AbstractList<IType> implements List<IType> { - - RepeatedFieldBuilder<MType, BType, IType> builder; - - MessageOrBuilderExternalList( - RepeatedFieldBuilder<MType, BType, IType> builder) { - this.builder = builder; - } - - public int size() { - return this.builder.getCount(); - } - - public IType get(int index) { - return builder.getMessageOrBuilder(index); - } - - void incrementModCount() { - modCount++; - } - } -} diff --git a/java/src/main/java/com/google/protobuf/RopeByteString.java b/java/src/main/java/com/google/protobuf/RopeByteString.java deleted file mode 100644 index d1655b8..0000000 --- a/java/src/main/java/com/google/protobuf/RopeByteString.java +++ /dev/null @@ -1,957 +0,0 @@ -// 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; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.io.ByteArrayInputStream; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Stack; - -/** - * Class to represent {@code ByteStrings} formed by concatenation of other - * ByteStrings, without copying the data in the pieces. The concatenation is - * represented as a tree whose leaf nodes are each a {@link LiteralByteString}. - * - * <p>Most of the operation here is inspired by the now-famous paper <a - * href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf"> - * BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and - * michael plass - * - * <p>The algorithms described in the paper have been implemented for character - * strings in {@link com.google.common.string.Rope} and in the c++ class {@code - * cord.cc}. - * - * <p>Fundamentally the Rope algorithm represents the collection of pieces as a - * binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum - * sequence length, sequences that are too short relative to their depth cause a - * tree rebalance. More precisely, a tree of depth d is "balanced" in the - * terminology of BAP95 if its length is at least F(d+2), where F(n) is the - * n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have minimum - * lengths 1, 2, 3, 5, 8, 13,... - * - * @author carlanton@google.com (Carl Haverl) - */ -class RopeByteString extends ByteString { - - /** - * BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of - * depth n is "balanced", i.e flat enough, if its length is at least Fn+2, - * e.g. a "balanced" {@link RopeByteString} of depth 1 must have length at - * least 2, of depth 4 must have length >= 8, etc. - * - * <p>There's nothing special about using the Fibonacci numbers for this, but - * they are a reasonable sequence for encapsulating the idea that we are OK - * with longer strings being encoded in deeper binary trees. - * - * <p>For 32-bit integers, this array has length 46. - */ - private static final int[] minLengthByDepth; - - static { - // Dynamically generate the list of Fibonacci numbers the first time this - // class is accessed. - List<Integer> numbers = new ArrayList<Integer>(); - - // we skip the first Fibonacci number (1). So instead of: 1 1 2 3 5 8 ... - // we have: 1 2 3 5 8 ... - int f1 = 1; - int f2 = 1; - - // get all the values until we roll over. - while (f2 > 0) { - numbers.add(f2); - int temp = f1 + f2; - f1 = f2; - f2 = temp; - } - - // we include this here so that we can index this array to [x + 1] in the - // loops below. - numbers.add(Integer.MAX_VALUE); - minLengthByDepth = new int[numbers.size()]; - for (int i = 0; i < minLengthByDepth.length; i++) { - // unbox all the values - minLengthByDepth[i] = numbers.get(i); - } - } - - private final int totalLength; - private final ByteString left; - private final ByteString right; - private final int leftLength; - private final int treeDepth; - - /** - * Create a new RopeByteString, which can be thought of as a new tree node, by - * recording references to the two given strings. - * - * @param left string on the left of this node, should have {@code size() > - * 0} - * @param right string on the right of this node, should have {@code size() > - * 0} - */ - private RopeByteString(ByteString left, ByteString right) { - this.left = left; - this.right = right; - leftLength = left.size(); - totalLength = leftLength + right.size(); - treeDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1; - } - - /** - * Concatenate the given strings while performing various optimizations to - * slow the growth rate of tree depth and tree node count. The result is - * either a {@link LiteralByteString} or a {@link RopeByteString} - * depending on which optimizations, if any, were applied. - * - * <p>Small pieces of length less than {@link - * ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in - * BAP95. Large pieces are referenced without copy. - * - * @param left string on the left - * @param right string on the right - * @return concatenation representing the same sequence as the given strings - */ - static ByteString concatenate(ByteString left, ByteString right) { - ByteString result; - RopeByteString leftRope = - (left instanceof RopeByteString) ? (RopeByteString) left : null; - if (right.size() == 0) { - result = left; - } else if (left.size() == 0) { - result = right; - } else { - int newLength = left.size() + right.size(); - if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) { - // Optimization from BAP95: For short (leaves in paper, but just short - // here) total length, do a copy of data to a new leaf. - result = concatenateBytes(left, right); - } else if (leftRope != null - && leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) { - // Optimization from BAP95: As an optimization of the case where the - // ByteString is constructed by repeated concatenate, recognize the case - // where a short string is concatenated to a left-hand node whose - // right-hand branch is short. In the paper this applies to leaves, but - // we just look at the length here. This has the advantage of shedding - // references to unneeded data when substrings have been taken. - // - // When we recognize this case, we do a copy of the data and create a - // new parent node so that the depth of the result is the same as the - // given left tree. - ByteString newRight = concatenateBytes(leftRope.right, right); - result = new RopeByteString(leftRope.left, newRight); - } else if (leftRope != null - && leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth() - && leftRope.getTreeDepth() > right.getTreeDepth()) { - // Typically for concatenate-built strings the left-side is deeper than - // the right. This is our final attempt to concatenate without - // increasing the tree depth. We'll redo the the node on the RHS. This - // is yet another optimization for building the string by repeatedly - // concatenating on the right. - ByteString newRight = new RopeByteString(leftRope.right, right); - result = new RopeByteString(leftRope.left, newRight); - } else { - // Fine, we'll add a node and increase the tree depth--unless we - // rebalance ;^) - int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1; - if (newLength >= minLengthByDepth[newDepth]) { - // The tree is shallow enough, so don't rebalance - result = new RopeByteString(left, right); - } else { - result = new Balancer().balance(left, right); - } - } - } - return result; - } - - /** - * Concatenates two strings by copying data values. This is called in a few - * cases in order to reduce the growth of the number of tree nodes. - * - * @param left string on the left - * @param right string on the right - * @return string formed by copying data bytes - */ - private static LiteralByteString concatenateBytes(ByteString left, - ByteString right) { - int leftSize = left.size(); - int rightSize = right.size(); - byte[] bytes = new byte[leftSize + rightSize]; - left.copyTo(bytes, 0, 0, leftSize); - right.copyTo(bytes, 0, leftSize, rightSize); - return new LiteralByteString(bytes); // Constructor wraps bytes - } - - /** - * Create a new RopeByteString for testing only while bypassing all the - * defenses of {@link #concatenate(ByteString, ByteString)}. This allows - * testing trees of specific structure. We are also able to insert empty - * leaves, though these are dis-allowed, so that we can make sure the - * implementation can withstand their presence. - * - * @param left string on the left of this node - * @param right string on the right of this node - * @return an unsafe instance for testing only - */ - static RopeByteString newInstanceForTest(ByteString left, ByteString right) { - return new RopeByteString(left, right); - } - - /** - * Gets the byte at the given index. - * Throws {@link ArrayIndexOutOfBoundsException} for backwards-compatibility - * reasons although it would more properly be {@link - * IndexOutOfBoundsException}. - * - * @param index index of byte - * @return the value - * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size - */ - @Override - public byte byteAt(int index) { - if (index < 0) { - throw new ArrayIndexOutOfBoundsException("Index < 0: " + index); - } - if (index > totalLength) { - throw new ArrayIndexOutOfBoundsException( - "Index > length: " + index + ", " + totalLength); - } - - byte result; - // Find the relevant piece by recursive descent - if (index < leftLength) { - result = left.byteAt(index); - } else { - result = right.byteAt(index - leftLength); - } - return result; - } - - @Override - public int size() { - return totalLength; - } - - // ================================================================= - // Pieces - - @Override - protected int getTreeDepth() { - return treeDepth; - } - - /** - * Determines if the tree is balanced according to BAP95, which means the tree - * is flat-enough with respect to the bounds. Note that this definition of - * balanced is one where sub-trees of balanced trees are not necessarily - * balanced. - * - * @return true if the tree is balanced - */ - @Override - protected boolean isBalanced() { - return totalLength >= minLengthByDepth[treeDepth]; - } - - /** - * Takes a substring of this one. This involves recursive descent along the - * left and right edges of the substring, and referencing any wholly contained - * segments in between. Any leaf nodes entirely uninvolved in the substring - * will not be referenced by the substring. - * - * <p>Substrings of {@code length < 2} should result in at most a single - * recursive call chain, terminating at a leaf node. Thus the result will be a - * {@link LiteralByteString}. {@link #RopeByteString(ByteString, - * ByteString)}. - * - * @param beginIndex start at this index - * @param endIndex the last character is the one before this index - * @return substring leaf node or tree - */ - @Override - public ByteString substring(int beginIndex, int endIndex) { - if (beginIndex < 0) { - throw new IndexOutOfBoundsException( - "Beginning index: " + beginIndex + " < 0"); - } - if (endIndex > totalLength) { - throw new IndexOutOfBoundsException( - "End index: " + endIndex + " > " + totalLength); - } - int substringLength = endIndex - beginIndex; - if (substringLength < 0) { - throw new IndexOutOfBoundsException( - "Beginning index larger than ending index: " + beginIndex + ", " - + endIndex); - } - - ByteString result; - if (substringLength == 0) { - // Empty substring - result = ByteString.EMPTY; - } else if (substringLength == totalLength) { - // The whole string - result = this; - } else { - // Proper substring - if (endIndex <= leftLength) { - // Substring on the left - result = left.substring(beginIndex, endIndex); - } else if (beginIndex >= leftLength) { - // Substring on the right - result = right - .substring(beginIndex - leftLength, endIndex - leftLength); - } else { - // Split substring - ByteString leftSub = left.substring(beginIndex); - ByteString rightSub = right.substring(0, endIndex - leftLength); - // Intentionally not rebalancing, since in many cases these two - // substrings will already be less deep than the top-level - // RopeByteString we're taking a substring of. - result = new RopeByteString(leftSub, rightSub); - } - } - return result; - } - - // ================================================================= - // ByteString -> byte[] - - @Override - protected void copyToInternal(byte[] target, int sourceOffset, - int targetOffset, int numberToCopy) { - if (sourceOffset + numberToCopy <= leftLength) { - left.copyToInternal(target, sourceOffset, targetOffset, numberToCopy); - } else if (sourceOffset >= leftLength) { - right.copyToInternal(target, sourceOffset - leftLength, targetOffset, - numberToCopy); - } else { - int leftLength = this.leftLength - sourceOffset; - left.copyToInternal(target, sourceOffset, targetOffset, leftLength); - right.copyToInternal(target, 0, targetOffset + leftLength, - numberToCopy - leftLength); - } - } - - @Override - public void copyTo(ByteBuffer target) { - left.copyTo(target); - right.copyTo(target); - } - - @Override - public ByteBuffer asReadOnlyByteBuffer() { - ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray()); - return byteBuffer.asReadOnlyBuffer(); - } - - @Override - public List<ByteBuffer> asReadOnlyByteBufferList() { - // Walk through the list of LiteralByteString's that make up this - // rope, and add each one as a read-only ByteBuffer. - List<ByteBuffer> result = new ArrayList<ByteBuffer>(); - PieceIterator pieces = new PieceIterator(this); - while (pieces.hasNext()) { - LiteralByteString byteString = pieces.next(); - result.add(byteString.asReadOnlyByteBuffer()); - } - return result; - } - - @Override - public void writeTo(OutputStream outputStream) throws IOException { - left.writeTo(outputStream); - right.writeTo(outputStream); - } - - @Override - void writeToInternal(OutputStream out, int sourceOffset, - int numberToWrite) throws IOException { - if (sourceOffset + numberToWrite <= leftLength) { - left.writeToInternal(out, sourceOffset, numberToWrite); - } else if (sourceOffset >= leftLength) { - right.writeToInternal(out, sourceOffset - leftLength, numberToWrite); - } else { - int numberToWriteInLeft = leftLength - sourceOffset; - left.writeToInternal(out, sourceOffset, numberToWriteInLeft); - right.writeToInternal(out, 0, numberToWrite - numberToWriteInLeft); - } - } - - @Override - public String toString(String charsetName) - throws UnsupportedEncodingException { - return new String(toByteArray(), charsetName); - } - - // ================================================================= - // UTF-8 decoding - - @Override - public boolean isValidUtf8() { - int leftPartial = left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength); - int state = right.partialIsValidUtf8(leftPartial, 0, right.size()); - return state == Utf8.COMPLETE; - } - - @Override - protected int partialIsValidUtf8(int state, int offset, int length) { - int toIndex = offset + length; - if (toIndex <= leftLength) { - return left.partialIsValidUtf8(state, offset, length); - } else if (offset >= leftLength) { - return right.partialIsValidUtf8(state, offset - leftLength, length); - } else { - int leftLength = this.leftLength - offset; - int leftPartial = left.partialIsValidUtf8(state, offset, leftLength); - return right.partialIsValidUtf8(leftPartial, 0, length - leftLength); - } - } - - // ================================================================= - // equals() and hashCode() - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof ByteString)) { - return false; - } - - ByteString otherByteString = (ByteString) other; - if (totalLength != otherByteString.size()) { - return false; - } - if (totalLength == 0) { - return true; - } - - // You don't really want to be calling equals on long strings, but since - // we cache the hashCode, we effectively cache inequality. We use the cached - // hashCode if it's already computed. It's arguable we should compute the - // hashCode here, and if we're going to be testing a bunch of byteStrings, - // it might even make sense. - if (hash != 0) { - int cachedOtherHash = otherByteString.peekCachedHashCode(); - if (cachedOtherHash != 0 && hash != cachedOtherHash) { - return false; - } - } - - return equalsFragments(otherByteString); - } - - /** - * Determines if this string is equal to another of the same length by - * iterating over the leaf nodes. On each step of the iteration, the - * overlapping segments of the leaves are compared. - * - * @param other string of the same length as this one - * @return true if the values of this string equals the value of the given - * one - */ - private boolean equalsFragments(ByteString other) { - int thisOffset = 0; - Iterator<LiteralByteString> thisIter = new PieceIterator(this); - LiteralByteString thisString = thisIter.next(); - - int thatOffset = 0; - Iterator<LiteralByteString> thatIter = new PieceIterator(other); - LiteralByteString thatString = thatIter.next(); - - int pos = 0; - while (true) { - int thisRemaining = thisString.size() - thisOffset; - int thatRemaining = thatString.size() - thatOffset; - int bytesToCompare = Math.min(thisRemaining, thatRemaining); - - // At least one of the offsets will be zero - boolean stillEqual = (thisOffset == 0) - ? thisString.equalsRange(thatString, thatOffset, bytesToCompare) - : thatString.equalsRange(thisString, thisOffset, bytesToCompare); - if (!stillEqual) { - return false; - } - - pos += bytesToCompare; - if (pos >= totalLength) { - if (pos == totalLength) { - return true; - } - throw new IllegalStateException(); - } - // We always get to the end of at least one of the pieces - if (bytesToCompare == thisRemaining) { // If reached end of this - thisOffset = 0; - thisString = thisIter.next(); - } else { - thisOffset += bytesToCompare; - } - if (bytesToCompare == thatRemaining) { // If reached end of that - thatOffset = 0; - thatString = thatIter.next(); - } else { - thatOffset += bytesToCompare; - } - } - } - - /** - * Cached hash value. Intentionally accessed via a data race, which is safe - * because of the Java Memory Model's "no out-of-thin-air values" guarantees - * for ints. - */ - private int hash = 0; - - @Override - public int hashCode() { - int h = hash; - - if (h == 0) { - h = totalLength; - h = partialHash(h, 0, totalLength); - if (h == 0) { - h = 1; - } - hash = h; - } - return h; - } - - @Override - protected int peekCachedHashCode() { - return hash; - } - - @Override - protected int partialHash(int h, int offset, int length) { - int toIndex = offset + length; - if (toIndex <= leftLength) { - return left.partialHash(h, offset, length); - } else if (offset >= leftLength) { - return right.partialHash(h, offset - leftLength, length); - } else { - int leftLength = this.leftLength - offset; - int leftPartial = left.partialHash(h, offset, leftLength); - return right.partialHash(leftPartial, 0, length - leftLength); - } - } - - // ================================================================= - // Input stream - - @Override - public CodedInputStream newCodedInput() { - return CodedInputStream.newInstance(new RopeInputStream()); - } - - @Override - public InputStream newInput() { - return new RopeInputStream(); - } - - /** - * This class implements the balancing algorithm of BAP95. In the paper the - * authors use an array to keep track of pieces, while here we use a stack. - * The tree is balanced by traversing subtrees in left to right order, and the - * stack always contains the part of the string we've traversed so far. - * - * <p>One surprising aspect of the algorithm is the result of balancing is not - * necessarily balanced, though it is nearly balanced. For details, see - * BAP95. - */ - private static class Balancer { - // Stack containing the part of the string, starting from the left, that - // we've already traversed. The final string should be the equivalent of - // concatenating the strings on the stack from bottom to top. - private final Stack<ByteString> prefixesStack = new Stack<ByteString>(); - - private ByteString balance(ByteString left, ByteString right) { - doBalance(left); - doBalance(right); - - // Sweep stack to gather the result - ByteString partialString = prefixesStack.pop(); - while (!prefixesStack.isEmpty()) { - ByteString newLeft = prefixesStack.pop(); - partialString = new RopeByteString(newLeft, partialString); - } - // We should end up with a RopeByteString since at a minimum we will - // create one from concatenating left and right - return partialString; - } - - private void doBalance(ByteString root) { - // BAP95: Insert balanced subtrees whole. This means the result might not - // be balanced, leading to repeated rebalancings on concatenate. However, - // these rebalancings are shallow due to ignoring balanced subtrees, and - // relatively few calls to insert() result. - if (root.isBalanced()) { - insert(root); - } else if (root instanceof RopeByteString) { - RopeByteString rbs = (RopeByteString) root; - doBalance(rbs.left); - doBalance(rbs.right); - } else { - throw new IllegalArgumentException( - "Has a new type of ByteString been created? Found " + - root.getClass()); - } - } - - /** - * Push a string on the balance stack (BAP95). BAP95 uses an array and - * calls the elements in the array 'bins'. We instead use a stack, so the - * 'bins' of lengths are represented by differences between the elements of - * minLengthByDepth. - * - * <p>If the length bin for our string, and all shorter length bins, are - * empty, we just push it on the stack. Otherwise, we need to start - * concatenating, putting the given string in the "middle" and continuing - * until we land in an empty length bin that matches the length of our - * concatenation. - * - * @param byteString string to place on the balance stack - */ - private void insert(ByteString byteString) { - int depthBin = getDepthBinForLength(byteString.size()); - int binEnd = minLengthByDepth[depthBin + 1]; - - // BAP95: Concatenate all trees occupying bins representing the length of - // our new piece or of shorter pieces, to the extent that is possible. - // The goal is to clear the bin which our piece belongs in, but that may - // not be entirely possible if there aren't enough longer bins occupied. - if (prefixesStack.isEmpty() || prefixesStack.peek().size() >= binEnd) { - prefixesStack.push(byteString); - } else { - int binStart = minLengthByDepth[depthBin]; - - // Concatenate the subtrees of shorter length - ByteString newTree = prefixesStack.pop(); - while (!prefixesStack.isEmpty() - && prefixesStack.peek().size() < binStart) { - ByteString left = prefixesStack.pop(); - newTree = new RopeByteString(left, newTree); - } - - // Concatenate the given string - newTree = new RopeByteString(newTree, byteString); - - // Continue concatenating until we land in an empty bin - while (!prefixesStack.isEmpty()) { - depthBin = getDepthBinForLength(newTree.size()); - binEnd = minLengthByDepth[depthBin + 1]; - if (prefixesStack.peek().size() < binEnd) { - ByteString left = prefixesStack.pop(); - newTree = new RopeByteString(left, newTree); - } else { - break; - } - } - prefixesStack.push(newTree); - } - } - - private int getDepthBinForLength(int length) { - int depth = Arrays.binarySearch(minLengthByDepth, length); - if (depth < 0) { - // It wasn't an exact match, so convert to the index of the containing - // fragment, which is one less even than the insertion point. - int insertionPoint = -(depth + 1); - depth = insertionPoint - 1; - } - - return depth; - } - } - - /** - * This class is a continuable tree traversal, which keeps the state - * information which would exist on the stack in a recursive traversal instead - * on a stack of "Bread Crumbs". The maximum depth of the stack in this - * iterator is the same as the depth of the tree being traversed. - * - * <p>This iterator is used to implement - * {@link RopeByteString#equalsFragments(ByteString)}. - */ - private static class PieceIterator implements Iterator<LiteralByteString> { - - private final Stack<RopeByteString> breadCrumbs = - new Stack<RopeByteString>(); - private LiteralByteString next; - - private PieceIterator(ByteString root) { - next = getLeafByLeft(root); - } - - private LiteralByteString getLeafByLeft(ByteString root) { - ByteString pos = root; - while (pos instanceof RopeByteString) { - RopeByteString rbs = (RopeByteString) pos; - breadCrumbs.push(rbs); - pos = rbs.left; - } - return (LiteralByteString) pos; - } - - private LiteralByteString getNextNonEmptyLeaf() { - while (true) { - // Almost always, we go through this loop exactly once. However, if - // we discover an empty string in the rope, we toss it and try again. - if (breadCrumbs.isEmpty()) { - return null; - } else { - LiteralByteString result = getLeafByLeft(breadCrumbs.pop().right); - if (!result.isEmpty()) { - return result; - } - } - } - } - - public boolean hasNext() { - return next != null; - } - - /** - * Returns the next item and advances one {@code LiteralByteString}. - * - * @return next non-empty LiteralByteString or {@code null} - */ - public LiteralByteString next() { - if (next == null) { - throw new NoSuchElementException(); - } - LiteralByteString result = next; - next = getNextNonEmptyLeaf(); - return result; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - } - - // ================================================================= - // ByteIterator - - @Override - public ByteIterator iterator() { - return new RopeByteIterator(); - } - - private class RopeByteIterator implements ByteString.ByteIterator { - - private final PieceIterator pieces; - private ByteIterator bytes; - int bytesRemaining; - - private RopeByteIterator() { - pieces = new PieceIterator(RopeByteString.this); - bytes = pieces.next().iterator(); - bytesRemaining = size(); - } - - public boolean hasNext() { - return (bytesRemaining > 0); - } - - public Byte next() { - return nextByte(); // Does not instantiate a Byte - } - - public byte nextByte() { - if (!bytes.hasNext()) { - bytes = pieces.next().iterator(); - } - --bytesRemaining; - return bytes.nextByte(); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - } - - /** - * This class is the {@link RopeByteString} equivalent for - * {@link ByteArrayInputStream}. - */ - private class RopeInputStream extends InputStream { - // Iterates through the pieces of the rope - private PieceIterator pieceIterator; - // The current piece - private LiteralByteString currentPiece; - // The size of the current piece - private int currentPieceSize; - // The index of the next byte to read in the current piece - private int currentPieceIndex; - // The offset of the start of the current piece in the rope byte string - private int currentPieceOffsetInRope; - // Offset in the buffer at which user called mark(); - private int mark; - - public RopeInputStream() { - initialize(); - } - - @Override - public int read(byte b[], int offset, int length) { - if (b == null) { - throw new NullPointerException(); - } else if (offset < 0 || length < 0 || length > b.length - offset) { - throw new IndexOutOfBoundsException(); - } - return readSkipInternal(b, offset, length); - } - - @Override - public long skip(long length) { - if (length < 0) { - throw new IndexOutOfBoundsException(); - } else if (length > Integer.MAX_VALUE) { - length = Integer.MAX_VALUE; - } - return readSkipInternal(null, 0, (int) length); - } - - /** - * Internal implementation of read and skip. If b != null, then read the - * next {@code length} bytes into the buffer {@code b} at - * offset {@code offset}. If b == null, then skip the next {@code length) - * bytes. - * <p> - * This method assumes that all error checking has already happened. - * <p> - * Returns the actual number of bytes read or skipped. - */ - private int readSkipInternal(byte b[], int offset, int length) { - int bytesRemaining = length; - while (bytesRemaining > 0) { - advanceIfCurrentPieceFullyRead(); - if (currentPiece == null) { - if (bytesRemaining == length) { - // We didn't manage to read anything - return -1; - } - break; - } else { - // Copy the bytes from this piece. - int currentPieceRemaining = currentPieceSize - currentPieceIndex; - int count = Math.min(currentPieceRemaining, bytesRemaining); - if (b != null) { - currentPiece.copyTo(b, currentPieceIndex, offset, count); - offset += count; - } - currentPieceIndex += count; - bytesRemaining -= count; - } - } - // Return the number of bytes read. - return length - bytesRemaining; - } - - @Override - public int read() throws IOException { - advanceIfCurrentPieceFullyRead(); - if (currentPiece == null) { - return -1; - } else { - return currentPiece.byteAt(currentPieceIndex++) & 0xFF; - } - } - - @Override - public int available() throws IOException { - int bytesRead = currentPieceOffsetInRope + currentPieceIndex; - return RopeByteString.this.size() - bytesRead; - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public void mark(int readAheadLimit) { - // Set the mark to our position in the byte string - mark = currentPieceOffsetInRope + currentPieceIndex; - } - - @Override - public synchronized void reset() { - // Just reinitialize and skip the specified number of bytes. - initialize(); - readSkipInternal(null, 0, mark); - } - - /** Common initialization code used by both the constructor and reset() */ - private void initialize() { - pieceIterator = new PieceIterator(RopeByteString.this); - currentPiece = pieceIterator.next(); - currentPieceSize = currentPiece.size(); - currentPieceIndex = 0; - currentPieceOffsetInRope = 0; - } - - /** - * Skips to the next piece if we have read all the data in the current - * piece. Sets currentPiece to null if we have reached the end of the - * input. - */ - private void advanceIfCurrentPieceFullyRead() { - if (currentPiece != null && currentPieceIndex == currentPieceSize) { - // Generally, we can only go through this loop at most once, since - // empty strings can't end up in a rope. But better to test. - currentPieceOffsetInRope += currentPieceSize; - currentPieceIndex = 0; - if (pieceIterator.hasNext()) { - currentPiece = pieceIterator.next(); - currentPieceSize = currentPiece.size(); - } else { - currentPiece = null; - currentPieceSize = 0; - } - } - } - } -} diff --git a/java/src/main/java/com/google/protobuf/RpcCallback.java b/java/src/main/java/com/google/protobuf/RpcCallback.java index 1075296..1fd35ed 100644 --- a/java/src/main/java/com/google/protobuf/RpcCallback.java +++ b/java/src/main/java/com/google/protobuf/RpcCallback.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/RpcChannel.java b/java/src/main/java/com/google/protobuf/RpcChannel.java index f272f4a..c6ec54f 100644 --- a/java/src/main/java/com/google/protobuf/RpcChannel.java +++ b/java/src/main/java/com/google/protobuf/RpcChannel.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/RpcController.java b/java/src/main/java/com/google/protobuf/RpcController.java index a92dd7b..aaa5446 100644 --- a/java/src/main/java/com/google/protobuf/RpcController.java +++ b/java/src/main/java/com/google/protobuf/RpcController.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/src/main/java/com/google/protobuf/RpcUtil.java index 694b8d1..b1b959a 100644 --- a/java/src/main/java/com/google/protobuf/RpcUtil.java +++ b/java/src/main/java/com/google/protobuf/RpcUtil.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -91,8 +91,9 @@ public final class RpcUtil { @SuppressWarnings("unchecked") private static <Type extends Message> Type copyAsType( final Type typeDefaultInstance, final Message source) { - return (Type) typeDefaultInstance - .newBuilderForType().mergeFrom(source).build(); + return (Type)typeDefaultInstance.newBuilderForType() + .mergeFrom(source) + .build(); } /** diff --git a/java/src/main/java/com/google/protobuf/Service.java b/java/src/main/java/com/google/protobuf/Service.java index ba7b033..541585f 100644 --- a/java/src/main/java/com/google/protobuf/Service.java +++ b/java/src/main/java/com/google/protobuf/Service.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/src/main/java/com/google/protobuf/ServiceException.java index 00d5707..c043a77 100644 --- a/java/src/main/java/com/google/protobuf/ServiceException.java +++ b/java/src/main/java/com/google/protobuf/ServiceException.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -35,18 +35,10 @@ package com.google.protobuf; * * @author cpovirk@google.com (Chris Povirk) */ -public class ServiceException extends Exception { +public final class ServiceException extends Exception { private static final long serialVersionUID = -1219262335729891920L; public ServiceException(final String message) { super(message); } - - public ServiceException(final Throwable cause) { - super(cause); - } - - public ServiceException(final String message, final Throwable cause) { - super(message, cause); - } } diff --git a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java deleted file mode 100644 index 13a36d4..0000000 --- a/java/src/main/java/com/google/protobuf/SingleFieldBuilder.java +++ /dev/null @@ -1,241 +0,0 @@ -// 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; - -/** - * {@code SingleFieldBuilder} implements a structure that a protocol - * message uses to hold a single field of another protocol message. It supports - * the classical use case of setting an immutable {@link Message} as the value - * of the field and is highly optimized around this. - * <br> - * It also supports the additional use case of setting a {@link Message.Builder} - * as the field and deferring conversion of that {@code Builder} - * to an immutable {@code Message}. In this way, it's possible to maintain - * a tree of {@code Builder}'s that acts as a fully read/write data - * structure. - * <br> - * Logically, one can think of a tree of builders as converting the entire tree - * to messages when build is called on the root or when any method is called - * that desires a Message instead of a Builder. In terms of the implementation, - * the {@code SingleFieldBuilder} and {@code RepeatedFieldBuilder} - * classes cache messages that were created so that messages only need to be - * created when some change occured in its builder or a builder for one of its - * descendants. - * - * @param <MType> the type of message for the field - * @param <BType> the type of builder for the field - * @param <IType> the common interface for the message and the builder - * - * @author jonp@google.com (Jon Perlow) - */ -public class SingleFieldBuilder - <MType extends GeneratedMessage, - BType extends GeneratedMessage.Builder, - IType extends MessageOrBuilder> - implements GeneratedMessage.BuilderParent { - - // Parent to send changes to. - private GeneratedMessage.BuilderParent parent; - - // Invariant: one of builder or message fields must be non-null. - - // If set, this is the case where we are backed by a builder. In this case, - // message field represents a cached message for the builder (or null if - // there is no cached message). - private BType builder; - - // If builder is non-null, this represents a cached message from the builder. - // If builder is null, this is the authoritative message for the field. - private MType message; - - // Indicates that we've built a message and so we are now obligated - // to dispatch dirty invalidations. See GeneratedMessage.BuilderListener. - private boolean isClean; - - public SingleFieldBuilder( - MType message, - GeneratedMessage.BuilderParent parent, - boolean isClean) { - if (message == null) { - throw new NullPointerException(); - } - this.message = message; - this.parent = parent; - this.isClean = isClean; - } - - public void dispose() { - // Null out parent so we stop sending it invalidations. - parent = null; - } - - /** - * Get the message for the field. If the message is currently stored - * as a {@code Builder}, it is converted to a {@code Message} by - * calling {@link Message.Builder#buildPartial} on it. If no message has - * been set, returns the default instance of the message. - * - * @return the message for the field - */ - @SuppressWarnings("unchecked") - public MType getMessage() { - if (message == null) { - // If message is null, the invariant is that we must be have a builder. - message = (MType) builder.buildPartial(); - } - return message; - } - - /** - * Builds the message and returns it. - * - * @return the message - */ - public MType build() { - // Now that build has been called, we are required to dispatch - // invalidations. - isClean = true; - return getMessage(); - } - - /** - * Gets a builder for the field. If no builder has been created yet, a - * builder is created on demand by calling {@link Message#toBuilder}. - * - * @return The builder for the field - */ - @SuppressWarnings("unchecked") - public BType getBuilder() { - if (builder == null) { - // builder.mergeFrom() on a fresh builder - // does not create any sub-objects with independent clean/dirty states, - // therefore setting the builder itself to clean without actually calling - // build() cannot break any invariants. - builder = (BType) message.newBuilderForType(this); - builder.mergeFrom(message); // no-op if message is the default message - builder.markClean(); - } - return builder; - } - - /** - * Gets the base class interface for the field. This may either be a builder - * or a message. It will return whatever is more efficient. - * - * @return the message or builder for the field as the base class interface - */ - @SuppressWarnings("unchecked") - public IType getMessageOrBuilder() { - if (builder != null) { - return (IType) builder; - } else { - return (IType) message; - } - } - - /** - * Sets a message for the field replacing any existing value. - * - * @param message the message to set - * @return the builder - */ - public SingleFieldBuilder<MType, BType, IType> setMessage( - MType message) { - if (message == null) { - throw new NullPointerException(); - } - this.message = message; - if (builder != null) { - builder.dispose(); - builder = null; - } - onChanged(); - return this; - } - - /** - * Merges the field from another field. - * - * @param value the value to merge from - * @return the builder - */ - public SingleFieldBuilder<MType, BType, IType> mergeFrom( - MType value) { - if (builder == null && message == message.getDefaultInstanceForType()) { - message = value; - } else { - getBuilder().mergeFrom(value); - } - onChanged(); - return this; - } - - /** - * Clears the value of the field. - * - * @return the builder - */ - @SuppressWarnings("unchecked") - public SingleFieldBuilder<MType, BType, IType> clear() { - message = (MType) (message != null ? - message.getDefaultInstanceForType() : - builder.getDefaultInstanceForType()); - if (builder != null) { - builder.dispose(); - builder = null; - } - onChanged(); - return this; - } - - /** - * Called when a the builder or one of its nested children has changed - * and any parent should be notified of its invalidation. - */ - private void onChanged() { - // If builder is null, this is the case where onChanged is being called - // from setMessage or clear. - if (builder != null) { - message = null; - } - if (isClean && parent != null) { - parent.markDirty(); - - // Don't keep dispatching invalidations until build is called again. - isClean = false; - } - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void markDirty() { - onChanged(); - } -} diff --git a/java/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/src/main/java/com/google/protobuf/SmallSortedMap.java deleted file mode 100644 index 0674d2e..0000000 --- a/java/src/main/java/com/google/protobuf/SmallSortedMap.java +++ /dev/null @@ -1,618 +0,0 @@ -// 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.AbstractMap; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.TreeMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedMap; - -/** - * A custom map implementation from FieldDescriptor to Object optimized to - * minimize the number of memory allocations for instances with a small number - * of mappings. The implementation stores the first {@code k} mappings in an - * array for a configurable value of {@code k}, allowing direct access to the - * corresponding {@code Entry}s without the need to create an Iterator. The - * remaining entries are stored in an overflow map. Iteration over the entries - * in the map should be done as follows: - * - * <pre> {@code - * for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { - * process(fieldMap.getArrayEntryAt(i)); - * } - * for (Map.Entry<K, V> entry : fieldMap.getOverflowEntries()) { - * process(entry); - * } - * }</pre> - * - * The resulting iteration is in order of ascending field tag number. The - * object returned by {@link #entrySet()} adheres to the same contract but is - * less efficient as it necessarily involves creating an object for iteration. - * <p> - * The tradeoff for this memory efficiency is that the worst case running time - * of the {@code put()} operation is {@code O(k + lg n)}, which happens when - * entries are added in descending order. {@code k} should be chosen such that - * it covers enough common cases without adversely affecting larger maps. In - * practice, the worst case scenario does not happen for extensions because - * extension fields are serialized and deserialized in order of ascending tag - * number, but the worst case scenario can happen for DynamicMessages. - * <p> - * The running time for all other operations is similar to that of - * {@code TreeMap}. - * <p> - * Instances are not thread-safe until {@link #makeImmutable()} is called, - * after which any modifying operation will result in an - * {@link UnsupportedOperationException}. - * - * @author darick@google.com Darick Tong - */ -// This class is final for all intents and purposes because the constructor is -// private. However, the FieldDescriptor-specific logic is encapsulated in -// a subclass to aid testability of the core logic. -class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> { - - /** - * Creates a new instance for mapping FieldDescriptors to their values. - * The {@link #makeImmutable()} implementation will convert the List values - * of any repeated fields to unmodifiable lists. - * - * @param arraySize The size of the entry array containing the - * lexicographically smallest mappings. - */ - static <FieldDescriptorType extends - FieldSet.FieldDescriptorLite<FieldDescriptorType>> - SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) { - return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) { - @Override - @SuppressWarnings("unchecked") - public void makeImmutable() { - if (!isImmutable()) { - for (int i = 0; i < getNumArrayEntries(); i++) { - final Map.Entry<FieldDescriptorType, Object> entry = - getArrayEntryAt(i); - if (entry.getKey().isRepeated()) { - final List value = (List) entry.getValue(); - entry.setValue(Collections.unmodifiableList(value)); - } - } - for (Map.Entry<FieldDescriptorType, Object> entry : - getOverflowEntries()) { - if (entry.getKey().isRepeated()) { - final List value = (List) entry.getValue(); - entry.setValue(Collections.unmodifiableList(value)); - } - } - } - super.makeImmutable(); - } - }; - } - - /** - * Creates a new instance for testing. - * - * @param arraySize The size of the entry array containing the - * lexicographically smallest mappings. - */ - static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest( - int arraySize) { - return new SmallSortedMap<K, V>(arraySize); - } - - private final int maxArraySize; - // The "entry array" is actually a List because generic arrays are not - // allowed. ArrayList also nicely handles the entry shifting on inserts and - // removes. - private List<Entry> entryList; - private Map<K, V> overflowEntries; - private boolean isImmutable; - // The EntrySet is a stateless view of the Map. It's initialized the first - // time it is requested and reused henceforth. - private volatile EntrySet lazyEntrySet; - - /** - * @code arraySize Size of the array in which the lexicographically smallest - * mappings are stored. (i.e. the {@code k} referred to in the class - * documentation). - */ - private SmallSortedMap(int arraySize) { - this.maxArraySize = arraySize; - this.entryList = Collections.emptyList(); - this.overflowEntries = Collections.emptyMap(); - } - - /** Make this map immutable from this point forward. */ - public void makeImmutable() { - if (!isImmutable) { - // Note: There's no need to wrap the entryList in an unmodifiableList - // because none of the list's accessors are exposed. The iterator() of - // overflowEntries, on the other hand, is exposed so it must be made - // unmodifiable. - overflowEntries = overflowEntries.isEmpty() ? - Collections.<K, V>emptyMap() : - Collections.unmodifiableMap(overflowEntries); - isImmutable = true; - } - } - - /** @return Whether {@link #makeImmutable()} has been called. */ - public boolean isImmutable() { - return isImmutable; - } - - /** @return The number of entries in the entry array. */ - public int getNumArrayEntries() { - return entryList.size(); - } - - /** @return The array entry at the given {@code index}. */ - public Map.Entry<K, V> getArrayEntryAt(int index) { - return entryList.get(index); - } - - /** @return There number of overflow entries. */ - public int getNumOverflowEntries() { - return overflowEntries.size(); - } - - /** @return An iterable over the overflow entries. */ - public Iterable<Map.Entry<K, V>> getOverflowEntries() { - return overflowEntries.isEmpty() ? - EmptySet.<Map.Entry<K, V>>iterable() : - overflowEntries.entrySet(); - } - - @Override - public int size() { - return entryList.size() + overflowEntries.size(); - } - - /** - * The implementation throws a {@code ClassCastException} if o is not an - * object of type {@code K}. - * - * {@inheritDoc} - */ - @Override - public boolean containsKey(Object o) { - @SuppressWarnings("unchecked") - final K key = (K) o; - return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key); - } - - /** - * The implementation throws a {@code ClassCastException} if o is not an - * object of type {@code K}. - * - * {@inheritDoc} - */ - @Override - public V get(Object o) { - @SuppressWarnings("unchecked") - final K key = (K) o; - final int index = binarySearchInArray(key); - if (index >= 0) { - return entryList.get(index).getValue(); - } - return overflowEntries.get(key); - } - - @Override - public V put(K key, V value) { - checkMutable(); - final int index = binarySearchInArray(key); - if (index >= 0) { - // Replace existing array entry. - return entryList.get(index).setValue(value); - } - ensureEntryArrayMutable(); - final int insertionPoint = -(index + 1); - if (insertionPoint >= maxArraySize) { - // Put directly in overflow. - return getOverflowEntriesMutable().put(key, value); - } - // Insert new Entry in array. - if (entryList.size() == maxArraySize) { - // Shift the last array entry into overflow. - final Entry lastEntryInArray = entryList.remove(maxArraySize - 1); - getOverflowEntriesMutable().put(lastEntryInArray.getKey(), - lastEntryInArray.getValue()); - } - entryList.add(insertionPoint, new Entry(key, value)); - return null; - } - - @Override - public void clear() { - checkMutable(); - if (!entryList.isEmpty()) { - entryList.clear(); - } - if (!overflowEntries.isEmpty()) { - overflowEntries.clear(); - } - } - - /** - * The implementation throws a {@code ClassCastException} if o is not an - * object of type {@code K}. - * - * {@inheritDoc} - */ - @Override - public V remove(Object o) { - checkMutable(); - @SuppressWarnings("unchecked") - final K key = (K) o; - final int index = binarySearchInArray(key); - if (index >= 0) { - return removeArrayEntryAt(index); - } - // overflowEntries might be Collections.unmodifiableMap(), so only - // call remove() if it is non-empty. - if (overflowEntries.isEmpty()) { - return null; - } else { - return overflowEntries.remove(key); - } - } - - private V removeArrayEntryAt(int index) { - checkMutable(); - final V removed = entryList.remove(index).getValue(); - if (!overflowEntries.isEmpty()) { - // Shift the first entry in the overflow to be the last entry in the - // array. - final Iterator<Map.Entry<K, V>> iterator = - getOverflowEntriesMutable().entrySet().iterator(); - entryList.add(new Entry(iterator.next())); - iterator.remove(); - } - return removed; - } - - /** - * @param key The key to find in the entry array. - * @return The returned integer position follows the same semantics as the - * value returned by {@link java.util.Arrays#binarySearch()}. - */ - private int binarySearchInArray(K key) { - int left = 0; - int right = entryList.size() - 1; - - // Optimization: For the common case in which entries are added in - // ascending tag order, check the largest element in the array before - // doing a full binary search. - if (right >= 0) { - int cmp = key.compareTo(entryList.get(right).getKey()); - if (cmp > 0) { - return -(right + 2); // Insert point is after "right". - } else if (cmp == 0) { - return right; - } - } - - while (left <= right) { - int mid = (left + right) / 2; - int cmp = key.compareTo(entryList.get(mid).getKey()); - if (cmp < 0) { - right = mid - 1; - } else if (cmp > 0) { - left = mid + 1; - } else { - return mid; - } - } - return -(left + 1); - } - - /** - * Similar to the AbstractMap implementation of {@code keySet()} and - * {@code values()}, the entry set is created the first time this method is - * called, and returned in response to all subsequent calls. - * - * {@inheritDoc} - */ - @Override - public Set<Map.Entry<K, V>> entrySet() { - if (lazyEntrySet == null) { - lazyEntrySet = new EntrySet(); - } - return lazyEntrySet; - } - - /** - * @throws UnsupportedOperationException if {@link #makeImmutable()} has - * has been called. - */ - private void checkMutable() { - if (isImmutable) { - throw new UnsupportedOperationException(); - } - } - - /** - * @return a {@link SortedMap} to which overflow entries mappings can be - * added or removed. - * @throws UnsupportedOperationException if {@link #makeImmutable()} has been - * called. - */ - @SuppressWarnings("unchecked") - private SortedMap<K, V> getOverflowEntriesMutable() { - checkMutable(); - if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) { - overflowEntries = new TreeMap<K, V>(); - } - return (SortedMap<K, V>) overflowEntries; - } - - /** - * Lazily creates the entry list. Any code that adds to the list must first - * call this method. - */ - private void ensureEntryArrayMutable() { - checkMutable(); - if (entryList.isEmpty() && !(entryList instanceof ArrayList)) { - entryList = new ArrayList<Entry>(maxArraySize); - } - } - - /** - * Entry implementation that implements Comparable in order to support - * binary search within the entry array. Also checks mutability in - * {@link #setValue()}. - */ - private class Entry implements Map.Entry<K, V>, Comparable<Entry> { - - private final K key; - private V value; - - Entry(Map.Entry<K, V> copy) { - this(copy.getKey(), copy.getValue()); - } - - Entry(K key, V value) { - this.key = key; - this.value = value; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public K getKey() { - return key; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public V getValue() { - return value; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public int compareTo(Entry other) { - return getKey().compareTo(other.getKey()); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public V setValue(V newValue) { - checkMutable(); - final V oldValue = this.value; - this.value = newValue; - return oldValue; - } - - @Override - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof Map.Entry)) { - return false; - } - @SuppressWarnings("unchecked") - Map.Entry<?, ?> other = (Map.Entry<?, ?>) o; - return equals(key, other.getKey()) && equals(value, other.getValue()); - } - - @Override - public int hashCode() { - return (key == null ? 0 : key.hashCode()) ^ - (value == null ? 0 : value.hashCode()); - } - - @Override - public String toString() { - return key + "=" + value; - } - - /** equals() that handles null values. */ - private boolean equals(Object o1, Object o2) { - return o1 == null ? o2 == null : o1.equals(o2); - } - } - - /** - * Stateless view of the entries in the field map. - */ - private class EntrySet extends AbstractSet<Map.Entry<K, V>> { - - @Override - public Iterator<Map.Entry<K, V>> iterator() { - return new EntryIterator(); - } - - @Override - public int size() { - return SmallSortedMap.this.size(); - } - - /** - * Throws a {@link ClassCastException} if o is not of the expected type. - * - * {@inheritDoc} - */ - @Override - public boolean contains(Object o) { - @SuppressWarnings("unchecked") - final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; - final V existing = get(entry.getKey()); - final V value = entry.getValue(); - return existing == value || - (existing != null && existing.equals(value)); - } - - @Override - public boolean add(Map.Entry<K, V> entry) { - if (!contains(entry)) { - put(entry.getKey(), entry.getValue()); - return true; - } - return false; - } - - /** - * Throws a {@link ClassCastException} if o is not of the expected type. - * - * {@inheritDoc} - */ - @Override - public boolean remove(Object o) { - @SuppressWarnings("unchecked") - final Map.Entry<K, V> entry = (Map.Entry<K, V>) o; - if (contains(entry)) { - SmallSortedMap.this.remove(entry.getKey()); - return true; - } - return false; - } - - @Override - public void clear() { - SmallSortedMap.this.clear(); - } - } - - /** - * Iterator implementation that switches from the entry array to the overflow - * entries appropriately. - */ - private class EntryIterator implements Iterator<Map.Entry<K, V>> { - - private int pos = -1; - private boolean nextCalledBeforeRemove; - private Iterator<Map.Entry<K, V>> lazyOverflowIterator; - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasNext() { - return (pos + 1) < entryList.size() || - getOverflowIterator().hasNext(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Map.Entry<K, V> next() { - nextCalledBeforeRemove = true; - // Always increment pos so that we know whether the last returned value - // was from the array or from overflow. - if (++pos < entryList.size()) { - return entryList.get(pos); - } - return getOverflowIterator().next(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void remove() { - if (!nextCalledBeforeRemove) { - throw new IllegalStateException("remove() was called before next()"); - } - nextCalledBeforeRemove = false; - checkMutable(); - - if (pos < entryList.size()) { - removeArrayEntryAt(pos--); - } else { - getOverflowIterator().remove(); - } - } - - /** - * It is important to create the overflow iterator only after the array - * entries have been iterated over because the overflow entry set changes - * when the client calls remove() on the array entries, which invalidates - * any existing iterators. - */ - private Iterator<Map.Entry<K, V>> getOverflowIterator() { - if (lazyOverflowIterator == null) { - lazyOverflowIterator = overflowEntries.entrySet().iterator(); - } - return lazyOverflowIterator; - } - } - - /** - * Helper class that holds immutable instances of an Iterable/Iterator that - * we return when the overflow entries is empty. This eliminates the creation - * of an Iterator object when there is nothing to iterate over. - */ - private static class EmptySet { - - private static final Iterator<Object> ITERATOR = new Iterator<Object>() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasNext() { - return false; - } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Object next() { - throw new NoSuchElementException(); - } - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void remove() { - throw new UnsupportedOperationException(); - } - }; - - private static final Iterable<Object> ITERABLE = new Iterable<Object>() { - //@Override (Java 1.6 override semantics, but we must support 1.5) - public Iterator<Object> iterator() { - return ITERATOR; - } - }; - - @SuppressWarnings("unchecked") - static <T> Iterable<T> iterable() { - return (Iterable<T>) ITERABLE; - } - } -} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 57d0ca6..cb23f0c 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -31,119 +31,63 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.EnumDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; -import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; -import java.math.BigInteger; import java.nio.CharBuffer; +import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * Provide text parsing and formatting support for proto2 instances. + * Provide ascii text parsing and formatting support for proto2 instances. * The implementation largely follows google/protobuf/text_format.cc. * * @author wenboz@google.com Wenbo Zhu * @author kenton@google.com Kenton Varda */ public final class TextFormat { - private TextFormat() {} - - private static final Logger logger = - Logger.getLogger(TextFormat.class.getName()); - - private static final Printer DEFAULT_PRINTER = new Printer(); - private static final Printer SINGLE_LINE_PRINTER = - (new Printer()).setSingleLineMode(true); - private static final Printer UNICODE_PRINTER = - (new Printer()).setEscapeNonAscii(false); + private TextFormat() { + } /** * Outputs a textual representation of the Protocol Message supplied into * the parameter output. (This representation is the new version of the * classic "ProtocolPrinter" output from the original Protocol Buffer system) */ - public static void print( - final MessageOrBuilder message, final Appendable output) - throws IOException { - DEFAULT_PRINTER.print(message, new TextGenerator(output)); + public static void print(final Message message, final Appendable output) + throws IOException { + final TextGenerator generator = new TextGenerator(output); + print(message, generator); } /** Outputs a textual representation of {@code fields} to {@code output}. */ public static void print(final UnknownFieldSet fields, final Appendable output) throws IOException { - DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output)); - } - - /** - * Same as {@code print()}, except that non-ASCII characters are not - * escaped. - */ - public static void printUnicode( - final MessageOrBuilder message, final Appendable output) - throws IOException { - UNICODE_PRINTER.print(message, new TextGenerator(output)); - } - - /** - * Same as {@code print()}, except that non-ASCII characters are not - * escaped. - */ - public static void printUnicode(final UnknownFieldSet fields, - final Appendable output) - throws IOException { - UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(output)); - } - - /** - * Generates a human readable form of this message, useful for debugging and - * other purposes, with no newline characters. - */ - public static String shortDebugString(final MessageOrBuilder message) { - try { - final StringBuilder sb = new StringBuilder(); - SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb)); - // Single line mode currently might have an extra space at the end. - return sb.toString().trim(); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - /** - * Generates a human readable form of the unknown fields, useful for debugging - * and other purposes, with no newline characters. - */ - public static String shortDebugString(final UnknownFieldSet fields) { - try { - final StringBuilder sb = new StringBuilder(); - SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb)); - // Single line mode currently might have an extra space at the end. - return sb.toString().trim(); - } catch (IOException e) { - throw new IllegalStateException(e); - } + final TextGenerator generator = new TextGenerator(output); + printUnknownFields(fields, generator); } /** * Like {@code print()}, but writes directly to a {@code String} and * returns it. */ - public static String printToString(final MessageOrBuilder message) { + public static String printToString(final Message message) { try { final StringBuilder text = new StringBuilder(); print(message, text); return text.toString(); } catch (IOException e) { - throw new IllegalStateException(e); + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); } } @@ -157,43 +101,28 @@ public final class TextFormat { print(fields, text); return text.toString(); } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - /** - * Same as {@code printToString()}, except that non-ASCII characters - * in string type fields are not escaped in backslash+octals. - */ - public static String printToUnicodeString(final MessageOrBuilder message) { - try { - final StringBuilder text = new StringBuilder(); - UNICODE_PRINTER.print(message, new TextGenerator(text)); - return text.toString(); - } catch (IOException e) { - throw new IllegalStateException(e); + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); } } - /** - * Same as {@code printToString()}, except that non-ASCII characters - * in string type fields are not escaped in backslash+octals. - */ - public static String printToUnicodeString(final UnknownFieldSet fields) { - try { - final StringBuilder text = new StringBuilder(); - UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(text)); - return text.toString(); - } catch (IOException e) { - throw new IllegalStateException(e); + private static void print(final Message message, + final TextGenerator generator) + throws IOException { + for (final Map.Entry<FieldDescriptor, Object> field : + message.getAllFields().entrySet()) { + printField(field.getKey(), field.getValue(), generator); } + printUnknownFields(message.getUnknownFields(), generator); } public static void printField(final FieldDescriptor field, final Object value, final Appendable output) throws IOException { - DEFAULT_PRINTER.printField(field, value, new TextGenerator(output)); + final TextGenerator generator = new TextGenerator(output); + printField(field, value, generator); } public static String printFieldToString(final FieldDescriptor field, @@ -203,298 +132,173 @@ public final class TextFormat { printField(field, value, text); return text.toString(); } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - /** - * Outputs a textual representation of the value of given field value. - * - * @param field the descriptor of the field - * @param value the value of the field - * @param output the output to which to append the formatted value - * @throws ClassCastException if the value is not appropriate for the - * given field descriptor - * @throws IOException if there is an exception writing to the output - */ - public static void printFieldValue(final FieldDescriptor field, - final Object value, - final Appendable output) - throws IOException { - DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output)); - } - - /** - * Outputs a textual representation of the value of an unknown field. - * - * @param tag the field's tag number - * @param value the value of the field - * @param output the output to which to append the formatted value - * @throws ClassCastException if the value is not appropriate for the - * given field descriptor - * @throws IOException if there is an exception writing to the output - */ - public static void printUnknownFieldValue(final int tag, - final Object value, - final Appendable output) - throws IOException { - printUnknownFieldValue(tag, value, new TextGenerator(output)); - } - - private static void printUnknownFieldValue(final int tag, - final Object value, - final TextGenerator generator) - throws IOException { - switch (WireFormat.getTagWireType(tag)) { - case WireFormat.WIRETYPE_VARINT: - generator.print(unsignedToString((Long) value)); - break; - case WireFormat.WIRETYPE_FIXED32: - generator.print( - String.format((Locale) null, "0x%08x", (Integer) value)); - break; - case WireFormat.WIRETYPE_FIXED64: - generator.print(String.format((Locale) null, "0x%016x", (Long) value)); - break; - case WireFormat.WIRETYPE_LENGTH_DELIMITED: - generator.print("\""); - generator.print(escapeBytes((ByteString) value)); - generator.print("\""); - break; - case WireFormat.WIRETYPE_START_GROUP: - DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator); - break; - default: - throw new IllegalArgumentException("Bad tag: " + tag); + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); } } - /** Helper class for converting protobufs to text. */ - private static final class Printer { - /** Whether to omit newlines from the output. */ - boolean singleLineMode = false; - - /** Whether to escape non ASCII characters with backslash and octal. */ - boolean escapeNonAscii = true; - - private Printer() {} - - /** Setter of singleLineMode */ - private Printer setSingleLineMode(boolean singleLineMode) { - this.singleLineMode = singleLineMode; - return this; - } - - /** Setter of escapeNonAscii */ - private Printer setEscapeNonAscii(boolean escapeNonAscii) { - this.escapeNonAscii = escapeNonAscii; - return this; - } - - private void print( - final MessageOrBuilder message, final TextGenerator generator) - throws IOException { - for (Map.Entry<FieldDescriptor, Object> field - : message.getAllFields().entrySet()) { - printField(field.getKey(), field.getValue(), generator); - } - printUnknownFields(message.getUnknownFields(), generator); - } - - private void printField(final FieldDescriptor field, final Object value, - final TextGenerator generator) throws IOException { - if (field.isRepeated()) { - // Repeated field. Print each element. - for (Object element : (List<?>) value) { - printSingleField(field, element, generator); - } - } else { - printSingleField(field, value, generator); + private static void printField(final FieldDescriptor field, + final Object value, + final TextGenerator generator) + throws IOException { + if (field.isRepeated()) { + // Repeated field. Print each element. + for (final Object element : (List) value) { + printSingleField(field, element, generator); } + } else { + printSingleField(field, value, generator); } + } - private void printSingleField(final FieldDescriptor field, - final Object value, - final TextGenerator generator) - throws IOException { - if (field.isExtension()) { - generator.print("["); - // We special-case MessageSet elements for compatibility with proto1. - if (field.getContainingType().getOptions().getMessageSetWireFormat() - && (field.getType() == FieldDescriptor.Type.MESSAGE) - && (field.isOptional()) - // object equality - && (field.getExtensionScope() == field.getMessageType())) { - generator.print(field.getMessageType().getFullName()); - } else { - generator.print(field.getFullName()); - } - generator.print("]"); + private static void printSingleField(final FieldDescriptor field, + final Object value, + final TextGenerator generator) + throws IOException { + if (field.isExtension()) { + generator.print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field.getContainingType().getOptions().getMessageSetWireFormat() + && (field.getType() == FieldDescriptor.Type.MESSAGE) + && (field.isOptional()) + // object equality + && (field.getExtensionScope() == field.getMessageType())) { + generator.print(field.getMessageType().getFullName()); } else { - if (field.getType() == FieldDescriptor.Type.GROUP) { - // Groups must be serialized with their original capitalization. - generator.print(field.getMessageType().getName()); - } else { - generator.print(field.getName()); - } + generator.print(field.getFullName()); } - - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (singleLineMode) { - generator.print(" { "); - } else { - generator.print(" {\n"); - generator.indent(); - } + generator.print("]"); + } else { + if (field.getType() == FieldDescriptor.Type.GROUP) { + // Groups must be serialized with their original capitalization. + generator.print(field.getMessageType().getName()); } else { - generator.print(": "); + generator.print(field.getName()); } + } - printFieldValue(field, value, generator); - - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (singleLineMode) { - generator.print("} "); - } else { - generator.outdent(); - generator.print("}\n"); - } - } else { - if (singleLineMode) { - generator.print(" "); - } else { - generator.print("\n"); - } - } + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.print(" {\n"); + generator.indent(); + } else { + generator.print(": "); } - private void printFieldValue(final FieldDescriptor field, - final Object value, - final TextGenerator generator) - throws IOException { - switch (field.getType()) { - case INT32: - case SINT32: - case SFIXED32: - generator.print(((Integer) value).toString()); - break; + printFieldValue(field, value, generator); - case INT64: - case SINT64: - case SFIXED64: - generator.print(((Long) value).toString()); - break; + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.outdent(); + generator.print("}"); + } + generator.print("\n"); + } - case BOOL: - generator.print(((Boolean) value).toString()); - break; + private static void printFieldValue(final FieldDescriptor field, + final Object value, + final TextGenerator generator) + throws IOException { + switch (field.getType()) { + case INT32: + case INT64: + case SINT32: + case SINT64: + case SFIXED32: + case SFIXED64: + case FLOAT: + case DOUBLE: + case BOOL: + // Good old toString() does what we want for these types. + generator.print(value.toString()); + break; - case FLOAT: - generator.print(((Float) value).toString()); - break; + case UINT32: + case FIXED32: + generator.print(unsignedToString((Integer) value)); + break; - case DOUBLE: - generator.print(((Double) value).toString()); - break; + case UINT64: + case FIXED64: + generator.print(unsignedToString((Long) value)); + break; - case UINT32: - case FIXED32: - generator.print(unsignedToString((Integer) value)); - break; + case STRING: + generator.print("\""); + generator.print(escapeText((String) value)); + generator.print("\""); + break; - case UINT64: - case FIXED64: - generator.print(unsignedToString((Long) value)); - break; + case BYTES: + generator.print("\""); + generator.print(escapeBytes((ByteString) value)); + generator.print("\""); + break; - case STRING: - generator.print("\""); - generator.print(escapeNonAscii ? - escapeText((String) value) : - escapeDoubleQuotesAndBackslashes((String) value)); - generator.print("\""); - break; + case ENUM: + generator.print(((EnumValueDescriptor) value).getName()); + break; - case BYTES: - generator.print("\""); - if (value instanceof ByteString) { - generator.print(escapeBytes((ByteString) value)); - } else { - generator.print(escapeBytes((byte[]) value)); - } - generator.print("\""); - break; + case MESSAGE: + case GROUP: + print((Message) value, generator); + break; + } + } - case ENUM: - generator.print(((EnumValueDescriptor) value).getName()); - break; + private static void printUnknownFields(final UnknownFieldSet unknownFields, + final TextGenerator generator) + throws IOException { + for (final Map.Entry<Integer, UnknownFieldSet.Field> entry : + unknownFields.asMap().entrySet()) { + final String prefix = entry.getKey().toString() + ": "; + final UnknownFieldSet.Field field = entry.getValue(); - case MESSAGE: - case GROUP: - print((Message) value, generator); - break; + for (final long value : field.getVarintList()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(unsignedToString(value)); + generator.print("\n"); } - } - - private void printUnknownFields(final UnknownFieldSet unknownFields, - final TextGenerator generator) - throws IOException { - for (Map.Entry<Integer, UnknownFieldSet.Field> entry : - unknownFields.asMap().entrySet()) { - final int number = entry.getKey(); - final UnknownFieldSet.Field field = entry.getValue(); - printUnknownField(number, WireFormat.WIRETYPE_VARINT, - field.getVarintList(), generator); - printUnknownField(number, WireFormat.WIRETYPE_FIXED32, - field.getFixed32List(), generator); - printUnknownField(number, WireFormat.WIRETYPE_FIXED64, - field.getFixed64List(), generator); - printUnknownField(number, WireFormat.WIRETYPE_LENGTH_DELIMITED, - field.getLengthDelimitedList(), generator); - for (final UnknownFieldSet value : field.getGroupList()) { - generator.print(entry.getKey().toString()); - if (singleLineMode) { - generator.print(" { "); - } else { - generator.print(" {\n"); - generator.indent(); - } - printUnknownFields(value, generator); - if (singleLineMode) { - generator.print("} "); - } else { - generator.outdent(); - generator.print("}\n"); - } - } + for (final int value : field.getFixed32List()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(String.format((Locale) null, "0x%08x", value)); + generator.print("\n"); } - } - - private void printUnknownField(final int number, - final int wireType, - final List<?> values, - final TextGenerator generator) - throws IOException { - for (final Object value : values) { - generator.print(String.valueOf(number)); + for (final long value : field.getFixed64List()) { + generator.print(entry.getKey().toString()); generator.print(": "); - printUnknownFieldValue(wireType, value, generator); - generator.print(singleLineMode ? " " : "\n"); + generator.print(String.format((Locale) null, "0x%016x", value)); + generator.print("\n"); + } + for (final ByteString value : field.getLengthDelimitedList()) { + generator.print(entry.getKey().toString()); + generator.print(": \""); + generator.print(escapeBytes(value)); + generator.print("\"\n"); + } + for (final UnknownFieldSet value : field.getGroupList()) { + generator.print(entry.getKey().toString()); + generator.print(" {\n"); + generator.indent(); + printUnknownFields(value, generator); + generator.outdent(); + generator.print("}\n"); } } } /** Convert an unsigned 32-bit integer to a string. */ - public static String unsignedToString(final int value) { + private static String unsignedToString(final int value) { if (value >= 0) { return Integer.toString(value); } else { - return Long.toString(value & 0x00000000FFFFFFFFL); + return Long.toString(((long) value) & 0x00000000FFFFFFFFL); } } /** Convert an unsigned 64-bit integer to a string. */ - public static String unsignedToString(final long value) { + private static String unsignedToString(final long value) { if (value >= 0) { return Long.toString(value); } else { @@ -509,9 +313,9 @@ public final class TextFormat { * An inner class for writing text to the output stream. */ private static final class TextGenerator { - private final Appendable output; - private final StringBuilder indent = new StringBuilder(); + private Appendable output; private boolean atStartOfLine = true; + private final StringBuilder indent = new StringBuilder(); private TextGenerator(final Appendable output) { this.output = output; @@ -548,16 +352,17 @@ public final class TextFormat { for (int i = 0; i < size; i++) { if (text.charAt(i) == '\n') { - write(text.subSequence(pos, i + 1)); + write(text.subSequence(pos, size), i - pos + 1); pos = i + 1; atStartOfLine = true; } } - write(text.subSequence(pos, size)); + write(text.subSequence(pos, size), size - pos); } - private void write(final CharSequence data) throws IOException { - if (data.length() == 0) { + private void write(final CharSequence data, final int size) + throws IOException { + if (size == 0) { return; } if (atStartOfLine) { @@ -616,7 +421,7 @@ public final class TextFormat { private int previousLine = 0; private int previousColumn = 0; - // We use possessive quantifiers (*+ and ++) because otherwise the Java + // We use possesive quantifiers (*+ and ++) because otherwise the Java // regex matcher has stack overflows on large inputs. private static final Pattern WHITESPACE = Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); @@ -734,14 +539,6 @@ public final class TextFormat { } /** - * Returns {@code true} if the current token's text is equal to that - * specified. - */ - public boolean lookingAt(String text) { - return currentToken.equals(text); - } - - /** * If the next token is an identifier, consume it and return its value. * Otherwise, throw a {@link ParseException}. */ @@ -754,8 +551,7 @@ public final class TextFormat { (c == '_') || (c == '.')) { // OK } else { - throw parseException( - "Expected identifier. Found '" + currentToken + "'"); + throw parseException("Expected identifier."); } } @@ -765,19 +561,6 @@ public final class TextFormat { } /** - * If the next token is an identifier, consume it and return {@code true}. - * Otherwise, return {@code false} without doing anything. - */ - public boolean tryConsumeIdentifier() { - try { - consumeIdentifier(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a 32-bit signed integer, consume it and return its * value. Otherwise, throw a {@link ParseException}. */ @@ -820,19 +603,6 @@ public final class TextFormat { } /** - * If the next token is a 64-bit signed integer, consume it and return - * {@code true}. Otherwise, return {@code false} without doing anything. - */ - public boolean tryConsumeInt64() { - try { - consumeInt64(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a 64-bit unsigned integer, consume it and return its * value. Otherwise, throw a {@link ParseException}. */ @@ -847,19 +617,6 @@ public final class TextFormat { } /** - * If the next token is a 64-bit unsigned integer, consume it and return - * {@code true}. Otherwise, return {@code false} without doing anything. - */ - public boolean tryConsumeUInt64() { - try { - consumeUInt64(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a double, consume it and return its value. * Otherwise, throw a {@link ParseException}. */ @@ -885,19 +642,6 @@ public final class TextFormat { } /** - * If the next token is a double, consume it and return {@code true}. - * Otherwise, return {@code false} without doing anything. - */ - public boolean tryConsumeDouble() { - try { - consumeDouble(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a float, consume it and return its value. * Otherwise, throw a {@link ParseException}. */ @@ -923,31 +667,14 @@ public final class TextFormat { } /** - * If the next token is a float, consume it and return {@code true}. - * Otherwise, return {@code false} without doing anything. - */ - public boolean tryConsumeFloat() { - try { - consumeFloat(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a boolean, consume it and return its value. * Otherwise, throw a {@link ParseException}. */ public boolean consumeBoolean() throws ParseException { - if (currentToken.equals("true") || - currentToken.equals("t") || - currentToken.equals("1")) { + if (currentToken.equals("true")) { nextToken(); return true; - } else if (currentToken.equals("false") || - currentToken.equals("f") || - currentToken.equals("0")) { + } else if (currentToken.equals("false")) { nextToken(); return false; } else { @@ -964,19 +691,6 @@ public final class TextFormat { } /** - * If the next token is a string, consume it and return true. Otherwise, - * return false. - */ - public boolean tryConsumeString() { - try { - consumeString(); - return true; - } catch (ParseException e) { - return false; - } - } - - /** * If the next token is a string, consume it, unescape it as a * {@link ByteString}, and return it. Otherwise, throw a * {@link ParseException}. @@ -996,8 +710,7 @@ public final class TextFormat { * multiple adjacent tokens which are automatically concatenated, like in * C or Python. */ - private void consumeByteString(List<ByteString> list) - throws ParseException { + private void consumeByteString(List<ByteString> list) throws ParseException { final char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; if (quote != '\"' && quote != '\'') { @@ -1027,7 +740,7 @@ public final class TextFormat { public ParseException parseException(final String description) { // Note: People generally prefer one-based line and column numbers. return new ParseException( - line + 1, column + 1, description); + (line + 1) + ":" + (column + 1) + ": " + description); } /** @@ -1038,7 +751,7 @@ public final class TextFormat { final String description) { // Note: People generally prefer one-based line and column numbers. return new ParseException( - previousLine + 1, previousColumn + 1, description); + (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); } /** @@ -1063,58 +776,11 @@ public final class TextFormat { public static class ParseException extends IOException { private static final long serialVersionUID = 3196188060225107702L; - private final int line; - private final int column; - - /** Create a new instance, with -1 as the line and column numbers. */ public ParseException(final String message) { - this(-1, -1, message); - } - - /** - * Create a new instance - * - * @param line the line number where the parse error occurred, - * using 1-offset. - * @param column the column number where the parser error occurred, - * using 1-offset. - */ - public ParseException(final int line, final int column, - final String message) { - super(Integer.toString(line) + ":" + column + ": " + message); - this.line = line; - this.column = column; - } - - /** - * Return the line where the parse exception occurred, or -1 when - * none is provided. The value is specified as 1-offset, so the first - * line is line 1. - */ - public int getLine() { - return line; - } - - /** - * Return the column where the parse exception occurred, or -1 when - * none is provided. The value is specified as 1-offset, so the first - * line is line 1. - */ - public int getColumn() { - return column; + super(message); } } - private static final Parser PARSER = Parser.newBuilder().build(); - - /** - * Return a {@link Parser} instance which can parse text-format - * messages. The returned instance is thread-safe. - */ - public static Parser getParser() { - return PARSER; - } - /** * Parse a text-format message from {@code input} and merge the contents * into {@code builder}. @@ -1122,7 +788,7 @@ public final class TextFormat { public static void merge(final Readable input, final Message.Builder builder) throws IOException { - PARSER.merge(input, builder); + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); } /** @@ -1132,7 +798,7 @@ public final class TextFormat { public static void merge(final CharSequence input, final Message.Builder builder) throws ParseException { - PARSER.merge(input, builder); + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); } /** @@ -1144,9 +810,35 @@ public final class TextFormat { final ExtensionRegistry extensionRegistry, final Message.Builder builder) throws IOException { - PARSER.merge(input, extensionRegistry, builder); + // Read the entire input to a String then parse that. + + // If StreamTokenizer were not quite so crippled, or if there were a kind + // of Reader that could read in chunks that match some particular regex, + // or if we wanted to write a custom Reader to tokenize our stream, then + // we would not have to read to one big String. Alas, none of these is + // the case. Oh well. + + merge(toStringBuilder(input), extensionRegistry, builder); } + private static final int BUFFER_SIZE = 4096; + + // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) + // overhead is worthwhile + private static StringBuilder toStringBuilder(final Readable input) + throws IOException { + final StringBuilder text = new StringBuilder(); + final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); + while (true) { + final int n = input.read(buffer); + if (n == -1) { + break; + } + buffer.flip(); + text.append(buffer, 0, n); + } + return text; + } /** * Parse a text-format message from {@code input} and merge the contents @@ -1157,466 +849,187 @@ public final class TextFormat { final ExtensionRegistry extensionRegistry, final Message.Builder builder) throws ParseException { - PARSER.merge(input, extensionRegistry, builder); - } + final Tokenizer tokenizer = new Tokenizer(input); + while (!tokenizer.atEnd()) { + mergeField(tokenizer, extensionRegistry, builder); + } + } /** - * Parser for text-format proto2 instances. This class is thread-safe. - * The implementation largely follows google/protobuf/text_format.cc. - * - * <p>Use {@link TextFormat#getParser()} to obtain the default parser, or - * {@link Builder} to control the parser behavior. + * Parse a single field from {@code tokenizer} and merge it into + * {@code builder}. */ - public static class Parser { - /** - * Determines if repeated values for non-repeated fields and - * oneofs are permitted. For example, given required/optional field "foo" - * and a oneof containing "baz" and "qux": - * <li> - * <ul>"foo: 1 foo: 2" - * <ul>"baz: 1 qux: 2" - * <ul>merging "foo: 2" into a proto in which foo is already set, or - * <ul>merging "qux: 2" into a proto in which baz is already set. - * </li> - */ - public enum SingularOverwritePolicy { - /** The last value is retained. */ - ALLOW_SINGULAR_OVERWRITES, - /** An error is issued. */ - FORBID_SINGULAR_OVERWRITES - } - - private final boolean allowUnknownFields; - private final SingularOverwritePolicy singularOverwritePolicy; - - private Parser(boolean allowUnknownFields, - SingularOverwritePolicy singularOverwritePolicy) { - this.allowUnknownFields = allowUnknownFields; - this.singularOverwritePolicy = singularOverwritePolicy; - } - - /** - * Returns a new instance of {@link Builder}. - */ - public static Builder newBuilder() { - return new Builder(); - } + private static void mergeField(final Tokenizer tokenizer, + final ExtensionRegistry extensionRegistry, + final Message.Builder builder) + throws ParseException { + FieldDescriptor field; + final Descriptor type = builder.getDescriptorForType(); + ExtensionRegistry.ExtensionInfo extension = null; - /** - * Builder that can be used to obtain new instances of {@link Parser}. - */ - public static class Builder { - private boolean allowUnknownFields = false; - private SingularOverwritePolicy singularOverwritePolicy = - SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES; - - /** - * Sets parser behavior when a non-repeated field appears more than once. - */ - public Builder setSingularOverwritePolicy(SingularOverwritePolicy p) { - this.singularOverwritePolicy = p; - return this; + if (tokenizer.tryConsume("[")) { + // An extension. + final StringBuilder name = + new StringBuilder(tokenizer.consumeIdentifier()); + while (tokenizer.tryConsume(".")) { + name.append('.'); + name.append(tokenizer.consumeIdentifier()); } - public Parser build() { - return new Parser(allowUnknownFields, singularOverwritePolicy); - } - } - - /** - * Parse a text-format message from {@code input} and merge the contents - * into {@code builder}. - */ - public void merge(final Readable input, - final Message.Builder builder) - throws IOException { - merge(input, ExtensionRegistry.getEmptyRegistry(), builder); - } - - /** - * Parse a text-format message from {@code input} and merge the contents - * into {@code builder}. - */ - public void merge(final CharSequence input, - final Message.Builder builder) - throws ParseException { - merge(input, ExtensionRegistry.getEmptyRegistry(), builder); - } + extension = extensionRegistry.findExtensionByName(name.toString()); - /** - * Parse a text-format message from {@code input} and merge the contents - * into {@code builder}. Extensions will be recognized if they are - * registered in {@code extensionRegistry}. - */ - public void merge(final Readable input, - final ExtensionRegistry extensionRegistry, - final Message.Builder builder) - throws IOException { - // Read the entire input to a String then parse that. - - // If StreamTokenizer were not quite so crippled, or if there were a kind - // of Reader that could read in chunks that match some particular regex, - // or if we wanted to write a custom Reader to tokenize our stream, then - // we would not have to read to one big String. Alas, none of these is - // the case. Oh well. - - merge(toStringBuilder(input), extensionRegistry, builder); - } + if (extension == null) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" not found in the ExtensionRegistry."); + } else if (extension.descriptor.getContainingType() != type) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" does not extend message type \"" + + type.getFullName() + "\"."); + } + tokenizer.consume("]"); - private static final int BUFFER_SIZE = 4096; + field = extension.descriptor; + } else { + final String name = tokenizer.consumeIdentifier(); + field = type.findFieldByName(name); - // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) - // overhead is worthwhile - private static StringBuilder toStringBuilder(final Readable input) - throws IOException { - final StringBuilder text = new StringBuilder(); - final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); - while (true) { - final int n = input.read(buffer); - if (n == -1) { - break; + // Group names are expected to be capitalized as they appear in the + // .proto file, which actually matches their type names, not their field + // names. + if (field == null) { + // Explicitly specify US locale so that this code does not break when + // executing in Turkey. + final String lowerName = name.toLowerCase(Locale.US); + field = type.findFieldByName(lowerName); + // If the case-insensitive match worked but the field is NOT a group, + if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { + field = null; } - buffer.flip(); - text.append(buffer, 0, n); } - return text; - } + // Again, special-case group names as described above. + if (field != null && field.getType() == FieldDescriptor.Type.GROUP && + !field.getMessageType().getName().equals(name)) { + field = null; + } - /** - * Parse a text-format message from {@code input} and merge the contents - * into {@code builder}. Extensions will be recognized if they are - * registered in {@code extensionRegistry}. - */ - public void merge(final CharSequence input, - final ExtensionRegistry extensionRegistry, - final Message.Builder builder) - throws ParseException { - final Tokenizer tokenizer = new Tokenizer(input); - MessageReflection.BuilderAdapter target = - new MessageReflection.BuilderAdapter(builder); - - while (!tokenizer.atEnd()) { - mergeField(tokenizer, extensionRegistry, target); + if (field == null) { + throw tokenizer.parseExceptionPreviousToken( + "Message type \"" + type.getFullName() + + "\" has no field named \"" + name + "\"."); } } + Object value = null; - /** - * Parse a single field from {@code tokenizer} and merge it into - * {@code builder}. - */ - private void mergeField(final Tokenizer tokenizer, - final ExtensionRegistry extensionRegistry, - final MessageReflection.MergeTarget target) - throws ParseException { - FieldDescriptor field = null; - final Descriptor type = target.getDescriptorForType(); - ExtensionRegistry.ExtensionInfo extension = null; - - if (tokenizer.tryConsume("[")) { - // An extension. - final StringBuilder name = - new StringBuilder(tokenizer.consumeIdentifier()); - while (tokenizer.tryConsume(".")) { - name.append('.'); - name.append(tokenizer.consumeIdentifier()); - } - - extension = target.findExtensionByName( - extensionRegistry, name.toString()); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + tokenizer.tryConsume(":"); // optional - if (extension == null) { - if (!allowUnknownFields) { - throw tokenizer.parseExceptionPreviousToken( - "Extension \"" + name + "\" not found in the ExtensionRegistry."); - } else { - logger.warning( - "Extension \"" + name + "\" not found in the ExtensionRegistry."); - } - } else { - if (extension.descriptor.getContainingType() != type) { - throw tokenizer.parseExceptionPreviousToken( - "Extension \"" + name + "\" does not extend message type \"" + - type.getFullName() + "\"."); - } - field = extension.descriptor; - } - - tokenizer.consume("]"); + final String endToken; + if (tokenizer.tryConsume("<")) { + endToken = ">"; } else { - final String name = tokenizer.consumeIdentifier(); - field = type.findFieldByName(name); - - // Group names are expected to be capitalized as they appear in the - // .proto file, which actually matches their type names, not their field - // names. - if (field == null) { - // Explicitly specify US locale so that this code does not break when - // executing in Turkey. - final String lowerName = name.toLowerCase(Locale.US); - field = type.findFieldByName(lowerName); - // If the case-insensitive match worked but the field is NOT a group, - if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { - field = null; - } - } - // Again, special-case group names as described above. - if (field != null && field.getType() == FieldDescriptor.Type.GROUP && - !field.getMessageType().getName().equals(name)) { - field = null; - } - - if (field == null) { - if (!allowUnknownFields) { - throw tokenizer.parseExceptionPreviousToken( - "Message type \"" + type.getFullName() + - "\" has no field named \"" + name + "\"."); - } else { - logger.warning( - "Message type \"" + type.getFullName() + - "\" has no field named \"" + name + "\"."); - } - } - } - - // Skips unknown fields. - if (field == null) { - // Try to guess the type of this field. - // If this field is not a message, there should be a ":" between the - // field name and the field value and also the field value should not - // start with "{" or "<" which indicates the begining of a message body. - // If there is no ":" or there is a "{" or "<" after ":", this field has - // to be a message or the input is ill-formed. - if (tokenizer.tryConsume(":") && !tokenizer.lookingAt("{") && - !tokenizer.lookingAt("<")) { - skipFieldValue(tokenizer); - } else { - skipFieldMessage(tokenizer); - } - return; + tokenizer.consume("{"); + endToken = "}"; } - // Handle potential ':'. - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - tokenizer.tryConsume(":"); // optional + final Message.Builder subBuilder; + if (extension == null) { + subBuilder = builder.newBuilderForField(field); } else { - tokenizer.consume(":"); // required + subBuilder = extension.defaultInstance.newBuilderForType(); } - // Support specifying repeated field values as a comma-separated list. - // Ex."foo: [1, 2, 3]" - if (field.isRepeated() && tokenizer.tryConsume("[")) { - while (true) { - consumeFieldValue(tokenizer, extensionRegistry, target, field, extension); - if (tokenizer.tryConsume("]")) { - // End of list. - break; - } - tokenizer.consume(","); + + while (!tokenizer.tryConsume(endToken)) { + if (tokenizer.atEnd()) { + throw tokenizer.parseException( + "Expected \"" + endToken + "\"."); } - } else { - consumeFieldValue(tokenizer, extensionRegistry, target, field, extension); + mergeField(tokenizer, extensionRegistry, subBuilder); } - } - /** - * Parse a single field value from {@code tokenizer} and merge it into - * {@code builder}. - */ - private void consumeFieldValue( - final Tokenizer tokenizer, - final ExtensionRegistry extensionRegistry, - final MessageReflection.MergeTarget target, - final FieldDescriptor field, - final ExtensionRegistry.ExtensionInfo extension) - throws ParseException { - Object value = null; - - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - final String endToken; - if (tokenizer.tryConsume("<")) { - endToken = ">"; - } else { - tokenizer.consume("{"); - endToken = "}"; - } + value = subBuilder.build(); - final MessageReflection.MergeTarget subField; - subField = target.newMergeTargetForField(field, - (extension == null) ? null : extension.defaultInstance); + } else { + tokenizer.consume(":"); - while (!tokenizer.tryConsume(endToken)) { - if (tokenizer.atEnd()) { - throw tokenizer.parseException( - "Expected \"" + endToken + "\"."); - } - mergeField(tokenizer, extensionRegistry, subField); - } + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + value = tokenizer.consumeInt32(); + break; - value = subField.finish(); + case INT64: + case SINT64: + case SFIXED64: + value = tokenizer.consumeInt64(); + break; - } else { - switch (field.getType()) { - case INT32: - case SINT32: - case SFIXED32: - value = tokenizer.consumeInt32(); - break; - - case INT64: - case SINT64: - case SFIXED64: - value = tokenizer.consumeInt64(); - break; - - case UINT32: - case FIXED32: - value = tokenizer.consumeUInt32(); - break; - - case UINT64: - case FIXED64: - value = tokenizer.consumeUInt64(); - break; - - case FLOAT: - value = tokenizer.consumeFloat(); - break; - - case DOUBLE: - value = tokenizer.consumeDouble(); - break; - - case BOOL: - value = tokenizer.consumeBoolean(); - break; - - case STRING: - value = tokenizer.consumeString(); - break; - - case BYTES: - value = tokenizer.consumeByteString(); - break; - - case ENUM: - final EnumDescriptor enumType = field.getEnumType(); - - if (tokenizer.lookingAtInteger()) { - final int number = tokenizer.consumeInt32(); - value = enumType.findValueByNumber(number); - if (value == null) { - throw tokenizer.parseExceptionPreviousToken( - "Enum type \"" + enumType.getFullName() + - "\" has no value with number " + number + '.'); - } - } else { - final String id = tokenizer.consumeIdentifier(); - value = enumType.findValueByName(id); - if (value == null) { - throw tokenizer.parseExceptionPreviousToken( - "Enum type \"" + enumType.getFullName() + - "\" has no value named \"" + id + "\"."); - } - } + case UINT32: + case FIXED32: + value = tokenizer.consumeUInt32(); + break; - break; + case UINT64: + case FIXED64: + value = tokenizer.consumeUInt64(); + break; - case MESSAGE: - case GROUP: - throw new RuntimeException("Can't get here."); - } - } + case FLOAT: + value = tokenizer.consumeFloat(); + break; - if (field.isRepeated()) { - target.addRepeatedField(field, value); - } else if ((singularOverwritePolicy - == SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES) - && target.hasField(field)) { - throw tokenizer.parseExceptionPreviousToken("Non-repeated field \"" - + field.getFullName() + "\" cannot be overwritten."); - } else if ((singularOverwritePolicy - == SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES) - && field.getContainingOneof() != null - && target.hasOneof(field.getContainingOneof())) { - Descriptors.OneofDescriptor oneof = field.getContainingOneof(); - throw tokenizer.parseExceptionPreviousToken("Field \"" - + field.getFullName() + "\" is specified along with field \"" - + target.getOneofFieldDescriptor(oneof).getFullName() - + "\", another member of oneof \"" + oneof.getName() + "\"."); - } else { - target.setField(field, value); - } - } + case DOUBLE: + value = tokenizer.consumeDouble(); + break; - /** - * Skips the next field including the field's name and value. - */ - private void skipField(Tokenizer tokenizer) throws ParseException { - if (tokenizer.tryConsume("[")) { - // Extension name. - do { - tokenizer.consumeIdentifier(); - } while (tokenizer.tryConsume(".")); - tokenizer.consume("]"); - } else { - tokenizer.consumeIdentifier(); - } + case BOOL: + value = tokenizer.consumeBoolean(); + break; - // Try to guess the type of this field. - // If this field is not a message, there should be a ":" between the - // field name and the field value and also the field value should not - // start with "{" or "<" which indicates the begining of a message body. - // If there is no ":" or there is a "{" or "<" after ":", this field has - // to be a message or the input is ill-formed. - if (tokenizer.tryConsume(":") && !tokenizer.lookingAt("<") && - !tokenizer.lookingAt("{")) { - skipFieldValue(tokenizer); - } else { - skipFieldMessage(tokenizer); - } - // For historical reasons, fields may optionally be separated by commas or - // semicolons. - if (!tokenizer.tryConsume(";")) { - tokenizer.tryConsume(","); - } - } + case STRING: + value = tokenizer.consumeString(); + break; - /** - * Skips the whole body of a message including the beginning delimeter and - * the ending delimeter. - */ - private void skipFieldMessage(Tokenizer tokenizer) throws ParseException { - final String delimiter; - if (tokenizer.tryConsume("<")) { - delimiter = ">"; - } else { - tokenizer.consume("{"); - delimiter = "}"; - } - while (!tokenizer.lookingAt(">") && !tokenizer.lookingAt("}")) { - skipField(tokenizer); + case BYTES: + value = tokenizer.consumeByteString(); + break; + + case ENUM: + final EnumDescriptor enumType = field.getEnumType(); + + if (tokenizer.lookingAtInteger()) { + final int number = tokenizer.consumeInt32(); + value = enumType.findValueByNumber(number); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value with number " + number + '.'); + } + } else { + final String id = tokenizer.consumeIdentifier(); + value = enumType.findValueByName(id); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value named \"" + id + "\"."); + } + } + + break; + + case MESSAGE: + case GROUP: + throw new RuntimeException("Can't get here."); } - tokenizer.consume(delimiter); } - /** - * Skips a field value. - */ - private void skipFieldValue(Tokenizer tokenizer) throws ParseException { - if (tokenizer.tryConsumeString()) { - while (tokenizer.tryConsumeString()) {} - return; - } - if (!tokenizer.tryConsumeIdentifier() && // includes enum & boolean - !tokenizer.tryConsumeInt64() && // includes int32 - !tokenizer.tryConsumeUInt64() && // includes uint32 - !tokenizer.tryConsumeDouble() && - !tokenizer.tryConsumeFloat()) { - throw tokenizer.parseException( - "Invalid field value: " + tokenizer.currentToken); - } + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); } } @@ -1626,11 +1039,6 @@ public final class TextFormat { // Some of these methods are package-private because Descriptors.java uses // them. - private interface ByteSequence { - int size(); - byte byteAt(int offset); - } - /** * Escapes bytes in the format used in protocol buffer text format, which * is the same as the format used for C string literals. All bytes @@ -1639,7 +1047,7 @@ public final class TextFormat { * which no defined short-hand escape sequence is defined will be escaped * using 3-digit octal sequences. */ - private static String escapeBytes(final ByteSequence input) { + static String escapeBytes(final ByteString input) { final StringBuilder builder = new StringBuilder(input.size()); for (int i = 0; i < input.size(); i++) { final byte b = input.byteAt(i); @@ -1656,9 +1064,6 @@ 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) { builder.append((char) b); } else { @@ -1674,74 +1079,31 @@ public final class TextFormat { } /** - * Escapes bytes in the format used in protocol buffer text format, which - * is the same as the format used for C string literals. All bytes - * that are not printable 7-bit ASCII characters are escaped, as well as - * backslash, single-quote, and double-quote characters. Characters for - * which no defined short-hand escape sequence is defined will be escaped - * using 3-digit octal sequences. - */ - static String escapeBytes(final ByteString input) { - return escapeBytes(new ByteSequence() { - public int size() { - return input.size(); - } - public byte byteAt(int offset) { - return input.byteAt(offset); - } - }); - } - - /** - * Like {@link #escapeBytes(ByteString)}, but used for byte array. - */ - static String escapeBytes(final byte[] input) { - return escapeBytes(new ByteSequence() { - public int size() { - return input.length; - } - public byte byteAt(int offset) { - return input[offset]; - } - }); - } - - /** * Un-escape a byte sequence as escaped using * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(final CharSequence charString) + static ByteString unescapeBytes(final CharSequence input) throws InvalidEscapeSequenceException { - // First convert the Java character sequence to UTF-8 bytes. - ByteString input = ByteString.copyFromUtf8(charString.toString()); - // Then unescape certain byte sequences introduced by ASCII '\\'. The valid - // escapes can all be expressed with ASCII characters, so it is safe to - // operate on bytes here. - // - // Unescaping the input byte array will result in a byte sequence that's no - // longer than the input. That's because each escape sequence is between - // two and four bytes long and stands for a single byte. - final byte[] result = new byte[input.size()]; + final byte[] result = new byte[input.length()]; int pos = 0; - for (int i = 0; i < input.size(); i++) { - byte c = input.byteAt(i); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); if (c == '\\') { - if (i + 1 < input.size()) { + if (i + 1 < input.length()) { ++i; - c = input.byteAt(i); + c = input.charAt(i); if (isOctal(c)) { // Octal escape. int code = digitValue(c); - if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { ++i; - code = code * 8 + digitValue(input.byteAt(i)); + code = code * 8 + digitValue(input.charAt(i)); } - if (i + 1 < input.size() && isOctal(input.byteAt(i + 1))) { + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { ++i; - code = code * 8 + digitValue(input.byteAt(i)); + code = code * 8 + digitValue(input.charAt(i)); } - // TODO: Check that 0 <= code && code <= 0xFF. result[pos++] = (byte)code; } else { switch (c) { @@ -1759,31 +1121,31 @@ public final class TextFormat { case 'x': // hex escape int code = 0; - if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { ++i; - code = digitValue(input.byteAt(i)); + code = digitValue(input.charAt(i)); } else { throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\x' with no digits"); + "Invalid escape sequence: '\\x' with no digits"); } - if (i + 1 < input.size() && isHex(input.byteAt(i + 1))) { + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { ++i; - code = code * 16 + digitValue(input.byteAt(i)); + code = code * 16 + digitValue(input.charAt(i)); } result[pos++] = (byte)code; break; default: throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\" + (char)c + '\''); + "Invalid escape sequence: '\\" + c + '\''); } } } else { throw new InvalidEscapeSequenceException( - "Invalid escape sequence: '\\' at end of string."); + "Invalid escape sequence: '\\' at end of string."); } } else { - result[pos++] = c; + result[pos++] = (byte)c; } } @@ -1812,13 +1174,6 @@ public final class TextFormat { } /** - * Escape double quotes and backslashes in a String for unicode output of a message. - */ - public static String escapeDoubleQuotesAndBackslashes(final String input) { - return input.replace("\\", "\\\\").replace("\"", "\\\""); - } - - /** * Un-escape a text string as escaped using {@link #escapeText(String)}. * Two-digit hex escapes (starting with "\x") are also recognized. */ @@ -1828,12 +1183,12 @@ public final class TextFormat { } /** Is this an octal digit? */ - private static boolean isOctal(final byte c) { + private static boolean isOctal(final char c) { return '0' <= c && c <= '7'; } /** Is this a hex digit? */ - private static boolean isHex(final byte c) { + private static boolean isHex(final char c) { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); @@ -1844,7 +1199,7 @@ public final class TextFormat { * numeric value. This is like {@code Character.digit()} but we don't accept * non-ASCII digits. */ - private static int digitValue(final byte c) { + private static int digitValue(final char c) { if ('0' <= c && c <= '9') { return c - '0'; } else if ('a' <= c && c <= 'z') { @@ -1857,7 +1212,7 @@ public final class TextFormat { /** * Parse a 32-bit signed integer from the text. Unlike the Java standard * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" - * and "0" to signify hexadecimal and octal numbers, respectively. + * and "0" to signify hexidecimal and octal numbers, respectively. */ static int parseInt32(final String text) throws NumberFormatException { return (int) parseInteger(text, true, false); @@ -1866,7 +1221,7 @@ public final class TextFormat { /** * Parse a 32-bit unsigned integer from the text. Unlike the Java standard * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" - * and "0" to signify hexadecimal and octal numbers, respectively. The + * and "0" to signify hexidecimal and octal numbers, respectively. The * result is coerced to a (signed) {@code int} when returned since Java has * no unsigned integer type. */ @@ -1877,7 +1232,7 @@ public final class TextFormat { /** * Parse a 64-bit signed integer from the text. Unlike the Java standard * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" - * and "0" to signify hexadecimal and octal numbers, respectively. + * and "0" to signify hexidecimal and octal numbers, respectively. */ static long parseInt64(final String text) throws NumberFormatException { return parseInteger(text, true, true); @@ -1886,7 +1241,7 @@ public final class TextFormat { /** * Parse a 64-bit unsigned integer from the text. Unlike the Java standard * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" - * and "0" to signify hexadecimal and octal numbers, respectively. The + * and "0" to signify hexidecimal and octal numbers, respectively. The * result is coerced to a (signed) {@code long} when returned since Java has * no unsigned long type. */ diff --git a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java index 5714c06..8743c12 100644 --- a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java +++ b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java index 99de373..26a15d0 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -46,7 +46,7 @@ import java.util.TreeMap; * {@code UnknownFieldSet} 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 + * and then messages containing those feilds are read by old software that was * compiled before the new types were added. * * <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every @@ -91,7 +91,6 @@ public final class UnknownFieldSet implements MessageLite { } private Map<Integer, Field> fields; - @Override public boolean equals(final Object other) { if (this == other) { @@ -368,22 +367,6 @@ public final class UnknownFieldSet implements MessageLite { reinitialize(); return this; } - - /** Clear fields from the set with a given field number. */ - public Builder clearField(final int number) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } - if (lastField != null && lastFieldNumber == number) { - // Discard this. - lastField = null; - lastFieldNumber = 0; - } - if (fields.containsKey(number)) { - fields.remove(number); - } - return this; - } /** * Merge the fields from {@code other} into this set. If a field number @@ -485,7 +468,7 @@ public final class UnknownFieldSet implements MessageLite { /** * Parse a single field from {@code input} and merge it into this set. * @param tag The field's tag number, which was already parsed. - * @return {@code false} if the tag is an end group tag. + * @return {@code false} if the tag is an engroup tag. */ public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { @@ -967,29 +950,4 @@ public final class UnknownFieldSet implements MessageLite { } } } - - /** - * Parser to implement MessageLite interface. - */ - public static final class Parser extends AbstractParser<UnknownFieldSet> { - public UnknownFieldSet parsePartialFrom( - CodedInputStream input, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - Builder builder = newBuilder(); - try { - builder.mergeFrom(input); - } catch (InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(builder.buildPartial()); - } catch (IOException e) { - throw new InvalidProtocolBufferException(e.getMessage()) - .setUnfinishedMessage(builder.buildPartial()); - } - return builder.buildPartial(); - } - } - - private static final Parser PARSER = new Parser(); - public final Parser getParserForType() { - return PARSER; - } } diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java deleted file mode 100644 index 5cc005d..0000000 --- a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java +++ /dev/null @@ -1,205 +0,0 @@ -// 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.AbstractList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.RandomAccess; - -/** - * An implementation of {@link LazyStringList} that wraps another - * {@link LazyStringList} such that it cannot be modified via the wrapper. - * - * @author jonp@google.com (Jon Perlow) - */ -public class UnmodifiableLazyStringList extends AbstractList<String> - implements LazyStringList, RandomAccess { - - private final LazyStringList list; - - public UnmodifiableLazyStringList(LazyStringList list) { - this.list = list; - } - - @Override - public String get(int index) { - return list.get(index); - } - - @Override - public int size() { - return list.size(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public ByteString getByteString(int index) { - return list.getByteString(index); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void add(ByteString element) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void set(int index, ByteString element) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean addAllByteString(Collection<? extends ByteString> element) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public byte[] getByteArray(int index) { - return list.getByteArray(index); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void add(byte[] element) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void set(int index, byte[] element) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean addAllByteArray(Collection<byte[]> element) { - throw new UnsupportedOperationException(); - } - - @Override - public ListIterator<String> listIterator(final int index) { - return new ListIterator<String>() { - ListIterator<String> iter = list.listIterator(index); - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasNext() { - return iter.hasNext(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public String next() { - return iter.next(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasPrevious() { - return iter.hasPrevious(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public String previous() { - return iter.previous(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public int nextIndex() { - return iter.nextIndex(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public int previousIndex() { - return iter.previousIndex(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void remove() { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void set(String o) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void add(String o) { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - public Iterator<String> iterator() { - return new Iterator<String>() { - Iterator<String> iter = list.iterator(); - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public boolean hasNext() { - return iter.hasNext(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public String next() { - return iter.next(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public List<?> getUnderlyingElements() { - // The returned value is already unmodifiable. - return list.getUnderlyingElements(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public void mergeFrom(LazyStringList other) { - throw new UnsupportedOperationException(); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public List<byte[]> asByteArrayList() { - return Collections.unmodifiableList(list.asByteArrayList()); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public List<ByteString> asByteStringList() { - return Collections.unmodifiableList(list.asByteStringList()); - } - - //@Override (Java 1.6 override semantics, but we must support 1.5) - public LazyStringList getUnmodifiableView() { - return this; - } -} diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/src/main/java/com/google/protobuf/Utf8.java deleted file mode 100644 index 4d0ef53..0000000 --- a/java/src/main/java/com/google/protobuf/Utf8.java +++ /dev/null @@ -1,349 +0,0 @@ -// 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; - -/** - * A set of low-level, high-performance static utility methods related - * to the UTF-8 character encoding. This class has no dependencies - * outside of the core JDK libraries. - * - * <p>There are several variants of UTF-8. The one implemented by - * this class is the restricted definition of UTF-8 introduced in - * Unicode 3.1, which mandates the rejection of "overlong" byte - * sequences as well as rejection of 3-byte surrogate codepoint byte - * sequences. Note that the UTF-8 decoder included in Oracle's JDK - * has been modified to also reject "overlong" byte sequences, but (as - * of 2011) still accepts 3-byte surrogate codepoint byte sequences. - * - * <p>The byte sequences considered valid by this class are exactly - * those that can be roundtrip converted to Strings and back to bytes - * using the UTF-8 charset, without loss: <pre> {@code - * Arrays.equals(bytes, new String(bytes, "UTF-8").getBytes("UTF-8")) - * }</pre> - * - * <p>See the Unicode Standard,</br> - * Table 3-6. <em>UTF-8 Bit Distribution</em>,</br> - * Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>. - * - * <p>This class supports decoding of partial byte sequences, so that the - * bytes in a complete UTF-8 byte sequences can be stored in multiple - * segments. Methods typically return {@link #MALFORMED} if the partial - * byte sequence is definitely not well-formed, {@link #COMPLETE} if it is - * well-formed in the absence of additional input, or if the byte sequence - * apparently terminated in the middle of a character, an opaque integer - * "state" value containing enough information to decode the character when - * passed to a subsequent invocation of a partial decoding method. - * - * @author martinrb@google.com (Martin Buchholz) - */ -final class Utf8 { - private Utf8() {} - - /** - * State value indicating that the byte sequence is well-formed and - * complete (no further bytes are needed to complete a character). - */ - public static final int COMPLETE = 0; - - /** - * State value indicating that the byte sequence is definitely not - * well-formed. - */ - public static final int MALFORMED = -1; - - // Other state values include the partial bytes of the incomplete - // character to be decoded in the simplest way: we pack the bytes - // into the state int in little-endian order. For example: - // - // int state = byte1 ^ (byte2 << 8) ^ (byte3 << 16); - // - // Such a state is unpacked thus (note the ~ operation for byte2 to - // undo byte1's sign-extension bits): - // - // int byte1 = (byte) state; - // int byte2 = (byte) ~(state >> 8); - // int byte3 = (byte) (state >> 16); - // - // We cannot store a zero byte in the state because it would be - // indistinguishable from the absence of a byte. But we don't need - // to, because partial bytes must always be negative. When building - // a state, we ensure that byte1 is negative and subsequent bytes - // are valid trailing bytes. - - /** - * Returns {@code true} if the given byte array is a well-formed - * UTF-8 byte sequence. - * - * <p>This is a convenience method, equivalent to a call to {@code - * isValidUtf8(bytes, 0, bytes.length)}. - */ - public static boolean isValidUtf8(byte[] bytes) { - return isValidUtf8(bytes, 0, bytes.length); - } - - /** - * Returns {@code true} if the given byte array slice is a - * well-formed UTF-8 byte sequence. The range of bytes to be - * checked extends from index {@code index}, inclusive, to {@code - * limit}, exclusive. - * - * <p>This is a convenience method, equivalent to {@code - * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}. - */ - public static boolean isValidUtf8(byte[] bytes, int index, int limit) { - return partialIsValidUtf8(bytes, index, limit) == COMPLETE; - } - - /** - * Tells whether the given byte array slice is a well-formed, - * malformed, or incomplete UTF-8 byte sequence. The range of bytes - * to be checked extends from index {@code index}, inclusive, to - * {@code limit}, exclusive. - * - * @param state either {@link Utf8#COMPLETE} (if this is the initial decoding - * operation) or the value returned from a call to a partial decoding method - * for the previous bytes - * - * @return {@link #MALFORMED} if the partial byte sequence is - * definitely not well-formed, {@link #COMPLETE} if it is well-formed - * (no additional input needed), or if the byte sequence is - * "incomplete", i.e. apparently terminated in the middle of a character, - * an opaque integer "state" value containing enough information to - * decode the character when passed to a subsequent invocation of a - * partial decoding method. - */ - public static int partialIsValidUtf8( - int state, byte[] bytes, int index, int limit) { - if (state != COMPLETE) { - // The previous decoding operation was incomplete (or malformed). - // We look for a well-formed sequence consisting of bytes from - // the previous decoding operation (stored in state) together - // with bytes from the array slice. - // - // We expect such "straddler characters" to be rare. - - if (index >= limit) { // No bytes? No progress. - return state; - } - int byte1 = (byte) state; - // byte1 is never ASCII. - if (byte1 < (byte) 0xE0) { - // two-byte form - - // Simultaneously checks for illegal trailing-byte in - // leading position and overlong 2-byte form. - if (byte1 < (byte) 0xC2 || - // byte2 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else if (byte1 < (byte) 0xF0) { - // three-byte form - - // Get byte2 from saved state or array - int byte2 = (byte) ~(state >> 8); - if (byte2 == 0) { - byte2 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2); - } - } - if (byte2 > (byte) 0xBF || - // overlong? 5 most significant bits must not all be zero - (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || - // illegal surrogate codepoint? - (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else { - // four-byte form - - // Get byte2 and byte3 from saved state or array - int byte2 = (byte) ~(state >> 8); - int byte3 = 0; - if (byte2 == 0) { - byte2 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2); - } - } else { - byte3 = (byte) (state >> 16); - } - if (byte3 == 0) { - byte3 = bytes[index++]; - if (index >= limit) { - return incompleteStateFor(byte1, byte2, byte3); - } - } - - // If we were called with state == MALFORMED, then byte1 is 0xFF, - // which never occurs in well-formed UTF-8, and so we will return - // MALFORMED again below. - - if (byte2 > (byte) 0xBF || - // Check that 1 <= plane <= 16. Tricky optimized form of: - // if (byte1 > (byte) 0xF4 || - // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || - // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) - (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || - // byte3 trailing-byte test - byte3 > (byte) 0xBF || - // byte4 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } - } - - return partialIsValidUtf8(bytes, index, limit); - } - - /** - * Tells whether the given byte array slice is a well-formed, - * malformed, or incomplete UTF-8 byte sequence. The range of bytes - * to be checked extends from index {@code index}, inclusive, to - * {@code limit}, exclusive. - * - * <p>This is a convenience method, equivalent to a call to {@code - * partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}. - * - * @return {@link #MALFORMED} if the partial byte sequence is - * definitely not well-formed, {@link #COMPLETE} if it is well-formed - * (no additional input needed), or if the byte sequence is - * "incomplete", i.e. apparently terminated in the middle of a character, - * an opaque integer "state" value containing enough information to - * decode the character when passed to a subsequent invocation of a - * partial decoding method. - */ - public static int partialIsValidUtf8( - byte[] bytes, int index, int limit) { - // Optimize for 100% ASCII. - // Hotspot loves small simple top-level loops like this. - while (index < limit && bytes[index] >= 0) { - index++; - } - - return (index >= limit) ? COMPLETE : - partialIsValidUtf8NonAscii(bytes, index, limit); - } - - private static int partialIsValidUtf8NonAscii( - byte[] bytes, int index, int limit) { - for (;;) { - int byte1, byte2; - - // Optimize for interior runs of ASCII bytes. - do { - if (index >= limit) { - return COMPLETE; - } - } while ((byte1 = bytes[index++]) >= 0); - - if (byte1 < (byte) 0xE0) { - // two-byte form - - if (index >= limit) { - return byte1; - } - - // Simultaneously checks for illegal trailing-byte in - // leading position and overlong 2-byte form. - if (byte1 < (byte) 0xC2 || - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else if (byte1 < (byte) 0xF0) { - // three-byte form - - if (index >= limit - 1) { // incomplete sequence - return incompleteStateFor(bytes, index, limit); - } - if ((byte2 = bytes[index++]) > (byte) 0xBF || - // overlong? 5 most significant bits must not all be zero - (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) || - // check for illegal surrogate codepoints - (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } else { - // four-byte form - - if (index >= limit - 2) { // incomplete sequence - return incompleteStateFor(bytes, index, limit); - } - if ((byte2 = bytes[index++]) > (byte) 0xBF || - // Check that 1 <= plane <= 16. Tricky optimized form of: - // if (byte1 > (byte) 0xF4 || - // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 || - // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F) - (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 || - // byte3 trailing-byte test - bytes[index++] > (byte) 0xBF || - // byte4 trailing-byte test - bytes[index++] > (byte) 0xBF) { - return MALFORMED; - } - } - } - } - - private static int incompleteStateFor(int byte1) { - return (byte1 > (byte) 0xF4) ? - MALFORMED : byte1; - } - - private static int incompleteStateFor(int byte1, int byte2) { - return (byte1 > (byte) 0xF4 || - byte2 > (byte) 0xBF) ? - MALFORMED : byte1 ^ (byte2 << 8); - } - - private static int incompleteStateFor(int byte1, int byte2, int byte3) { - return (byte1 > (byte) 0xF4 || - byte2 > (byte) 0xBF || - byte3 > (byte) 0xBF) ? - MALFORMED : byte1 ^ (byte2 << 8) ^ (byte3 << 16); - } - - private static int incompleteStateFor(byte[] bytes, int index, int limit) { - int byte1 = bytes[index - 1]; - switch (limit - index) { - case 0: return incompleteStateFor(byte1); - case 1: return incompleteStateFor(byte1, bytes[index]); - case 2: return incompleteStateFor(byte1, bytes[index], bytes[index + 1]); - default: throw new AssertionError(); - } - } -} diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index eba2528..c46f7b0 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -1,6 +1,6 @@ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ +// http://code.google.com/p/protobuf/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -45,12 +45,12 @@ public final class WireFormat { // Do not allow instantiation. private WireFormat() {} - public static final int WIRETYPE_VARINT = 0; - public static final int WIRETYPE_FIXED64 = 1; - public static final int WIRETYPE_LENGTH_DELIMITED = 2; - public static final int WIRETYPE_START_GROUP = 3; - public static final int WIRETYPE_END_GROUP = 4; - public static final int WIRETYPE_FIXED32 = 5; + static final int WIRETYPE_VARINT = 0; + static final int WIRETYPE_FIXED64 = 1; + static final int WIRETYPE_LENGTH_DELIMITED = 2; + static final int WIRETYPE_START_GROUP = 3; + static final int WIRETYPE_END_GROUP = 4; + static final int WIRETYPE_FIXED32 = 5; static final int TAG_TYPE_BITS = 3; static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1; @@ -146,7 +146,7 @@ public final class WireFormat { public boolean isPackable() { return true; } } - // Field numbers for fields in MessageSet wire format. + // Field numbers for feilds in MessageSet wire format. static final int MESSAGE_SET_ITEM = 1; static final int MESSAGE_SET_TYPE_ID = 2; static final int MESSAGE_SET_MESSAGE = 3; diff --git a/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java b/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java index 324a63f..88df38d 100644 --- a/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java +++ b/java/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java @@ -31,9 +31,7 @@ package com.google.protobuf.nano; import java.io.IOException; -import java.nio.BufferOverflowException; -import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; +import java.io.UnsupportedEncodingException; /** * Encodes and writes protocol message fields. @@ -50,17 +48,15 @@ import java.nio.ReadOnlyBufferException; * @author kneton@google.com Kenton Varda */ public final class CodedOutputByteBufferNano { - /* max bytes per java UTF-16 char in UTF-8 */ - private static final int MAX_UTF8_EXPANSION = 3; - private final ByteBuffer buffer; + private final byte[] buffer; + private final int limit; + private int position; private CodedOutputByteBufferNano(final byte[] buffer, final int offset, final int length) { - this(ByteBuffer.wrap(buffer, offset, length)); - } - - private CodedOutputByteBufferNano(final ByteBuffer buffer) { this.buffer = buffer; + position = offset; + limit = offset + length; } /** @@ -292,203 +288,14 @@ public final class CodedOutputByteBufferNano { /** Write a {@code string} field to the stream. */ public void writeStringNoTag(final String value) throws IOException { - // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), - // and at most 3 times of it. Optimize for the case where we know this length results in a - // constant varint length - saves measuring length of the string. - try { - final int minLengthVarIntSize = computeRawVarint32Size(value.length()); - final int maxLengthVarIntSize = computeRawVarint32Size(value.length() * MAX_UTF8_EXPANSION); - if (minLengthVarIntSize == maxLengthVarIntSize) { - int oldPosition = buffer.position(); - buffer.position(oldPosition + minLengthVarIntSize); - encode(value, buffer); - int newPosition = buffer.position(); - buffer.position(oldPosition); - writeRawVarint32(newPosition - oldPosition - minLengthVarIntSize); - buffer.position(newPosition); - } else { - writeRawVarint32(encodedLength(value)); - encode(value, buffer); - } - } catch (BufferOverflowException e) { - throw new OutOfSpaceException(buffer.position(), buffer.limit()); - } - } - - // These UTF-8 handling methods are copied from Guava's Utf8 class. - /** - * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, - * this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in - * both time and space. - * - * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired - * surrogates) - */ - private static int encodedLength(CharSequence sequence) { - // Warning to maintainers: this implementation is highly optimized. - int utf16Length = sequence.length(); - int utf8Length = utf16Length; - int i = 0; - - // This loop optimizes for pure ASCII. - while (i < utf16Length && sequence.charAt(i) < 0x80) { - i++; - } - - // This loop optimizes for chars less than 0x800. - for (; i < utf16Length; i++) { - char c = sequence.charAt(i); - if (c < 0x800) { - utf8Length += ((0x7f - c) >>> 31); // branch free! - } else { - utf8Length += encodedLengthGeneral(sequence, i); - break; - } - } - - if (utf8Length < utf16Length) { - // Necessary and sufficient condition for overflow because of maximum 3x expansion - throw new IllegalArgumentException("UTF-8 length does not fit in int: " - + (utf8Length + (1L << 32))); - } - return utf8Length; - } - - private static int encodedLengthGeneral(CharSequence sequence, int start) { - int utf16Length = sequence.length(); - int utf8Length = 0; - for (int i = start; i < utf16Length; i++) { - char c = sequence.charAt(i); - if (c < 0x800) { - utf8Length += (0x7f - c) >>> 31; // branch free! - } else { - utf8Length += 2; - // jdk7+: if (Character.isSurrogate(c)) { - if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) { - // Check that we have a well-formed surrogate pair. - int cp = Character.codePointAt(sequence, i); - if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - throw new IllegalArgumentException("Unpaired surrogate at index " + i); - } - i++; - } - } - } - return utf8Length; - } - - /** - * Encodes {@code sequence} into UTF-8, in {@code byteBuffer}. For a string, this method is - * equivalent to {@code buffer.put(string.getBytes(UTF_8))}, but is more efficient in both time - * and space. Bytes are written starting at the current position. This method requires paired - * surrogates, and therefore does not support chunking. - * - * <p>To ensure sufficient space in the output buffer, either call {@link #encodedLength} to - * compute the exact amount needed, or leave room for {@code 3 * sequence.length()}, which is the - * largest possible number of bytes that any input can be encoded to. - * - * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired - * surrogates) - * @throws BufferOverflowException if {@code sequence} encoded in UTF-8 does not fit in - * {@code byteBuffer}'s remaining space. - * @throws ReadOnlyBufferException if {@code byteBuffer} is a read-only buffer. - */ - private static void encode(CharSequence sequence, ByteBuffer byteBuffer) { - if (byteBuffer.isReadOnly()) { - throw new ReadOnlyBufferException(); - } else if (byteBuffer.hasArray()) { - try { - int encoded = encode(sequence, - byteBuffer.array(), - byteBuffer.arrayOffset() + byteBuffer.position(), - byteBuffer.remaining()); - byteBuffer.position(encoded - byteBuffer.arrayOffset()); - } catch (ArrayIndexOutOfBoundsException e) { - BufferOverflowException boe = new BufferOverflowException(); - boe.initCause(e); - throw boe; - } - } else { - encodeDirect(sequence, byteBuffer); - } - } - - private static void encodeDirect(CharSequence sequence, ByteBuffer byteBuffer) { - int utf16Length = sequence.length(); - for (int i = 0; i < utf16Length; i++) { - final char c = sequence.charAt(i); - if (c < 0x80) { // ASCII - byteBuffer.put((byte) c); - } else if (c < 0x800) { // 11 bits, two UTF-8 bytes - byteBuffer.put((byte) ((0xF << 6) | (c >>> 6))); - byteBuffer.put((byte) (0x80 | (0x3F & c))); - } else if (c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) { - // Maximium single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes - byteBuffer.put((byte) ((0xF << 5) | (c >>> 12))); - byteBuffer.put((byte) (0x80 | (0x3F & (c >>> 6)))); - byteBuffer.put((byte) (0x80 | (0x3F & c))); - } else { - final char low; - if (i + 1 == sequence.length() - || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) { - throw new IllegalArgumentException("Unpaired surrogate at index " + (i - 1)); - } - int codePoint = Character.toCodePoint(c, low); - byteBuffer.put((byte) ((0xF << 4) | (codePoint >>> 18))); - byteBuffer.put((byte) (0x80 | (0x3F & (codePoint >>> 12)))); - byteBuffer.put((byte) (0x80 | (0x3F & (codePoint >>> 6)))); - byteBuffer.put((byte) (0x80 | (0x3F & codePoint))); - } - } - } - - private static int encode(CharSequence sequence, byte[] bytes, int offset, int length) { - int utf16Length = sequence.length(); - int j = offset; - int i = 0; - int limit = offset + length; - // Designed to take advantage of - // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination - for (char c; i < utf16Length && i + j < limit && (c = sequence.charAt(i)) < 0x80; i++) { - bytes[j + i] = (byte) c; - } - if (i == utf16Length) { - return j + utf16Length; - } - j += i; - for (char c; i < utf16Length; i++) { - c = sequence.charAt(i); - if (c < 0x80 && j < limit) { - bytes[j++] = (byte) c; - } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes - bytes[j++] = (byte) ((0xF << 6) | (c >>> 6)); - bytes[j++] = (byte) (0x80 | (0x3F & c)); - } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) { - // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes - bytes[j++] = (byte) ((0xF << 5) | (c >>> 12)); - bytes[j++] = (byte) (0x80 | (0x3F & (c >>> 6))); - bytes[j++] = (byte) (0x80 | (0x3F & c)); - } else if (j <= limit - 4) { - // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 bytes - final char low; - if (i + 1 == sequence.length() - || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) { - throw new IllegalArgumentException("Unpaired surrogate at index " + (i - 1)); - } - int codePoint = Character.toCodePoint(c, low); - bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18)); - bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12))); - bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6))); - bytes[j++] = (byte) (0x80 | (0x3F & codePoint)); - } else { - throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j); - } - } - return j; + // Unfortunately there does not appear to be any way to tell Java to encode + // UTF-8 directly into our buffer, so we have to let it create its own byte + // array and then copy. + final byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); } - // End guava UTF-8 methods - /** Write a {@code group} field to the stream. */ public void writeGroupNoTag(final MessageNano value) throws IOException { value.writeTo(this); @@ -796,8 +603,13 @@ public final class CodedOutputByteBufferNano { * {@code string} field. */ public static int computeStringSizeNoTag(final String value) { - final int length = encodedLength(value); - return computeRawVarint32Size(length) + length; + try { + final byte[] bytes = value.getBytes("UTF-8"); + return computeRawVarint32Size(bytes.length) + + bytes.length; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported."); + } } /** @@ -880,7 +692,7 @@ public final class CodedOutputByteBufferNano { * Otherwise, throws {@code UnsupportedOperationException}. */ public int spaceLeft() { - return buffer.remaining(); + return limit - position; } /** @@ -898,23 +710,6 @@ public final class CodedOutputByteBufferNano { } /** - * Returns the position within the internal buffer. - */ - public int position() { - return buffer.position(); - } - - /** - * Resets the position within the internal buffer to zero. - * - * @see #position - * @see #spaceLeft - */ - public void reset() { - buffer.clear(); - } - - /** * If you create a CodedOutputStream around a simple flat array, you must * not attempt to write more bytes than the array has space. Otherwise, * this exception will be thrown. @@ -930,12 +725,12 @@ public final class CodedOutputByteBufferNano { /** Write a single byte. */ public void writeRawByte(final byte value) throws IOException { - if (!buffer.hasRemaining()) { + if (position == limit) { // We're writing to a single buffer. - throw new OutOfSpaceException(buffer.position(), buffer.limit()); + throw new OutOfSpaceException(position, limit); } - buffer.put(value); + buffer[position++] = value; } /** Write a single byte, represented by an integer value. */ @@ -951,11 +746,13 @@ public final class CodedOutputByteBufferNano { /** Write part of an array of bytes. */ public void writeRawBytes(final byte[] value, int offset, int length) throws IOException { - if (buffer.remaining() >= length) { - buffer.put(value, offset, length); + if (limit - position >= length) { + // We have room in the current buffer. + System.arraycopy(value, offset, buffer, position, length); + position += length; } else { // We're writing to a single buffer. - throw new OutOfSpaceException(buffer.position(), buffer.limit()); + throw new OutOfSpaceException(position, limit); } } diff --git a/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java index 4fe8dce..5984d35 100644 --- a/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java +++ b/java/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java @@ -68,18 +68,6 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>> } /** - * Checks if there is a value stored for the specified extension in this - * message. - */ - public final boolean hasExtension(Extension<M, ?> extension) { - if (unknownFieldData == null) { - return false; - } - FieldData field = unknownFieldData.get(WireFormatNano.getTagFieldNumber(extension.tag)); - return field != null; - } - - /** * Gets the value stored in the specified extension of this message. */ public final <T> T getExtension(Extension<M, T> extension) { @@ -160,10 +148,28 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>> return true; } - @Override - public M clone() throws CloneNotSupportedException { - M cloned = (M) super.clone(); - InternalNano.cloneUnknownFieldData(this, cloned); - return cloned; + /** + * Returns whether the stored unknown field data in this message is equivalent to that in the + * other message. + * + * @param other the other message. + * @return whether the two sets of unknown field data are equal. + */ + protected final boolean unknownFieldDataEquals(M other) { + if (unknownFieldData == null || unknownFieldData.isEmpty()) { + return other.unknownFieldData == null || other.unknownFieldData.isEmpty(); + } else { + return unknownFieldData.equals(other.unknownFieldData); + } + } + + /** + * Computes the hashcode representing the unknown field data stored in this message. + * + * @return the hashcode for the unknown field data. + */ + protected final int unknownFieldDataHashCode() { + return (unknownFieldData == null || unknownFieldData.isEmpty() + ? 0 : unknownFieldData.hashCode()); } } diff --git a/java/src/main/java/com/google/protobuf/nano/Extension.java b/java/src/main/java/com/google/protobuf/nano/Extension.java index 6e2202e..962f66e 100644 --- a/java/src/main/java/com/google/protobuf/nano/Extension.java +++ b/java/src/main/java/com/google/protobuf/nano/Extension.java @@ -79,30 +79,12 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { * Should be used by the generated code only. * * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} - * @deprecated use {@link #createMessageTyped(int, Class, long)} instead. */ - @Deprecated public static <M extends ExtendableMessageNano<M>, T extends MessageNano> Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) { return new Extension<M, T>(type, clazz, tag, false); } - // Note: these create...() methods take a long for the tag parameter, - // because tags are represented as unsigned ints, and these values exist - // in generated code as long values. However, they can fit in 32-bits, so - // it's safe to cast them to int without loss of precision. - - /** - * Creates an {@code Extension} of the given message type and tag number. - * Should be used by the generated code only. - * - * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} - */ - public static <M extends ExtendableMessageNano<M>, T extends MessageNano> - Extension<M, T> createMessageTyped(int type, Class<T> clazz, long tag) { - return new Extension<M, T>(type, clazz, (int) tag, false); - } - /** * Creates a repeated {@code Extension} of the given message type and tag number. * Should be used by the generated code only. @@ -110,8 +92,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} */ public static <M extends ExtendableMessageNano<M>, T extends MessageNano> - Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, long tag) { - return new Extension<M, T[]>(type, clazz, (int) tag, true); + Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, int tag) { + return new Extension<M, T[]>(type, clazz, tag, true); } /** @@ -122,8 +104,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { * @param clazz the boxed Java type of this extension */ public static <M extends ExtendableMessageNano<M>, T> - Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, long tag) { - return new PrimitiveExtension<M, T>(type, clazz, (int) tag, false, 0, 0); + Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, int tag) { + return new PrimitiveExtension<M, T>(type, clazz, tag, false, 0, 0); } /** @@ -135,9 +117,8 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { */ public static <M extends ExtendableMessageNano<M>, T> Extension<M, T> createRepeatedPrimitiveTyped( - int type, Class<T> clazz, long tag, long nonPackedTag, long packedTag) { - return new PrimitiveExtension<M, T>(type, clazz, (int) tag, true, - (int) nonPackedTag, (int) packedTag); + int type, Class<T> clazz, int tag, int nonPackedTag, int packedTag) { + return new PrimitiveExtension<M, T>(type, clazz, tag, true, nonPackedTag, packedTag); } /** @@ -155,9 +136,9 @@ public class Extension<M extends ExtendableMessageNano<M>, T> { protected final Class<T> clazz; /** - * Tag number of this extension. The data should be viewed as an unsigned 32-bit value. + * Tag number of this extension. */ - public final int tag; + protected final int tag; /** * Whether this extension is repeated. diff --git a/java/src/main/java/com/google/protobuf/nano/FieldArray.java b/java/src/main/java/com/google/protobuf/nano/FieldArray.java index 5e8856d..ab923a4 100644 --- a/java/src/main/java/com/google/protobuf/nano/FieldArray.java +++ b/java/src/main/java/com/google/protobuf/nano/FieldArray.java @@ -35,12 +35,9 @@ package com.google.protobuf.nano; * A custom version of {@link android.util.SparseArray} with the minimal API * for storing {@link FieldData} objects. * - * <p>This class is an internal implementation detail of nano and should not - * be called directly by clients. - * * Based on {@link android.support.v4.util.SpareArrayCompat}. */ -public final class FieldArray implements Cloneable { +class FieldArray { private static final FieldData DELETED = new FieldData(); private boolean mGarbage = false; @@ -51,7 +48,7 @@ public final class FieldArray implements Cloneable { /** * Creates a new FieldArray containing no fields. */ - FieldArray() { + public FieldArray() { this(10); } @@ -60,7 +57,7 @@ public final class FieldArray implements Cloneable { * require any additional memory allocation to store the specified * number of mappings. */ - FieldArray(int initialCapacity) { + public FieldArray(int initialCapacity) { initialCapacity = idealIntArraySize(initialCapacity); mFieldNumbers = new int[initialCapacity]; mData = new FieldData[initialCapacity]; @@ -71,7 +68,7 @@ public final class FieldArray implements Cloneable { * Gets the FieldData mapped from the specified fieldNumber, or <code>null</code> * if no such mapping has been made. */ - FieldData get(int fieldNumber) { + public FieldData get(int fieldNumber) { int i = binarySearch(fieldNumber); if (i < 0 || mData[i] == DELETED) { @@ -84,7 +81,7 @@ public final class FieldArray implements Cloneable { /** * Removes the data from the specified fieldNumber, if there was any. */ - void remove(int fieldNumber) { + public void remove(int fieldNumber) { int i = binarySearch(fieldNumber); if (i >= 0 && mData[i] != DELETED) { @@ -121,7 +118,7 @@ public final class FieldArray implements Cloneable { * Adds a mapping from the specified fieldNumber to the specified data, * replacing the previous mapping if there was one. */ - void put(int fieldNumber, FieldData data) { + public void put(int fieldNumber, FieldData data) { int i = binarySearch(fieldNumber); if (i >= 0) { @@ -170,7 +167,7 @@ public final class FieldArray implements Cloneable { * Returns the number of key-value mappings that this FieldArray * currently stores. */ - int size() { + public int size() { if (mGarbage) { gc(); } @@ -187,7 +184,7 @@ public final class FieldArray implements Cloneable { * the value from the <code>index</code>th key-value mapping that this * FieldArray stores. */ - FieldData dataAt(int index) { + public FieldData dataAt(int index) { if (mGarbage) { gc(); } @@ -273,19 +270,4 @@ public final class FieldArray implements Cloneable { } return true; } - - @Override - public final FieldArray clone() { - // Trigger GC so we compact and don't copy DELETED elements. - int size = size(); - FieldArray clone = new FieldArray(size); - System.arraycopy(mFieldNumbers, 0, clone.mFieldNumbers, 0, size); - for (int i = 0; i < size; i++) { - if (mData[i] != null) { - clone.mData[i] = mData[i].clone(); - } - } - clone.mSize = size; - return clone; - } } diff --git a/java/src/main/java/com/google/protobuf/nano/FieldData.java b/java/src/main/java/com/google/protobuf/nano/FieldData.java index 20a5142..7a5eb4c 100644 --- a/java/src/main/java/com/google/protobuf/nano/FieldData.java +++ b/java/src/main/java/com/google/protobuf/nano/FieldData.java @@ -39,7 +39,7 @@ import java.util.List; * Stores unknown fields. These might be extensions or fields that the generated API doesn't * know about yet. */ -class FieldData implements Cloneable { +class FieldData { private Extension<?, ?> cachedExtension; private Object value; /** The serialised values for this object. Will be cleared if getValue is called */ @@ -58,23 +58,6 @@ class FieldData implements Cloneable { unknownFieldData.add(unknownField); } - UnknownFieldData getUnknownField(int index) { - if (unknownFieldData == null) { - return null; - } - if (index < unknownFieldData.size()) { - return unknownFieldData.get(index); - } - return null; - } - - int getUnknownFieldSize() { - if (unknownFieldData == null) { - return 0; - } - return unknownFieldData.size(); - } - <T> T getValue(Extension<?, T> extension) { if (value != null){ if (cachedExtension != extension) { // Extension objects are singletons. @@ -187,54 +170,4 @@ class FieldData implements Cloneable { return result; } - @Override - public final FieldData clone() { - FieldData clone = new FieldData(); - try { - clone.cachedExtension = cachedExtension; - if (unknownFieldData == null) { - clone.unknownFieldData = null; - } else { - clone.unknownFieldData.addAll(unknownFieldData); - } - - // Whether we need to deep clone value depends on its type. Primitive reference types - // (e.g. Integer, Long etc.) are ok, since they're immutable. We need to clone arrays - // and messages. - if (value == null) { - // No cloning required. - } else if (value instanceof MessageNano) { - clone.value = ((MessageNano) value).clone(); - } else if (value instanceof byte[]) { - clone.value = ((byte[]) value).clone(); - } else if (value instanceof byte[][]) { - byte[][] valueArray = (byte[][]) value; - byte[][] cloneArray = new byte[valueArray.length][]; - clone.value = cloneArray; - for (int i = 0; i < valueArray.length; i++) { - cloneArray[i] = valueArray[i].clone(); - } - } else if (value instanceof boolean[]) { - clone.value = ((boolean[]) value).clone(); - } else if (value instanceof int[]) { - clone.value = ((int[]) value).clone(); - } else if (value instanceof long[]) { - clone.value = ((long[]) value).clone(); - } else if (value instanceof float[]) { - clone.value = ((float[]) value).clone(); - } else if (value instanceof double[]) { - clone.value = ((double[]) value).clone(); - } else if (value instanceof MessageNano[]) { - MessageNano[] valueArray = (MessageNano[]) value; - MessageNano[] cloneArray = new MessageNano[valueArray.length]; - clone.value = cloneArray; - for (int i = 0; i < valueArray.length; i++) { - cloneArray[i] = valueArray[i].clone(); - } - } - return clone; - } catch (CloneNotSupportedException e) { - throw new AssertionError(e); - } - } } diff --git a/java/src/main/java/com/google/protobuf/nano/InternalNano.java b/java/src/main/java/com/google/protobuf/nano/InternalNano.java index c4adfa5..90ca11d 100644 --- a/java/src/main/java/com/google/protobuf/nano/InternalNano.java +++ b/java/src/main/java/com/google/protobuf/nano/InternalNano.java @@ -330,12 +330,4 @@ public final class InternalNano { return result; } - // This avoids having to make FieldArray public. - public static void cloneUnknownFieldData(ExtendableMessageNano original, - ExtendableMessageNano cloned) { - if (original.unknownFieldData != null) { - cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone(); - } - } - } diff --git a/java/src/main/java/com/google/protobuf/nano/MessageNano.java b/java/src/main/java/com/google/protobuf/nano/MessageNano.java index ea91b21..d6288c9 100644 --- a/java/src/main/java/com/google/protobuf/nano/MessageNano.java +++ b/java/src/main/java/com/google/protobuf/nano/MessageNano.java @@ -31,7 +31,6 @@ package com.google.protobuf.nano; import java.io.IOException; -import java.util.Arrays; /** * Abstract interface implemented by Protocol Message objects. @@ -152,31 +151,6 @@ public abstract class MessageNano { } /** - * Compares two {@code MessageNano}s and returns true if the message's are the same class and - * have serialized form equality (i.e. all of the field values are the same). - */ - public static final boolean messageNanoEquals(MessageNano a, MessageNano b) { - if (a == b) { - return true; - } - if (a == null || b == null) { - return false; - } - if (a.getClass() != b.getClass()) { - return false; - } - final int serializedSize = a.getSerializedSize(); - if (b.getSerializedSize() != serializedSize) { - return false; - } - final byte[] aByteArray = new byte[serializedSize]; - final byte[] bByteArray = new byte[serializedSize]; - toByteArray(a, aByteArray, 0, serializedSize); - toByteArray(b, bByteArray, 0, serializedSize); - return Arrays.equals(aByteArray, bByteArray); - } - - /** * Returns a string that is (mostly) compatible with ProtoBuffer's TextFormat. Note that groups * (which are deprecated) are not serialized with the correct field name. * @@ -187,12 +161,4 @@ public abstract class MessageNano { public String toString() { return MessageNanoPrinter.print(this); } - - /** - * Provides support for cloning. This only works if you specify the generate_clone method. - */ - @Override - public MessageNano clone() throws CloneNotSupportedException { - return (MessageNano) super.clone(); - } } diff --git a/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java b/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java index a30f2f3..572a707 100644 --- a/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java +++ b/java/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java @@ -108,10 +108,6 @@ public final class MessageNanoPrinter { for (Field field : clazz.getFields()) { int modifiers = field.getModifiers(); String fieldName = field.getName(); - if ("cachedSize".equals(fieldName)) { - // TODO(bduff): perhaps cachedSize should have a more obscure name. - continue; - } if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC && (modifiers & Modifier.STATIC) != Modifier.STATIC @@ -247,7 +243,7 @@ public final class MessageNanoPrinter { builder.append('"'); for (int i = 0; i < bytes.length; ++i) { - int ch = bytes[i] & 0xff; + int ch = bytes[i]; if (ch == '\\' || ch == '"') { builder.append('\\').append((char) ch); } else if (ch >= 32 && ch < 127) { diff --git a/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java index bf34bed..2032e1a 100644 --- a/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java +++ b/java/src/main/java/com/google/protobuf/nano/UnknownFieldData.java @@ -42,10 +42,6 @@ import java.util.Arrays; final class UnknownFieldData { final int tag; - /** - * Important: this should be treated as immutable, even though it's possible - * to change the array values. - */ final byte[] bytes; UnknownFieldData(int tag, byte[] bytes) { diff --git a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java index a3405e5..1ff8f06 100644 --- a/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java +++ b/java/src/main/java/com/google/protobuf/nano/WireFormatNano.java @@ -113,7 +113,11 @@ public final class WireFormatNano { int arrayLength = 1; int startPos = input.getPosition(); input.skipField(tag); - while (input.readTag() == tag) { + while (input.getBytesUntilLimit() > 0) { + int thisTag = input.readTag(); + if (thisTag != tag) { + break; + } input.skipField(tag); arrayLength++; } |