diff options
Diffstat (limited to 'java/src/main')
23 files changed, 4028 insertions, 2228 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index 53175cdd..e5bdefe3 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -30,12 +30,12 @@ package com.google.protobuf; +import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; -import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; -import java.io.OutputStream; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -45,11 +45,12 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -public abstract class AbstractMessage implements Message { +public abstract class AbstractMessage extends AbstractMessageLite + implements Message { @SuppressWarnings("unchecked") public boolean isInitialized() { // Check that all required fields are present. - for (FieldDescriptor field : getDescriptorForType().getFields()) { + for (final FieldDescriptor field : getDescriptorForType().getFields()) { if (field.isRequired()) { if (!hasField(field)) { return false; @@ -58,11 +59,12 @@ public abstract class AbstractMessage implements Message { } // Check that embedded messages are initialized. - for (Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); + for (final Map.Entry<FieldDescriptor, Object> entry : + getAllFields().entrySet()) { + final FieldDescriptor field = entry.getKey(); if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { if (field.isRepeated()) { - for (Message element : (List<Message>) entry.getValue()) { + for (final Message element : (List<Message>) entry.getValue()) { if (!element.isInitialized()) { return false; } @@ -83,117 +85,59 @@ public abstract class AbstractMessage implements Message { return TextFormat.printToString(this); } - public void writeTo(CodedOutputStream output) throws IOException { - for (Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); - if (field.isRepeated()) { - List valueList = (List) entry.getValue(); - if (field.getOptions().getPacked()) { - - output.writeTag(field.getNumber(), - WireFormat.WIRETYPE_LENGTH_DELIMITED); - int dataSize = 0; - for (Object element : valueList) { - dataSize += CodedOutputStream.computeFieldSizeNoTag( - field.getType(), element); - } - output.writeRawVarint32(dataSize); - - for (Object element : valueList) { - output.writeFieldNoTag(field.getType(), element); - } - } else { - for (Object element : valueList) { - output.writeField(field.getType(), field.getNumber(), element); - } - } + public void writeTo(final CodedOutputStream output) throws IOException { + 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 { - output.writeField(field.getType(), field.getNumber(), entry.getValue()); + FieldSet.writeField(field, value, output); } } - UnknownFieldSet unknownFields = getUnknownFields(); - if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + final UnknownFieldSet unknownFields = getUnknownFields(); + if (isMessageSet) { unknownFields.writeAsMessageSetTo(output); } else { unknownFields.writeTo(output); } } - public ByteString toByteString() { - try { - ByteString.CodedBuilder out = - ByteString.newCodedBuilder(getSerializedSize()); - writeTo(out.getCodedOutput()); - return out.build(); - } catch (IOException e) { - throw new RuntimeException( - "Serializing to a ByteString threw an IOException (should " + - "never happen).", e); - } - } - - public byte[] toByteArray() { - try { - byte[] result = new byte[getSerializedSize()]; - CodedOutputStream output = CodedOutputStream.newInstance(result); - writeTo(output); - output.checkNoSpaceLeft(); - return result; - } catch (IOException e) { - throw new RuntimeException( - "Serializing to a byte array threw an IOException " + - "(should never happen).", e); - } - } - - public void writeTo(OutputStream output) throws IOException { - CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); - writeTo(codedOutput); - codedOutput.flush(); - } - - public void writeDelimitedTo(OutputStream output) throws IOException { - CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); - codedOutput.writeRawVarint32(getSerializedSize()); - writeTo(codedOutput); - codedOutput.flush(); - } - private int memoizedSize = -1; public int getSerializedSize() { int size = memoizedSize; - if (size != -1) return size; + if (size != -1) { + return size; + } size = 0; - for (Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); - if (field.isRepeated()) { - List valueList = (List) entry.getValue(); - if (field.getOptions().getPacked()) { - int dataSize = 0; - for (Object element : valueList) { - dataSize += CodedOutputStream.computeFieldSizeNoTag( - field.getType(), element); - } - size += dataSize; - size += CodedOutputStream.computeTagSize(field.getNumber()); - size += CodedOutputStream.computeRawVarint32Size(dataSize); - } else { - for (Object element : valueList) { - size += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), element); - } - } + 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 += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), entry.getValue()); + size += FieldSet.computeFieldSize(field, value); } } - UnknownFieldSet unknownFields = getUnknownFields(); - if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + final UnknownFieldSet unknownFields = getUnknownFields(); + if (isMessageSet) { size += unknownFields.getSerializedSizeAsMessageSet(); } else { size += unknownFields.getSerializedSize(); @@ -204,14 +148,14 @@ public abstract class AbstractMessage implements Message { } @Override - public boolean equals(Object other) { + public boolean equals(final Object other) { if (other == this) { return true; } if (!(other instanceof Message)) { return false; } - Message otherMessage = (Message) other; + final Message otherMessage = (Message) other; if (getDescriptorForType() != otherMessage.getDescriptorForType()) { return false; } @@ -237,20 +181,21 @@ public abstract class AbstractMessage implements Message { */ @SuppressWarnings("unchecked") public static abstract class Builder<BuilderType extends Builder> + extends AbstractMessageLite.Builder<BuilderType> implements Message.Builder { // The compiler produces an error if this is not declared explicitly. @Override public abstract BuilderType clone(); public BuilderType clear() { - for (Map.Entry<FieldDescriptor, Object> entry : + for (final Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { clearField(entry.getKey()); } return (BuilderType) this; } - public BuilderType mergeFrom(Message other) { + public BuilderType mergeFrom(final Message other) { if (other.getDescriptorForType() != getDescriptorForType()) { throw new IllegalArgumentException( "mergeFrom(Message) can only merge messages of the same type."); @@ -265,15 +210,15 @@ public abstract class AbstractMessage implements Message { // TODO(kenton): Provide a function somewhere called makeDeepCopy() // which allows people to make secure deep copies of messages. - for (Map.Entry<FieldDescriptor, Object> entry : + for (final Map.Entry<FieldDescriptor, Object> entry : other.getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); + final FieldDescriptor field = entry.getKey(); if (field.isRepeated()) { - for (Object element : (List)entry.getValue()) { + for (final Object element : (List)entry.getValue()) { addRepeatedField(field, element); } } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - Message existingValue = (Message)getField(field); + final Message existingValue = (Message)getField(field); if (existingValue == existingValue.getDefaultInstanceForType()) { setField(field, entry.getValue()); } else { @@ -288,24 +233,288 @@ public abstract class AbstractMessage implements Message { } } + mergeUnknownFields(other.getUnknownFields()); + return (BuilderType) this; } - public BuilderType mergeFrom(CodedInputStream input) throws IOException { + @Override + public BuilderType mergeFrom(final CodedInputStream input) + throws IOException { return mergeFrom(input, ExtensionRegistry.getEmptyRegistry()); } - public BuilderType mergeFrom(CodedInputStream input, - ExtensionRegistry extensionRegistry) - throws IOException { - UnknownFieldSet.Builder unknownFields = + @Override + public BuilderType mergeFrom( + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry) + throws IOException { + final UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder(getUnknownFields()); - FieldSet.mergeFrom(input, unknownFields, extensionRegistry, this); + while (true) { + final int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (!mergeFieldFrom(input, unknownFields, extensionRegistry, + this, tag)) { + // end group tag + break; + } + } setUnknownFields(unknownFields.build()); return (BuilderType) this; } - public BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + /** + * 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; + } + } else { + field = null; + } + } else { + field = type.findFieldByNumber(fieldNumber); + } + + if (field == null || wireType != + FieldSet.getWireFormatForFieldType( + field.getLiteType(), + field.getOptions().getPacked())) { + // Unknown field or wrong wire type. Skip. + return unknownFields.mergeFieldFrom(tag, input); + } + + if (field.getOptions().getPacked()) { + 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()) .mergeFrom(unknownFields) @@ -313,145 +522,169 @@ public abstract class AbstractMessage implements Message { return (BuilderType) this; } - public BuilderType mergeFrom(ByteString data) - throws InvalidProtocolBufferException { - try { - CodedInputStream input = data.newCodedInput(); - mergeFrom(input); - input.checkLastTagWas(0); - return (BuilderType) this; - } catch (InvalidProtocolBufferException e) { - throw e; - } catch (IOException e) { - throw new RuntimeException( - "Reading from a ByteString threw an IOException (should " + - "never happen).", e); + /** + * Construct an UninitializedMessageException reporting missing fields in + * the given message. + */ + protected static UninitializedMessageException + newUninitializedMessageException(Message 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); + } + } + } } } - public BuilderType mergeFrom(ByteString data, - ExtensionRegistry extensionRegistry) - throws InvalidProtocolBufferException { - try { - CodedInputStream input = data.newCodedInput(); - mergeFrom(input, extensionRegistry); - input.checkLastTagWas(0); - return (BuilderType) this; - } catch (InvalidProtocolBufferException e) { - throw e; - } catch (IOException e) { - throw new RuntimeException( - "Reading from a ByteString threw an IOException (should " + - "never happen).", e); + 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(); } - public BuilderType mergeFrom(byte[] data) + // =============================================================== + // The following definitions seem to be required in order to make javac + // not produce weird errors like: + // + // java/com/google/protobuf/DynamicMessage.java:203: types + // com.google.protobuf.AbstractMessage.Builder< + // com.google.protobuf.DynamicMessage.Builder> and + // com.google.protobuf.AbstractMessage.Builder< + // com.google.protobuf.DynamicMessage.Builder> are incompatible; both + // define mergeFrom(com.google.protobuf.ByteString), but with unrelated + // return types. + // + // Strangely, these lines are only needed if javac is invoked separately + // on AbstractMessage.java and AbstractMessageLite.java. If javac is + // invoked on both simultaneously, it works. (Or maybe the important + // point is whether or not DynamicMessage.java is compiled together with + // AbstractMessageLite.java -- not sure.) I suspect this is a compiler + // bug. + + @Override + public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException { - return mergeFrom(data, 0, data.length); + return super.mergeFrom(data); } - public BuilderType mergeFrom(byte[] data, int off, int len) + @Override + public BuilderType mergeFrom( + final ByteString data, + final ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { - try { - CodedInputStream input = CodedInputStream.newInstance(data, off, len); - mergeFrom(input); - input.checkLastTagWas(0); - return (BuilderType) this; - } catch (InvalidProtocolBufferException e) { - throw e; - } catch (IOException e) { - throw new RuntimeException( - "Reading from a byte array threw an IOException (should " + - "never happen).", e); - } + return super.mergeFrom(data, extensionRegistry); } - public BuilderType mergeFrom( - byte[] data, - ExtensionRegistry extensionRegistry) + @Override + public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException { - return mergeFrom(data, 0, data.length, extensionRegistry); + return super.mergeFrom(data); } + @Override public BuilderType mergeFrom( - byte[] data, int off, int len, - ExtensionRegistry extensionRegistry) + final byte[] data, final int off, final int len) throws InvalidProtocolBufferException { - try { - CodedInputStream input = CodedInputStream.newInstance(data, off, len); - mergeFrom(input, extensionRegistry); - input.checkLastTagWas(0); - return (BuilderType) this; - } catch (InvalidProtocolBufferException e) { - throw e; - } catch (IOException e) { - throw new RuntimeException( - "Reading from a byte array threw an IOException (should " + - "never happen).", e); - } + return super.mergeFrom(data, off, len); } - public BuilderType mergeFrom(InputStream input) throws IOException { - CodedInputStream codedInput = CodedInputStream.newInstance(input); - mergeFrom(codedInput); - codedInput.checkLastTagWas(0); - return (BuilderType) this; + @Override + public BuilderType mergeFrom( + final byte[] data, + final ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return super.mergeFrom(data, extensionRegistry); } - public BuilderType mergeFrom(InputStream input, - ExtensionRegistry extensionRegistry) - throws IOException { - CodedInputStream codedInput = CodedInputStream.newInstance(input); - mergeFrom(codedInput, extensionRegistry); - codedInput.checkLastTagWas(0); - return (BuilderType) this; + @Override + public BuilderType mergeFrom( + final byte[] data, final int off, final int len, + final ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return super.mergeFrom(data, off, len, extensionRegistry); } - public BuilderType mergeDelimitedFrom(InputStream input, - ExtensionRegistry extensionRegistry) - throws IOException { - final int size = CodedInputStream.readRawVarint32(input); - - // A stream which will not read more than |size| bytes. - InputStream limitedInput = new FilterInputStream(input) { - int limit = size; - - @Override - public int available() throws IOException { - return Math.min(super.available(), limit); - } - - @Override - public int read() throws IOException { - if (limit <= 0) return -1; - int result = super.read(); - if (result >= 0) --limit; - return result; - } + @Override + public BuilderType mergeFrom(final InputStream input) + throws IOException { + return super.mergeFrom(input); + } - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (limit <= 0) return -1; - len = Math.min(len, limit); - int result = super.read(b, off, len); - if (result >= 0) limit -= result; - return result; - } + @Override + public BuilderType mergeFrom( + final InputStream input, + final ExtensionRegistryLite extensionRegistry) + throws IOException { + return super.mergeFrom(input, extensionRegistry); + } - @Override - public long skip(long n) throws IOException { - long result = super.skip(Math.min(n, limit)); - if (result >= 0) limit -= result; - return result; - } - }; - return mergeFrom(limitedInput, extensionRegistry); + @Override + public BuilderType mergeDelimitedFrom(final InputStream input) + throws IOException { + return super.mergeDelimitedFrom(input); } - public BuilderType mergeDelimitedFrom(InputStream input) + @Override + public BuilderType mergeDelimitedFrom( + final InputStream input, + final ExtensionRegistryLite extensionRegistry) throws IOException { - return mergeDelimitedFrom(input, ExtensionRegistry.getEmptyRegistry()); + 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 new file mode 100644 index 00000000..489577df --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractMessageLite.java @@ -0,0 +1,321 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collection; + +/** + * A partial implementation of the {@link MessageLite} interface which + * implements as many methods of that interface as possible in terms of other + * methods. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class AbstractMessageLite implements MessageLite { + public ByteString toByteString() { + try { + final ByteString.CodedBuilder out = + ByteString.newCodedBuilder(getSerializedSize()); + writeTo(out.getCodedOutput()); + return out.build(); + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public byte[] toByteArray() { + try { + final byte[] result = new byte[getSerializedSize()]; + final CodedOutputStream output = CodedOutputStream.newInstance(result); + writeTo(output); + output.checkNoSpaceLeft(); + return result; + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + public void writeTo(final OutputStream output) throws IOException { + final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + public void writeDelimitedTo(final OutputStream output) throws IOException { + final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + codedOutput.writeRawVarint32(getSerializedSize()); + writeTo(codedOutput); + codedOutput.flush(); + } + + /** + * A partial implementation of the {@link Message.Builder} interface which + * implements as many methods of that interface as possible in terms of + * other methods. + */ + @SuppressWarnings("unchecked") + public static abstract class Builder<BuilderType extends Builder> + implements MessageLite.Builder { + // The compiler produces an error if this is not declared explicitly. + @Override + public abstract BuilderType clone(); + + public BuilderType mergeFrom(final CodedInputStream input) + throws IOException { + // TODO(kenton): Don't use null here. Currently we have to because + // using ExtensionRegistry.getEmptyRegistry() would imply a dependency + // on ExtensionRegistry. However, AbstractMessage overrides this with + // a correct implementation, and lite messages don't yet support + // extensions, so it ends up not mattering for now. It will matter + // once lite messages support extensions. + return mergeFrom(input, null); + } + + // Re-defined here for return type covariance. + public abstract BuilderType mergeFrom( + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry) + throws IOException; + + public BuilderType mergeFrom(final ByteString data) + throws InvalidProtocolBufferException { + try { + final CodedInputStream input = data.newCodedInput(); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom( + final ByteString data, + final ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + final CodedInputStream input = data.newCodedInput(); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(final byte[] data) + throws InvalidProtocolBufferException { + return mergeFrom(data, 0, data.length); + } + + public BuilderType mergeFrom(final byte[] data, final int off, + final int len) + throws InvalidProtocolBufferException { + try { + final CodedInputStream input = + CodedInputStream.newInstance(data, off, len); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom( + final byte[] data, + final ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + return mergeFrom(data, 0, data.length, extensionRegistry); + } + + public BuilderType mergeFrom( + final byte[] data, final int off, final int len, + final ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + try { + final CodedInputStream input = + CodedInputStream.newInstance(data, off, len); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(final InputStream input) throws IOException { + final CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + + public BuilderType mergeFrom( + final InputStream input, + final ExtensionRegistryLite extensionRegistry) + throws IOException { + final CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput, extensionRegistry); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + + /** + * An InputStream implementations which reads from some other InputStream + * but is limited to a particular number of bytes. Used by + * mergeDelimitedFrom(). This is intentionally package-private so that + * UnknownFieldSet can share it. + */ + static final class LimitedInputStream extends FilterInputStream { + private int limit; + + LimitedInputStream(InputStream in, int limit) { + super(in); + this.limit = limit; + } + + @Override + public int available() throws IOException { + return Math.min(super.available(), limit); + } + + @Override + public int read() throws IOException { + if (limit <= 0) { + return -1; + } + final int result = super.read(); + if (result >= 0) { + --limit; + } + return result; + } + + @Override + public int read(final byte[] b, final int off, int len) + throws IOException { + if (limit <= 0) { + return -1; + } + len = Math.min(len, limit); + final int result = super.read(b, off, len); + if (result >= 0) { + limit -= result; + } + return result; + } + + @Override + public long skip(final long n) throws IOException { + final long result = super.skip(Math.min(n, limit)); + if (result >= 0) { + limit -= result; + } + return result; + } + } + + public BuilderType mergeDelimitedFrom( + final InputStream input, + final ExtensionRegistryLite extensionRegistry) + throws IOException { + final int size = CodedInputStream.readRawVarint32(input); + final InputStream limitedInput = new LimitedInputStream(input, size); + return mergeFrom(limitedInput, extensionRegistry); + } + + public BuilderType mergeDelimitedFrom(final InputStream input) + throws IOException { + final int size = CodedInputStream.readRawVarint32(input); + final InputStream limitedInput = new LimitedInputStream(input, size); + return mergeFrom(limitedInput); + } + + /** + * Construct an UninitializedMessageException reporting missing fields in + * the given message. + */ + protected static UninitializedMessageException + newUninitializedMessageException(MessageLite message) { + return new UninitializedMessageException(message); + } + + /** + * Adds the {@code values} to the {@code list}. This is a helper method + * used by generated code. Users should ignore it. + * + * @throws NullPointerException if any of the elements of {@code values} is + * null. + */ + protected static <T> void addAll(final Iterable<T> values, + final Collection<? super T> list) { + 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) { + list.add(value); + } + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index f376e7a1..c83c335c 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -46,7 +46,7 @@ import java.nio.ByteBuffer; public final class ByteString { private final byte[] bytes; - private ByteString(byte[] bytes) { + private ByteString(final byte[] bytes) { this.bytes = bytes; } @@ -55,7 +55,7 @@ public final class ByteString { * * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size */ - public byte byteAt(int index) { + public byte byteAt(final int index) { return bytes[index]; } @@ -63,14 +63,14 @@ public final class ByteString { * Gets the number of bytes. */ public int size() { - return this.bytes.length; + return bytes.length; } /** * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. */ public boolean isEmpty() { - return this.bytes.length == 0; + return bytes.length == 0; } // ================================================================= @@ -84,8 +84,9 @@ public final class ByteString { /** * Copies the given bytes into a {@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 ByteString(copy); } @@ -93,7 +94,7 @@ public final class ByteString { /** * Copies the given bytes into a {@code ByteString}. */ - public static ByteString copyFrom(byte[] bytes) { + public static ByteString copyFrom(final byte[] bytes) { return copyFrom(bytes, 0, bytes.length); } @@ -101,7 +102,7 @@ public final class ByteString { * Encodes {@code text} into a sequence of bytes using the named charset * and returns the result as a {@code ByteString}. */ - public static ByteString copyFrom(String text, String charsetName) + public static ByteString copyFrom(final String text, final String charsetName) throws UnsupportedEncodingException { return new ByteString(text.getBytes(charsetName)); } @@ -110,7 +111,7 @@ public final class ByteString { * Encodes {@code text} into a sequence of UTF-8 bytes and returns the * result as a {@code ByteString}. */ - public static ByteString copyFromUtf8(String text) { + public static ByteString copyFromUtf8(final String text) { try { return new ByteString(text.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { @@ -127,7 +128,7 @@ public final class ByteString { * @param target buffer to copy into * @param offset in the target buffer */ - public void copyTo(byte[] target, int offset) { + public void copyTo(final byte[] target, final int offset) { System.arraycopy(bytes, 0, target, offset, bytes.length); } @@ -139,8 +140,9 @@ public final class ByteString { * @param targetOffset offset within the target buffer * @param size number of bytes to copy */ - public void copyTo(byte[] target, int sourceOffset, int targetOffset, - int size) { + public void copyTo(final byte[] target, final int sourceOffset, + final int targetOffset, + final int size) { System.arraycopy(bytes, sourceOffset, target, targetOffset, size); } @@ -148,9 +150,9 @@ public final class ByteString { * Copies bytes to a {@code byte[]}. */ public byte[] toByteArray() { - int size = this.bytes.length; - byte[] copy = new byte[size]; - System.arraycopy(this.bytes, 0, copy, 0, size); + final int size = bytes.length; + final byte[] copy = new byte[size]; + System.arraycopy(bytes, 0, copy, 0, size); return copy; } @@ -159,7 +161,7 @@ public final class ByteString { * same backing byte array. */ public ByteBuffer asReadOnlyByteBuffer() { - ByteBuffer byteBuffer = ByteBuffer.wrap(this.bytes); + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); return byteBuffer.asReadOnlyBuffer(); } @@ -167,9 +169,9 @@ public final class ByteString { * Constructs a new {@code String} by decoding the bytes using the * specified charset. */ - public String toString(String charsetName) + public String toString(final String charsetName) throws UnsupportedEncodingException { - return new String(this.bytes, charsetName); + return new String(bytes, charsetName); } /** @@ -177,7 +179,7 @@ public final class ByteString { */ public String toStringUtf8() { try { - return new String(this.bytes, "UTF-8"); + return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 not supported?", e); } @@ -187,7 +189,7 @@ public final class ByteString { // equals() and hashCode() @Override - public boolean equals(Object o) { + public boolean equals(final Object o) { if (o == this) { return true; } @@ -196,16 +198,16 @@ public final class ByteString { return false; } - ByteString other = (ByteString) o; - int size = this.bytes.length; + final ByteString other = (ByteString) o; + final int size = bytes.length; if (size != other.bytes.length) { return false; } - byte[] bytes = this.bytes; - byte[] otherBytes = other.bytes; + final byte[] thisBytes = bytes; + final byte[] otherBytes = other.bytes; for (int i = 0; i < size; i++) { - if (bytes[i] != otherBytes[i]) { + if (thisBytes[i] != otherBytes[i]) { return false; } } @@ -213,25 +215,25 @@ public final class ByteString { return true; } - volatile int hash = 0; + private volatile int hash = 0; @Override public int hashCode() { - int h = this.hash; + int h = hash; if (h == 0) { - byte[] bytes = this.bytes; - int size = this.bytes.length; + final byte[] thisBytes = bytes; + final int size = bytes.length; h = size; for (int i = 0; i < size; i++) { - h = h * 31 + bytes[i]; + h = h * 31 + thisBytes[i]; } if (h == 0) { h = 1; } - this.hash = h; + hash = h; } return h; @@ -264,7 +266,7 @@ public final class ByteString { /** * Creates a new {@link Output} with the given initial capacity. */ - public static Output newOutput(int initialCapacity) { + public static Output newOutput(final int initialCapacity) { return new Output(new ByteArrayOutputStream(initialCapacity)); } @@ -285,7 +287,7 @@ public final class ByteString { /** * Constructs a new output with the given initial capacity. */ - private Output(ByteArrayOutputStream bout) { + private Output(final ByteArrayOutputStream bout) { super(bout); this.bout = bout; } @@ -294,14 +296,14 @@ public final class ByteString { * Creates a {@code ByteString} instance from this {@code Output}. */ public ByteString toByteString() { - byte[] byteArray = bout.toByteArray(); + final byte[] byteArray = bout.toByteArray(); return new ByteString(byteArray); } } /** * Constructs a new ByteString builder, which allows you to efficiently - * construct a {@code ByteString} by writing to a {@link CodedOutputSteam}. + * 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}. * @@ -312,7 +314,7 @@ public final class ByteString { * @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); } @@ -321,7 +323,7 @@ public final class ByteString { 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); } diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index b6be2e87..9125957d 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -51,21 +51,22 @@ public final class CodedInputStream { /** * Create a new CodedInputStream wrapping the given InputStream. */ - public static CodedInputStream newInstance(InputStream input) { + public static CodedInputStream newInstance(final InputStream input) { return new CodedInputStream(input); } /** * Create a new CodedInputStream wrapping the given byte array. */ - public static CodedInputStream newInstance(byte[] buf) { + public static CodedInputStream newInstance(final byte[] buf) { return newInstance(buf, 0, buf.length); } /** * Create a new CodedInputStream wrapping the given byte array slice. */ - public static CodedInputStream newInstance(byte[] buf, int off, int len) { + public static CodedInputStream newInstance(final byte[] buf, final int off, + final int len) { return new CodedInputStream(buf, off, len); } @@ -98,7 +99,8 @@ public final class CodedInputStream { * @throws InvalidProtocolBufferException {@code value} does not match the * last tag. */ - public void checkLastTagWas(int value) throws InvalidProtocolBufferException { + public void checkLastTagWas(final int value) + throws InvalidProtocolBufferException { if (lastTag != value) { throw InvalidProtocolBufferException.invalidEndTag(); } @@ -110,7 +112,7 @@ public final class CodedInputStream { * @return {@code false} if the tag is an endgroup tag, in which case * nothing is skipped. Otherwise, returns {@code true}. */ - public boolean skipField(int tag) throws IOException { + public boolean skipField(final int tag) throws IOException { switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: readInt32(); @@ -143,8 +145,10 @@ public final class CodedInputStream { */ public void skipMessage() throws IOException { while (true) { - int tag = readTag(); - if (tag == 0 || !skipField(tag)) return; + final int tag = readTag(); + if (tag == 0 || !skipField(tag)) { + return; + } } } @@ -192,11 +196,11 @@ public final class CodedInputStream { /** Read a {@code string} field value from the stream. */ public String readString() throws IOException { - int size = readRawVarint32(); + 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. - String result = new String(buffer, bufferPos, size, "UTF-8"); + final String result = new String(buffer, bufferPos, size, "UTF-8"); bufferPos += size; return result; } else { @@ -206,8 +210,9 @@ public final class CodedInputStream { } /** Read a {@code group} field value from the stream. */ - public void readGroup(int fieldNumber, Message.Builder builder, - ExtensionRegistry extensionRegistry) + public void readGroup(final int fieldNumber, + final MessageLite.Builder builder, + final ExtensionRegistryLite extensionRegistry) throws IOException { if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.recursionLimitExceeded(); @@ -222,28 +227,31 @@ public final class CodedInputStream { /** * Reads a {@code group} field value from the stream and merges it into the * given {@link UnknownFieldSet}. + * + * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so + * you can just call {@link #readGroup}. */ - public void readUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) + @Deprecated + public void readUnknownGroup(final int fieldNumber, + final MessageLite.Builder builder) throws IOException { - if (recursionDepth >= recursionLimit) { - throw InvalidProtocolBufferException.recursionLimitExceeded(); - } - ++recursionDepth; - builder.mergeFrom(this); - checkLastTagWas( - WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); - --recursionDepth; + // We know that UnknownFieldSet will ignore any ExtensionRegistry so it + // is safe to pass null here. (We can't call + // ExtensionRegistry.getEmptyRegistry() because that would make this + // class depend on ExtensionRegistry, which is not part of the lite + // library.) + readGroup(fieldNumber, builder, null); } /** Read an embedded message field value from the stream. */ - public void readMessage(Message.Builder builder, - ExtensionRegistry extensionRegistry) + public void readMessage(final MessageLite.Builder builder, + final ExtensionRegistryLite extensionRegistry) throws IOException { - int length = readRawVarint32(); + final int length = readRawVarint32(); if (recursionDepth >= recursionLimit) { throw InvalidProtocolBufferException.recursionLimitExceeded(); } - int oldLimit = pushLimit(length); + final int oldLimit = pushLimit(length); ++recursionDepth; builder.mergeFrom(this, extensionRegistry); checkLastTagWas(0); @@ -253,11 +261,11 @@ public final class CodedInputStream { /** Read a {@code bytes} field value from the stream. */ public ByteString readBytes() throws IOException { - int size = readRawVarint32(); - if (size < bufferSize - bufferPos && size > 0) { + 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. - ByteString result = ByteString.copyFrom(buffer, bufferPos, size); + final ByteString result = ByteString.copyFrom(buffer, bufferPos, size); bufferPos += size; return result; } else { @@ -299,52 +307,6 @@ public final class CodedInputStream { return decodeZigZag64(readRawVarint64()); } - /** - * Read a field of any primitive type. Enums, groups, and embedded - * messages are not handled by this method. - * - * @param type Declared type of the field. - * @return An object representing the field's value, of the exact - * type which would be returned by - * {@link Message#getField(Descriptors.FieldDescriptor)} for - * this field. - */ - public Object readPrimitiveField( - Descriptors.FieldDescriptor.Type type) throws IOException { - switch (type) { - case DOUBLE : return readDouble (); - case FLOAT : return readFloat (); - case INT64 : return readInt64 (); - case UINT64 : return readUInt64 (); - case INT32 : return readInt32 (); - case FIXED64 : return readFixed64 (); - case FIXED32 : return readFixed32 (); - case BOOL : return readBool (); - case STRING : return readString (); - case BYTES : return readBytes (); - case UINT32 : return readUInt32 (); - case SFIXED32: return readSFixed32(); - case SFIXED64: return readSFixed64(); - case SINT32 : return readSInt32 (); - case SINT64 : return readSInt64 (); - - case GROUP: - throw new IllegalArgumentException( - "readPrimitiveField() cannot handle nested groups."); - case MESSAGE: - throw new IllegalArgumentException( - "readPrimitiveField() cannot handle embedded messages."); - case ENUM: - // We don't hanlde enums because we don't know what to do if the - // value is not recognized. - throw new IllegalArgumentException( - "readPrimitiveField() cannot handle enums."); - } - - throw new RuntimeException( - "There is no way to get here, but the compiler thinks otherwise."); - } - // ================================================================= /** @@ -373,7 +335,9 @@ public final class CodedInputStream { if (tmp < 0) { // Discard upper 32 bits. for (int i = 0; i < 5; i++) { - if (readRawByte() >= 0) return result; + if (readRawByte() >= 0) { + return result; + } } throw InvalidProtocolBufferException.malformedVarint(); } @@ -390,11 +354,11 @@ public final class CodedInputStream { * then you would probably end up reading past the end of the varint since * CodedInputStream buffers its input. */ - static int readRawVarint32(InputStream input) throws IOException { + static int readRawVarint32(final InputStream input) throws IOException { int result = 0; int offset = 0; for (; offset < 32; offset += 7) { - int b = input.read(); + final int b = input.read(); if (b == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } @@ -405,7 +369,7 @@ public final class CodedInputStream { } // Keep reading up to 64 bits. for (; offset < 64; offset += 7) { - int b = input.read(); + final int b = input.read(); if (b == -1) { throw InvalidProtocolBufferException.truncatedMessage(); } @@ -421,9 +385,11 @@ public final class CodedInputStream { int shift = 0; long result = 0; while (shift < 64) { - byte b = readRawByte(); + final byte b = readRawByte(); result |= (long)(b & 0x7F) << shift; - if ((b & 0x80) == 0) return result; + if ((b & 0x80) == 0) { + return result; + } shift += 7; } throw InvalidProtocolBufferException.malformedVarint(); @@ -431,10 +397,10 @@ public final class CodedInputStream { /** Read a 32-bit little-endian integer from the stream. */ public int readRawLittleEndian32() throws IOException { - byte b1 = readRawByte(); - byte b2 = readRawByte(); - byte b3 = readRawByte(); - byte b4 = readRawByte(); + 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) | @@ -443,14 +409,14 @@ public final class CodedInputStream { /** Read a 64-bit little-endian integer from the stream. */ public long readRawLittleEndian64() throws IOException { - byte b1 = readRawByte(); - byte b2 = readRawByte(); - byte b3 = readRawByte(); - byte b4 = readRawByte(); - byte b5 = readRawByte(); - byte b6 = readRawByte(); - byte b7 = readRawByte(); - byte b8 = readRawByte(); + 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) | @@ -471,7 +437,7 @@ public final class CodedInputStream { * Java has no explicit unsigned support. * @return A signed 32-bit integer. */ - public static int decodeZigZag32(int n) { + public static int decodeZigZag32(final int n) { return (n >>> 1) ^ -(n & 1); } @@ -485,31 +451,31 @@ public final class CodedInputStream { * Java has no explicit unsigned support. * @return A signed 64-bit integer. */ - public static long decodeZigZag64(long n) { + public static long decodeZigZag64(final long n) { return (n >>> 1) ^ -(n & 1); } // ----------------------------------------------------------------- - private byte[] buffer; + private final byte[] buffer; private int bufferSize; - private int bufferSizeAfterLimit = 0; + private int bufferSizeAfterLimit; private int bufferPos; - private InputStream input; - private int lastTag = 0; + private final InputStream input; + private int lastTag; /** * The total number of bytes read before the current buffer. The total * bytes read up to the current position can be computed as * {@code totalBytesRetired + bufferPos}. */ - private int totalBytesRetired = 0; + private int totalBytesRetired; /** The absolute position of the end of the current message. */ private int currentLimit = Integer.MAX_VALUE; /** See setRecursionLimit() */ - private int recursionDepth = 0; + private int recursionDepth; private int recursionLimit = DEFAULT_RECURSION_LIMIT; /** See setSizeLimit() */ @@ -519,17 +485,17 @@ public final class CodedInputStream { private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB private static final int BUFFER_SIZE = 4096; - private CodedInputStream(byte[] buffer, int off, int len) { + private CodedInputStream(final byte[] buffer, final int off, final int len) { this.buffer = buffer; - this.bufferSize = off + len; - this.bufferPos = off; - this.input = null; + bufferSize = off + len; + bufferPos = off; + input = null; } - private CodedInputStream(InputStream input) { - this.buffer = new byte[BUFFER_SIZE]; - this.bufferSize = 0; - this.bufferPos = 0; + private CodedInputStream(final InputStream input) { + buffer = new byte[BUFFER_SIZE]; + bufferSize = 0; + bufferPos = 0; this.input = input; } @@ -540,12 +506,12 @@ public final class CodedInputStream { * * @return the old limit. */ - public int setRecursionLimit(int limit) { + public int setRecursionLimit(final int limit) { if (limit < 0) { throw new IllegalArgumentException( "Recursion limit cannot be negative: " + limit); } - int oldLimit = recursionLimit; + final int oldLimit = recursionLimit; recursionLimit = limit; return oldLimit; } @@ -566,12 +532,12 @@ public final class CodedInputStream { * * @return the old limit. */ - public int setSizeLimit(int limit) { + public int setSizeLimit(final int limit) { if (limit < 0) { throw new IllegalArgumentException( "Size limit cannot be negative: " + limit); } - int oldLimit = sizeLimit; + final int oldLimit = sizeLimit; sizeLimit = limit; return oldLimit; } @@ -594,7 +560,7 @@ public final class CodedInputStream { throw InvalidProtocolBufferException.negativeSize(); } byteLimit += totalBytesRetired + bufferPos; - int oldLimit = currentLimit; + final int oldLimit = currentLimit; if (byteLimit > oldLimit) { throw InvalidProtocolBufferException.truncatedMessage(); } @@ -607,7 +573,7 @@ public final class CodedInputStream { private void recomputeBufferSizeAfterLimit() { bufferSize += bufferSizeAfterLimit; - int bufferEnd = totalBytesRetired + bufferSize; + final int bufferEnd = totalBytesRetired + bufferSize; if (bufferEnd > currentLimit) { // Limit is in current buffer. bufferSizeAfterLimit = bufferEnd - currentLimit; @@ -622,7 +588,7 @@ public final class CodedInputStream { * * @param oldLimit The old limit, as returned by {@code pushLimit}. */ - public void popLimit(int oldLimit) { + public void popLimit(final int oldLimit) { currentLimit = oldLimit; recomputeBufferSizeAfterLimit(); } @@ -636,7 +602,7 @@ public final class CodedInputStream { return -1; } - int currentAbsolutePosition = totalBytesRetired + bufferPos; + final int currentAbsolutePosition = totalBytesRetired + bufferPos; return currentLimit - currentAbsolutePosition; } @@ -656,7 +622,7 @@ public final class CodedInputStream { * or it will throw an exception. If {@code mustSucceed} is false, * refillBuffer() returns false if no more bytes were available. */ - private boolean refillBuffer(boolean mustSucceed) throws IOException { + private boolean refillBuffer(final boolean mustSucceed) throws IOException { if (bufferPos < bufferSize) { throw new IllegalStateException( "refillBuffer() called when buffer wasn't empty."); @@ -689,7 +655,7 @@ public final class CodedInputStream { } } else { recomputeBufferSizeAfterLimit(); - int totalBytesRead = + final int totalBytesRead = totalBytesRetired + bufferSize + bufferSizeAfterLimit; if (totalBytesRead > sizeLimit || totalBytesRead < 0) { throw InvalidProtocolBufferException.sizeLimitExceeded(); @@ -717,7 +683,7 @@ public final class CodedInputStream { * @throws InvalidProtocolBufferException The end of the stream or the current * limit was reached. */ - public byte[] readRawBytes(int size) throws IOException { + public byte[] readRawBytes(final int size) throws IOException { if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } @@ -731,7 +697,7 @@ public final class CodedInputStream { if (size <= bufferSize - bufferPos) { // We have all the bytes we need already. - byte[] bytes = new byte[size]; + final byte[] bytes = new byte[size]; System.arraycopy(buffer, bufferPos, bytes, 0, size); bufferPos += size; return bytes; @@ -740,7 +706,7 @@ public final class CodedInputStream { // of bytes. We can safely allocate the resulting array ahead of time. // First copy what we have. - byte[] bytes = new byte[size]; + final byte[] bytes = new byte[size]; int pos = bufferSize - bufferPos; System.arraycopy(buffer, bufferPos, bytes, 0, pos); bufferPos = bufferSize; @@ -772,8 +738,8 @@ public final class CodedInputStream { // Remember the buffer markers since we'll have to copy the bytes out of // it later. - int originalBufferPos = bufferPos; - int originalBufferSize = bufferSize; + final int originalBufferPos = bufferPos; + final int originalBufferSize = bufferSize; // Mark the current buffer consumed. totalBytesRetired += bufferSize; @@ -782,13 +748,13 @@ public final class CodedInputStream { // Read all the rest of the bytes we need. int sizeLeft = size - (originalBufferSize - originalBufferPos); - List<byte[]> chunks = new ArrayList<byte[]>(); + final List<byte[]> chunks = new ArrayList<byte[]>(); while (sizeLeft > 0) { - byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)]; + final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)]; int pos = 0; while (pos < chunk.length) { - int n = (input == null) ? -1 : + final int n = (input == null) ? -1 : input.read(chunk, pos, chunk.length - pos); if (n == -1) { throw InvalidProtocolBufferException.truncatedMessage(); @@ -801,14 +767,14 @@ public final class CodedInputStream { } // OK, got everything. Now concatenate it all into one buffer. - byte[] bytes = new byte[size]; + final byte[] bytes = new byte[size]; // Start by copying the leftover bytes from this.buffer. int pos = originalBufferSize - originalBufferPos; System.arraycopy(buffer, originalBufferPos, bytes, 0, pos); // And now all the chunks. - for (byte[] chunk : chunks) { + for (final byte[] chunk : chunks) { System.arraycopy(chunk, 0, bytes, pos, chunk.length); pos += chunk.length; } @@ -824,7 +790,7 @@ public final class CodedInputStream { * @throws InvalidProtocolBufferException The end of the stream or the current * limit was reached. */ - public void skipRawBytes(int size) throws IOException { + public void skipRawBytes(final int size) throws IOException { if (size < 0) { throw InvalidProtocolBufferException.negativeSize(); } @@ -848,7 +814,7 @@ public final class CodedInputStream { // Then skip directly from the InputStream for the rest. while (pos < size) { - int n = (input == null) ? -1 : (int) input.skip(size - pos); + final int n = (input == null) ? -1 : (int) input.skip(size - pos); if (n <= 0) { throw InvalidProtocolBufferException.truncatedMessage(); } diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java index 69c22d89..d3907a7c 100644 --- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -32,6 +32,7 @@ package com.google.protobuf; import java.io.OutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; /** * Encodes and writes protocol message fields. @@ -55,29 +56,30 @@ public final class CodedOutputStream { private final OutputStream output; /** - * The buffer size used in {@link #newInstance(java.io.OutputStream)}. + * The buffer size used in {@link #newInstance(OutputStream)}. */ public static final int DEFAULT_BUFFER_SIZE = 4096; - private CodedOutputStream(byte[] buffer, int offset, int length) { - this.output = null; + private CodedOutputStream(final byte[] buffer, final int offset, + final int length) { + output = null; this.buffer = buffer; - this.position = offset; - this.limit = offset + length; + position = offset; + limit = offset + length; } - private CodedOutputStream(OutputStream output, byte[] buffer) { + private CodedOutputStream(final OutputStream output, final byte[] buffer) { this.output = output; this.buffer = buffer; - this.position = 0; - this.limit = buffer.length; + position = 0; + limit = buffer.length; } /** * Create a new {@code CodedOutputStream} wrapping the given * {@code OutputStream}. */ - public static CodedOutputStream newInstance(OutputStream output) { + public static CodedOutputStream newInstance(final OutputStream output) { return newInstance(output, DEFAULT_BUFFER_SIZE); } @@ -85,8 +87,8 @@ public final class CodedOutputStream { * Create a new {@code CodedOutputStream} wrapping the given * {@code OutputStream} with a given buffer size. */ - public static CodedOutputStream newInstance(OutputStream output, - int bufferSize) { + public static CodedOutputStream newInstance(final OutputStream output, + final int bufferSize) { return new CodedOutputStream(output, new byte[bufferSize]); } @@ -97,7 +99,7 @@ public final class CodedOutputStream { * array is faster than writing to an {@code OutputStream}. See also * {@link ByteString#newCodedBuilder}. */ - public static CodedOutputStream newInstance(byte[] flatArray) { + public static CodedOutputStream newInstance(final byte[] flatArray) { return newInstance(flatArray, 0, flatArray.length); } @@ -108,96 +110,115 @@ public final class CodedOutputStream { * array is faster than writing to an {@code OutputStream}. See also * {@link ByteString#newCodedBuilder}. */ - public static CodedOutputStream newInstance(byte[] flatArray, int offset, - int length) { + public static CodedOutputStream newInstance(final byte[] flatArray, + final int offset, + final int length) { return new CodedOutputStream(flatArray, offset, length); } // ----------------------------------------------------------------- /** Write a {@code double} field, including tag, to the stream. */ - public void writeDouble(int fieldNumber, double value) throws IOException { + public void writeDouble(final int fieldNumber, final double value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); writeDoubleNoTag(value); } /** Write a {@code float} field, including tag, to the stream. */ - public void writeFloat(int fieldNumber, float value) throws IOException { + public void writeFloat(final int fieldNumber, final float value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); writeFloatNoTag(value); } /** Write a {@code uint64} field, including tag, to the stream. */ - public void writeUInt64(int fieldNumber, long value) throws IOException { + public void writeUInt64(final int fieldNumber, final long value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeUInt64NoTag(value); } /** Write an {@code int64} field, including tag, to the stream. */ - public void writeInt64(int fieldNumber, long value) throws IOException { + public void writeInt64(final int fieldNumber, final long value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeInt64NoTag(value); } /** Write an {@code int32} field, including tag, to the stream. */ - public void writeInt32(int fieldNumber, int value) throws IOException { + public void writeInt32(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeInt32NoTag(value); } /** Write a {@code fixed64} field, including tag, to the stream. */ - public void writeFixed64(int fieldNumber, long value) throws IOException { + public void writeFixed64(final int fieldNumber, final long value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); writeFixed64NoTag(value); } /** Write a {@code fixed32} field, including tag, to the stream. */ - public void writeFixed32(int fieldNumber, int value) throws IOException { + public void writeFixed32(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); writeFixed32NoTag(value); } /** Write a {@code bool} field, including tag, to the stream. */ - public void writeBool(int fieldNumber, boolean value) throws IOException { + public void writeBool(final int fieldNumber, final boolean value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeBoolNoTag(value); } /** Write a {@code string} field, including tag, to the stream. */ - public void writeString(int fieldNumber, String value) throws IOException { + public void writeString(final int fieldNumber, final String value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeStringNoTag(value); } /** Write a {@code group} field, including tag, to the stream. */ - public void writeGroup(int fieldNumber, Message value) throws IOException { + public void writeGroup(final int fieldNumber, final MessageLite value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); writeGroupNoTag(value); writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); } - /** Write a group represented by an {@link UnknownFieldSet}. */ - public void writeUnknownGroup(int fieldNumber, UnknownFieldSet value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); - writeUnknownGroupNoTag(value); - writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + /** + * Write a group represented by an {@link UnknownFieldSet}. + * + * @deprecated UnknownFieldSet now implements MessageLite, so you can just + * call {@link #writeGroup}. + */ + @Deprecated + public void writeUnknownGroup(final int fieldNumber, + final MessageLite value) + throws IOException { + writeGroup(fieldNumber, value); } /** Write an embedded message field, including tag, to the stream. */ - public void writeMessage(int fieldNumber, Message value) throws IOException { + public void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeMessageNoTag(value); } /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeBytes(int fieldNumber, ByteString value) throws IOException { + public void writeBytes(final int fieldNumber, final ByteString value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeBytesNoTag(value); } /** Write a {@code uint32} field, including tag, to the stream. */ - public void writeUInt32(int fieldNumber, int value) throws IOException { + public void writeUInt32(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeUInt32NoTag(value); } @@ -206,31 +227,36 @@ public final class CodedOutputStream { * Write an enum field, including tag, to the stream. Caller is responsible * for converting the enum value to its numeric value. */ - public void writeEnum(int fieldNumber, int value) throws IOException { + public void writeEnum(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeEnumNoTag(value); } /** Write an {@code sfixed32} field, including tag, to the stream. */ - public void writeSFixed32(int fieldNumber, int value) throws IOException { + public void writeSFixed32(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); writeSFixed32NoTag(value); } /** Write an {@code sfixed64} field, including tag, to the stream. */ - public void writeSFixed64(int fieldNumber, long value) throws IOException { + public void writeSFixed64(final int fieldNumber, final long value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); writeSFixed64NoTag(value); } /** Write an {@code sint32} field, including tag, to the stream. */ - public void writeSInt32(int fieldNumber, int value) throws IOException { + public void writeSInt32(final int fieldNumber, final int value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeSInt32NoTag(value); } /** Write an {@code sint64} field, including tag, to the stream. */ - public void writeSInt64(int fieldNumber, long value) throws IOException { + public void writeSInt64(final int fieldNumber, final long value) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeSInt64NoTag(value); } @@ -239,8 +265,9 @@ public final class CodedOutputStream { * Write a MessageSet extension field to the stream. For historical reasons, * the wire format differs from normal fields. */ - public void writeMessageSetExtension(int fieldNumber, Message value) - throws IOException { + public void writeMessageSetExtension(final int fieldNumber, + final MessageLite value) + throws IOException { writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); @@ -251,7 +278,8 @@ public final class CodedOutputStream { * Write an unparsed MessageSet extension field to the stream. For * historical reasons, the wire format differs from normal fields. */ - public void writeRawMessageSetExtension(int fieldNumber, ByteString value) + public void writeRawMessageSetExtension(final int fieldNumber, + final ByteString value) throws IOException { writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); @@ -259,89 +287,30 @@ public final class CodedOutputStream { writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); } - /** - * Write a field of arbitrary type, including tag, to the stream. - * - * @param type The field's type. - * @param number The field's number. - * @param value Object representing the field's value. Must be of the exact - * type which would be returned by - * {@link Message#getField(Descriptors.FieldDescriptor)} for - * this field. - */ - public void writeField(Descriptors.FieldDescriptor.Type type, - int number, - Object value) throws IOException { - // Special case for groups, which need a start and end tag; other fields - // can just use writeTag() and writeFieldNoTag(). - if (type == Descriptors.FieldDescriptor.Type.GROUP) { - writeGroup(number, (Message) value); - } else { - writeTag(number, WireFormat.getWireFormatForFieldType(type)); - writeFieldNoTag(type, value); - } - } - - /** - * Write a field of arbitrary type, without its tag, to the stream. - * - * @param type The field's type. - * @param value Object representing the field's value. Must be of the exact - * type which would be returned by - * {@link Message#getField(Descriptors.FieldDescriptor)} for - * this field. - */ - public void writeFieldNoTag(Descriptors.FieldDescriptor.Type type, - Object value) throws IOException { - switch (type) { - case DOUBLE : writeDoubleNoTag ((Double ) value); break; - case FLOAT : writeFloatNoTag ((Float ) value); break; - case INT64 : writeInt64NoTag ((Long ) value); break; - case UINT64 : writeUInt64NoTag ((Long ) value); break; - case INT32 : writeInt32NoTag ((Integer ) value); break; - case FIXED64 : writeFixed64NoTag ((Long ) value); break; - case FIXED32 : writeFixed32NoTag ((Integer ) value); break; - case BOOL : writeBoolNoTag ((Boolean ) value); break; - case STRING : writeStringNoTag ((String ) value); break; - case GROUP : writeGroupNoTag ((Message ) value); break; - case MESSAGE : writeMessageNoTag ((Message ) value); break; - case BYTES : writeBytesNoTag ((ByteString) value); break; - case UINT32 : writeUInt32NoTag ((Integer ) value); break; - case SFIXED32: writeSFixed32NoTag((Integer ) value); break; - case SFIXED64: writeSFixed64NoTag((Long ) value); break; - case SINT32 : writeSInt32NoTag ((Integer ) value); break; - case SINT64 : writeSInt64NoTag ((Long ) value); break; - - case ENUM: - writeEnumNoTag(((Descriptors.EnumValueDescriptor) value).getNumber()); - break; - } - } - // ----------------------------------------------------------------- /** Write a {@code double} field to the stream. */ - public void writeDoubleNoTag(double value) throws IOException { + public void writeDoubleNoTag(final double value) throws IOException { writeRawLittleEndian64(Double.doubleToRawLongBits(value)); } /** Write a {@code float} field to the stream. */ - public void writeFloatNoTag(float value) throws IOException { + public void writeFloatNoTag(final float value) throws IOException { writeRawLittleEndian32(Float.floatToRawIntBits(value)); } /** Write a {@code uint64} field to the stream. */ - public void writeUInt64NoTag(long value) throws IOException { + public void writeUInt64NoTag(final long value) throws IOException { writeRawVarint64(value); } /** Write an {@code int64} field to the stream. */ - public void writeInt64NoTag(long value) throws IOException { + public void writeInt64NoTag(final long value) throws IOException { writeRawVarint64(value); } /** Write an {@code int32} field to the stream. */ - public void writeInt32NoTag(int value) throws IOException { + public void writeInt32NoTag(final int value) throws IOException { if (value >= 0) { writeRawVarint32(value); } else { @@ -351,56 +320,62 @@ public final class CodedOutputStream { } /** Write a {@code fixed64} field to the stream. */ - public void writeFixed64NoTag(long value) throws IOException { + public void writeFixed64NoTag(final long value) throws IOException { writeRawLittleEndian64(value); } /** Write a {@code fixed32} field to the stream. */ - public void writeFixed32NoTag(int value) throws IOException { + public void writeFixed32NoTag(final int value) throws IOException { writeRawLittleEndian32(value); } /** Write a {@code bool} field to the stream. */ - public void writeBoolNoTag(boolean value) throws IOException { + public void writeBoolNoTag(final boolean value) throws IOException { writeRawByte(value ? 1 : 0); } /** Write a {@code string} field to the stream. */ - public void writeStringNoTag(String value) throws IOException { + public void writeStringNoTag(final String value) throws IOException { // 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. - byte[] bytes = value.getBytes("UTF-8"); + final byte[] bytes = value.getBytes("UTF-8"); writeRawVarint32(bytes.length); writeRawBytes(bytes); } /** Write a {@code group} field to the stream. */ - public void writeGroupNoTag(Message value) throws IOException { + public void writeGroupNoTag(final MessageLite value) throws IOException { value.writeTo(this); } - /** Write a group represented by an {@link UnknownFieldSet}. */ - public void writeUnknownGroupNoTag(UnknownFieldSet value) + /** + * Write a group represented by an {@link UnknownFieldSet}. + * + * @deprecated UnknownFieldSet now implements MessageLite, so you can just + * call {@link #writeGroupNoTag}. + */ + @Deprecated + public void writeUnknownGroupNoTag(final MessageLite value) throws IOException { - value.writeTo(this); + writeGroupNoTag(value); } /** Write an embedded message field to the stream. */ - public void writeMessageNoTag(Message value) throws IOException { + public void writeMessageNoTag(final MessageLite value) throws IOException { writeRawVarint32(value.getSerializedSize()); value.writeTo(this); } /** Write a {@code bytes} field to the stream. */ - public void writeBytesNoTag(ByteString value) throws IOException { - byte[] bytes = value.toByteArray(); + public void writeBytesNoTag(final ByteString value) throws IOException { + final byte[] bytes = value.toByteArray(); writeRawVarint32(bytes.length); writeRawBytes(bytes); } /** Write a {@code uint32} field to the stream. */ - public void writeUInt32NoTag(int value) throws IOException { + public void writeUInt32NoTag(final int value) throws IOException { writeRawVarint32(value); } @@ -408,27 +383,27 @@ public final class CodedOutputStream { * Write an enum field to the stream. Caller is responsible * for converting the enum value to its numeric value. */ - public void writeEnumNoTag(int value) throws IOException { + public void writeEnumNoTag(final int value) throws IOException { writeRawVarint32(value); } /** Write an {@code sfixed32} field to the stream. */ - public void writeSFixed32NoTag(int value) throws IOException { + public void writeSFixed32NoTag(final int value) throws IOException { writeRawLittleEndian32(value); } /** Write an {@code sfixed64} field to the stream. */ - public void writeSFixed64NoTag(long value) throws IOException { + public void writeSFixed64NoTag(final long value) throws IOException { writeRawLittleEndian64(value); } /** Write an {@code sint32} field to the stream. */ - public void writeSInt32NoTag(int value) throws IOException { + public void writeSInt32NoTag(final int value) throws IOException { writeRawVarint32(encodeZigZag32(value)); } /** Write an {@code sint64} field to the stream. */ - public void writeSInt64NoTag(long value) throws IOException { + public void writeSInt64NoTag(final long value) throws IOException { writeRawVarint64(encodeZigZag64(value)); } @@ -438,7 +413,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code double} field, including tag. */ - public static int computeDoubleSize(int fieldNumber, double value) { + public static int computeDoubleSize(final int fieldNumber, + final double value) { return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); } @@ -446,7 +422,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code float} field, including tag. */ - public static int computeFloatSize(int fieldNumber, float value) { + public static int computeFloatSize(final int fieldNumber, final float value) { return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); } @@ -454,7 +430,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code uint64} field, including tag. */ - public static int computeUInt64Size(int fieldNumber, long value) { + public static int computeUInt64Size(final int fieldNumber, final long value) { return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); } @@ -462,7 +438,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code int64} field, including tag. */ - public static int computeInt64Size(int fieldNumber, long value) { + public static int computeInt64Size(final int fieldNumber, final long value) { return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); } @@ -470,7 +446,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code int32} field, including tag. */ - public static int computeInt32Size(int fieldNumber, int value) { + public static int computeInt32Size(final int fieldNumber, final int value) { return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); } @@ -478,7 +454,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code fixed64} field, including tag. */ - public static int computeFixed64Size(int fieldNumber, long value) { + public static int computeFixed64Size(final int fieldNumber, + final long value) { return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); } @@ -486,7 +463,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code fixed32} field, including tag. */ - public static int computeFixed32Size(int fieldNumber, int value) { + public static int computeFixed32Size(final int fieldNumber, + final int value) { return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); } @@ -494,7 +472,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bool} field, including tag. */ - public static int computeBoolSize(int fieldNumber, boolean value) { + public static int computeBoolSize(final int fieldNumber, + final boolean value) { return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); } @@ -502,7 +481,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code string} field, including tag. */ - public static int computeStringSize(int fieldNumber, String value) { + public static int computeStringSize(final int fieldNumber, + final String value) { return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); } @@ -510,7 +490,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code group} field, including tag. */ - public static int computeGroupSize(int fieldNumber, Message value) { + public static int computeGroupSize(final int fieldNumber, + final MessageLite value) { return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); } @@ -518,18 +499,22 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code group} field represented by an {@code UnknownFieldSet}, including * tag. + * + * @deprecated UnknownFieldSet now implements MessageLite, so you can just + * call {@link #computeGroupSize}. */ - public static int computeUnknownGroupSize(int fieldNumber, - UnknownFieldSet value) { - return computeTagSize(fieldNumber) * 2 + - computeUnknownGroupSizeNoTag(value); + @Deprecated + public static int computeUnknownGroupSize(final int fieldNumber, + final MessageLite value) { + return computeGroupSize(fieldNumber, value); } /** * Compute the number of bytes that would be needed to encode an * embedded message field, including tag. */ - public static int computeMessageSize(int fieldNumber, Message value) { + public static int computeMessageSize(final int fieldNumber, + final MessageLite value) { return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); } @@ -537,7 +522,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bytes} field, including tag. */ - public static int computeBytesSize(int fieldNumber, ByteString value) { + public static int computeBytesSize(final int fieldNumber, + final ByteString value) { return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); } @@ -545,7 +531,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code uint32} field, including tag. */ - public static int computeUInt32Size(int fieldNumber, int value) { + public static int computeUInt32Size(final int fieldNumber, final int value) { return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); } @@ -554,7 +540,7 @@ public final class CodedOutputStream { * enum field, including tag. Caller is responsible for converting the * enum value to its numeric value. */ - public static int computeEnumSize(int fieldNumber, int value) { + public static int computeEnumSize(final int fieldNumber, final int value) { return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); } @@ -562,7 +548,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sfixed32} field, including tag. */ - public static int computeSFixed32Size(int fieldNumber, int value) { + public static int computeSFixed32Size(final int fieldNumber, + final int value) { return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); } @@ -570,7 +557,8 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sfixed64} field, including tag. */ - public static int computeSFixed64Size(int fieldNumber, long value) { + public static int computeSFixed64Size(final int fieldNumber, + final long value) { return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); } @@ -578,7 +566,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sint32} field, including tag. */ - public static int computeSInt32Size(int fieldNumber, int value) { + public static int computeSInt32Size(final int fieldNumber, final int value) { return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); } @@ -586,7 +574,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sint64} field, including tag. */ - public static int computeSInt64Size(int fieldNumber, long value) { + public static int computeSInt64Size(final int fieldNumber, final long value) { return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); } @@ -596,7 +584,7 @@ public final class CodedOutputStream { * the wire format differs from normal fields. */ public static int computeMessageSetExtensionSize( - int fieldNumber, Message value) { + final int fieldNumber, final MessageLite value) { return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); @@ -608,7 +596,7 @@ public final class CodedOutputStream { * historical reasons, the wire format differs from normal fields. */ public static int computeRawMessageSetExtensionSize( - int fieldNumber, ByteString value) { + final int fieldNumber, final ByteString value) { return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); @@ -620,7 +608,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code double} field, including tag. */ - public static int computeDoubleSizeNoTag(double value) { + public static int computeDoubleSizeNoTag(final double value) { return LITTLE_ENDIAN_64_SIZE; } @@ -628,7 +616,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code float} field, including tag. */ - public static int computeFloatSizeNoTag(float value) { + public static int computeFloatSizeNoTag(final float value) { return LITTLE_ENDIAN_32_SIZE; } @@ -636,7 +624,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code uint64} field, including tag. */ - public static int computeUInt64SizeNoTag(long value) { + public static int computeUInt64SizeNoTag(final long value) { return computeRawVarint64Size(value); } @@ -644,7 +632,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code int64} field, including tag. */ - public static int computeInt64SizeNoTag(long value) { + public static int computeInt64SizeNoTag(final long value) { return computeRawVarint64Size(value); } @@ -652,7 +640,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code int32} field, including tag. */ - public static int computeInt32SizeNoTag(int value) { + public static int computeInt32SizeNoTag(final int value) { if (value >= 0) { return computeRawVarint32Size(value); } else { @@ -665,7 +653,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code fixed64} field. */ - public static int computeFixed64SizeNoTag(long value) { + public static int computeFixed64SizeNoTag(final long value) { return LITTLE_ENDIAN_64_SIZE; } @@ -673,7 +661,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code fixed32} field. */ - public static int computeFixed32SizeNoTag(int value) { + public static int computeFixed32SizeNoTag(final int value) { return LITTLE_ENDIAN_32_SIZE; } @@ -681,7 +669,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bool} field. */ - public static int computeBoolSizeNoTag(boolean value) { + public static int computeBoolSizeNoTag(final boolean value) { return 1; } @@ -689,12 +677,12 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code string} field. */ - public static int computeStringSizeNoTag(String value) { + public static int computeStringSizeNoTag(final String value) { try { - byte[] bytes = value.getBytes("UTF-8"); + final byte[] bytes = value.getBytes("UTF-8"); return computeRawVarint32Size(bytes.length) + bytes.length; - } catch (java.io.UnsupportedEncodingException e) { + } catch (UnsupportedEncodingException e) { throw new RuntimeException("UTF-8 not supported.", e); } } @@ -703,7 +691,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code group} field. */ - public static int computeGroupSizeNoTag(Message value) { + public static int computeGroupSizeNoTag(final MessageLite value) { return value.getSerializedSize(); } @@ -711,17 +699,21 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code group} field represented by an {@code UnknownFieldSet}, including * tag. + * + * @deprecated UnknownFieldSet now implements MessageLite, so you can just + * call {@link #computeUnknownGroupSizeNoTag}. */ - public static int computeUnknownGroupSizeNoTag(UnknownFieldSet value) { - return value.getSerializedSize(); + @Deprecated + public static int computeUnknownGroupSizeNoTag(final MessageLite value) { + return computeGroupSizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode an embedded * message field. */ - public static int computeMessageSizeNoTag(Message value) { - int size = value.getSerializedSize(); + public static int computeMessageSizeNoTag(final MessageLite value) { + final int size = value.getSerializedSize(); return computeRawVarint32Size(size) + size; } @@ -729,7 +721,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bytes} field. */ - public static int computeBytesSizeNoTag(ByteString value) { + public static int computeBytesSizeNoTag(final ByteString value) { return computeRawVarint32Size(value.size()) + value.size(); } @@ -738,15 +730,15 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code uint32} field. */ - public static int computeUInt32SizeNoTag(int value) { + public static int computeUInt32SizeNoTag(final int value) { return computeRawVarint32Size(value); } /** - * Compute the number of bytes that would be needed to encode an enum field. + * Compute the number of bytes that would be needed to encode an enum field. * Caller is responsible for converting the enum value to its numeric value. */ - public static int computeEnumSizeNoTag(int value) { + public static int computeEnumSizeNoTag(final int value) { return computeRawVarint32Size(value); } @@ -754,7 +746,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sfixed32} field. */ - public static int computeSFixed32SizeNoTag(int value) { + public static int computeSFixed32SizeNoTag(final int value) { return LITTLE_ENDIAN_32_SIZE; } @@ -762,7 +754,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sfixed64} field. */ - public static int computeSFixed64SizeNoTag(long value) { + public static int computeSFixed64SizeNoTag(final long value) { return LITTLE_ENDIAN_64_SIZE; } @@ -770,7 +762,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sint32} field. */ - public static int computeSInt32SizeNoTag(int value) { + public static int computeSInt32SizeNoTag(final int value) { return computeRawVarint32Size(encodeZigZag32(value)); } @@ -778,71 +770,10 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * {@code sint64} field. */ - public static int computeSInt64SizeNoTag(long value) { + public static int computeSInt64SizeNoTag(final long value) { return computeRawVarint64Size(encodeZigZag64(value)); } - /** - * Compute the number of bytes that would be needed to encode a - * field of arbitrary type, including tag, to the stream. - * - * @param type The field's type. - * @param number The field's number. - * @param value Object representing the field's value. Must be of the exact - * type which would be returned by - * {@link Message#getField(Descriptors.FieldDescriptor)} for - * this field. - */ - public static int computeFieldSize( - Descriptors.FieldDescriptor.Type type, - int number, Object value) { - int tagSize = computeTagSize(number); - if (type == Descriptors.FieldDescriptor.Type.GROUP) { - tagSize *= 2; - } - return tagSize + computeFieldSizeNoTag(type, value); - } - - /** - * Compute the number of bytes that would be needed to encode a - * field of arbitrary type, excluding tag, to the stream. - * - * @param type The field's type. - * @param value Object representing the field's value. Must be of the exact - * type which would be returned by - * {@link Message#getField(Descriptors.FieldDescriptor)} for - * this field. - */ - public static int computeFieldSizeNoTag( - Descriptors.FieldDescriptor.Type type, Object value) { - switch (type) { - case DOUBLE : return computeDoubleSizeNoTag ((Double )value); - case FLOAT : return computeFloatSizeNoTag ((Float )value); - case INT64 : return computeInt64SizeNoTag ((Long )value); - case UINT64 : return computeUInt64SizeNoTag ((Long )value); - case INT32 : return computeInt32SizeNoTag ((Integer )value); - case FIXED64 : return computeFixed64SizeNoTag ((Long )value); - case FIXED32 : return computeFixed32SizeNoTag ((Integer )value); - case BOOL : return computeBoolSizeNoTag ((Boolean )value); - case STRING : return computeStringSizeNoTag ((String )value); - case GROUP : return computeGroupSizeNoTag ((Message )value); - case MESSAGE : return computeMessageSizeNoTag ((Message )value); - case BYTES : return computeBytesSizeNoTag ((ByteString)value); - case UINT32 : return computeUInt32SizeNoTag ((Integer )value); - case SFIXED32: return computeSFixed32SizeNoTag((Integer )value); - case SFIXED64: return computeSFixed64SizeNoTag((Long )value); - case SINT32 : return computeSInt32SizeNoTag ((Integer )value); - case SINT64 : return computeSInt64SizeNoTag ((Long )value); - - case ENUM: - return computeEnumSizeNoTag( - ((Descriptors.EnumValueDescriptor)value).getNumber()); - } - - throw new RuntimeException( - "There is no way to get here, but the compiler thinks otherwise."); - } - // ================================================================= /** @@ -905,6 +836,8 @@ public final class CodedOutputStream { * this exception will be thrown. */ public static class OutOfSpaceException extends IOException { + private static final long serialVersionUID = -6947486886997889499L; + OutOfSpaceException() { super("CodedOutputStream was writing to a flat byte array and ran " + "out of space."); @@ -912,7 +845,7 @@ public final class CodedOutputStream { } /** Write a single byte. */ - public void writeRawByte(byte value) throws IOException { + public void writeRawByte(final byte value) throws IOException { if (position == limit) { refreshBuffer(); } @@ -921,17 +854,17 @@ public final class CodedOutputStream { } /** Write a single byte, represented by an integer value. */ - public void writeRawByte(int value) throws IOException { + public void writeRawByte(final int value) throws IOException { writeRawByte((byte) value); } /** Write an array of bytes. */ - public void writeRawBytes(byte[] value) throws IOException { + public void writeRawBytes(final byte[] value) throws IOException { writeRawBytes(value, 0, value.length); } /** Write part of an array of bytes. */ - public void writeRawBytes(byte[] value, int offset, int length) + public void writeRawBytes(final byte[] value, int offset, int length) throws IOException { if (limit - position >= length) { // We have room in the current buffer. @@ -940,7 +873,7 @@ public final class CodedOutputStream { } else { // Write extends past current buffer. Fill the rest of this buffer and // flush. - int bytesWritten = limit - position; + final int bytesWritten = limit - position; System.arraycopy(value, offset, buffer, position, bytesWritten); offset += bytesWritten; length -= bytesWritten; @@ -962,12 +895,13 @@ public final class CodedOutputStream { } /** Encode and write a tag. */ - public void writeTag(int fieldNumber, int wireType) throws IOException { + public void writeTag(final int fieldNumber, final int wireType) + throws IOException { writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); } /** Compute the number of bytes that would be needed to encode a tag. */ - public static int computeTagSize(int fieldNumber) { + public static int computeTagSize(final int fieldNumber) { return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); } @@ -992,7 +926,7 @@ public final class CodedOutputStream { * {@code value} is treated as unsigned, so it won't be sign-extended if * negative. */ - public static int computeRawVarint32Size(int value) { + public static int computeRawVarint32Size(final int value) { if ((value & (0xffffffff << 7)) == 0) return 1; if ((value & (0xffffffff << 14)) == 0) return 2; if ((value & (0xffffffff << 21)) == 0) return 3; @@ -1014,7 +948,7 @@ public final class CodedOutputStream { } /** Compute the number of bytes that would be needed to encode a varint. */ - public static int computeRawVarint64Size(long value) { + public static int computeRawVarint64Size(final long value) { if ((value & (0xffffffffffffffffL << 7)) == 0) return 1; if ((value & (0xffffffffffffffffL << 14)) == 0) return 2; if ((value & (0xffffffffffffffffL << 21)) == 0) return 3; @@ -1028,7 +962,7 @@ public final class CodedOutputStream { } /** Write a little-endian 32-bit integer. */ - public void writeRawLittleEndian32(int value) throws IOException { + public void writeRawLittleEndian32(final int value) throws IOException { writeRawByte((value ) & 0xFF); writeRawByte((value >> 8) & 0xFF); writeRawByte((value >> 16) & 0xFF); @@ -1038,7 +972,7 @@ public final class CodedOutputStream { public static final int LITTLE_ENDIAN_32_SIZE = 4; /** Write a little-endian 64-bit integer. */ - public void writeRawLittleEndian64(long value) throws IOException { + public void writeRawLittleEndian64(final long value) throws IOException { writeRawByte((int)(value ) & 0xFF); writeRawByte((int)(value >> 8) & 0xFF); writeRawByte((int)(value >> 16) & 0xFF); @@ -1061,7 +995,7 @@ public final class CodedOutputStream { * @return An unsigned 32-bit integer, stored in a signed int because * Java has no explicit unsigned support. */ - public static int encodeZigZag32(int n) { + public static int encodeZigZag32(final int n) { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 31); } @@ -1076,7 +1010,7 @@ public final class CodedOutputStream { * @return An unsigned 64-bit integer, stored in a signed int because * Java has no explicit unsigned support. */ - public static long encodeZigZag64(long n) { + public static long encodeZigZag64(final long n) { // Note: the right-shift must be arithmetic return (n << 1) ^ (n >> 63); } diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index 24499efb..0d5e47d1 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -37,11 +37,12 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.io.UnsupportedEncodingException; /** * Contains a collection of classes which describe protocol message types. * - * Every message type has a {@link Descriptors.Descriptor}, which lists all + * Every message type has a {@link Descriptor}, which lists all * 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()}. @@ -106,11 +107,13 @@ public final class Descriptors { public Descriptor findMessageTypeByName(String name) { // Don't allow looking up nested types. This will make optimization // easier later. - if (name.indexOf('.') != -1) return null; + if (name.indexOf('.') != -1) { + return null; + } if (getPackage().length() > 0) { - name = getPackage() + "." + name; + name = getPackage() + '.' + name; } - GenericDescriptor result = pool.findSymbol(name); + final GenericDescriptor result = pool.findSymbol(name); if (result != null && result instanceof Descriptor && result.getFile() == this) { return (Descriptor)result; @@ -128,11 +131,13 @@ public final class Descriptors { public EnumDescriptor findEnumTypeByName(String name) { // Don't allow looking up nested types. This will make optimization // easier later. - if (name.indexOf('.') != -1) return null; + if (name.indexOf('.') != -1) { + return null; + } if (getPackage().length() > 0) { - name = getPackage() + "." + name; + name = getPackage() + '.' + name; } - GenericDescriptor result = pool.findSymbol(name); + final GenericDescriptor result = pool.findSymbol(name); if (result != null && result instanceof EnumDescriptor && result.getFile() == this) { return (EnumDescriptor)result; @@ -150,11 +155,13 @@ public final class Descriptors { public ServiceDescriptor findServiceByName(String name) { // Don't allow looking up nested types. This will make optimization // easier later. - if (name.indexOf('.') != -1) return null; + if (name.indexOf('.') != -1) { + return null; + } if (getPackage().length() > 0) { - name = getPackage() + "." + name; + name = getPackage() + '.' + name; } - GenericDescriptor result = pool.findSymbol(name); + final GenericDescriptor result = pool.findSymbol(name); if (result != null && result instanceof ServiceDescriptor && result.getFile() == this) { return (ServiceDescriptor)result; @@ -171,11 +178,13 @@ public final class Descriptors { * @return The extension's descriptor, or {@code null} if not found. */ public FieldDescriptor findExtensionByName(String name) { - if (name.indexOf('.') != -1) return null; + if (name.indexOf('.') != -1) { + return null; + } if (getPackage().length() > 0) { - name = getPackage() + "." + name; + name = getPackage() + '.' + name; } - GenericDescriptor result = pool.findSymbol(name); + final GenericDescriptor result = pool.findSymbol(name); if (result != null && result instanceof FieldDescriptor && result.getFile() == this) { return (FieldDescriptor)result; @@ -196,8 +205,8 @@ public final class Descriptors { * because a field has an undefined type or because two messages * were defined with the same name. */ - public static FileDescriptor buildFrom(FileDescriptorProto proto, - FileDescriptor[] dependencies) + public static FileDescriptor buildFrom(final FileDescriptorProto proto, + final FileDescriptor[] dependencies) throws DescriptorValidationException { // Building decsriptors involves two steps: translating and linking. // In the translation step (implemented by FileDescriptor's @@ -208,8 +217,9 @@ 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. - DescriptorPool pool = new DescriptorPool(dependencies); - FileDescriptor result = new FileDescriptor(proto, dependencies, pool); + final DescriptorPool pool = new DescriptorPool(dependencies); + final FileDescriptor result = + new FileDescriptor(proto, dependencies, pool); if (dependencies.length != proto.getDependencyCount()) { throw new DescriptorValidationException(result, @@ -234,8 +244,8 @@ public final class Descriptors { * encoded in protocol buffer wire format. */ public static void internalBuildGeneratedFileFrom( - String descriptorData, FileDescriptor[] dependencies, - InternalDescriptorAssigner descriptorAssigner) { + final String descriptorData, final FileDescriptor[] dependencies, + final InternalDescriptorAssigner descriptorAssigner) { // Hack: We can't embed a raw byte array inside generated Java code // (at least, not efficiently), but we can embed Strings. So, the // protocol compiler embeds the FileDescriptorProto as a giant @@ -245,10 +255,10 @@ public final class Descriptors { // serialized form. So, if we convert it to bytes in ISO-8859-1, we // should get the original bytes that we want. - byte[] descriptorBytes; + final byte[] descriptorBytes; try { descriptorBytes = descriptorData.getBytes("ISO-8859-1"); - } catch (java.io.UnsupportedEncodingException e) { + } catch (UnsupportedEncodingException e) { throw new RuntimeException( "Standard encoding ISO-8859-1 not supported by JVM.", e); } @@ -261,7 +271,7 @@ public final class Descriptors { "Failed to parse protocol buffer descriptor for generated code.", e); } - FileDescriptor result; + final FileDescriptor result; try { result = buildFrom(proto, dependencies); } catch (DescriptorValidationException e) { @@ -269,7 +279,8 @@ public final class Descriptors { "Invalid embedded descriptor for \"" + proto.getName() + "\".", e); } - ExtensionRegistry registry = descriptorAssigner.assignDescriptors(result); + final ExtensionRegistry registry = + descriptorAssigner.assignDescriptors(result); if (registry != null) { // We must re-parse the proto using the registry. @@ -297,7 +308,7 @@ public final class Descriptors { * descriptor.proto. The callback may also return null to indicate that * no extensions are used in the decsriptor. */ - public static interface InternalDescriptorAssigner { + public interface InternalDescriptorAssigner { ExtensionRegistry assignDescriptors(FileDescriptor root); } @@ -309,9 +320,9 @@ public final class Descriptors { private final FileDescriptor[] dependencies; private final DescriptorPool pool; - private FileDescriptor(FileDescriptorProto proto, - FileDescriptor[] dependencies, - DescriptorPool pool) + private FileDescriptor(final FileDescriptorProto proto, + final FileDescriptor[] dependencies, + final DescriptorPool pool) throws DescriptorValidationException { this.pool = pool; this.proto = proto; @@ -344,16 +355,16 @@ public final class Descriptors { /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { - for (int i = 0; i < messageTypes.length; i++) { - messageTypes[i].crossLink(); + for (final Descriptor messageType : messageTypes) { + messageType.crossLink(); } - for (int i = 0; i < services.length; i++) { - services[i].crossLink(); + for (final ServiceDescriptor service : services) { + service.crossLink(); } - for (int i = 0; i < extensions.length; i++) { - extensions[i].crossLink(); + for (final FieldDescriptor extension : extensions) { + extension.crossLink(); } } @@ -367,7 +378,7 @@ public final class Descriptors { * protos. So, we have to parse the descriptor protos a second time after * constructing the descriptors. */ - private void setProto(FileDescriptorProto proto) { + private void setProto(final FileDescriptorProto proto) { this.proto = proto; for (int i = 0; i < messageTypes.length; i++) { @@ -456,8 +467,9 @@ public final class Descriptors { } /** Determines if the given field number is an extension. */ - public boolean isExtensionNumber(int number) { - for (DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) { + public boolean isExtensionNumber(final int number) { + for (final DescriptorProto.ExtensionRange range : + proto.getExtensionRangeList()) { if (range.getStart() <= number && number < range.getEnd()) { return true; } @@ -470,8 +482,9 @@ public final class Descriptors { * @param name The unqualified name of the field (e.g. "foo"). * @return The field's descriptor, or {@code null} if not found. */ - public FieldDescriptor findFieldByName(String name) { - GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + public FieldDescriptor findFieldByName(final String name) { + final GenericDescriptor result = + file.pool.findSymbol(fullName + '.' + name); if (result != null && result instanceof FieldDescriptor) { return (FieldDescriptor)result; } else { @@ -484,7 +497,7 @@ public final class Descriptors { * @param number The field number within this message type. * @return The field's descriptor, or {@code null} if not found. */ - public FieldDescriptor findFieldByNumber(int number) { + public FieldDescriptor findFieldByNumber(final int number) { return file.pool.fieldsByNumber.get( new DescriptorPool.DescriptorIntPair(this, number)); } @@ -494,8 +507,9 @@ public final class Descriptors { * @param name The unqualified name of the nested type (e.g. "Foo"). * @return The types's descriptor, or {@code null} if not found. */ - public Descriptor findNestedTypeByName(String name) { - GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + public Descriptor findNestedTypeByName(final String name) { + final GenericDescriptor result = + file.pool.findSymbol(fullName + '.' + name); if (result != null && result instanceof Descriptor) { return (Descriptor)result; } else { @@ -508,8 +522,9 @@ public final class Descriptors { * @param name The unqualified name of the nested type (e.g. "Foo"). * @return The types's descriptor, or {@code null} if not found. */ - public EnumDescriptor findEnumTypeByName(String name) { - GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + public EnumDescriptor findEnumTypeByName(final String name) { + final GenericDescriptor result = + file.pool.findSymbol(fullName + '.' + name); if (result != null && result instanceof EnumDescriptor) { return (EnumDescriptor)result; } else { @@ -527,38 +542,38 @@ public final class Descriptors { private final FieldDescriptor[] fields; private final FieldDescriptor[] extensions; - private Descriptor(DescriptorProto proto, - FileDescriptor file, - Descriptor parent, - int index) + private Descriptor(final DescriptorProto proto, + final FileDescriptor file, + final Descriptor parent, + final int index) throws DescriptorValidationException { this.index = index; this.proto = proto; - this.fullName = computeFullName(file, parent, proto.getName()); + fullName = computeFullName(file, parent, proto.getName()); this.file = file; - this.containingType = parent; + containingType = parent; - this.nestedTypes = new Descriptor[proto.getNestedTypeCount()]; + nestedTypes = new Descriptor[proto.getNestedTypeCount()]; for (int i = 0; i < proto.getNestedTypeCount(); i++) { - this.nestedTypes[i] = new Descriptor( + nestedTypes[i] = new Descriptor( proto.getNestedType(i), file, this, i); } - this.enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; + enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; for (int i = 0; i < proto.getEnumTypeCount(); i++) { - this.enumTypes[i] = new EnumDescriptor( + enumTypes[i] = new EnumDescriptor( proto.getEnumType(i), file, this, i); } - this.fields = new FieldDescriptor[proto.getFieldCount()]; + fields = new FieldDescriptor[proto.getFieldCount()]; for (int i = 0; i < proto.getFieldCount(); i++) { - this.fields[i] = new FieldDescriptor( + fields[i] = new FieldDescriptor( proto.getField(i), file, this, i, false); } - this.extensions = new FieldDescriptor[proto.getExtensionCount()]; + extensions = new FieldDescriptor[proto.getExtensionCount()]; for (int i = 0; i < proto.getExtensionCount(); i++) { - this.extensions[i] = new FieldDescriptor( + extensions[i] = new FieldDescriptor( proto.getExtension(i), file, this, i, true); } @@ -567,21 +582,21 @@ public final class Descriptors { /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { - for (int i = 0; i < nestedTypes.length; i++) { - nestedTypes[i].crossLink(); + for (final Descriptor nestedType : nestedTypes) { + nestedType.crossLink(); } - for (int i = 0; i < fields.length; i++) { - fields[i].crossLink(); + for (final FieldDescriptor field : fields) { + field.crossLink(); } - for (int i = 0; i < extensions.length; i++) { - extensions[i].crossLink(); + for (final FieldDescriptor extension : extensions) { + extension.crossLink(); } } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(DescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final DescriptorProto proto) { this.proto = proto; for (int i = 0; i < nestedTypes.length; i++) { @@ -606,10 +621,11 @@ public final class Descriptors { /** Describes a field of a message type. */ public static final class FieldDescriptor - implements GenericDescriptor, 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; } @@ -624,7 +640,7 @@ public final class Descriptors { /** * Get the field's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -634,12 +650,27 @@ public final class Descriptors { */ public JavaType getJavaType() { return type.getJavaType(); } + /** For internal use only. */ + public WireFormat.JavaType getLiteJavaType() { + return getLiteType().getJavaType(); + } + /** Get the {@code FileDescriptor} containing this descriptor. */ public FileDescriptor getFile() { return file; } /** Get the field's declared type. */ public Type getType() { return type; } + /** For internal use only. */ + public WireFormat.FieldType getLiteType() { + return table[type.ordinal()]; + } + // 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. + private static final WireFormat.FieldType[] table = + WireFormat.FieldType.values(); + /** Is this field declared required? */ public boolean isRequired() { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; @@ -655,6 +686,11 @@ public final class Descriptors { return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; } + /** Does this field have the {@code [packed = true]} option? */ + public boolean isPacked() { + return getOptions().getPacked(); + } + /** Returns true if the field had an explicitly-defined default value. */ public boolean hasDefaultValue() { return proto.hasDefaultValue(); } @@ -742,7 +778,7 @@ public final class Descriptors { * @return negative, zero, or positive if {@code this} is less than, * equal to, or greater than {@code other}, respectively. */ - public int compareTo(FieldDescriptor other) { + public int compareTo(final FieldDescriptor other) { if (other.containingType != containingType) { throw new IllegalArgumentException( "FieldDescriptors can only be compared to other FieldDescriptors " + @@ -765,7 +801,7 @@ public final class Descriptors { private EnumDescriptor enumType; private Object defaultValue; - public static enum Type { + public enum Type { DOUBLE (FieldDescriptorProto.Type.TYPE_DOUBLE , JavaType.DOUBLE ), FLOAT (FieldDescriptorProto.Type.TYPE_FLOAT , JavaType.FLOAT ), INT64 (FieldDescriptorProto.Type.TYPE_INT64 , JavaType.LONG ), @@ -785,11 +821,11 @@ public final class Descriptors { SINT32 (FieldDescriptorProto.Type.TYPE_SINT32 , JavaType.INT ), SINT64 (FieldDescriptorProto.Type.TYPE_SINT64 , JavaType.LONG ); - private Type(FieldDescriptorProto.Type proto, JavaType javaType) { + Type(final FieldDescriptorProto.Type proto, final JavaType javaType) { this.proto = proto; this.javaType = javaType; - if (this.ordinal() != proto.getNumber() - 1) { + if (ordinal() != proto.getNumber() - 1) { throw new RuntimeException( "descriptor.proto changed but Desrciptors.java wasn't updated."); } @@ -801,7 +837,7 @@ public final class Descriptors { public FieldDescriptorProto.Type toProto() { return proto; } public JavaType getJavaType() { return javaType; } - public static Type valueOf(FieldDescriptorProto.Type type) { + public static Type valueOf(final FieldDescriptorProto.Type type) { return values()[type.getNumber() - 1]; } } @@ -815,7 +851,7 @@ public final class Descriptors { } } - public static enum JavaType { + public enum JavaType { INT(0), LONG(0L), FLOAT(0F), @@ -826,7 +862,7 @@ public final class Descriptors { ENUM(null), MESSAGE(null); - private JavaType(Object defaultDefault) { + JavaType(final Object defaultDefault) { this.defaultDefault = defaultDefault; } @@ -834,22 +870,22 @@ public final class Descriptors { * The default default value for fields of this type, if it's a primitive * type. This is meant for use inside this file only, hence is private. */ - private Object defaultDefault; + private final Object defaultDefault; } - private FieldDescriptor(FieldDescriptorProto proto, - FileDescriptor file, - Descriptor parent, - int index, - boolean isExtension) + private FieldDescriptor(final FieldDescriptorProto proto, + final FileDescriptor file, + final Descriptor parent, + final int index, + final boolean isExtension) throws DescriptorValidationException { this.index = index; this.proto = proto; - this.fullName = computeFullName(file, parent, proto.getName()); + fullName = computeFullName(file, parent, proto.getName()); this.file = file; if (proto.hasType()) { - this.type = Type.valueOf(proto.getType()); + type = Type.valueOf(proto.getType()); } if (getNumber() <= 0) { @@ -875,19 +911,19 @@ public final class Descriptors { throw new DescriptorValidationException(this, "FieldDescriptorProto.extendee not set for extension field."); } - this.containingType = null; // Will be filled in when cross-linking + containingType = null; // Will be filled in when cross-linking if (parent != null) { - this.extensionScope = parent; + extensionScope = parent; } else { - this.extensionScope = null; + extensionScope = null; } } else { if (proto.hasExtendee()) { throw new DescriptorValidationException(this, "FieldDescriptorProto.extendee set for non-extension field."); } - this.containingType = parent; - this.extensionScope = null; + containingType = parent; + extensionScope = null; } file.pool.addSymbol(this); @@ -896,43 +932,44 @@ public final class Descriptors { /** Look up and cross-link all field types, etc. */ private void crossLink() throws DescriptorValidationException { if (proto.hasExtendee()) { - GenericDescriptor extendee = + final GenericDescriptor extendee = file.pool.lookupSymbol(proto.getExtendee(), this); if (!(extendee instanceof Descriptor)) { throw new DescriptorValidationException(this, - "\"" + proto.getExtendee() + "\" is not a message type."); + '\"' + proto.getExtendee() + "\" is not a message type."); } - this.containingType = (Descriptor)extendee; + containingType = (Descriptor)extendee; if (!getContainingType().isExtensionNumber(getNumber())) { throw new DescriptorValidationException(this, - "\"" + getContainingType().getFullName() + "\" does not declare " + - getNumber() + " as an extension number."); + '\"' + getContainingType().getFullName() + + "\" does not declare " + getNumber() + + " as an extension number."); } } if (proto.hasTypeName()) { - GenericDescriptor typeDescriptor = + final GenericDescriptor typeDescriptor = file.pool.lookupSymbol(proto.getTypeName(), this); if (!proto.hasType()) { // Choose field type based on symbol. if (typeDescriptor instanceof Descriptor) { - this.type = Type.MESSAGE; + type = Type.MESSAGE; } else if (typeDescriptor instanceof EnumDescriptor) { - this.type = Type.ENUM; + type = Type.ENUM; } else { throw new DescriptorValidationException(this, - "\"" + proto.getTypeName() + "\" is not a type."); + '\"' + proto.getTypeName() + "\" is not a type."); } } if (getJavaType() == JavaType.MESSAGE) { if (!(typeDescriptor instanceof Descriptor)) { throw new DescriptorValidationException(this, - "\"" + proto.getTypeName() + "\" is not a message type."); + '\"' + proto.getTypeName() + "\" is not a message type."); } - this.messageType = (Descriptor)typeDescriptor; + messageType = (Descriptor)typeDescriptor; if (proto.hasDefaultValue()) { throw new DescriptorValidationException(this, @@ -941,9 +978,9 @@ public final class Descriptors { } else if (getJavaType() == JavaType.ENUM) { if (!(typeDescriptor instanceof EnumDescriptor)) { throw new DescriptorValidationException(this, - "\"" + proto.getTypeName() + "\" is not an enum type."); + '\"' + proto.getTypeName() + "\" is not an enum type."); } - this.enumType = (EnumDescriptor)typeDescriptor; + enumType = (EnumDescriptor)typeDescriptor; } else { throw new DescriptorValidationException(this, "Field with primitive type has type_name."); @@ -972,20 +1009,18 @@ public final class Descriptors { defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); break; case UINT32: - case FIXED32: { + case FIXED32: defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); break; - } case INT64: case SINT64: case SFIXED64: defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); break; case UINT64: - case FIXED64: { + case FIXED64: defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); break; - } case FLOAT: defaultValue = Float.valueOf(proto.getDefaultValue()); break; @@ -1002,9 +1037,9 @@ public final class Descriptors { try { defaultValue = TextFormat.unescapeBytes(proto.getDefaultValue()); - } catch (TextFormat.InvalidEscapeSequence e) { + } catch (TextFormat.InvalidEscapeSequenceException e) { throw new DescriptorValidationException(this, - "Couldn't parse default value: " + e.getMessage()); + "Couldn't parse default value: " + e.getMessage(), e); } break; case ENUM: @@ -1012,7 +1047,7 @@ public final class Descriptors { if (defaultValue == null) { throw new DescriptorValidationException(this, "Unknown enum default value: \"" + - proto.getDefaultValue() + "\""); + proto.getDefaultValue() + '\"'); } break; case MESSAGE: @@ -1021,17 +1056,17 @@ public final class Descriptors { "Message type had default value."); } } catch (NumberFormatException e) { - DescriptorValidationException validationException = + final DescriptorValidationException validationException = new DescriptorValidationException(this, "Could not parse default value: \"" + - proto.getDefaultValue() + "\""); + proto.getDefaultValue() + '\"'); validationException.initCause(e); throw validationException; } } else { // Determine the default default for this field. if (isRepeated()) { - defaultValue = Collections.EMPTY_LIST; + defaultValue = Collections.emptyList(); } else { switch (getJavaType()) { case ENUM: @@ -1067,19 +1102,32 @@ public final class Descriptors { } } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(FieldDescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final FieldDescriptorProto proto) { this.proto = proto; } + + /** + * For internal use only. This is to satisfy the FieldDescriptorLite + * interface. + */ + @Override + public MessageLite.Builder internalMergeFrom( + MessageLite.Builder to, MessageLite from) { + // FieldDescriptors are only used with non-lite messages so we can just + // down-cast and call mergeFrom directly. + return ((Message.Builder) to).mergeFrom((Message) from); + } } // ================================================================= /** Describes an enum type. */ - public static final class EnumDescriptor implements GenericDescriptor { + 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; } @@ -1091,7 +1139,7 @@ public final class Descriptors { /** * Get the type's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1114,8 +1162,9 @@ public final class Descriptors { * @param name The unqualified name of the value (e.g. "FOO"). * @return the value's decsriptor, or {@code null} if not found. */ - public EnumValueDescriptor findValueByName(String name) { - GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + public EnumValueDescriptor findValueByName(final String name) { + final GenericDescriptor result = + file.pool.findSymbol(fullName + '.' + name); if (result != null && result instanceof EnumValueDescriptor) { return (EnumValueDescriptor)result; } else { @@ -1129,7 +1178,7 @@ public final class Descriptors { * @param number The value's number. * @return the value's decsriptor, or {@code null} if not found. */ - public EnumValueDescriptor findValueByNumber(int number) { + public EnumValueDescriptor findValueByNumber(final int number) { return file.pool.enumValuesByNumber.get( new DescriptorPool.DescriptorIntPair(this, number)); } @@ -1141,16 +1190,16 @@ public final class Descriptors { private final Descriptor containingType; private EnumValueDescriptor[] values; - private EnumDescriptor(EnumDescriptorProto proto, - FileDescriptor file, - Descriptor parent, - int index) + private EnumDescriptor(final EnumDescriptorProto proto, + final FileDescriptor file, + final Descriptor parent, + final int index) throws DescriptorValidationException { this.index = index; this.proto = proto; - this.fullName = computeFullName(file, parent, proto.getName()); + fullName = computeFullName(file, parent, proto.getName()); this.file = file; - this.containingType = parent; + containingType = parent; if (proto.getValueCount() == 0) { // We cannot allow enums with no values because this would mean there @@ -1161,15 +1210,15 @@ public final class Descriptors { values = new EnumValueDescriptor[proto.getValueCount()]; for (int i = 0; i < proto.getValueCount(); i++) { - this.values[i] = new EnumValueDescriptor( + values[i] = new EnumValueDescriptor( proto.getValue(i), file, this, i); } file.pool.addSymbol(this); } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(EnumDescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final EnumDescriptorProto proto) { this.proto = proto; for (int i = 0; i < values.length; i++) { @@ -1186,10 +1235,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 implements GenericDescriptor { + 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; } @@ -1204,7 +1254,7 @@ public final class Descriptors { /** * Get the value's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1225,24 +1275,24 @@ public final class Descriptors { private final FileDescriptor file; private final EnumDescriptor type; - private EnumValueDescriptor(EnumValueDescriptorProto proto, - FileDescriptor file, - EnumDescriptor parent, - int index) + private EnumValueDescriptor(final EnumValueDescriptorProto proto, + final FileDescriptor file, + final EnumDescriptor parent, + final int index) throws DescriptorValidationException { this.index = index; this.proto = proto; this.file = file; - this.type = parent; + type = parent; - this.fullName = parent.getFullName() + "." + proto.getName(); + fullName = parent.getFullName() + '.' + proto.getName(); file.pool.addSymbol(this); file.pool.addEnumValueByNumber(this); } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(EnumValueDescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final EnumValueDescriptorProto proto) { this.proto = proto; } } @@ -1265,7 +1315,7 @@ public final class Descriptors { /** * Get the type's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1285,8 +1335,9 @@ public final class Descriptors { * @param name The unqualified name of the method (e.g. "Foo"). * @return the method's decsriptor, or {@code null} if not found. */ - public MethodDescriptor findMethodByName(String name) { - GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + public MethodDescriptor findMethodByName(final String name) { + final GenericDescriptor result = + file.pool.findSymbol(fullName + '.' + name); if (result != null && result instanceof MethodDescriptor) { return (MethodDescriptor)result; } else { @@ -1300,18 +1351,18 @@ public final class Descriptors { private final FileDescriptor file; private MethodDescriptor[] methods; - private ServiceDescriptor(ServiceDescriptorProto proto, - FileDescriptor file, - int index) + private ServiceDescriptor(final ServiceDescriptorProto proto, + final FileDescriptor file, + final int index) throws DescriptorValidationException { this.index = index; this.proto = proto; - this.fullName = computeFullName(file, null, proto.getName()); + fullName = computeFullName(file, null, proto.getName()); this.file = file; - this.methods = new MethodDescriptor[proto.getMethodCount()]; + methods = new MethodDescriptor[proto.getMethodCount()]; for (int i = 0; i < proto.getMethodCount(); i++) { - this.methods[i] = new MethodDescriptor( + methods[i] = new MethodDescriptor( proto.getMethod(i), file, this, i); } @@ -1319,13 +1370,13 @@ public final class Descriptors { } private void crossLink() throws DescriptorValidationException { - for (int i = 0; i < methods.length; i++) { - methods[i].crossLink(); + for (final MethodDescriptor method : methods) { + method.crossLink(); } } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(ServiceDescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final ServiceDescriptorProto proto) { this.proto = proto; for (int i = 0; i < methods.length; i++) { @@ -1354,7 +1405,7 @@ public final class Descriptors { /** * Get the method's fully-qualified name. - * @see Descriptors.Descriptor#getFullName() + * @see Descriptor#getFullName() */ public String getFullName() { return fullName; } @@ -1385,54 +1436,54 @@ public final class Descriptors { private Descriptor inputType; private Descriptor outputType; - private MethodDescriptor(MethodDescriptorProto proto, - FileDescriptor file, - ServiceDescriptor parent, - int index) + private MethodDescriptor(final MethodDescriptorProto proto, + final FileDescriptor file, + final ServiceDescriptor parent, + final int index) throws DescriptorValidationException { this.index = index; this.proto = proto; this.file = file; - this.service = parent; + service = parent; - this.fullName = parent.getFullName() + "." + proto.getName(); + fullName = parent.getFullName() + '.' + proto.getName(); file.pool.addSymbol(this); } private void crossLink() throws DescriptorValidationException { - GenericDescriptor inputType = + final GenericDescriptor input = file.pool.lookupSymbol(proto.getInputType(), this); - if (!(inputType instanceof Descriptor)) { + if (!(input instanceof Descriptor)) { throw new DescriptorValidationException(this, - "\"" + proto.getInputType() + "\" is not a message type."); + '\"' + proto.getInputType() + "\" is not a message type."); } - this.inputType = (Descriptor)inputType; + inputType = (Descriptor)input; - GenericDescriptor outputType = + final GenericDescriptor output = file.pool.lookupSymbol(proto.getOutputType(), this); - if (!(outputType instanceof Descriptor)) { + if (!(output instanceof Descriptor)) { throw new DescriptorValidationException(this, - "\"" + proto.getOutputType() + "\" is not a message type."); + '\"' + proto.getOutputType() + "\" is not a message type."); } - this.outputType = (Descriptor)outputType; + outputType = (Descriptor)output; } - /** See {@link FileDescriptor.setProto}. */ - private void setProto(MethodDescriptorProto proto) { + /** See {@link FileDescriptor#setProto}. */ + private void setProto(final MethodDescriptorProto proto) { this.proto = proto; } } // ================================================================= - private static String computeFullName(FileDescriptor file, - Descriptor parent, - String name) { + private static String computeFullName(final FileDescriptor file, + final Descriptor parent, + final String name) { if (parent != null) { - return parent.getFullName() + "." + name; + return parent.getFullName() + '.' + name; } else if (file.getPackage().length() > 0) { - return file.getPackage() + "." + name; + return file.getPackage() + '.' + name; } else { return name; } @@ -1444,7 +1495,7 @@ public final class Descriptors { * All descriptors except {@code FileDescriptor} implement this to make * {@code DescriptorPool}'s life easier. */ - private static interface GenericDescriptor { + private interface GenericDescriptor { Message toProto(); String getName(); String getFullName(); @@ -1456,6 +1507,8 @@ public final class Descriptors { * are not valid. */ public static class DescriptorValidationException extends Exception { + private static final long serialVersionUID = 5750205775490483148L; + /** Gets the full name of the descriptor where the error occurred. */ public String getProblemSymbolName() { return name; } @@ -1473,27 +1526,36 @@ public final class Descriptors { private final Message proto; private final String description; - private DescriptorValidationException(GenericDescriptor problemDescriptor, - String description) { - super(problemDescriptor.getFullName() + ": " + description); + private DescriptorValidationException( + final GenericDescriptor problemDescriptor, + final String description) { + this(problemDescriptor, description, null); + } + + private DescriptorValidationException( + final GenericDescriptor problemDescriptor, + final String description, + final Throwable cause) { + super(problemDescriptor.getFullName() + ": " + description, cause); // Note that problemDescriptor may be partially uninitialized, so we // don't want to expose it directly to the user. So, we only provide // the name and the original proto. - this.name = problemDescriptor.getFullName(); - this.proto = problemDescriptor.toProto(); + name = problemDescriptor.getFullName(); + proto = problemDescriptor.toProto(); this.description = description; } - private DescriptorValidationException(FileDescriptor problemDescriptor, - String description) { + private DescriptorValidationException( + final FileDescriptor problemDescriptor, + final String description) { super(problemDescriptor.getName() + ": " + description); // Note that problemDescriptor may be partially uninitialized, so we // don't want to expose it directly to the user. So, we only provide // the name and the original proto. - this.name = problemDescriptor.getName(); - this.proto = problemDescriptor.toProto(); + name = problemDescriptor.getName(); + proto = problemDescriptor.toProto(); this.description = description; } } @@ -1505,16 +1567,16 @@ public final class Descriptors { * descriptors defined in a particular file. */ private static final class DescriptorPool { - DescriptorPool(FileDescriptor[] dependencies) { + DescriptorPool(final FileDescriptor[] dependencies) { this.dependencies = new DescriptorPool[dependencies.length]; for (int i = 0; i < dependencies.length; i++) { this.dependencies[i] = dependencies[i].pool; } - for (int i = 0; i < dependencies.length; i++) { + for (final FileDescriptor dependency : dependencies) { try { - addPackage(dependencies[i].getPackage(), dependencies[i]); + addPackage(dependency.getPackage(), dependency); } catch (DescriptorValidationException e) { // Can't happen, because addPackage() only fails when the name // conflicts with a non-package, but we have not yet added any @@ -1524,23 +1586,27 @@ public final class Descriptors { } } - final DescriptorPool[] dependencies; + private final DescriptorPool[] dependencies; - final Map<String, GenericDescriptor> descriptorsByName = + private final Map<String, GenericDescriptor> descriptorsByName = new HashMap<String, GenericDescriptor>(); - final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber = + private final Map<DescriptorIntPair, FieldDescriptor> fieldsByNumber = new HashMap<DescriptorIntPair, FieldDescriptor>(); - final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber = - new HashMap<DescriptorIntPair, EnumValueDescriptor>(); + private final Map<DescriptorIntPair, EnumValueDescriptor> enumValuesByNumber + = new HashMap<DescriptorIntPair, EnumValueDescriptor>(); /** Find a generic descriptor by fully-qualified name. */ - GenericDescriptor findSymbol(String fullName) { + GenericDescriptor findSymbol(final String fullName) { GenericDescriptor result = descriptorsByName.get(fullName); - if (result != null) return result; + if (result != null) { + return result; + } - for (int i = 0; i < dependencies.length; i++) { - result = dependencies[i].descriptorsByName.get(fullName); - if (result != null) return result; + for (final DescriptorPool dependency : dependencies) { + result = dependency.descriptorsByName.get(fullName); + if (result != null) { + return result; + } } return null; @@ -1552,8 +1618,8 @@ public final class Descriptors { * partially-qualified, or unqualified. C++-like name lookup semantics * are used to search for the matching descriptor. */ - GenericDescriptor lookupSymbol(String name, - GenericDescriptor relativeTo) + GenericDescriptor lookupSymbol(final String name, + final GenericDescriptor relativeTo) throws DescriptorValidationException { // TODO(kenton): This could be optimized in a number of ways. @@ -1564,8 +1630,8 @@ public final class Descriptors { } else { // If "name" is a compound identifier, we want to search for the // first component of it, then search within it for the rest. - int firstPartLength = name.indexOf('.'); - String firstPart; + final int firstPartLength = name.indexOf('.'); + final String firstPart; if (firstPartLength == -1) { firstPart = name; } else { @@ -1574,11 +1640,12 @@ public final class Descriptors { // We will search each parent scope of "relativeTo" looking for the // symbol. - StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); + final StringBuilder scopeToTry = + new StringBuilder(relativeTo.getFullName()); while (true) { // Chop off the last component of the scope. - int dotpos = scopeToTry.lastIndexOf("."); + final int dotpos = scopeToTry.lastIndexOf("."); if (dotpos == -1) { result = findSymbol(name); break; @@ -1609,7 +1676,7 @@ public final class Descriptors { if (result == null) { throw new DescriptorValidationException(relativeTo, - "\"" + name + "\" is not defined."); + '\"' + name + "\" is not defined."); } else { return result; } @@ -1619,30 +1686,30 @@ public final class Descriptors { * Adds a symbol to the symbol table. If a symbol with the same name * already exists, throws an error. */ - void addSymbol(GenericDescriptor descriptor) + void addSymbol(final GenericDescriptor descriptor) throws DescriptorValidationException { validateSymbolName(descriptor); - String fullName = descriptor.getFullName(); - int dotpos = fullName.lastIndexOf('.'); + final String fullName = descriptor.getFullName(); + final int dotpos = fullName.lastIndexOf('.'); - GenericDescriptor old = descriptorsByName.put(fullName, descriptor); + final GenericDescriptor old = descriptorsByName.put(fullName, descriptor); if (old != null) { descriptorsByName.put(fullName, old); if (descriptor.getFile() == old.getFile()) { if (dotpos == -1) { throw new DescriptorValidationException(descriptor, - "\"" + fullName + "\" is already defined."); + '\"' + fullName + "\" is already defined."); } else { throw new DescriptorValidationException(descriptor, - "\"" + fullName.substring(dotpos + 1) + + '\"' + fullName.substring(dotpos + 1) + "\" is already defined in \"" + fullName.substring(0, dotpos) + "\"."); } } else { throw new DescriptorValidationException(descriptor, - "\"" + fullName + "\" is already defined in file \"" + + '\"' + fullName + "\" is already defined in file \"" + old.getFile().getName() + "\"."); } } @@ -1653,21 +1720,22 @@ 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. */ - static final class PackageDescriptor implements GenericDescriptor { + private static final class PackageDescriptor implements GenericDescriptor { public Message toProto() { return file.toProto(); } public String getName() { return name; } public String getFullName() { return fullName; } public FileDescriptor getFile() { return file; } - PackageDescriptor(String name, String fullName, FileDescriptor file) { + PackageDescriptor(final String name, final String fullName, + final FileDescriptor file) { this.file = file; this.fullName = fullName; this.name = name; } - String name; - String fullName; - FileDescriptor file; + private final String name; + private final String fullName; + private final FileDescriptor file; } /** @@ -1676,46 +1744,50 @@ public final class Descriptors { * under the same name, an exception is thrown. If the package has * multiple components, this also adds the parent package(s). */ - void addPackage(String fullName, FileDescriptor file) + void addPackage(final String fullName, final FileDescriptor file) throws DescriptorValidationException { - int dotpos = fullName.lastIndexOf('.'); - String name; - if (dotpos != -1) { + final int dotpos = fullName.lastIndexOf('.'); + final String name; + if (dotpos == -1) { + name = fullName; + } else { addPackage(fullName.substring(0, dotpos), file); name = fullName.substring(dotpos + 1); - } else { - name = fullName; } - GenericDescriptor old = + final GenericDescriptor old = descriptorsByName.put(fullName, new PackageDescriptor(name, fullName, file)); if (old != null) { descriptorsByName.put(fullName, old); if (!(old instanceof PackageDescriptor)) { throw new DescriptorValidationException(file, - "\"" + name + "\" is already defined (as something other than a " + - "package) in file \"" + old.getFile().getName() + "\"."); + '\"' + name + "\" is already defined (as something other than a " + + "package) in file \"" + old.getFile().getName() + "\"."); } } } /** A (GenericDescriptor, int) pair, used as a map key. */ - static final class DescriptorIntPair { - final GenericDescriptor descriptor; - final int number; + private static final class DescriptorIntPair { + private final GenericDescriptor descriptor; + private final int number; - DescriptorIntPair(GenericDescriptor descriptor, int number) { + DescriptorIntPair(final GenericDescriptor descriptor, final int number) { this.descriptor = descriptor; this.number = number; } + @Override public int hashCode() { return descriptor.hashCode() * ((1 << 16) - 1) + number; } - public boolean equals(Object obj) { - if (!(obj instanceof DescriptorIntPair)) return false; - DescriptorIntPair other = (DescriptorIntPair)obj; + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof DescriptorIntPair)) { + return false; + } + final DescriptorIntPair other = (DescriptorIntPair)obj; return descriptor == other.descriptor && number == other.number; } } @@ -1724,11 +1796,11 @@ public final class Descriptors { * Adds a field to the fieldsByNumber table. Throws an exception if a * field with hte same containing type and number already exists. */ - void addFieldByNumber(FieldDescriptor field) + void addFieldByNumber(final FieldDescriptor field) throws DescriptorValidationException { - DescriptorIntPair key = + final DescriptorIntPair key = new DescriptorIntPair(field.getContainingType(), field.getNumber()); - FieldDescriptor old = fieldsByNumber.put(key, field); + final FieldDescriptor old = fieldsByNumber.put(key, field); if (old != null) { fieldsByNumber.put(key, old); throw new DescriptorValidationException(field, @@ -1744,10 +1816,10 @@ public final class Descriptors { * with the same type and number already exists, does nothing. (This is * allowed; the first value define with the number takes precedence.) */ - void addEnumValueByNumber(EnumValueDescriptor value) { - DescriptorIntPair key = + void addEnumValueByNumber(final EnumValueDescriptor value) { + final DescriptorIntPair key = new DescriptorIntPair(value.getType(), value.getNumber()); - EnumValueDescriptor old = enumValuesByNumber.put(key, value); + final EnumValueDescriptor old = enumValuesByNumber.put(key, value); if (old != null) { enumValuesByNumber.put(key, old); // Not an error: Multiple enum values may have the same number, but @@ -1759,15 +1831,15 @@ public final class Descriptors { * Verifies that the descriptor's name is valid (i.e. it contains only * letters, digits, and underscores, and does not start with a digit). */ - void validateSymbolName(GenericDescriptor descriptor) - throws DescriptorValidationException { - String name = descriptor.getName(); + static void validateSymbolName(final GenericDescriptor descriptor) + throws DescriptorValidationException { + final String name = descriptor.getName(); if (name.length() == 0) { throw new DescriptorValidationException(descriptor, "Missing name."); } else { boolean valid = true; for (int i = 0; i < name.length(); i++) { - char c = name.charAt(i); + final char c = name.charAt(i); // Non-ASCII characters are not valid in protobuf identifiers, even // if they are letters or digits. if (c >= 128) { @@ -1784,7 +1856,7 @@ public final class Descriptors { } if (!valid) { throw new DescriptorValidationException(descriptor, - "\"" + name + "\" is not a valid identifier."); + '\"' + name + "\" is not a valid identifier."); } } } diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java index 59fb7b02..c106b662 100644 --- a/java/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -45,14 +45,14 @@ import java.util.Map; */ public final class DynamicMessage extends AbstractMessage { private final Descriptor type; - private final FieldSet fields; + private final FieldSet<FieldDescriptor> fields; private final UnknownFieldSet unknownFields; private int memoizedSize = -1; /** * Construct a {@code DynamicMessage} using the given {@code FieldSet}. */ - private DynamicMessage(Descriptor type, FieldSet fields, + private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields, UnknownFieldSet unknownFields) { this.type = type; this.fields = fields; @@ -64,7 +64,7 @@ public final class DynamicMessage extends AbstractMessage { * given type. */ public static DynamicMessage getDefaultInstance(Descriptor type) { - return new DynamicMessage(type, FieldSet.emptySet(), + return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(), UnknownFieldSet.getDefaultInstance()); } @@ -160,7 +160,11 @@ public final class DynamicMessage extends AbstractMessage { verifyContainingType(field); Object result = fields.getField(field); if (result == null) { - result = getDefaultInstance(field.getMessageType()); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + result = getDefaultInstance(field.getMessageType()); + } else { + result = field.getDefaultValue(); + } } return result; } @@ -179,15 +183,31 @@ public final class DynamicMessage extends AbstractMessage { return unknownFields; } + 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()) { + if (!fields.hasField(field)) { + return false; + } + } + } + + // Check that embedded messages are initialized. + return fields.isInitialized(); + } + public boolean isInitialized() { - return fields.isInitialized(type); + return isInitialized(type, fields); } public void writeTo(CodedOutputStream output) throws IOException { - fields.writeTo(output); if (type.getOptions().getMessageSetWireFormat()) { + fields.writeMessageSetTo(output); unknownFields.writeAsMessageSetTo(output); } else { + fields.writeTo(output); unknownFields.writeTo(output); } } @@ -196,10 +216,11 @@ public final class DynamicMessage extends AbstractMessage { int size = memoizedSize; if (size != -1) return size; - size = fields.getSerializedSize(); if (type.getOptions().getMessageSetWireFormat()) { + size = fields.getMessageSetSerializedSize(); size += unknownFields.getSerializedSizeAsMessageSet(); } else { + size = fields.getSerializedSize(); size += unknownFields.getSerializedSize(); } @@ -230,7 +251,7 @@ public final class DynamicMessage extends AbstractMessage { */ public static final class Builder extends AbstractMessage.Builder<Builder> { private final Descriptor type; - private FieldSet fields; + private FieldSet<FieldDescriptor> fields; private UnknownFieldSet unknownFields; /** Construct a {@code Builder} for the given type. */ @@ -244,25 +265,33 @@ public final class DynamicMessage extends AbstractMessage { // Implementation of Message.Builder interface. public Builder clear() { + if (fields == null) { + throw new IllegalStateException("Cannot call clear() after build()."); + } fields.clear(); return this; } public Builder mergeFrom(Message other) { - if (other.getDescriptorForType() != type) { - throw new IllegalArgumentException( - "mergeFrom(Message) can only merge messages of the same type."); + if (other instanceof DynamicMessage) { + // This should be somewhat faster than calling super.mergeFrom(). + DynamicMessage otherDynamicMessage = (DynamicMessage) other; + if (otherDynamicMessage.type != type) { + throw new IllegalArgumentException( + "mergeFrom(Message) can only merge messages of the same type."); + } + fields.mergeFrom(otherDynamicMessage.fields); + mergeUnknownFields(otherDynamicMessage.unknownFields); + return this; + } else { + return super.mergeFrom(other); } - - fields.mergeFrom(other); - mergeUnknownFields(other.getUnknownFields()); - return this; } public DynamicMessage build() { // If fields == null, we'll throw an appropriate exception later. if (fields != null && !isInitialized()) { - throw new UninitializedMessageException( + throw newUninitializedMessageException( new DynamicMessage(type, fields, unknownFields)); } return buildPartial(); @@ -275,7 +304,7 @@ public final class DynamicMessage extends AbstractMessage { */ private DynamicMessage buildParsed() throws InvalidProtocolBufferException { if (!isInitialized()) { - throw new UninitializedMessageException( + throw newUninitializedMessageException( new DynamicMessage(type, fields, unknownFields)) .asInvalidProtocolBufferException(); } @@ -302,17 +331,7 @@ public final class DynamicMessage extends AbstractMessage { } public boolean isInitialized() { - return fields.isInitialized(type); - } - - public Builder mergeFrom(CodedInputStream input, - ExtensionRegistry extensionRegistry) - throws IOException { - UnknownFieldSet.Builder unknownFieldsBuilder = - UnknownFieldSet.newBuilder(unknownFields); - fields.mergeFrom(input, unknownFieldsBuilder, extensionRegistry, this); - unknownFields = unknownFieldsBuilder.build(); - return this; + return DynamicMessage.isInitialized(type, fields); } public Descriptor getDescriptorForType() { @@ -347,7 +366,11 @@ public final class DynamicMessage extends AbstractMessage { verifyContainingType(field); Object result = fields.getField(field); if (result == null) { - result = getDefaultInstance(field.getMessageType()); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + result = getDefaultInstance(field.getMessageType()); + } else { + result = field.getDefaultValue(); + } } return result; } diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java index 45396f6f..87bbd6eb 100644 --- a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java @@ -84,18 +84,16 @@ import java.util.Map; * would be slow. Second, corrupt data would not be detected until first * access, at which point it would be much harder to deal with it. Third, it * could violate the expectation that message objects are immutable, since the - * type provided could be any arbitrary message class. An unpriviledged user + * type provided could be any arbitrary message class. An unprivileged user * could take advantage of this to inject a mutable object into a message - * belonging to priviledged code and create mischief. + * belonging to privileged code and create mischief. * * @author kenton@google.com Kenton Varda */ -public final class ExtensionRegistry { +public final class ExtensionRegistry extends ExtensionRegistryLite { /** Construct a new, empty instance. */ public static ExtensionRegistry newInstance() { - return new ExtensionRegistry( - new HashMap<String, ExtensionInfo>(), - new HashMap<DescriptorIntPair, ExtensionInfo>()); + return new ExtensionRegistry(); } /** Get the unmodifiable singleton empty instance. */ @@ -104,10 +102,9 @@ public final class ExtensionRegistry { } /** Returns an unmodifiable view of the registry. */ + @Override public ExtensionRegistry getUnmodifiable() { - return new ExtensionRegistry( - Collections.unmodifiableMap(extensionsByName), - Collections.unmodifiableMap(extensionsByNumber)); + return new ExtensionRegistry(this); } /** A (Descriptor, Message) pair, returned by lookup methods. */ @@ -121,11 +118,12 @@ public final class ExtensionRegistry { */ public final Message defaultInstance; - private ExtensionInfo(FieldDescriptor descriptor) { + private ExtensionInfo(final FieldDescriptor descriptor) { this.descriptor = descriptor; - this.defaultInstance = null; + defaultInstance = null; } - private ExtensionInfo(FieldDescriptor descriptor, Message defaultInstance) { + private ExtensionInfo(final FieldDescriptor descriptor, + final Message defaultInstance) { this.descriptor = descriptor; this.defaultInstance = defaultInstance; } @@ -139,7 +137,7 @@ public final class ExtensionRegistry { * @return Information about the extension if found, or {@code null} * otherwise. */ - public ExtensionInfo findExtensionByName(String fullName) { + public ExtensionInfo findExtensionByName(final String fullName) { return extensionsByName.get(fullName); } @@ -149,14 +147,14 @@ public final class ExtensionRegistry { * @return Information about the extension if found, or {@code null} * otherwise. */ - public ExtensionInfo findExtensionByNumber(Descriptor containingType, - int fieldNumber) { + public ExtensionInfo findExtensionByNumber(final Descriptor containingType, + final int fieldNumber) { return extensionsByNumber.get( new DescriptorIntPair(containingType, fieldNumber)); } /** Add an extension from a generated file to the registry. */ - public void add(GeneratedMessage.GeneratedExtension<?, ?> extension) { + public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) { if (extension.getDescriptor().getJavaType() == FieldDescriptor.JavaType.MESSAGE) { add(new ExtensionInfo(extension.getDescriptor(), @@ -167,7 +165,7 @@ public final class ExtensionRegistry { } /** Add a non-message-type extension to the registry by descriptor. */ - public void add(FieldDescriptor type) { + public void add(final FieldDescriptor type) { if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { throw new IllegalArgumentException( "ExtensionRegistry.add() must be provided a default instance when " + @@ -177,7 +175,7 @@ public final class ExtensionRegistry { } /** Add a message-type extension to the registry by descriptor. */ - public void add(FieldDescriptor type, Message defaultInstance) { + public void add(final FieldDescriptor type, final Message defaultInstance) { if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { throw new IllegalArgumentException( "ExtensionRegistry.add() provided a default instance for a " + @@ -189,22 +187,30 @@ public final class ExtensionRegistry { // ================================================================= // Private stuff. - private ExtensionRegistry( - Map<String, ExtensionInfo> extensionsByName, - Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber) { - this.extensionsByName = extensionsByName; - this.extensionsByNumber = extensionsByNumber; + private ExtensionRegistry() { + this.extensionsByName = new HashMap<String, ExtensionInfo>(); + this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>(); + } + + private ExtensionRegistry(ExtensionRegistry other) { + super(other); + this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName); + this.extensionsByNumber = + Collections.unmodifiableMap(other.extensionsByNumber); } private final Map<String, ExtensionInfo> extensionsByName; private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber; - private static final ExtensionRegistry EMPTY = - new ExtensionRegistry( - Collections.<String, ExtensionInfo>emptyMap(), - Collections.<DescriptorIntPair, ExtensionInfo>emptyMap()); + private ExtensionRegistry(boolean empty) { + super(ExtensionRegistryLite.getEmptyRegistry()); + this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap(); + this.extensionsByNumber = + Collections.<DescriptorIntPair, ExtensionInfo>emptyMap(); + } + private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true); - private void add(ExtensionInfo extension) { + private void add(final ExtensionInfo extension) { if (!extension.descriptor.isExtension()) { throw new IllegalArgumentException( "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + @@ -217,7 +223,7 @@ public final class ExtensionRegistry { extension.descriptor.getNumber()), extension); - FieldDescriptor field = extension.descriptor; + final FieldDescriptor field = extension.descriptor; if (field.getContainingType().getOptions().getMessageSetWireFormat() && field.getType() == FieldDescriptor.Type.MESSAGE && field.isOptional() && @@ -231,20 +237,24 @@ public final class ExtensionRegistry { /** A (GenericDescriptor, int) pair, used as a map key. */ private static final class DescriptorIntPair { - final Descriptor descriptor; - final int number; + private final Descriptor descriptor; + private final int number; - DescriptorIntPair(Descriptor descriptor, int number) { + DescriptorIntPair(final Descriptor descriptor, final int number) { this.descriptor = descriptor; this.number = number; } + @Override public int hashCode() { return descriptor.hashCode() * ((1 << 16) - 1) + number; } - public boolean equals(Object obj) { - if (!(obj instanceof DescriptorIntPair)) return false; - DescriptorIntPair other = (DescriptorIntPair)obj; + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof DescriptorIntPair)) { + return false; + } + final DescriptorIntPair other = (DescriptorIntPair)obj; return descriptor == other.descriptor && number == other.number; } } diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java new file mode 100644 index 00000000..d5288dd8 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java @@ -0,0 +1,169 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Equivalent to {@link ExtensionRegistry} but supports only "lite" types. + * <p> + * If all of your types are lite types, then you only need to use + * {@code ExtensionRegistryLite}. Similarly, if all your types are regular + * types, then you only need {@link ExtensionRegistry}. Typically it does not + * 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-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> + * There are three factors to consider: Whether the type being extended is + * lite, whether the embedded type (in the case of a message-typed extension) + * is lite, and whether the extension itself is lite. Since all three are + * declared in different files, they could all be different. Here are all + * the combinations and which type of registry to use: + * <pre> + * Extended type Inner type Extension Use registry + * ======================================================================= + * lite lite lite ExtensionRegistryLite + * lite regular lite ExtensionRegistry + * regular regular regular ExtensionRegistry + * all other combinations not supported + * </pre> + * <p> + * Note that just as regular types are not allowed to contain lite-type fields, + * they are also not allowed to contain lite-type extensions. This is because + * regular types must be fully accessible via reflection, which in turn means + * that all the inner messages must also support reflection. On the other hand, + * since regular types implement the entire lite interface, there is no problem + * with embedding regular types inside lite types. + * + * @author kenton@google.com Kenton Varda + */ +public class ExtensionRegistryLite { + /** Construct a new, empty instance. */ + public static ExtensionRegistryLite newInstance() { + return new ExtensionRegistryLite(); + } + + /** Get the unmodifiable singleton empty instance. */ + public static ExtensionRegistryLite getEmptyRegistry() { + return EMPTY; + } + + /** Returns an unmodifiable view of the registry. */ + public ExtensionRegistryLite getUnmodifiable() { + return new ExtensionRegistryLite(this); + } + + /** + * Find an extension by containing type and field number. + * + * @return Information about the extension if found, or {@code null} + * otherwise. + */ + @SuppressWarnings("unchecked") + public <ContainingType extends MessageLite> + GeneratedMessageLite.GeneratedExtension<ContainingType, ?> + findLiteExtensionByNumber( + final ContainingType containingTypeDefaultInstance, + final int fieldNumber) { + return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>) + extensionsByNumber.get( + new ObjectIntPair(containingTypeDefaultInstance, fieldNumber)); + } + + /** Add an extension from a lite generated file to the registry. */ + public final void add( + final GeneratedMessageLite.GeneratedExtension<?, ?> extension) { + extensionsByNumber.put( + new ObjectIntPair(extension.getContainingTypeDefaultInstance(), + extension.getNumber()), + extension); + } + + // ================================================================= + // Private stuff. + + // Constructors are package-private so that ExtensionRegistry can subclass + // this. + + ExtensionRegistryLite() { + this.extensionsByNumber = + new HashMap<ObjectIntPair, + GeneratedMessageLite.GeneratedExtension<?, ?>>(); + } + + ExtensionRegistryLite(ExtensionRegistryLite other) { + if (other == EMPTY) { + this.extensionsByNumber = Collections.emptyMap(); + } else { + this.extensionsByNumber = + Collections.unmodifiableMap(other.extensionsByNumber); + } + } + + private final Map<ObjectIntPair, + GeneratedMessageLite.GeneratedExtension<?, ?>> + extensionsByNumber; + + private ExtensionRegistryLite(boolean empty) { + this.extensionsByNumber = Collections.emptyMap(); + } + private static final ExtensionRegistryLite EMPTY = + new ExtensionRegistryLite(true); + + /** A (Object, int) pair, used as a map key. */ + private static final class ObjectIntPair { + private final Object object; + private final int number; + + ObjectIntPair(final Object object, final int number) { + this.object = object; + this.number = number; + } + + @Override + public int hashCode() { + return System.identityHashCode(object) * ((1 << 16) - 1) + number; + } + @Override + public boolean equals(final Object obj) { + if (!(obj instanceof ObjectIntPair)) { + return false; + } + final ObjectIntPair other = (ObjectIntPair)obj; + return object == other.object && number == other.number; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java index 01da3faf..93e55f29 100644 --- a/java/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -30,16 +30,13 @@ package com.google.protobuf; -import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.Descriptors.FieldDescriptor; -import com.google.protobuf.Descriptors.EnumValueDescriptor; - 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. @@ -49,43 +46,70 @@ import java.util.Map; * * @author kenton@google.com Kenton Varda */ -final class FieldSet { - private Map<FieldDescriptor, Object> fields; +final class FieldSet<FieldDescriptorType extends + FieldSet.FieldDescriptorLite<FieldDescriptorType>> { + /** + * Interface for a FieldDescriptor or lite extension descriptor. This + * prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}. + */ + public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>> + extends Comparable<T> { + int getNumber(); + WireFormat.FieldType getLiteType(); + WireFormat.JavaType getLiteJavaType(); + boolean isRepeated(); + boolean isPacked(); + Internal.EnumLiteMap<?> getEnumType(); + + // If getLiteJavaType() == MESSAGE, this merges a message object of the + // type into a builder of the type. Returns {@code to}. + MessageLite.Builder internalMergeFrom( + MessageLite.Builder to, MessageLite from); + } + + private Map<FieldDescriptorType, Object> fields; /** Construct a new FieldSet. */ private FieldSet() { // Use a TreeMap because fields need to be in canonical order when // serializing. - this.fields = new TreeMap<FieldDescriptor, Object>(); + // 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>(); } /** - * Construct a new FieldSet with the given map. This is only used by - * DEFAULT_INSTANCE, to pass in an immutable empty map. + * Construct an empty FieldSet. This is only used to initialize + * DEFAULT_INSTANCE. */ - private FieldSet(Map<FieldDescriptor, Object> fields) { - this.fields = fields; + private FieldSet(final boolean dummy) { + this.fields = Collections.emptyMap(); } /** Construct a new FieldSet. */ - public static FieldSet newFieldSet() { - return new FieldSet(); + public static <T extends FieldSet.FieldDescriptorLite<T>> + FieldSet<T> newFieldSet() { + return new FieldSet<T>(); } /** Get an immutable empty FieldSet. */ - public static FieldSet emptySet() { + @SuppressWarnings("unchecked") + public static <T extends FieldSet.FieldDescriptorLite<T>> + FieldSet<T> emptySet() { return DEFAULT_INSTANCE; } - private static final FieldSet DEFAULT_INSTANCE = - new FieldSet(Collections.<FieldDescriptor, Object>emptyMap()); + @SuppressWarnings("unchecked") + private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true); /** Make this FieldSet immutable from this point forward. */ @SuppressWarnings("unchecked") public void makeImmutable() { - for (Map.Entry<FieldDescriptor, Object> entry: fields.entrySet()) { + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { if (entry.getKey().isRepeated()) { - List value = (List)entry.getValue(); - entry.setValue(Collections.unmodifiableList(value)); + final List value = (List)entry.getValue(); + fields.put(entry.getKey(), Collections.unmodifiableList(value)); } } fields = Collections.unmodifiableMap(fields); @@ -98,56 +122,52 @@ final class FieldSet { fields.clear(); } - /** See {@link Message#getAllFields()}. */ - public Map<Descriptors.FieldDescriptor, Object> getAllFields() { + /** + * Get a simple map containing all the fields. + */ + public Map<FieldDescriptorType, Object> getAllFields() { return Collections.unmodifiableMap(fields); } /** - * Get an interator to the field map. This iterator should not be leaked + * 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<Descriptors.FieldDescriptor, Object>> iterator() { + public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() { return fields.entrySet().iterator(); } - /** See {@link Message#hasField(Descriptors.FieldDescriptor)}. */ - public boolean hasField(Descriptors.FieldDescriptor field) { - if (field.isRepeated()) { + /** + * Useful for implementing + * {@link Message#hasField(Descriptors.FieldDescriptor)}. + */ + public boolean hasField(final FieldDescriptorType descriptor) { + if (descriptor.isRepeated()) { throw new IllegalArgumentException( "hasField() can only be called on non-repeated fields."); } - return fields.containsKey(field); + return fields.get(descriptor) != null; } /** - * See {@link Message#getField(Descriptors.FieldDescriptor)}. This method - * returns {@code null} if the field is a singular message type and is not - * set; in this case it is up to the caller to fetch the message's default - * instance. + * Useful for implementing + * {@link Message#getField(Descriptors.FieldDescriptor)}. This method + * returns {@code null} if the field is not set; in this case it is up + * to the caller to fetch the field's default value. */ - public Object getField(Descriptors.FieldDescriptor field) { - Object result = fields.get(field); - if (result == null) { - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - return Collections.emptyList(); - } else { - return null; - } - } else { - return field.getDefaultValue(); - } - } else { - return result; - } + public Object getField(final FieldDescriptorType descriptor) { + return fields.get(descriptor); } - /** See {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */ + /** + * Useful for implementing + * {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. + */ @SuppressWarnings("unchecked") - public void setField(Descriptors.FieldDescriptor field, Object value) { - if (field.isRepeated()) { + public void setField(final FieldDescriptorType descriptor, + Object value) { + if (descriptor.isRepeated()) { if (!(value instanceof List)) { throw new IllegalArgumentException( "Wrong object type used with protocol message reflection."); @@ -155,78 +175,108 @@ final class FieldSet { // Wrap the contents in a new list so that the caller cannot change // the list's contents after setting it. - List newList = new ArrayList(); + final List newList = new ArrayList(); newList.addAll((List)value); - for (Object element : newList) { - verifyType(field, element); + for (final Object element : newList) { + verifyType(descriptor.getLiteType(), element); } value = newList; } else { - verifyType(field, value); + verifyType(descriptor.getLiteType(), value); } - fields.put(field, value); + fields.put(descriptor, value); } - /** See {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ - public void clearField(Descriptors.FieldDescriptor field) { - fields.remove(field); + /** + * Useful for implementing + * {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. + */ + public void clearField(final FieldDescriptorType descriptor) { + fields.remove(descriptor); } - /** See {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ - public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { - if (!field.isRepeated()) { + /** + * Useful for implementing + * {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. + */ + public int getRepeatedFieldCount(final FieldDescriptorType descriptor) { + if (!descriptor.isRepeated()) { throw new IllegalArgumentException( - "getRepeatedFieldCount() can only be called on repeated fields."); + "getRepeatedField() can only be called on repeated fields."); } - return ((List)getField(field)).size(); + final Object value = fields.get(descriptor); + if (value == null) { + return 0; + } else { + return ((List) value).size(); + } } - /** See {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */ - public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { - if (!field.isRepeated()) { + /** + * Useful for implementing + * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. + */ + public Object getRepeatedField(final FieldDescriptorType descriptor, + final int index) { + if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "getRepeatedField() can only be called on repeated fields."); } - return ((List)getField(field)).get(index); + final Object value = fields.get(descriptor); + + if (value == null) { + throw new IndexOutOfBoundsException(); + } else { + return ((List) value).get(index); + } } - /** See {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */ + /** + * Useful for implementing + * {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. + */ @SuppressWarnings("unchecked") - public void setRepeatedField(Descriptors.FieldDescriptor field, int index, - Object value) { - if (!field.isRepeated()) { + public void setRepeatedField(final FieldDescriptorType descriptor, + final int index, + final Object value) { + if (!descriptor.isRepeated()) { throw new IllegalArgumentException( - "setRepeatedField() can only be called on repeated fields."); + "getRepeatedField() can only be called on repeated fields."); } - verifyType(field, value); - - List list = (List)fields.get(field); + final Object list = fields.get(descriptor); if (list == null) { throw new IndexOutOfBoundsException(); } - list.set(index, value); + verifyType(descriptor.getLiteType(), value); + ((List) list).set(index, value); } - /** See {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */ + /** + * Useful for implementing + * {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. + */ @SuppressWarnings("unchecked") - public void addRepeatedField(Descriptors.FieldDescriptor field, - Object value) { - if (!field.isRepeated()) { + public void addRepeatedField(final FieldDescriptorType descriptor, + final Object value) { + if (!descriptor.isRepeated()) { throw new IllegalArgumentException( - "setRepeatedField() can only be called on repeated fields."); + "addRepeatedField() can only be called on repeated fields."); } - verifyType(field, value); + verifyType(descriptor.getLiteType(), value); - List list = (List)fields.get(field); - if (list == null) { + final Object existingValue = fields.get(descriptor); + List list; + if (existingValue == null) { list = new ArrayList(); - fields.put(field, list); + fields.put(descriptor, list); + } else { + list = (List) existingValue; } list.add(value); @@ -239,9 +289,14 @@ final class FieldSet { * * @throws IllegalArgumentException The value is not of the right type. */ - private void verifyType(FieldDescriptor field, Object value) { + private static void verifyType(final WireFormat.FieldType type, + final Object value) { + if (value == null) { + throw new NullPointerException(); + } + boolean isValid = false; - switch (field.getJavaType()) { + switch (type.getJavaType()) { case INT: isValid = value instanceof Integer ; break; case LONG: isValid = value instanceof Long ; break; case FLOAT: isValid = value instanceof Float ; break; @@ -250,26 +305,25 @@ final class FieldSet { case STRING: isValid = value instanceof String ; break; case BYTE_STRING: isValid = value instanceof ByteString; break; case ENUM: - isValid = value instanceof EnumValueDescriptor && - ((EnumValueDescriptor)value).getType() == field.getEnumType(); + // TODO(kenton): Caller must do type checking here, I guess. + isValid = value instanceof Internal.EnumLite; break; case MESSAGE: - isValid = value instanceof Message && - ((Message)value).getDescriptorForType() == field.getMessageType(); + // TODO(kenton): Caller must do type checking here, I guess. + isValid = value instanceof MessageLite; break; } if (!isValid) { - // When chaining calls to setField(), it can be hard to tell from - // the stack trace which exact call failed, since the whole chain is - // considered one line of code. So, let's make sure to include the - // field name and other useful info in the exception. + // TODO(kenton): When chaining calls to setField(), it can be hard to + // tell from the stack trace which exact call failed, since the whole + // chain is considered one line of code. It would be nice to print + // more information here, e.g. naming the field. We used to do that. + // But we can't now that FieldSet doesn't use descriptors. Maybe this + // isn't a big deal, though, since it would only really apply when using + // reflection and generally people don't chain reflection setters. throw new IllegalArgumentException( - "Wrong object type used with protocol message reflection. " + - "Message type \"" + field.getContainingType().getFullName() + - "\", field \"" + - (field.isExtension() ? field.getFullName() : field.getName()) + - "\", value was type \"" + value.getClass().getName() + "\"."); + "Wrong object type used with protocol message reflection."); } } @@ -284,17 +338,19 @@ final class FieldSet { */ @SuppressWarnings("unchecked") public boolean isInitialized() { - for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) { - FieldDescriptor field = entry.getKey(); - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - for (Message element : (List<Message>) entry.getValue()) { + 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 { - if (!((Message) entry.getValue()).isInitialized()) { + if (!((MessageLite) entry.getValue()).isInitialized()) { return false; } } @@ -305,59 +361,17 @@ final class FieldSet { } /** - * Like {@link #isInitialized()}, but also checks for the presence of - * all required fields in the given type. + * Given a field type, return the wire type. + * + * @returns One of the {@code WIRETYPE_} constants defined in + * {@link WireFormat}. */ - @SuppressWarnings("unchecked") - public boolean isInitialized(Descriptor type) { - // Check that all required fields are present. - for (FieldDescriptor field : type.getFields()) { - if (field.isRequired()) { - if (!hasField(field)) { - return false; - } - } - } - - // Check that embedded messages are initialized. - return isInitialized(); - } - - /** See {@link Message.Builder#mergeFrom(Message)}. */ - @SuppressWarnings("unchecked") - public void mergeFrom(Message other) { - // Note: We don't attempt to verify that other's fields have valid - // types. Doing so would be a losing battle. We'd have to verify - // all sub-messages as well, and we'd have to make copies of all of - // them to insure that they don't change after verification (since - // the Message interface itself cannot enforce immutability of - // implementations). - // TODO(kenton): Provide a function somewhere called makeDeepCopy() - // which allows people to make secure deep copies of messages. - - for (Map.Entry<FieldDescriptor, Object> entry : - other.getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); - if (field.isRepeated()) { - List existingValue = (List)fields.get(field); - if (existingValue == null) { - existingValue = new ArrayList(); - fields.put(field, existingValue); - } - existingValue.addAll((List)entry.getValue()); - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - Message existingValue = (Message)fields.get(field); - if (existingValue == null) { - setField(field, entry.getValue()); - } else { - setField(field, - existingValue.toBuilder() - .mergeFrom((Message)entry.getValue()) - .build()); - } - } else { - setField(field, entry.getValue()); - } + static int getWireFormatForFieldType(final WireFormat.FieldType type, + boolean isPacked) { + if (isPacked) { + return WireFormat.WIRETYPE_LENGTH_DELIMITED; + } else { + return type.getWireType(); } } @@ -365,360 +379,334 @@ final class FieldSet { * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. */ @SuppressWarnings("unchecked") - public void mergeFrom(FieldSet other) { - for (Map.Entry<FieldDescriptor, Object> entry : other.fields.entrySet()) { - FieldDescriptor field = entry.getKey(); - Object value = entry.getValue(); - - if (field.isRepeated()) { - List existingValue = (List)fields.get(field); - if (existingValue == null) { - existingValue = new ArrayList(); - fields.put(field, existingValue); + public void mergeFrom(final FieldSet<FieldDescriptorType> other) { + for (final Map.Entry<FieldDescriptorType, Object> entry: + other.fields.entrySet()) { + final FieldDescriptorType descriptor = entry.getKey(); + final Object otherValue = entry.getValue(); + + 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); } - existingValue.addAll((List)value); - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - Message existingValue = (Message)fields.get(field); - if (existingValue == null) { - setField(field, value); + } else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) { + Object value = fields.get(descriptor); + if (value == null) { + fields.put(descriptor, otherValue); } else { - setField(field, - existingValue.toBuilder() - .mergeFrom((Message)value) + // Merge the messages. + fields.put(descriptor, + descriptor.internalMergeFrom( + ((MessageLite) value).toBuilder(), (MessageLite) otherValue) .build()); } + } else { - setField(field, value); + fields.put(descriptor, otherValue); } } } - // TODO(kenton): Move parsing code into AbstractMessage, since it no longer - // uses any special knowledge from FieldSet. + // TODO(kenton): Move static parsing and serialization methods into some + // other class. Probably WireFormat. /** - * See {@link Message.Builder#mergeFrom(CodedInputStream)}. - * @param builder The {@code Builder} for the target message. + * 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. + * @return An object representing the field's value, of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. */ - public static void mergeFrom(CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistry extensionRegistry, - Message.Builder builder) - throws java.io.IOException { - while (true) { - int tag = input.readTag(); - if (tag == 0) { - break; - } + public static Object readPrimitiveField( + CodedInputStream input, + final WireFormat.FieldType type) throws IOException { + switch (type) { + case DOUBLE : return input.readDouble (); + case FLOAT : return input.readFloat (); + case INT64 : return input.readInt64 (); + case UINT64 : return input.readUInt64 (); + case INT32 : return input.readInt32 (); + case FIXED64 : return input.readFixed64 (); + case FIXED32 : return input.readFixed32 (); + case BOOL : return input.readBool (); + case STRING : return input.readString (); + case BYTES : return input.readBytes (); + case UINT32 : return input.readUInt32 (); + case SFIXED32: return input.readSFixed32(); + case SFIXED64: return input.readSFixed64(); + case SINT32 : return input.readSInt32 (); + case SINT64 : return input.readSInt64 (); + + case GROUP: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle nested groups."); + case MESSAGE: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle embedded messages."); + case ENUM: + // We don't handle enums because we don't know what to do if the + // value is not recognized. + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle enums."); + } - if (!mergeFieldFrom(input, unknownFields, extensionRegistry, - builder, tag)) { - // end group tag - break; - } + throw new RuntimeException( + "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 (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + writeField(entry.getKey(), entry.getValue(), output); } } /** - * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, - * ExtensionRegistry, Message.Builder)}, but parses a single field. - * @param tag The tag, which should have already been read. - * @return {@code true} unless the tag is an end-group tag. + * Like {@link #writeTo} but uses MessageSet wire format. */ - @SuppressWarnings("unchecked") - public static boolean mergeFieldFrom( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistry extensionRegistry, - Message.Builder builder, - int tag) throws java.io.IOException { - Descriptor type = builder.getDescriptorForType(); - - if (type.getOptions().getMessageSetWireFormat() && - tag == WireFormat.MESSAGE_SET_ITEM_TAG) { - mergeMessageSetExtensionFromCodedStream( - input, unknownFields, extensionRegistry, builder); - return true; - } - - int wireType = WireFormat.getTagWireType(tag); - int fieldNumber = WireFormat.getTagFieldNumber(tag); - - FieldDescriptor field; - Message defaultInstance = null; - - if (type.isExtensionNumber(fieldNumber)) { - ExtensionRegistry.ExtensionInfo extension = - extensionRegistry.findExtensionByNumber(type, fieldNumber); - if (extension == null) { - field = null; + public void writeMessageSetTo(final CodedOutputStream output) + throws IOException { + 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 { - field = extension.descriptor; - defaultInstance = extension.defaultInstance; + writeField(descriptor, entry.getValue(), output); } - } else { - field = type.findFieldByNumber(fieldNumber); } + } - if (field == null || - wireType != WireFormat.getWireFormatForField(field)) { - // Unknown field or wrong wire type. Skip. - return unknownFields.mergeFieldFrom(tag, input); + /** + * Write a single tag-value pair to the stream. + * + * @param output The output stream. + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + private static void writeElement(final CodedOutputStream output, + final WireFormat.FieldType type, + final int number, + final Object value) throws IOException { + // 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); } else { - if (field.getOptions().getPacked()) { - int length = input.readRawVarint32(); - int limit = input.pushLimit(length); - if (field.getType() == FieldDescriptor.Type.ENUM) { - while (input.getBytesUntilLimit() > 0) { - int rawValue = input.readEnum(); - 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) { - Object value = input.readPrimitiveField(field.getType()); - builder.addRepeatedField(field, value); - } - } - input.popLimit(limit); - } else { - Object value; - switch (field.getType()) { - case GROUP: { - 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: { - 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: { - 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 = input.readPrimitiveField(field.getType()); - break; - } - - if (field.isRepeated()) { - builder.addRepeatedField(field, value); - } else { - builder.setField(field, value); - } - } + output.writeTag(number, getWireFormatForFieldType(type, false)); + writeElementNoTag(output, type, value); } - - return true; } - /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ - private static void mergeMessageSetExtensionFromCodedStream( - CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistry extensionRegistry, - Message.Builder builder) throws java.io.IOException { - 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) { - int tag = input.readTag(); - if (tag == 0) { + /** + * Write a field of arbitrary type, without its tag, to the stream. + * + * @param output The output stream. + * @param type The field's type. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + private static void writeElementNoTag( + final CodedOutputStream output, + final WireFormat.FieldType type, + final Object value) throws IOException { + switch (type) { + case DOUBLE : output.writeDoubleNoTag ((Double ) value); break; + case FLOAT : output.writeFloatNoTag ((Float ) value); break; + case INT64 : output.writeInt64NoTag ((Long ) value); break; + case UINT64 : output.writeUInt64NoTag ((Long ) value); break; + case INT32 : output.writeInt32NoTag ((Integer ) value); break; + case FIXED64 : output.writeFixed64NoTag ((Long ) value); break; + case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break; + case BOOL : output.writeBoolNoTag ((Boolean ) value); break; + case STRING : output.writeStringNoTag ((String ) value); break; + case GROUP : output.writeGroupNoTag ((MessageLite) value); break; + case MESSAGE : output.writeMessageNoTag ((MessageLite) 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; + case SINT32 : output.writeSInt32NoTag ((Integer ) value); break; + case SINT64 : output.writeSInt64NoTag ((Long ) value); break; + + case ENUM: + output.writeEnumNoTag(((Internal.EnumLite) value).getNumber()); break; - } + } + } - if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { - typeId = input.readUInt32(); - // Zero is not a valid type ID. - if (typeId != 0) { - ExtensionRegistry.ExtensionInfo extension = - extensionRegistry.findExtensionByNumber(type, typeId); - if (extension != null) { - field = extension.descriptor; - subBuilder = extension.defaultInstance.newBuilderForType(); - 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; - } - } + /** Write a single field. */ + public static void writeField(final FieldDescriptorLite<?> descriptor, + final Object value, + final CodedOutputStream output) + throws IOException { + WireFormat.FieldType type = descriptor.getLiteType(); + int number = descriptor.getNumber(); + if (descriptor.isRepeated()) { + 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. + int dataSize = 0; + for (final Object element : valueList) { + dataSize += computeElementSizeNoTag(type, element); } - } 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); + output.writeRawVarint32(dataSize); + // Write the data itself, without any tags. + for (final Object element : valueList) { + writeElementNoTag(output, type, element); } } else { - // Unknown tag. Skip it. - if (!input.skipField(tag)) { - break; // end of group + for (final Object element : valueList) { + writeElement(output, type, number, element); } } - } - - input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); - - if (subBuilder != null) { - builder.setField(field, subBuilder.build()); + } else { + writeElement(output, type, number, value); } } - /** See {@link Message#writeTo(CodedOutputStream)}. */ - public void writeTo(CodedOutputStream output) - throws java.io.IOException { - for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) { - writeField(entry.getKey(), entry.getValue(), output); + /** + * See {@link Message#getSerializedSize()}. It's up to the caller to cache + * the resulting size if desired. + */ + public int getSerializedSize() { + int size = 0; + for (final Map.Entry<FieldDescriptorType, Object> entry: + fields.entrySet()) { + size += computeFieldSize(entry.getKey(), entry.getValue()); } + return size; } - /** Write a single field. */ - public void writeField(FieldDescriptor field, Object value, - CodedOutputStream output) throws java.io.IOException { - if (field.isExtension() && - field.getContainingType().getOptions().getMessageSetWireFormat()) { - output.writeMessageSetExtension(field.getNumber(), (Message)value); - } else { - if (field.isRepeated()) { - List valueList = (List)value; - if (field.getOptions().getPacked()) { - output.writeTag(field.getNumber(), - WireFormat.WIRETYPE_LENGTH_DELIMITED); - // Compute the total data size so the length can be written. - int dataSize = 0; - for (Object element : valueList) { - dataSize += output.computeFieldSizeNoTag(field.getType(), element); - } - output.writeRawVarint32(dataSize); - // Write the data itself, without any tags. - for (Object element : valueList) { - output.writeFieldNoTag(field.getType(), element); - } - } else { - for (Object element : valueList) { - output.writeField(field.getType(), field.getNumber(), element); - } - } + /** + * Like {@link #getSerializedSize} but uses MessageSet wire format. + */ + public int getMessageSetSerializedSize() { + int size = 0; + 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 { - output.writeField(field.getType(), field.getNumber(), value); + size += computeFieldSize(descriptor, entry.getValue()); } } + return size; } /** - * See {@link Message#getSerializedSize()}. It's up to the caller to cache - * the resulting size if desired. + * Compute the number of bytes that would be needed to encode a + * single tag/value pair of arbitrary type. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. */ - public int getSerializedSize() { - int size = 0; - for (Map.Entry<FieldDescriptor, Object> entry : fields.entrySet()) { - FieldDescriptor field = entry.getKey(); - Object value = entry.getValue(); + private static int computeElementSize( + final WireFormat.FieldType type, + final int number, final Object value) { + int tagSize = CodedOutputStream.computeTagSize(number); + if (type == WireFormat.FieldType.GROUP) { + tagSize *= 2; + } + return tagSize + computeElementSizeNoTag(type, value); + } - if (field.isExtension() && - field.getContainingType().getOptions().getMessageSetWireFormat()) { - size += CodedOutputStream.computeMessageSetExtensionSize( - field.getNumber(), (Message) value); + /** + * Compute the number of bytes that would be needed to encode a + * particular value of arbitrary type, excluding tag. + * + * @param type The field's type. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + private static int computeElementSizeNoTag( + final WireFormat.FieldType type, final Object value) { + switch (type) { + // Note: Minor violation of 80-char limit rule here because this would + // actually be harder to read if we wrapped the lines. + case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value); + case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value); + case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value); + case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value); + case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value); + case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value); + case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value); + case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value); + case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value); + case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)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 ENUM: + return CodedOutputStream.computeEnumSizeNoTag( + ((Internal.EnumLite) value).getNumber()); + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + /** + * Compute the number of bytes needed to encode a particular field. + */ + public static int computeFieldSize(final FieldDescriptorLite<?> descriptor, + final Object value) { + WireFormat.FieldType type = descriptor.getLiteType(); + int number = descriptor.getNumber(); + if (descriptor.isRepeated()) { + if (descriptor.isPacked()) { + int dataSize = 0; + for (final Object element : (List)value) { + dataSize += computeElementSizeNoTag(type, element); + } + return dataSize + + CodedOutputStream.computeTagSize(number) + + CodedOutputStream.computeRawVarint32Size(dataSize); } else { - if (field.isRepeated()) { - if (field.getOptions().getPacked()) { - int dataSize = 0; - for (Object element : (List)value) { - dataSize += CodedOutputStream.computeFieldSizeNoTag( - field.getType(), element); - } - size += dataSize + - CodedOutputStream.computeTagSize(field.getNumber()) + - CodedOutputStream.computeRawVarint32Size(dataSize); - } else { - for (Object element : (List)value) { - size += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), element); - } - } - } else { - size += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), value); + int size = 0; + for (final Object element : (List)value) { + size += computeElementSize(type, number, element); } + return size; } + } else { + return computeElementSize(type, number, value); } - 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 77c88c30..4994faad 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -36,8 +36,8 @@ import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -69,13 +69,13 @@ public abstract class GeneratedMessage extends AbstractMessage { } /** Internal helper which returns a mutable map. */ - private final Map<FieldDescriptor, Object> getAllFieldsMutable() { - TreeMap<FieldDescriptor, Object> result = + private Map<FieldDescriptor, Object> getAllFieldsMutable() { + final TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>(); - Descriptor descriptor = internalGetFieldAccessorTable().descriptor; - for (FieldDescriptor field : descriptor.getFields()) { + final Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + for (final FieldDescriptor field : descriptor.getFields()) { if (field.isRepeated()) { - List value = (List) getField(field); + final List value = (List) getField(field); if (!value.isEmpty()) { result.put(field, value); } @@ -88,8 +88,9 @@ public abstract class GeneratedMessage extends AbstractMessage { return result; } + @Override public boolean isInitialized() { - for (FieldDescriptor field : getDescriptorForType().getFields()) { + for (final FieldDescriptor field : getDescriptorForType().getFields()) { // Check that all required fields are present. if (field.isRequired()) { if (!hasField(field)) { @@ -99,9 +100,9 @@ public abstract class GeneratedMessage extends AbstractMessage { // Check that embedded messages are initialized. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { if (field.isRepeated()) { - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") final List<Message> messageList = (List<Message>) getField(field); - for (Message element : messageList) { + for (final Message element : messageList) { if (!element.isInitialized()) { return false; } @@ -121,20 +122,20 @@ public abstract class GeneratedMessage extends AbstractMessage { return Collections.unmodifiableMap(getAllFieldsMutable()); } - public boolean hasField(Descriptors.FieldDescriptor field) { + public boolean hasField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).has(this); } - public Object getField(FieldDescriptor field) { + public Object getField(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).get(this); } - public int getRepeatedFieldCount(FieldDescriptor field) { + public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field) .getRepeatedCount(this); } - public Object getRepeatedField(FieldDescriptor field, int index) { + public Object getRepeatedField(final FieldDescriptor field, final int index) { return internalGetFieldAccessorTable().getField(field) .getRepeated(this, index); } @@ -148,6 +149,15 @@ public abstract class GeneratedMessage extends AbstractMessage { extends AbstractMessage.Builder<BuilderType> { 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 + // of this dummy clone() implementation makes it go away. + @Override + public BuilderType clone() { + throw new UnsupportedOperationException( + "This is supposed to be overridden by subclasses."); + } + /** * Get the message being built. We don't just pass this to the * constructor because it becomes null when build() is called. @@ -163,55 +173,24 @@ public abstract class GeneratedMessage extends AbstractMessage { return internalGetResult().internalGetFieldAccessorTable(); } - public BuilderType mergeFrom(Message other) { - if (other.getDescriptorForType() != - internalGetFieldAccessorTable().descriptor) { - throw new IllegalArgumentException("Message type mismatch."); - } - - for (Map.Entry<FieldDescriptor, Object> entry : - other.getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); - if (field.isRepeated()) { - // Concatenate repeated fields. - for (Object element : (List) entry.getValue()) { - addRepeatedField(field, element); - } - } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE && - hasField(field)) { - // Merge singular embedded messages. - Message oldValue = (Message) getField(field); - setField(field, - oldValue.newBuilderForType() - .mergeFrom(oldValue) - .mergeFrom((Message) entry.getValue()) - .buildPartial()); - } else { - // Just overwrite. - setField(field, entry.getValue()); - } - } - return (BuilderType) this; - } - public Descriptor getDescriptorForType() { return internalGetFieldAccessorTable().descriptor; } - public Map<Descriptors.FieldDescriptor, Object> getAllFields() { + public Map<FieldDescriptor, Object> getAllFields() { return internalGetResult().getAllFields(); } public Message.Builder newBuilderForField( - Descriptors.FieldDescriptor field) { + final FieldDescriptor field) { return internalGetFieldAccessorTable().getField(field).newBuilder(); } - public boolean hasField(Descriptors.FieldDescriptor field) { + public boolean hasField(final FieldDescriptor field) { return internalGetResult().hasField(field); } - public Object getField(Descriptors.FieldDescriptor field) { + public Object getField(final FieldDescriptor field) { if (field.isRepeated()) { // The underlying list object is still modifiable at this point. // Make sure not to expose the modifiable list to the caller. @@ -222,35 +201,35 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public BuilderType setField(Descriptors.FieldDescriptor field, - Object value) { + public BuilderType setField(final FieldDescriptor field, + final Object value) { internalGetFieldAccessorTable().getField(field).set(this, value); return (BuilderType) this; } - public BuilderType clearField(Descriptors.FieldDescriptor field) { + public BuilderType clearField(final FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); return (BuilderType) this; } - public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + public int getRepeatedFieldCount(final FieldDescriptor field) { return internalGetResult().getRepeatedFieldCount(field); } - public Object getRepeatedField(Descriptors.FieldDescriptor field, - int index) { + public Object getRepeatedField(final FieldDescriptor field, + final int index) { return internalGetResult().getRepeatedField(field, index); } - public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, - int index, Object value) { + public BuilderType setRepeatedField(final FieldDescriptor field, + final int index, final Object value) { internalGetFieldAccessorTable().getField(field) .setRepeated(this, index, value); return (BuilderType) this; } - public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, - Object value) { + public BuilderType addRepeatedField(final FieldDescriptor field, + final Object value) { internalGetFieldAccessorTable().getField(field).addRepeated(this, value); return (BuilderType) this; } @@ -259,13 +238,16 @@ public abstract class GeneratedMessage extends AbstractMessage { return internalGetResult().unknownFields; } - public final BuilderType setUnknownFields(UnknownFieldSet unknownFields) { + public final BuilderType setUnknownFields( + final UnknownFieldSet unknownFields) { internalGetResult().unknownFields = unknownFields; return (BuilderType) this; } - public final BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { - GeneratedMessage result = internalGetResult(); + @Override + public final BuilderType mergeUnknownFields( + final UnknownFieldSet unknownFields) { + final GeneratedMessage result = internalGetResult(); result.unknownFields = UnknownFieldSet.newBuilder(result.unknownFields) .mergeFrom(unknownFields) @@ -281,36 +263,13 @@ public abstract class GeneratedMessage extends AbstractMessage { * 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, - ExtensionRegistry extensionRegistry, - int tag) - throws IOException { + protected boolean parseUnknownField( + final CodedInputStream input, + final UnknownFieldSet.Builder unknownFields, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { return unknownFields.mergeFieldFrom(tag, input); } - - /** - * Adds the {@code values} to the {@code list}. - * - * @throws NullPointerException if any of the elements of {@code values} is - * null. - */ - protected <T> void addAll(Iterable<T> values, Collection<? super T> list) { - for (T value : values) { - if (value == null) { - throw new NullPointerException(); - } - } - if (values instanceof Collection) { - @SuppressWarnings("unsafe") - Collection<T> collection = (Collection<T>) values; - list.addAll(collection); - } else { - for (T value : values) { - list.add(value); - } - } - } } // ================================================================= @@ -355,10 +314,10 @@ public abstract class GeneratedMessage extends AbstractMessage { MessageType extends ExtendableMessage> extends GeneratedMessage { protected ExtendableMessage() {} - private final FieldSet extensions = FieldSet.newFieldSet(); + private final FieldSet<FieldDescriptor> extensions = FieldSet.newFieldSet(); - private final void verifyExtensionContainingType( - GeneratedExtension<MessageType, ?> extension) { + private void verifyExtensionContainingType( + final GeneratedExtension<MessageType, ?> extension) { if (extension.getDescriptor().getContainingType() != getDescriptorForType()) { // This can only happen if someone uses unchecked operations. @@ -372,26 +331,33 @@ public abstract class GeneratedMessage extends AbstractMessage { /** Check if a singular extension is present. */ public final boolean hasExtension( - GeneratedExtension<MessageType, ?> extension) { + final GeneratedExtension<MessageType, ?> extension) { verifyExtensionContainingType(extension); return extensions.hasField(extension.getDescriptor()); } /** Get the number of elements in a repeated extension. */ public final <Type> int getExtensionCount( - GeneratedExtension<MessageType, List<Type>> extension) { + final GeneratedExtension<MessageType, List<Type>> extension) { verifyExtensionContainingType(extension); - return extensions.getRepeatedFieldCount(extension.getDescriptor()); + final FieldDescriptor descriptor = extension.getDescriptor(); + return extensions.getRepeatedFieldCount(descriptor); } /** Get the value of an extension. */ @SuppressWarnings("unchecked") public final <Type> Type getExtension( - GeneratedExtension<MessageType, Type> extension) { + final GeneratedExtension<MessageType, Type> extension) { verifyExtensionContainingType(extension); - Object value = extensions.getField(extension.getDescriptor()); + FieldDescriptor descriptor = extension.getDescriptor(); + final Object value = extensions.getField(descriptor); if (value == null) { - return (Type) extension.getMessageDefaultInstance(); + if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + return (Type) extension.getMessageDefaultInstance(); + } else { + return (Type) extension.fromReflectionType( + descriptor.getDefaultValue()); + } } else { return (Type) extension.fromReflectionType(value); } @@ -400,10 +366,12 @@ public abstract class GeneratedMessage extends AbstractMessage { /** Get one element of a repeated extension. */ @SuppressWarnings("unchecked") public final <Type> Type getExtension( - GeneratedExtension<MessageType, List<Type>> extension, int index) { + final GeneratedExtension<MessageType, List<Type>> extension, + final int index) { verifyExtensionContainingType(extension); + FieldDescriptor descriptor = extension.getDescriptor(); return (Type) extension.singularFromReflectionType( - extensions.getRepeatedField(extension.getDescriptor(), index)); + extensions.getRepeatedField(descriptor, index)); } /** Called by subclasses to check if all extensions are initialized. */ @@ -411,6 +379,7 @@ public abstract class GeneratedMessage extends AbstractMessage { return extensions.isInitialized(); } + @Override public boolean isInitialized() { return super.isInitialized() && extensionsAreInitialized(); } @@ -425,20 +394,30 @@ public abstract class GeneratedMessage extends AbstractMessage { // Imagine how much simpler this code would be if Java iterators had // a way to get the next element without advancing the iterator. - final Iterator<Map.Entry<FieldDescriptor, Object>> iter = + private final Iterator<Map.Entry<FieldDescriptor, Object>> iter = extensions.iterator(); - Map.Entry<FieldDescriptor, Object> next = null; + private Map.Entry<FieldDescriptor, Object> next; + private final boolean messageSetWireFormat; - private ExtensionWriter() { + private ExtensionWriter(final boolean messageSetWireFormat) { if (iter.hasNext()) { next = iter.next(); } + this.messageSetWireFormat = messageSetWireFormat; } - public void writeUntil(int end, CodedOutputStream output) + public void writeUntil(final int end, final CodedOutputStream output) throws IOException { while (next != null && next.getKey().getNumber() < end) { - extensions.writeField(next.getKey(), next.getValue(), output); + FieldDescriptor descriptor = next.getKey(); + if (messageSetWireFormat && descriptor.getLiteJavaType() == + WireFormat.JavaType.MESSAGE && + !descriptor.isRepeated()) { + output.writeMessageSetExtension(descriptor.getNumber(), + (Message) next.getValue()); + } else { + FieldSet.writeField(descriptor, next.getValue(), output); + } if (iter.hasNext()) { next = iter.next(); } else { @@ -449,24 +428,32 @@ public abstract class GeneratedMessage extends AbstractMessage { } protected ExtensionWriter newExtensionWriter() { - return new ExtensionWriter(); + return new ExtensionWriter(false); + } + protected ExtensionWriter newMessageSetExtensionWriter() { + return new ExtensionWriter(true); } /** Called by subclasses to compute the size of extensions. */ protected int extensionsSerializedSize() { return extensions.getSerializedSize(); } + protected int extensionsSerializedSizeAsMessageSet() { + return extensions.getMessageSetSerializedSize(); + } // --------------------------------------------------------------- // Reflection - public Map<Descriptors.FieldDescriptor, Object> getAllFields() { - Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); + @Override + public Map<FieldDescriptor, Object> getAllFields() { + final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); result.putAll(extensions.getAllFields()); return Collections.unmodifiableMap(result); } - public boolean hasField(FieldDescriptor field) { + @Override + public boolean hasField(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); return extensions.hasField(field); @@ -475,14 +462,19 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public Object getField(FieldDescriptor field) { + @Override + public Object getField(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); - Object value = extensions.getField(field); + final Object value = extensions.getField(field); if (value == null) { - // Lacking an ExtensionRegistry, we have no way to determine the - // extension's real type, so we return a DynamicMessage. - return DynamicMessage.getDefaultInstance(field.getMessageType()); + 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; } @@ -491,7 +483,8 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public int getRepeatedFieldCount(FieldDescriptor field) { + @Override + public int getRepeatedFieldCount(final FieldDescriptor field) { if (field.isExtension()) { verifyContainingType(field); return extensions.getRepeatedFieldCount(field); @@ -500,7 +493,9 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public Object getRepeatedField(FieldDescriptor field, int index) { + @Override + public Object getRepeatedField(final FieldDescriptor field, + final int index) { if (field.isExtension()) { verifyContainingType(field); return extensions.getRepeatedField(field, index); @@ -509,7 +504,7 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - private void verifyContainingType(FieldDescriptor field) { + private void verifyContainingType(final FieldDescriptor field) { if (field.getContainingType() != getDescriptorForType()) { throw new IllegalArgumentException( "FieldDescriptor does not match message type."); @@ -558,70 +553,87 @@ public abstract class GeneratedMessage extends AbstractMessage { public abstract static class ExtendableBuilder< MessageType extends ExtendableMessage, BuilderType extends ExtendableBuilder> - extends GeneratedMessage.Builder<BuilderType> { + extends Builder<BuilderType> { protected ExtendableBuilder() {} + + // 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."); + } + + @Override protected abstract ExtendableMessage<MessageType> internalGetResult(); /** Check if a singular extension is present. */ public final boolean hasExtension( - GeneratedExtension<MessageType, ?> extension) { + final GeneratedExtension<MessageType, ?> extension) { return internalGetResult().hasExtension(extension); } /** Get the number of elements in a repeated extension. */ public final <Type> int getExtensionCount( - GeneratedExtension<MessageType, List<Type>> extension) { + final GeneratedExtension<MessageType, List<Type>> extension) { return internalGetResult().getExtensionCount(extension); } /** Get the value of an extension. */ public final <Type> Type getExtension( - GeneratedExtension<MessageType, Type> extension) { + final GeneratedExtension<MessageType, Type> extension) { return internalGetResult().getExtension(extension); } /** Get one element of a repeated extension. */ public final <Type> Type getExtension( - GeneratedExtension<MessageType, List<Type>> extension, int index) { + final GeneratedExtension<MessageType, List<Type>> extension, + final int index) { return internalGetResult().getExtension(extension, index); } /** Set the value of an extension. */ public final <Type> BuilderType setExtension( - GeneratedExtension<MessageType, Type> extension, Type value) { - ExtendableMessage<MessageType> message = internalGetResult(); + final GeneratedExtension<MessageType, Type> extension, + final Type value) { + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyExtensionContainingType(extension); - message.extensions.setField(extension.getDescriptor(), + final FieldDescriptor descriptor = extension.getDescriptor(); + 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( - GeneratedExtension<MessageType, List<Type>> extension, - int index, Type value) { - ExtendableMessage<MessageType> message = internalGetResult(); + final GeneratedExtension<MessageType, List<Type>> extension, + final int index, final Type value) { + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyExtensionContainingType(extension); + final FieldDescriptor descriptor = extension.getDescriptor(); message.extensions.setRepeatedField( - extension.getDescriptor(), index, + descriptor, index, extension.singularToReflectionType(value)); return (BuilderType) this; } /** Append a value to a repeated extension. */ public final <Type> BuilderType addExtension( - GeneratedExtension<MessageType, List<Type>> extension, Type value) { - ExtendableMessage<MessageType> message = internalGetResult(); + final GeneratedExtension<MessageType, List<Type>> extension, + final Type value) { + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyExtensionContainingType(extension); + final FieldDescriptor descriptor = extension.getDescriptor(); message.extensions.addRepeatedField( - extension.getDescriptor(), extension.singularToReflectionType(value)); + descriptor, extension.singularToReflectionType(value)); return (BuilderType) this; } /** Clear an extension. */ public final <Type> BuilderType clearExtension( - GeneratedExtension<MessageType, ?> extension) { - ExtendableMessage<MessageType> message = internalGetResult(); + final GeneratedExtension<MessageType, ?> extension) { + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyExtensionContainingType(extension); message.extensions.clearField(extension.getDescriptor()); return (BuilderType) this; @@ -631,13 +643,14 @@ public abstract class GeneratedMessage extends AbstractMessage { * Called by subclasses to parse an unknown field or an extension. * @return {@code true} unless the tag is an end-group tag. */ - protected boolean parseUnknownField(CodedInputStream input, - UnknownFieldSet.Builder unknownFields, - ExtensionRegistry extensionRegistry, - int tag) - throws IOException { - ExtendableMessage<MessageType> message = internalGetResult(); - return message.extensions.mergeFieldFrom( + @Override + protected boolean parseUnknownField( + final CodedInputStream input, + final UnknownFieldSet.Builder unknownFields, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { + final ExtendableMessage<MessageType> message = internalGetResult(); + return AbstractMessage.Builder.mergeFieldFrom( input, unknownFields, extensionRegistry, this, tag); } @@ -647,9 +660,11 @@ public abstract class GeneratedMessage extends AbstractMessage { // We don't have to override the get*() methods here because they already // just forward to the underlying message. - public BuilderType setField(FieldDescriptor field, Object value) { + @Override + public BuilderType setField(final FieldDescriptor field, + final Object value) { if (field.isExtension()) { - ExtendableMessage<MessageType> message = internalGetResult(); + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.setField(field, value); return (BuilderType) this; @@ -658,9 +673,10 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public BuilderType clearField(Descriptors.FieldDescriptor field) { + @Override + public BuilderType clearField(final FieldDescriptor field) { if (field.isExtension()) { - ExtendableMessage<MessageType> message = internalGetResult(); + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.clearField(field); return (BuilderType) this; @@ -669,10 +685,11 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, - int index, Object value) { + @Override + public BuilderType setRepeatedField(final FieldDescriptor field, + final int index, final Object value) { if (field.isExtension()) { - ExtendableMessage<MessageType> message = internalGetResult(); + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.setRepeatedField(field, index, value); return (BuilderType) this; @@ -681,10 +698,11 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, - Object value) { + @Override + public BuilderType addRepeatedField(final FieldDescriptor field, + final Object value) { if (field.isExtension()) { - ExtendableMessage<MessageType> message = internalGetResult(); + final ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.addRepeatedField(field, value); return (BuilderType) this; @@ -693,7 +711,7 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - protected final void mergeExtensionFields(ExtendableMessage other) { + protected final void mergeExtensionFields(final ExtendableMessage other) { internalGetResult().extensions.mergeFrom(other.extensions); } } @@ -703,7 +721,8 @@ public abstract class GeneratedMessage extends AbstractMessage { /** For use by generated code only. */ public static <ContainingType extends Message, Type> GeneratedExtension<ContainingType, Type> - newGeneratedExtension(FieldDescriptor descriptor, Class<Type> type) { + newGeneratedExtension(final FieldDescriptor descriptor, + final Class<Type> type) { if (descriptor.isRepeated()) { throw new IllegalArgumentException( "Must call newRepeatedGeneratedExtension() for repeated types."); @@ -715,7 +734,7 @@ public abstract class GeneratedMessage extends AbstractMessage { public static <ContainingType extends Message, Type> GeneratedExtension<ContainingType, List<Type>> newRepeatedGeneratedExtension( - FieldDescriptor descriptor, Class<Type> type) { + final FieldDescriptor descriptor, final Class<Type> type) { if (!descriptor.isRepeated()) { throw new IllegalArgumentException( "Must call newGeneratedExtension() for non-repeated types."); @@ -753,7 +772,8 @@ public abstract class GeneratedMessage extends AbstractMessage { // TODO(kenton): Find ways to avoid using Java reflection within this // class. Also try to avoid suppressing unchecked warnings. - private GeneratedExtension(FieldDescriptor descriptor, Class type) { + private GeneratedExtension(final FieldDescriptor descriptor, + final Class type) { if (!descriptor.isExtension()) { throw new IllegalArgumentException( "GeneratedExtension given a regular (non-extension) field."); @@ -808,13 +828,13 @@ public abstract class GeneratedMessage extends AbstractMessage { * type. */ @SuppressWarnings("unchecked") - private Object fromReflectionType(Object value) { + private Object fromReflectionType(final Object value) { if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. - List result = new ArrayList(); - for (Object element : (List) value) { + final List result = new ArrayList(); + for (final Object element : (List) value) { result.add(singularFromReflectionType(element)); } return result; @@ -830,7 +850,7 @@ public abstract class GeneratedMessage extends AbstractMessage { * Like {@link #fromReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - private Object singularFromReflectionType(Object value) { + private Object singularFromReflectionType(final Object value) { switch (descriptor.getJavaType()) { case MESSAGE: if (type.isInstance(value)) { @@ -859,12 +879,12 @@ public abstract class GeneratedMessage extends AbstractMessage { * type. */ @SuppressWarnings("unchecked") - private Object toReflectionType(Object value) { + private Object toReflectionType(final Object value) { if (descriptor.isRepeated()) { if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. - List result = new ArrayList(); - for (Object element : (List) value) { + final List result = new ArrayList(); + for (final Object element : (List) value) { result.add(singularToReflectionType(element)); } return result; @@ -880,7 +900,7 @@ public abstract class GeneratedMessage extends AbstractMessage { * Like {@link #toReflectionType(Object)}, but if the type is a repeated * type, this converts a single element. */ - private Object singularToReflectionType(Object value) { + private Object singularToReflectionType(final Object value) { switch (descriptor.getJavaType()) { case ENUM: return invokeOrDie(enumGetValueDescriptor, value); @@ -895,7 +915,7 @@ public abstract class GeneratedMessage extends AbstractMessage { /** Calls Class.getMethod and throws a RuntimeException if it fails. */ @SuppressWarnings("unchecked") private static Method getMethodOrDie( - Class clazz, String name, Class... params) { + final Class clazz, final String name, final Class... params) { try { return clazz.getMethod(name, params); } catch (NoSuchMethodException e) { @@ -907,15 +927,15 @@ public abstract class GeneratedMessage extends AbstractMessage { /** Calls invoke and throws a RuntimeException if it fails. */ private static Object invokeOrDie( - Method method, Object object, Object... params) { + final Method method, final Object object, final 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 (java.lang.reflect.InvocationTargetException e) { - Throwable cause = e.getCause(); + } catch (InvocationTargetException e) { + final Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { @@ -944,15 +964,15 @@ public abstract class GeneratedMessage extends AbstractMessage { * @param builderClass The builder type. */ public FieldAccessorTable( - Descriptor descriptor, - String[] camelCaseNames, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final Descriptor descriptor, + final String[] camelCaseNames, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { this.descriptor = descriptor; fields = new FieldAccessor[descriptor.getFields().size()]; for (int i = 0; i < fields.length; i++) { - FieldDescriptor field = descriptor.getFields().get(i); + final FieldDescriptor field = descriptor.getFields().get(i); if (field.isRepeated()) { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { fields[i] = new RepeatedMessageFieldAccessor( @@ -983,7 +1003,7 @@ public abstract class GeneratedMessage extends AbstractMessage { private final FieldAccessor[] fields; /** Get the FieldAccessor for a particular field. */ - private FieldAccessor getField(FieldDescriptor field) { + private FieldAccessor getField(final FieldDescriptor field) { if (field.getContainingType() != descriptor) { throw new IllegalArgumentException( "FieldDescriptor does not match message type."); @@ -1000,16 +1020,16 @@ public abstract class GeneratedMessage extends AbstractMessage { * Abstract interface that provides access to a single field. This is * implemented differently depending on the field type and cardinality. */ - private static interface FieldAccessor { + private interface FieldAccessor { Object get(GeneratedMessage message); - void set(GeneratedMessage.Builder builder, Object value); + void set(Builder builder, Object value); Object getRepeated(GeneratedMessage message, int index); - void setRepeated(GeneratedMessage.Builder builder, + void setRepeated(Builder builder, int index, Object value); - void addRepeated(GeneratedMessage.Builder builder, Object value); + void addRepeated(Builder builder, Object value); boolean has(GeneratedMessage message); int getRepeatedCount(GeneratedMessage message); - void clear(GeneratedMessage.Builder builder); + void clear(Builder builder); Message.Builder newBuilder(); } @@ -1017,9 +1037,9 @@ public abstract class GeneratedMessage extends AbstractMessage { private static class SingularFieldAccessor implements FieldAccessor { SingularFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); type = getMethod.getReturnType(); setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); @@ -1031,39 +1051,40 @@ public abstract class GeneratedMessage extends AbstractMessage { // Note: We use Java reflection to call public methods rather than // access private fields directly as this avoids runtime security // checks. - Class type; - Method getMethod; - Method setMethod; - Method hasMethod; - Method clearMethod; + protected final Class type; + protected final Method getMethod; + protected final Method setMethod; + protected final Method hasMethod; + protected final Method clearMethod; - public Object get(GeneratedMessage message) { + public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } - public void set(GeneratedMessage.Builder builder, Object value) { + public void set(final Builder builder, final Object value) { invokeOrDie(setMethod, builder, value); } - public Object getRepeated(GeneratedMessage message, int index) { + public Object getRepeated(final GeneratedMessage message, + final int index) { throw new UnsupportedOperationException( "getRepeatedField() called on a singular field."); } - public void setRepeated(GeneratedMessage.Builder builder, - int index, Object value) { + public void setRepeated(final Builder builder, + final int index, final Object value) { throw new UnsupportedOperationException( "setRepeatedField() called on a singular field."); } - public void addRepeated(GeneratedMessage.Builder builder, Object value) { + public void addRepeated(final Builder builder, final Object value) { throw new UnsupportedOperationException( "addRepeatedField() called on a singular field."); } - public boolean has(GeneratedMessage message) { + public boolean has(final GeneratedMessage message) { return (Boolean) invokeOrDie(hasMethod, message); } - public int getRepeatedCount(GeneratedMessage message) { + public int getRepeatedCount(final GeneratedMessage message) { throw new UnsupportedOperationException( "getRepeatedFieldSize() called on a singular field."); } - public void clear(GeneratedMessage.Builder builder) { + public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } public Message.Builder newBuilder() { @@ -1074,10 +1095,11 @@ public abstract class GeneratedMessage extends AbstractMessage { private static class RepeatedFieldAccessor implements FieldAccessor { RepeatedFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { - getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { + getMethod = getMethodOrDie(messageClass, + "get" + camelCaseName + "List"); getRepeatedMethod = getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); @@ -1093,45 +1115,46 @@ public abstract class GeneratedMessage extends AbstractMessage { clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); } - Class type; - Method getMethod; - Method getRepeatedMethod; - Method setRepeatedMethod; - Method addRepeatedMethod; - Method getCountMethod; - Method clearMethod; + 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(GeneratedMessage message) { + public Object get(final GeneratedMessage message) { return invokeOrDie(getMethod, message); } - public void set(GeneratedMessage.Builder builder, Object value) { + 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 (Object element : (List) value) { + for (final Object element : (List) value) { addRepeated(builder, element); } } - public Object getRepeated(GeneratedMessage message, int index) { + public Object getRepeated(final GeneratedMessage message, + final int index) { return invokeOrDie(getRepeatedMethod, message, index); } - public void setRepeated(GeneratedMessage.Builder builder, - int index, Object value) { + public void setRepeated(final Builder builder, + final int index, final Object value) { invokeOrDie(setRepeatedMethod, builder, index, value); } - public void addRepeated(GeneratedMessage.Builder builder, Object value) { + public void addRepeated(final Builder builder, final Object value) { invokeOrDie(addRepeatedMethod, builder, value); } - public boolean has(GeneratedMessage message) { + public boolean has(final GeneratedMessage message) { throw new UnsupportedOperationException( "hasField() called on a singular field."); } - public int getRepeatedCount(GeneratedMessage message) { + public int getRepeatedCount(final GeneratedMessage message) { return (Integer) invokeOrDie(getCountMethod, message); } - public void clear(GeneratedMessage.Builder builder) { + public void clear(final Builder builder) { invokeOrDie(clearMethod, builder); } public Message.Builder newBuilder() { @@ -1145,9 +1168,9 @@ public abstract class GeneratedMessage extends AbstractMessage { private static final class SingularEnumFieldAccessor extends SingularFieldAccessor { SingularEnumFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); valueOfMethod = getMethodOrDie(type, "valueOf", @@ -1159,10 +1182,12 @@ public abstract class GeneratedMessage extends AbstractMessage { private Method valueOfMethod; private Method getValueDescriptorMethod; - public Object get(GeneratedMessage message) { + @Override + public Object get(final GeneratedMessage message) { return invokeOrDie(getValueDescriptorMethod, super.get(message)); } - public void set(GeneratedMessage.Builder builder, Object value) { + @Override + public void set(final Builder builder, final Object value) { super.set(builder, invokeOrDie(valueOfMethod, null, value)); } } @@ -1170,9 +1195,9 @@ public abstract class GeneratedMessage extends AbstractMessage { private static final class RepeatedEnumFieldAccessor extends RepeatedFieldAccessor { RepeatedEnumFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); valueOfMethod = getMethodOrDie(type, "valueOf", @@ -1181,26 +1206,32 @@ public abstract class GeneratedMessage extends AbstractMessage { getMethodOrDie(type, "getValueDescriptor"); } - private Method valueOfMethod; - private Method getValueDescriptorMethod; + private final Method valueOfMethod; + private final Method getValueDescriptorMethod; + @Override @SuppressWarnings("unchecked") - public Object get(GeneratedMessage message) { - List newList = new ArrayList(); - for (Object element : (List) super.get(message)) { + public Object get(final GeneratedMessage message) { + final List newList = new ArrayList(); + for (final Object element : (List) super.get(message)) { newList.add(invokeOrDie(getValueDescriptorMethod, element)); } return Collections.unmodifiableList(newList); } - public Object getRepeated(GeneratedMessage message, int index) { + @Override + public Object getRepeated(final GeneratedMessage message, + final int index) { return invokeOrDie(getValueDescriptorMethod, super.getRepeated(message, index)); } - public void setRepeated(GeneratedMessage.Builder builder, - int index, Object value) { - super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); + @Override + public void setRepeated(final Builder builder, + final int index, final Object value) { + super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, + value)); } - public void addRepeated(GeneratedMessage.Builder builder, Object value) { + @Override + public void addRepeated(final Builder builder, final Object value) { super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); } } @@ -1210,17 +1241,17 @@ public abstract class GeneratedMessage extends AbstractMessage { private static final class SingularMessageFieldAccessor extends SingularFieldAccessor { SingularMessageFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); newBuilderMethod = getMethodOrDie(type, "newBuilder"); } - private Method newBuilderMethod; + private final Method newBuilderMethod; - private Object coerceType(Object value) { + private Object coerceType(final Object value) { if (type.isInstance(value)) { return value; } else { @@ -1233,9 +1264,11 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public void set(GeneratedMessage.Builder builder, Object value) { + @Override + public void set(final Builder builder, final Object value) { super.set(builder, coerceType(value)); } + @Override public Message.Builder newBuilder() { return (Message.Builder) invokeOrDie(newBuilderMethod, null); } @@ -1244,17 +1277,17 @@ public abstract class GeneratedMessage extends AbstractMessage { private static final class RepeatedMessageFieldAccessor extends RepeatedFieldAccessor { RepeatedMessageFieldAccessor( - FieldDescriptor descriptor, String camelCaseName, - Class<? extends GeneratedMessage> messageClass, - Class<? extends GeneratedMessage.Builder> builderClass) { + final FieldDescriptor descriptor, final String camelCaseName, + final Class<? extends GeneratedMessage> messageClass, + final Class<? extends Builder> builderClass) { super(descriptor, camelCaseName, messageClass, builderClass); newBuilderMethod = getMethodOrDie(type, "newBuilder"); } - private Method newBuilderMethod; + private final Method newBuilderMethod; - private Object coerceType(Object value) { + private Object coerceType(final Object value) { if (type.isInstance(value)) { return value; } else { @@ -1267,13 +1300,16 @@ public abstract class GeneratedMessage extends AbstractMessage { } } - public void setRepeated(GeneratedMessage.Builder builder, - int index, Object value) { + @Override + public void setRepeated(final Builder builder, + final int index, final Object value) { super.setRepeated(builder, index, coerceType(value)); } - public void addRepeated(GeneratedMessage.Builder builder, Object value) { + @Override + public void addRepeated(final Builder builder, final Object value) { super.addRepeated(builder, coerceType(value)); } + @Override public Message.Builder newBuilder() { return (Message.Builder) invokeOrDie(newBuilderMethod, null); } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java new file mode 100644 index 00000000..c68414bd --- /dev/null +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -0,0 +1,539 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Lite version of {@link GeneratedMessage}. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class GeneratedMessageLite extends AbstractMessageLite { + protected GeneratedMessageLite() {} + + @SuppressWarnings("unchecked") + public abstract static class Builder<MessageType extends GeneratedMessageLite, + BuilderType extends Builder> + extends AbstractMessageLite.Builder<BuilderType> { + 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 + // of this dummy clone() implementation makes it go away. + @Override + public BuilderType clone() { + throw new UnsupportedOperationException( + "This is supposed to be overridden by subclasses."); + } + + /** All subclasses implement this. */ + public abstract BuilderType mergeFrom(MessageType message); + + // Defined here for return type covariance. + 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( + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { + return input.skipField(tag); + } + } + + // ================================================================= + // Extensions-related stuff + + /** + * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}. + */ + public abstract static class ExtendableMessage< + MessageType extends ExtendableMessage<MessageType>> + extends GeneratedMessageLite { + protected ExtendableMessage() {} + private final FieldSet<ExtensionDescriptor> extensions = + FieldSet.newFieldSet(); + + 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."); + } + } + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { + verifyExtensionContainingType(extension); + return extensions.hasField(extension.descriptor); + } + + /** Get the number of elements in a repeated extension. */ + public final <Type> int getExtensionCount( + final GeneratedExtension<MessageType, List<Type>> extension) { + verifyExtensionContainingType(extension); + return extensions.getRepeatedFieldCount(extension.descriptor); + } + + /** Get the value of an extension. */ + @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) value; + } + } + + /** Get one element of a repeated extension. */ + @SuppressWarnings("unchecked") + public final <Type> Type getExtension( + final GeneratedExtension<MessageType, List<Type>> extension, + final int index) { + verifyExtensionContainingType(extension); + return (Type) extensions.getRepeatedField(extension.descriptor, index); + } + + /** Called by subclasses to check if all extensions are initialized. */ + protected boolean extensionsAreInitialized() { + return extensions.isInitialized(); + } + + /** + * 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 + * individual ranges of extensions at once. + */ + protected class ExtensionWriter { + // Imagine how much simpler this code would be if Java iterators had + // a way to get the next element without advancing the iterator. + + private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter = + extensions.iterator(); + private Map.Entry<ExtensionDescriptor, Object> next; + private final boolean messageSetWireFormat; + + private ExtensionWriter(boolean messageSetWireFormat) { + if (iter.hasNext()) { + next = iter.next(); + } + this.messageSetWireFormat = messageSetWireFormat; + } + + public void writeUntil(final int end, final CodedOutputStream output) + throws IOException { + while (next != null && next.getKey().getNumber() < end) { + ExtensionDescriptor extension = next.getKey(); + if (messageSetWireFormat && extension.getLiteJavaType() == + WireFormat.JavaType.MESSAGE && + !extension.isRepeated()) { + output.writeMessageSetExtension(extension.getNumber(), + (MessageLite) next.getValue()); + } else { + FieldSet.writeField(extension, next.getValue(), output); + } + if (iter.hasNext()) { + next = iter.next(); + } else { + next = null; + } + } + } + } + + protected ExtensionWriter newExtensionWriter() { + return new ExtensionWriter(false); + } + protected ExtensionWriter newMessageSetExtensionWriter() { + return new ExtensionWriter(true); + } + + /** Called by subclasses to compute the size of extensions. */ + protected int extensionsSerializedSize() { + return extensions.getSerializedSize(); + } + protected int extensionsSerializedSizeAsMessageSet() { + return extensions.getMessageSetSerializedSize(); + } + } + + /** + * Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}. + */ + @SuppressWarnings("unchecked") + public abstract static class ExtendableBuilder< + MessageType extends ExtendableMessage<MessageType>, + BuilderType extends ExtendableBuilder<MessageType, BuilderType>> + extends Builder<MessageType, BuilderType> { + protected ExtendableBuilder() {} + + // 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."); + } + + @Override + protected abstract MessageType internalGetResult(); + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + final GeneratedExtension<MessageType, ?> extension) { + return internalGetResult().hasExtension(extension); + } + + /** Get the number of elements in a repeated extension. */ + public final <Type> int getExtensionCount( + final GeneratedExtension<MessageType, List<Type>> extension) { + return internalGetResult().getExtensionCount(extension); + } + + /** Get the value of an extension. */ + public final <Type> Type getExtension( + final GeneratedExtension<MessageType, Type> extension) { + return internalGetResult().getExtension(extension); + } + + /** Get one element of a repeated extension. */ + public final <Type> Type getExtension( + final GeneratedExtension<MessageType, List<Type>> extension, + final int index) { + return internalGetResult().getExtension(extension, index); + } + + /** Set the value of an extension. */ + public final <Type> BuilderType setExtension( + final GeneratedExtension<MessageType, Type> extension, + final Type value) { + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setField(extension.descriptor, value); + return (BuilderType) this; + } + + /** Set the value of one element of a repeated extension. */ + public final <Type> BuilderType setExtension( + final GeneratedExtension<MessageType, List<Type>> extension, + final int index, final Type value) { + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setRepeatedField(extension.descriptor, index, value); + return (BuilderType) this; + } + + /** Append a value to a repeated extension. */ + public final <Type> BuilderType addExtension( + final GeneratedExtension<MessageType, List<Type>> extension, + final Type 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) { + final ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.descriptor); + return (BuilderType) this; + } + + /** + * 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( + final CodedInputStream input, + final ExtensionRegistryLite extensionRegistry, + final int tag) throws IOException { + final FieldSet<ExtensionDescriptor> extensions = + internalGetResult().extensions; + + final int wireType = WireFormat.getTagWireType(tag); + final int fieldNumber = WireFormat.getTagFieldNumber(tag); + + final GeneratedExtension<MessageType, ?> extension = + extensionRegistry.findLiteExtensionByNumber( + getDefaultInstanceForType(), fieldNumber); + + if (extension == null || wireType != + FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + extension.descriptor.isPacked())) { + // Unknown field or wrong wire type. Skip. + return input.skipField(tag); + } + + if (extension.descriptor.isPacked()) { + 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); + } + } else { + while (input.getBytesUntilLimit() > 0) { + final Object value = + FieldSet.readPrimitiveField(input, + extension.descriptor.getLiteType()); + extensions.addRepeatedField(extension.descriptor, value); + } + } + 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; + } + 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; + } + + if (extension.descriptor.isRepeated()) { + extensions.addRepeatedField(extension.descriptor, value); + } else { + extensions.setField(extension.descriptor, value); + } + } + + return true; + } + + protected final void mergeExtensionFields(final MessageType other) { + internalGetResult().extensions.mergeFrom(other.extensions); + } + } + + // ----------------------------------------------------------------- + + /** For use by generated code only. */ + public static <ContainingType extends MessageLite, Type> + GeneratedExtension<ContainingType, Type> + newGeneratedExtension( + final ContainingType containingTypeDefaultInstance, + final Type defaultValue, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type) { + return new GeneratedExtension<ContainingType, Type>( + containingTypeDefaultInstance, defaultValue, messageDefaultInstance, + new ExtensionDescriptor(enumTypeMap, number, type, + false /* isRepeated */, false /* isPacked */)); + } + + /** For use by generated code only. */ + public static <ContainingType extends MessageLite, Type> + GeneratedExtension<ContainingType, List<Type>> + newRepeatedGeneratedExtension( + final ContainingType containingTypeDefaultInstance, + final MessageLite messageDefaultInstance, + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type, + final boolean isPacked) { + return new GeneratedExtension<ContainingType, List<Type>>( + containingTypeDefaultInstance, Collections.<Type>emptyList(), + messageDefaultInstance, + new ExtensionDescriptor( + enumTypeMap, number, type, true /* isRepeated */, isPacked)); + } + + private static final class ExtensionDescriptor + implements FieldSet.FieldDescriptorLite< + ExtensionDescriptor> { + private ExtensionDescriptor( + final Internal.EnumLiteMap<?> enumTypeMap, + final int number, + final WireFormat.FieldType type, + final boolean isRepeated, + final boolean isPacked) { + this.enumTypeMap = enumTypeMap; + this.number = number; + this.type = type; + this.isRepeated = isRepeated; + this.isPacked = 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; + } + + public WireFormat.FieldType getLiteType() { + return type; + } + + public WireFormat.JavaType getLiteJavaType() { + return type.getJavaType(); + } + + public boolean isRepeated() { + return isRepeated; + } + + public boolean isPacked() { + return isPacked; + } + + public Internal.EnumLiteMap<?> getEnumType() { + return enumTypeMap; + } + + @SuppressWarnings("unchecked") + public MessageLite.Builder internalMergeFrom( + MessageLite.Builder to, MessageLite from) { + return ((Builder) to).mergeFrom((GeneratedMessageLite) from); + } + + public int compareTo(ExtensionDescriptor other) { + return number - other.number; + } + } + + /** + * 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 final class GeneratedExtension< + ContainingType extends MessageLite, Type> { + private GeneratedExtension( + final ContainingType containingTypeDefaultInstance, + final Type defaultValue, + final MessageLite messageDefaultInstance, + final ExtensionDescriptor descriptor) { + this.containingTypeDefaultInstance = containingTypeDefaultInstance; + this.defaultValue = defaultValue; + this.messageDefaultInstance = messageDefaultInstance; + this.descriptor = descriptor; + } + + private final ContainingType containingTypeDefaultInstance; + private final Type defaultValue; + private final MessageLite messageDefaultInstance; + private final ExtensionDescriptor descriptor; + + /** + * Default instance of the type being extended, used to identify that type. + */ + public ContainingType getContainingTypeDefaultInstance() { + return containingTypeDefaultInstance; + } + + /** Get the field number. */ + public int getNumber() { + return descriptor.getNumber(); + } + + /** + * If the extension is an embedded message, this is the default instance of + * that type. + */ + public MessageLite getMessageDefaultInstance() { + return messageDefaultInstance; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java new file mode 100644 index 00000000..ba8e6aee --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -0,0 +1,194 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import java.io.UnsupportedEncodingException; + +/** + * The classes contained within are used internally by the Protocol Buffer + * library and generated message implementations. They are public only because + * those generated messages do not reside in the {@code protobuf} package. + * Others should not use this class directly. + * + * @author cyrusn@google.com (Cyrus Najmabadi) + */ +public class Internal { + /** + * Implementation of a Queue designed to have as little overhead as possible. + * No guarantees are made as to the order you will get values back from the + * queue. Currently it is a Last-In-First-Out implementation, but that may + * change in the future. + * + * Duplicate values are allowed, as are null values. + * + * Not threadsafe. + * + * @author cyrusn@google.com (Cyrus Najmabadi) + */ + public static final class QuickQueue<T> { + @SuppressWarnings("unchecked") + private T[] array = (T[]) new Object[16]; + private int size; + + /** + * Adds a value to the queue. + * + * @param value The value to add to the queue. + */ + public void offer(final T value) { + if (size == array.length) { + // I'd like to use Arrays.copy here. However, it is currently + // unavailable + // on android. So, for now, we just use the tried and true arraycopy + // technique. + @SuppressWarnings("unchecked") + final T[] copy = (T[]) new Object[size * 2]; + System.arraycopy(array, 0, copy, 0, array.length); + array = copy; + } + + array[size++] = value; + } + + /** + * Removes some previously added value to the queue, or {@code null} if the + * queue is empty. + * + * @return An existing value in the queue, or {@code null} if the queue is + * empty. + */ + public T poll() { + if (size == 0) { + return null; + } + + final T result = array[--size]; + // make sure we null out the entry so that we're not keeping anything + // alive unnecessarily. + array[size] = null; + + return result; + } + } + + /** + * Instances of this class will provide a unique {@code QuickQueue} to each + * thread that accesses it. Very useful for providing free lists without + * needing to take any locks. + * + * @author cyrusn@google.com (Cyrus Najmabadi) + */ + public static final class ThreadLocalQuickQueue<T> + extends ThreadLocal<QuickQueue<T>> { + @Override + protected QuickQueue<T> initialValue() { + return new QuickQueue<T>(); + } + } + + /** + * Helper called by generated code to construct default values for string + * fields. + * <p> + * The protocol compiler does not actually contain a UTF-8 decoder -- it + * just pushes UTF-8-encoded text around without touching it. The one place + * where this presents a problem is when generating Java string literals. + * Unicode characters in the string literal would normally need to be encoded + * using a Unicode escape sequence, which would require decoding them. + * To get around this, protoc instead embeds the UTF-8 bytes into the + * generated code and leaves it to the runtime library to decode them. + * <p> + * It gets worse, though. If protoc just generated a byte array, like: + * new byte[] {0x12, 0x34, 0x56, 0x78} + * Java actually generates *code* which allocates an array and then fills + * in each value. This is much less efficient than just embedding the bytes + * directly into the bytecode. To get around this, we need another + * work-around. String literals are embedded directly, so protoc actually + * generates a string literal corresponding to the bytes. The easiest way + * to do this is to use the ISO-8859-1 character set, which corresponds to + * the first 256 characters of the Unicode range. Protoc can then use + * good old CEscape to generate the string. + * <p> + * So we have a string literal which represents a set of bytes which + * represents another string. This function -- stringDefaultValue -- + * converts from the generated string to the string we actually want. The + * generated code calls this automatically. + */ + public static String stringDefaultValue(String bytes) { + try { + return new String(bytes.getBytes("ISO-8859-1"), "UTF-8"); + } catch (UnsupportedEncodingException e) { + // This should never happen since all JVMs are required to implement + // both of the above character sets. + 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 a lot like {@link #stringDefaultValue}, but for bytes fields. + * In this case we only need the second of the two hacks -- allowing us to + * embed raw bytes as a string literal with ISO-8859-1 encoding. + */ + public static ByteString bytesDefaultValue(String bytes) { + try { + return ByteString.copyFrom(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); + } + } + + /** + * Interface for an enum value or value descriptor, to be used in FieldSet. + * The lite library stores enum values directly in FieldSets but the full + * library stores EnumValueDescriptors in order to better support reflection. + */ + public interface EnumLite { + int getNumber(); + } + + /** + * Interface for an object which maps integers to {@link EnumLite}s. + * {@link Descriptors.EnumDescriptor} implements this interface by mapping + * numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally, + * every generated enum type has a static method internalGetValueMap() which + * returns an implementation of this type that maps numbers to enum values. + */ + public interface EnumLiteMap<T extends EnumLite> { + T findValueByNumber(int number); + } +} diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java index 593be0e9..90f7ffbc 100644 --- a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java +++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java @@ -39,7 +39,9 @@ import java.io.IOException; * @author kenton@google.com Kenton Varda */ public class InvalidProtocolBufferException extends IOException { - public InvalidProtocolBufferException(String description) { + private static final long serialVersionUID = -1616151763072450476L; + + public InvalidProtocolBufferException(final String description) { super(description); } diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index a5951232..c11abdc5 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -33,17 +33,22 @@ package com.google.protobuf; -import java.io.InputStream; import java.io.IOException; -import java.io.OutputStream; +import java.io.InputStream; import java.util.Map; /** * Abstract interface implemented by Protocol Message objects. + * <p> + * See also {@link MessageLite}, which defines most of the methods that typical + * users care about. {@link Message} adds to it methods that are not available + * in the "lite" runtime. The biggest added features are introspection and + * reflection -- i.e., getting descriptors for the message type and accessing + * the field values dynamically. * * @author kenton@google.com Kenton Varda */ -public interface Message { +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 @@ -53,14 +58,7 @@ public interface Message { */ Descriptors.Descriptor getDescriptorForType(); - /** - * 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 Message} interface - * whereas {@code getDefaultInstance()} is a static method of a specific - * class. They return the same thing. - */ + // (From MessageLite, re-declared here only for return type covariance.) Message getDefaultInstanceForType(); /** @@ -114,24 +112,6 @@ public interface Message { /** Get the {@link UnknownFieldSet} for this message. */ UnknownFieldSet getUnknownFields(); - /** - * 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 - * flush or close the stream. - */ - void writeTo(CodedOutputStream output) throws IOException; - - /** - * Get the number of bytes required to encode this message. The result - * is only computed on the first call and memoized after that. - */ - int getSerializedSize(); - // ----------------------------------------------------------------- // Comparison and hashing @@ -144,6 +124,7 @@ public interface Message { * @param other object to be compared for equality with this message * @return <tt>true</tt> if the specified object is equal to this message */ + @Override boolean equals(Object other); /** @@ -154,6 +135,7 @@ public interface Message { * @return the hash code value for this message * @see Map#hashCode() */ + @Override int hashCode(); // ----------------------------------------------------------------- @@ -163,67 +145,22 @@ public interface Message { * Converts the message to a string in protocol buffer text format. This is * just a trivial wrapper around {@link TextFormat#printToString(Message)}. */ + @Override String toString(); - /** - * Serializes the message to a {@code ByteString} and returns it. This is - * just a trivial wrapper around - * {@link #writeTo(CodedOutputStream)}. - */ - ByteString toByteString(); - - /** - * Serializes the message to a {@code byte} array and returns it. This is - * just a trivial wrapper around - * {@link #writeTo(CodedOutputStream)}. - */ - byte[] toByteArray(); - - /** - * Serializes the message and writes it to {@code output}. This is just a - * trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does - * not flush or close the stream. - * <p> - * NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write - * any more data to the stream after the message, you must somehow ensure - * that the parser on the receiving end does not interpret this as being - * part of the protocol message. This can be done e.g. by writing the size - * of the message before the data, then making sure to limit the input to - * that size on the receiving end (e.g. by wrapping the InputStream in one - * which limits the input). Alternatively, just use - * {@link #writeDelimitedTo(OutputStream)}. - */ - void writeTo(OutputStream output) throws IOException; - - /** - * Like {@link #writeTo(OutputStream)}, but writes the size of the message - * as a varint before writing the data. This allows more data to be written - * to the stream after the message without the need to delimit the message - * data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or - * the static method {@code YourMessageType.parseDelimitedFrom(InputStream)}) - * to parse messages written by this method. - */ - void writeDelimitedTo(OutputStream output) throws IOException; - // ================================================================= // Builders - /** - * Constructs a new builder for a message of the same type as this message. - */ + // (From MessageLite, re-declared here only for return type covariance.) Builder newBuilderForType(); - - /** - * Constructs a builder initialized with the current message. Use this to - * derive a new message from the current one. - */ Builder toBuilder(); /** * Abstract interface implemented by Protocol Message builders. */ - public static interface Builder extends Cloneable { - /** Resets all fields to their default values. */ + interface Builder extends MessageLite.Builder { + // (From MessageLite.Builder, re-declared here only for return type + // covariance.) Builder clear(); /** @@ -244,71 +181,14 @@ public interface Message { */ Builder mergeFrom(Message other); - /** - * Construct the final message. Once this is called, the Builder is no - * longer valid, and calling any other method 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. - */ + // (From MessageLite.Builder, re-declared here only for return type + // covariance.) Message build(); - - /** - * Like {@link #build()}, but does not throw an exception if the message - * is missing required fields. Instead, a partial message is returned. - */ Message buildPartial(); - - /** - * Clones the Builder. - * @see Object#clone() - */ 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, as if using {@link Builder#mergeFrom(Message)}. - * - * <p>Warning: This does not verify that all required fields are present in - * the input message. If you call {@link #build()} without setting all - * required fields, it will throw an {@link UninitializedMessageException}, - * which is a {@code RuntimeException} and thus might not be caught. There - * are a few good ways to deal with this: - * <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(Message)} - * 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> - * - * <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. - */ Builder mergeFrom(CodedInputStream input) throws IOException; - - /** - * Like {@link Builder#mergeFrom(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. - */ Builder mergeFrom(CodedInputStream input, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) throws IOException; /** @@ -317,10 +197,8 @@ public interface Message { */ Descriptors.Descriptor getDescriptorForType(); - /** - * Get the message's type's default instance. - * See {@link Message#getDefaultInstanceForType()}. - */ + // (From MessageLite.Builder, re-declared here only for return type + // covariance.) Message getDefaultInstanceForType(); /** @@ -399,92 +277,29 @@ public interface Message { // --------------------------------------------------------------- // Convenience methods. - /** - * 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)}. - */ + // (From MessageLite.Builder, re-declared here only for return type + // covariance.) 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,ExtensionRegistry)}. - */ Builder mergeFrom(ByteString data, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException; + Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; + Builder mergeFrom(byte[] data, int off, int len) 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)}. - */ - public Builder mergeFrom(byte[] 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)}. - */ - public Builder mergeFrom(byte[] data, int off, int len) 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,ExtensionRegistry)}. - */ Builder mergeFrom(byte[] data, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) 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,ExtensionRegistry)}. - */ Builder mergeFrom(byte[] data, int off, int len, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException; - - /** - * 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)}. 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 Message#writeDelimitedTo(OutputStream)} to write your message and - * {@link #mergeDelimitedFrom(InputStream)} to read it. - * <p> - * Despite usually reading the entire input, this does not close the stream. - */ 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,ExtensionRegistry)}. - */ Builder mergeFrom(InputStream input, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) throws IOException; - - /** - * Like {@link #mergeFrom(InputStream)}, but does not read until EOF. - * Instead, the size of the message (encoded as a varint) is read first, - * then the message data. Use - * {@link Message#writeDelimitedTo(OutputStream)} to write messages in this - * format. - */ Builder mergeDelimitedFrom(InputStream input) throws IOException; - - /** - * Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions. - */ Builder mergeDelimitedFrom(InputStream input, - ExtensionRegistry extensionRegistry) + ExtensionRegistryLite extensionRegistry) throws IOException; } } diff --git a/java/src/main/java/com/google/protobuf/MessageLite.java b/java/src/main/java/com/google/protobuf/MessageLite.java new file mode 100644 index 00000000..3001f360 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/MessageLite.java @@ -0,0 +1,331 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then +// mergeFrom*() could return BuilderType for better type-safety. + +package com.google.protobuf; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Abstract interface implemented by Protocol Message objects. + * + * <p>This interface is implemented by all protocol message objects. Most + * users will be more interested in the Message interface, which is a subclass + * of MessageLite. Use MessageLite instead when you only need the subset of + * features which it supports -- namely, nothing that uses descriptors or + * reflection. You can instruct the protocol compiler to generate classes + * which implement only MessageLite, not the full Message interface, by adding + * the follow line to the .proto file: + * <pre> + * option optimize_for = LITE_RUNTIME; + * </pre> + * + * <p>This is particularly useful on resource-constrained systems where the + * full protocol buffers runtime library is too big. + * + * <p>Note that on non-constrained systems (e.g. servers) when you need to link + * in lots of protocol definitions, a better way to reduce total code footprint + * is to use {@code optimize_for = CODE_SIZE}. This will make the generated + * code smaller while still supporting all the same features (at the expense of + * speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a + * small number of message types linked into your binary, in which case the + * size of the protocol buffers runtime itself is the biggest problem. + * + * @author kenton@google.com Kenton Varda + */ +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 + * flush or close the stream. + */ + void writeTo(CodedOutputStream output) throws IOException; + + /** + * Get the number of bytes required to encode this message. The result + * is only computed on the first call and memoized after that. + */ + int getSerializedSize(); + + // ----------------------------------------------------------------- + // Convenience methods. + + /** + * Serializes the message to a {@code ByteString} and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + ByteString toByteString(); + + /** + * Serializes the message to a {@code byte} array and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + byte[] toByteArray(); + + /** + * Serializes the message and writes it to {@code output}. This is just a + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does + * not flush or close the stream. + * <p> + * NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write + * any more data to the stream after the message, you must somehow ensure + * that the parser on the receiving end does not interpret this as being + * part of the protocol message. This can be done e.g. by writing the size + * of the message before the data, then making sure to limit the input to + * that size on the receiving end (e.g. by wrapping the InputStream in one + * which limits the input). Alternatively, just use + * {@link #writeDelimitedTo(OutputStream)}. + */ + void writeTo(OutputStream output) throws IOException; + + /** + * Like {@link #writeTo(OutputStream)}, but writes the size of the message + * as a varint before writing the data. This allows more data to be written + * to the stream after the message without the need to delimit the message + * data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or + * the static method {@code YourMessageType.parseDelimitedFrom(InputStream)}) + * to parse messages written by this method. + */ + void writeDelimitedTo(OutputStream output) throws IOException; + + // ================================================================= + // Builders + + /** + * Constructs a new builder for a message of the same type as this message. + */ + Builder newBuilderForType(); + + /** + * Constructs a builder initialized with the current message. Use this to + * derive a new message from the current one. + */ + Builder toBuilder(); + + /** + * Abstract interface implemented by Protocol Message builders. + */ + interface Builder extends Cloneable { + /** Resets all fields to their default values. */ + Builder clear(); + + /** + * 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. + */ + MessageLite build(); + + /** + * Like {@link #build()}, but does not throw an exception if the message + * is missing required fields. Instead, a partial message is returned. + * 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(); + + /** + * Clones the Builder. + * @see Object#clone() + */ + 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, 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 + * required fields, it will throw an {@link UninitializedMessageException}, + * which is a {@code RuntimeException} and thus might not be caught. There + * are a few good ways to deal with this: + * <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> + * + * <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. + */ + Builder mergeFrom(CodedInputStream input) throws IOException; + + /** + * Like {@link Builder#mergeFrom(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. + */ + Builder mergeFrom(CodedInputStream input, + ExtensionRegistryLite extensionRegistry) + throws IOException; + + /** + * Get the message's type's default instance. + * See {@link MessageLite#getDefaultInstanceForType()}. + */ + MessageLite getDefaultInstanceForType(); + + // --------------------------------------------------------------- + // Convenience methods. + + /** + * 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)}. + */ + 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,ExtensionRegistry)}. + */ + Builder mergeFrom(ByteString data, + ExtensionRegistryLite extensionRegistry) + 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)}. + */ + Builder mergeFrom(byte[] 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)}. + */ + Builder mergeFrom(byte[] data, int off, int len) + 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,ExtensionRegistry)}. + */ + Builder mergeFrom(byte[] data, + ExtensionRegistryLite extensionRegistry) + 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,ExtensionRegistry)}. + */ + Builder mergeFrom(byte[] data, int off, int len, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException; + + /** + * 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)}. 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(OutputStream)} to write your message + * and {@link #mergeDelimitedFrom(InputStream)} to read it. + * <p> + * Despite usually reading the entire input, this does not close the stream. + */ + 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,ExtensionRegistry)}. + */ + Builder mergeFrom(InputStream input, + ExtensionRegistryLite extensionRegistry) + throws IOException; + + /** + * Like {@link #mergeFrom(InputStream)}, but does not read until EOF. + * Instead, the size of the message (encoded as a varint) is read first, + * then the message data. Use + * {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in + * this format. + */ + Builder mergeDelimitedFrom(InputStream input) + throws IOException; + + /** + * Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions. + */ + Builder mergeDelimitedFrom(InputStream input, + ExtensionRegistryLite extensionRegistry) + throws IOException; + } +} diff --git a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java b/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java index 8efca8af..112400f4 100644 --- a/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java +++ b/java/src/main/java/com/google/protobuf/ProtocolMessageEnum.java @@ -37,7 +37,7 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor; * Interface of useful methods added to all enums generated by the protocol * compiler. */ -public interface ProtocolMessageEnum { +public interface ProtocolMessageEnum extends Internal.EnumLite { /** * Return the value's numeric value as defined in the .proto file. diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/src/main/java/com/google/protobuf/RpcUtil.java index 8144bbfb..b1b959a8 100644 --- a/java/src/main/java/com/google/protobuf/RpcUtil.java +++ b/java/src/main/java/com/google/protobuf/RpcUtil.java @@ -71,11 +71,11 @@ public final class RpcUtil { final Class<Type> originalClass, final Type defaultInstance) { return new RpcCallback<Message>() { - public void run(Message parameter) { + public void run(final Message parameter) { Type typedParameter; try { typedParameter = originalClass.cast(parameter); - } catch (ClassCastException e) { + } catch (ClassCastException ignored) { typedParameter = copyAsType(defaultInstance, parameter); } originalCallback.run(typedParameter); @@ -90,7 +90,7 @@ public final class RpcUtil { */ @SuppressWarnings("unchecked") private static <Type extends Message> Type copyAsType( - Type typeDefaultInstance, Message source) { + final Type typeDefaultInstance, final Message source) { return (Type)typeDefaultInstance.newBuilderForType() .mergeFrom(source) .build(); @@ -106,8 +106,9 @@ public final class RpcUtil { RpcCallback<ParameterType> newOneTimeCallback( final RpcCallback<ParameterType> originalCallback) { return new RpcCallback<ParameterType>() { - boolean alreadyCalled = false; - public void run(ParameterType parameter) { + private boolean alreadyCalled = false; + + public void run(final ParameterType parameter) { synchronized(this) { if (alreadyCalled) { throw new AlreadyCalledException(); @@ -124,6 +125,8 @@ public final class RpcUtil { * Exception thrown when a one-time callback is called more than once. */ public static final class AlreadyCalledException extends RuntimeException { + private static final long serialVersionUID = 5469741279507848266L; + public AlreadyCalledException() { super("This RpcCallback was already called and cannot be called " + "multiple times."); diff --git a/java/src/main/java/com/google/protobuf/ServiceException.java b/java/src/main/java/com/google/protobuf/ServiceException.java index 70b9d0c1..c043a775 100644 --- a/java/src/main/java/com/google/protobuf/ServiceException.java +++ b/java/src/main/java/com/google/protobuf/ServiceException.java @@ -32,11 +32,13 @@ package com.google.protobuf; /** * Thrown by blocking RPC methods when a failure occurs. - * + * * @author cpovirk@google.com (Chris Povirk) */ public final class ServiceException extends Exception { - public ServiceException(String message) { + private static final long serialVersionUID = -1219262335729891920L; + + public ServiceException(final String message) { super(message); } } diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 3dcf68c8..a855720b 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -52,22 +52,25 @@ import java.util.regex.Pattern; * @author kenton@google.com Kenton Varda */ public final class TextFormat { + 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(Message message, Appendable output) + public static void print(final Message message, final Appendable output) throws IOException { - TextGenerator generator = new TextGenerator(output); + final TextGenerator generator = new TextGenerator(output); print(message, generator); } /** Outputs a textual representation of {@code fields} to {@code output}. */ - public static void print(UnknownFieldSet fields, Appendable output) + public static void print(final UnknownFieldSet fields, + final Appendable output) throws IOException { - TextGenerator generator = new TextGenerator(output); + final TextGenerator generator = new TextGenerator(output); printUnknownFields(fields, generator); } @@ -75,9 +78,9 @@ public final class TextFormat { * Like {@code print()}, but writes directly to a {@code String} and * returns it. */ - public static String printToString(Message message) { + public static String printToString(final Message message) { try { - StringBuilder text = new StringBuilder(); + final StringBuilder text = new StringBuilder(); print(message, text); return text.toString(); } catch (IOException e) { @@ -91,9 +94,9 @@ public final class TextFormat { * Like {@code print()}, but writes directly to a {@code String} and * returns it. */ - public static String printToString(UnknownFieldSet fields) { + public static String printToString(final UnknownFieldSet fields) { try { - StringBuilder text = new StringBuilder(); + final StringBuilder text = new StringBuilder(); print(fields, text); return text.toString(); } catch (IOException e) { @@ -103,22 +106,44 @@ public final class TextFormat { } } - private static void print(Message message, TextGenerator generator) + private static void print(final Message message, + final TextGenerator generator) throws IOException { - for (Map.Entry<FieldDescriptor, Object> field : + 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 { + final TextGenerator generator = new TextGenerator(output); + printField(field, value, generator); + } - public static void printField(FieldDescriptor field, - Object value, - TextGenerator generator) + public static String printFieldToString(final FieldDescriptor field, + final Object value) { + try { + final StringBuilder text = new StringBuilder(); + printField(field, value, text); + return text.toString(); + } catch (IOException e) { + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); + } + } + + private static 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) { + for (final Object element : (List) value) { printSingleField(field, element, generator); } } else { @@ -126,9 +151,9 @@ public final class TextFormat { } } - private static void printSingleField(FieldDescriptor field, - Object value, - TextGenerator generator) + private static void printSingleField(final FieldDescriptor field, + final Object value, + final TextGenerator generator) throws IOException { if (field.isExtension()) { generator.print("["); @@ -168,9 +193,9 @@ public final class TextFormat { generator.print("\n"); } - private static void printFieldValue(FieldDescriptor field, - Object value, - TextGenerator generator) + private static void printFieldValue(final FieldDescriptor field, + final Object value, + final TextGenerator generator) throws IOException { switch (field.getType()) { case INT32: @@ -202,17 +227,15 @@ public final class TextFormat { generator.print("\""); break; - case BYTES: { + case BYTES: generator.print("\""); generator.print(escapeBytes((ByteString) value)); generator.print("\""); break; - } - case ENUM: { + case ENUM: generator.print(((EnumValueDescriptor) value).getName()); break; - } case MESSAGE: case GROUP: @@ -221,39 +244,39 @@ public final class TextFormat { } } - private static void printUnknownFields(UnknownFieldSet unknownFields, - TextGenerator generator) + private static void printUnknownFields(final UnknownFieldSet unknownFields, + final TextGenerator generator) throws IOException { - for (Map.Entry<Integer, UnknownFieldSet.Field> entry : + for (final Map.Entry<Integer, UnknownFieldSet.Field> entry : unknownFields.asMap().entrySet()) { - String prefix = entry.getKey().toString() + ": "; - UnknownFieldSet.Field field = entry.getValue(); + final String prefix = entry.getKey().toString() + ": "; + final UnknownFieldSet.Field field = entry.getValue(); - for (long value : field.getVarintList()) { + for (final long value : field.getVarintList()) { generator.print(entry.getKey().toString()); generator.print(": "); generator.print(unsignedToString(value)); generator.print("\n"); } - for (int value : field.getFixed32List()) { + 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"); } - for (long value : field.getFixed64List()) { + for (final long value : field.getFixed64List()) { generator.print(entry.getKey().toString()); generator.print(": "); generator.print(String.format((Locale) null, "0x%016x", value)); generator.print("\n"); } - for (ByteString value : field.getLengthDelimitedList()) { + for (final ByteString value : field.getLengthDelimitedList()) { generator.print(entry.getKey().toString()); generator.print(": \""); generator.print(escapeBytes(value)); generator.print("\"\n"); } - for (UnknownFieldSet value : field.getGroupList()) { + for (final UnknownFieldSet value : field.getGroupList()) { generator.print(entry.getKey().toString()); generator.print(" {\n"); generator.indent(); @@ -265,7 +288,7 @@ public final class TextFormat { } /** Convert an unsigned 32-bit integer to a string. */ - private static String unsignedToString(int value) { + private static String unsignedToString(final int value) { if (value >= 0) { return Integer.toString(value); } else { @@ -274,7 +297,7 @@ public final class TextFormat { } /** Convert an unsigned 64-bit integer to a string. */ - private static String unsignedToString(long value) { + private static String unsignedToString(final long value) { if (value >= 0) { return Long.toString(value); } else { @@ -288,13 +311,12 @@ public final class TextFormat { /** * An inner class for writing text to the output stream. */ - static private final class TextGenerator { + private static final class TextGenerator { + private Appendable output; + private boolean atStartOfLine = true; + private final StringBuilder indent = new StringBuilder(); - Appendable output; - boolean atStartOfLine = true; - StringBuilder indent = new StringBuilder(); - - public TextGenerator(Appendable output) { + private TextGenerator(final Appendable output) { this.output = output; } @@ -312,7 +334,7 @@ public final class TextFormat { * level is zero. */ public void outdent() { - int length = indent.length(); + final int length = indent.length(); if (length == 0) { throw new IllegalArgumentException( " Outdent() without matching Indent()."); @@ -323,8 +345,8 @@ public final class TextFormat { /** * Print text to the output stream. */ - public void print(CharSequence text) throws IOException { - int size = text.length(); + public void print(final CharSequence text) throws IOException { + final int size = text.length(); int pos = 0; for (int i = 0; i < size; i++) { @@ -337,7 +359,8 @@ public final class TextFormat { write(text.subSequence(pos, size), size - pos); } - private void write(CharSequence data, int size) throws IOException { + private void write(final CharSequence data, final int size) + throws IOException { if (size == 0) { return; } @@ -399,27 +422,27 @@ public final class TextFormat { // We use possesive quantifiers (*+ and ++) because otherwise the Java // regex matcher has stack overflows on large inputs. - private static Pattern WHITESPACE = + private static final Pattern WHITESPACE = Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); - private static Pattern TOKEN = Pattern.compile( + private static final Pattern TOKEN = Pattern.compile( "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier "[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string "\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string Pattern.MULTILINE); - private static Pattern DOUBLE_INFINITY = Pattern.compile( + private static final Pattern DOUBLE_INFINITY = Pattern.compile( "-?inf(inity)?", Pattern.CASE_INSENSITIVE); - private static Pattern FLOAT_INFINITY = Pattern.compile( + private static final Pattern FLOAT_INFINITY = Pattern.compile( "-?inf(inity)?f?", Pattern.CASE_INSENSITIVE); - private static Pattern FLOAT_NAN = Pattern.compile( + private static final Pattern FLOAT_NAN = Pattern.compile( "nanf?", Pattern.CASE_INSENSITIVE); /** Construct a tokenizer that parses tokens from the given text. */ - public Tokenizer(CharSequence text) { + private Tokenizer(final CharSequence text) { this.text = text; this.matcher = WHITESPACE.matcher(text); skipWhitespace(); @@ -481,7 +504,7 @@ public final class TextFormat { * If the next token exactly matches {@code token}, consume it and return * {@code true}. Otherwise, return {@code false} without doing anything. */ - public boolean tryConsume(String token) { + public boolean tryConsume(final String token) { if (currentToken.equals(token)) { nextToken(); return true; @@ -494,7 +517,7 @@ public final class TextFormat { * If the next token exactly matches {@code token}, consume it. Otherwise, * throw a {@link ParseException}. */ - public void consume(String token) throws ParseException { + public void consume(final String token) throws ParseException { if (!tryConsume(token)) { throw parseException("Expected \"" + token + "\"."); } @@ -509,7 +532,7 @@ public final class TextFormat { return false; } - char c = currentToken.charAt(0); + final char c = currentToken.charAt(0); return ('0' <= c && c <= '9') || c == '-' || c == '+'; } @@ -520,7 +543,7 @@ public final class TextFormat { */ public String consumeIdentifier() throws ParseException { for (int i = 0; i < currentToken.length(); i++) { - char c = currentToken.charAt(i); + final char c = currentToken.charAt(i); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || @@ -531,7 +554,7 @@ public final class TextFormat { } } - String result = currentToken; + final String result = currentToken; nextToken(); return result; } @@ -542,7 +565,7 @@ public final class TextFormat { */ public int consumeInt32() throws ParseException { try { - int result = parseInt32(currentToken); + final int result = parseInt32(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -556,7 +579,7 @@ public final class TextFormat { */ public int consumeUInt32() throws ParseException { try { - int result = parseUInt32(currentToken); + final int result = parseUInt32(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -570,7 +593,7 @@ public final class TextFormat { */ public long consumeInt64() throws ParseException { try { - long result = parseInt64(currentToken); + final long result = parseInt64(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -584,7 +607,7 @@ public final class TextFormat { */ public long consumeUInt64() throws ParseException { try { - long result = parseUInt64(currentToken); + final long result = parseUInt64(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -600,7 +623,7 @@ public final class TextFormat { // We need to parse infinity and nan separately because // Double.parseDouble() does not accept "inf", "infinity", or "nan". if (DOUBLE_INFINITY.matcher(currentToken).matches()) { - boolean negative = currentToken.startsWith("-"); + final boolean negative = currentToken.startsWith("-"); nextToken(); return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } @@ -609,7 +632,7 @@ public final class TextFormat { return Double.NaN; } try { - double result = Double.parseDouble(currentToken); + final double result = Double.parseDouble(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -625,7 +648,7 @@ public final class TextFormat { // We need to parse infinity and nan separately because // Float.parseFloat() does not accept "inf", "infinity", or "nan". if (FLOAT_INFINITY.matcher(currentToken).matches()) { - boolean negative = currentToken.startsWith("-"); + final boolean negative = currentToken.startsWith("-"); nextToken(); return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; } @@ -634,7 +657,7 @@ public final class TextFormat { return Float.NaN; } try { - float result = Float.parseFloat(currentToken); + final float result = Float.parseFloat(currentToken); nextToken(); return result; } catch (NumberFormatException e) { @@ -672,7 +695,8 @@ public final class TextFormat { * {@link ParseException}. */ public ByteString consumeByteString() throws ParseException { - char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; + final char quote = currentToken.length() > 0 ? currentToken.charAt(0) + : '\0'; if (quote != '\"' && quote != '\'') { throw parseException("Expected string."); } @@ -683,11 +707,12 @@ public final class TextFormat { } try { - String escaped = currentToken.substring(1, currentToken.length() - 1); - ByteString result = unescapeBytes(escaped); + final String escaped = + currentToken.substring(1, currentToken.length() - 1); + final ByteString result = unescapeBytes(escaped); nextToken(); return result; - } catch (InvalidEscapeSequence e) { + } catch (InvalidEscapeSequenceException e) { throw parseException(e.getMessage()); } } @@ -696,7 +721,7 @@ public final class TextFormat { * Returns a {@link ParseException} with the current line and column * numbers in the description, suitable for throwing. */ - public ParseException parseException(String description) { + public ParseException parseException(final String description) { // Note: People generally prefer one-based line and column numbers. return new ParseException( (line + 1) + ":" + (column + 1) + ": " + description); @@ -706,7 +731,8 @@ public final class TextFormat { * Returns a {@link ParseException} with the line and column numbers of * the previous token in the description, suitable for throwing. */ - public ParseException parseExceptionPreviousToken(String description) { + public ParseException parseExceptionPreviousToken( + final String description) { // Note: People generally prefer one-based line and column numbers. return new ParseException( (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); @@ -716,7 +742,8 @@ public final class TextFormat { * Constructs an appropriate {@link ParseException} for the given * {@code NumberFormatException} when trying to parse an integer. */ - private ParseException integerParseException(NumberFormatException e) { + private ParseException integerParseException( + final NumberFormatException e) { return parseException("Couldn't parse integer: " + e.getMessage()); } @@ -724,14 +751,16 @@ public final class TextFormat { * Constructs an appropriate {@link ParseException} for the given * {@code NumberFormatException} when trying to parse a float or double. */ - private ParseException floatParseException(NumberFormatException e) { + private ParseException floatParseException(final NumberFormatException e) { return parseException("Couldn't parse number: " + e.getMessage()); } } /** Thrown when parsing an invalid text format message. */ public static class ParseException extends IOException { - public ParseException(String message) { + private static final long serialVersionUID = 3196188060225107702L; + + public ParseException(final String message) { super(message); } } @@ -740,9 +769,9 @@ public final class TextFormat { * Parse a text-format message from {@code input} and merge the contents * into {@code builder}. */ - public static void merge(Readable input, - Message.Builder builder) - throws ParseException, IOException { + public static void merge(final Readable input, + final Message.Builder builder) + throws IOException { merge(input, ExtensionRegistry.getEmptyRegistry(), builder); } @@ -750,8 +779,8 @@ public final class TextFormat { * Parse a text-format message from {@code input} and merge the contents * into {@code builder}. */ - public static void merge(CharSequence input, - Message.Builder builder) + public static void merge(final CharSequence input, + final Message.Builder builder) throws ParseException { merge(input, ExtensionRegistry.getEmptyRegistry(), builder); } @@ -761,10 +790,10 @@ public final class TextFormat { * into {@code builder}. Extensions will be recognized if they are * registered in {@code extensionRegistry}. */ - public static void merge(Readable input, - ExtensionRegistry extensionRegistry, - Message.Builder builder) - throws ParseException, IOException { + public static 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 @@ -780,12 +809,12 @@ public final class TextFormat { // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) // overhead is worthwhile - private static StringBuilder toStringBuilder(Readable input) + private static StringBuilder toStringBuilder(final Readable input) throws IOException { - StringBuilder text = new StringBuilder(); - CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); + final StringBuilder text = new StringBuilder(); + final CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); while (true) { - int n = input.read(buffer); + final int n = input.read(buffer); if (n == -1) { break; } @@ -800,11 +829,11 @@ public final class TextFormat { * into {@code builder}. Extensions will be recognized if they are * registered in {@code extensionRegistry}. */ - public static void merge(CharSequence input, - ExtensionRegistry extensionRegistry, - Message.Builder builder) + public static void merge(final CharSequence input, + final ExtensionRegistry extensionRegistry, + final Message.Builder builder) throws ParseException { - Tokenizer tokenizer = new Tokenizer(input); + final Tokenizer tokenizer = new Tokenizer(input); while (!tokenizer.atEnd()) { mergeField(tokenizer, extensionRegistry, builder); @@ -815,19 +844,20 @@ public final class TextFormat { * Parse a single field from {@code tokenizer} and merge it into * {@code builder}. */ - private static void mergeField(Tokenizer tokenizer, - ExtensionRegistry extensionRegistry, - Message.Builder builder) + private static void mergeField(final Tokenizer tokenizer, + final ExtensionRegistry extensionRegistry, + final Message.Builder builder) throws ParseException { FieldDescriptor field; - Descriptor type = builder.getDescriptorForType(); + final Descriptor type = builder.getDescriptorForType(); ExtensionRegistry.ExtensionInfo extension = null; if (tokenizer.tryConsume("[")) { // An extension. - StringBuilder name = new StringBuilder(tokenizer.consumeIdentifier()); + final StringBuilder name = + new StringBuilder(tokenizer.consumeIdentifier()); while (tokenizer.tryConsume(".")) { - name.append("."); + name.append('.'); name.append(tokenizer.consumeIdentifier()); } @@ -846,7 +876,7 @@ public final class TextFormat { field = extension.descriptor; } else { - String name = tokenizer.consumeIdentifier(); + final String name = tokenizer.consumeIdentifier(); field = type.findFieldByName(name); // Group names are expected to be capitalized as they appear in the @@ -855,7 +885,7 @@ public final class TextFormat { if (field == null) { // Explicitly specify US locale so that this code does not break when // executing in Turkey. - String lowerName = name.toLowerCase(Locale.US); + 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) { @@ -880,7 +910,7 @@ public final class TextFormat { if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { tokenizer.tryConsume(":"); // optional - String endToken; + final String endToken; if (tokenizer.tryConsume("<")) { endToken = ">"; } else { @@ -888,7 +918,7 @@ public final class TextFormat { endToken = "}"; } - Message.Builder subBuilder; + final Message.Builder subBuilder; if (extension == null) { subBuilder = builder.newBuilderForField(field); } else { @@ -951,19 +981,19 @@ public final class TextFormat { value = tokenizer.consumeByteString(); break; - case ENUM: { - EnumDescriptor enumType = field.getEnumType(); + case ENUM: + final EnumDescriptor enumType = field.getEnumType(); if (tokenizer.lookingAtInteger()) { - int number = tokenizer.consumeInt32(); + 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 + "."); + "\" has no value with number " + number + '.'); } } else { - String id = tokenizer.consumeIdentifier(); + final String id = tokenizer.consumeIdentifier(); value = enumType.findValueByName(id); if (value == null) { throw tokenizer.parseExceptionPreviousToken( @@ -973,7 +1003,6 @@ public final class TextFormat { } break; - } case MESSAGE: case GROUP: @@ -1002,10 +1031,10 @@ public final class TextFormat { * which no defined short-hand escape sequence is defined will be escaped * using 3-digit octal sequences. */ - static String escapeBytes(ByteString input) { - StringBuilder builder = new StringBuilder(input.size()); + static String escapeBytes(final ByteString input) { + final StringBuilder builder = new StringBuilder(input.size()); for (int i = 0; i < input.size(); i++) { - byte b = input.byteAt(i); + final byte b = input.byteAt(i); switch (b) { // Java does not recognize \a or \v, apparently. case 0x07: builder.append("\\a" ); break; @@ -1038,9 +1067,9 @@ public final class TextFormat { * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with * "\x") are also recognized. */ - static ByteString unescapeBytes(CharSequence input) - throws InvalidEscapeSequence { - byte[] result = new byte[input.length()]; + static ByteString unescapeBytes(final CharSequence input) + throws InvalidEscapeSequenceException { + final byte[] result = new byte[input.length()]; int pos = 0; for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); @@ -1080,7 +1109,7 @@ public final class TextFormat { ++i; code = digitValue(input.charAt(i)); } else { - throw new InvalidEscapeSequence( + throw new InvalidEscapeSequenceException( "Invalid escape sequence: '\\x' with no digits"); } if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { @@ -1091,12 +1120,12 @@ public final class TextFormat { break; default: - throw new InvalidEscapeSequence( - "Invalid escape sequence: '\\" + c + "'"); + throw new InvalidEscapeSequenceException( + "Invalid escape sequence: '\\" + c + '\''); } } } else { - throw new InvalidEscapeSequence( + throw new InvalidEscapeSequenceException( "Invalid escape sequence: '\\' at end of string."); } } else { @@ -1111,8 +1140,10 @@ public final class TextFormat { * Thrown by {@link TextFormat#unescapeBytes} and * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. */ - static class InvalidEscapeSequence extends IOException { - public InvalidEscapeSequence(String description) { + static class InvalidEscapeSequenceException extends IOException { + private static final long serialVersionUID = -8164033650142593304L; + + InvalidEscapeSequenceException(final String description) { super(description); } } @@ -1122,7 +1153,7 @@ public final class TextFormat { * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped * individually as a 3-digit octal escape. Yes, it's weird. */ - static String escapeText(String input) { + static String escapeText(final String input) { return escapeBytes(ByteString.copyFromUtf8(input)); } @@ -1130,17 +1161,18 @@ public final class TextFormat { * Un-escape a text string as escaped using {@link #escapeText(String)}. * Two-digit hex escapes (starting with "\x") are also recognized. */ - static String unescapeText(String input) throws InvalidEscapeSequence { + static String unescapeText(final String input) + throws InvalidEscapeSequenceException { return unescapeBytes(input).toStringUtf8(); } /** Is this an octal digit? */ - private static boolean isOctal(char c) { + private static boolean isOctal(final char c) { return '0' <= c && c <= '7'; } /** Is this a hex digit? */ - private static boolean isHex(char c) { + private static boolean isHex(final char c) { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'); @@ -1151,7 +1183,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(char c) { + private static int digitValue(final char c) { if ('0' <= c && c <= '9') { return c - '0'; } else if ('a' <= c && c <= 'z') { @@ -1166,7 +1198,7 @@ public final class TextFormat { * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" * and "0" to signify hexidecimal and octal numbers, respectively. */ - static int parseInt32(String text) throws NumberFormatException { + static int parseInt32(final String text) throws NumberFormatException { return (int) parseInteger(text, true, false); } @@ -1177,7 +1209,7 @@ public final class TextFormat { * result is coerced to a (signed) {@code int} when returned since Java has * no unsigned integer type. */ - static int parseUInt32(String text) throws NumberFormatException { + static int parseUInt32(final String text) throws NumberFormatException { return (int) parseInteger(text, false, false); } @@ -1186,7 +1218,7 @@ public final class TextFormat { * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" * and "0" to signify hexidecimal and octal numbers, respectively. */ - static long parseInt64(String text) throws NumberFormatException { + static long parseInt64(final String text) throws NumberFormatException { return parseInteger(text, true, true); } @@ -1197,13 +1229,13 @@ public final class TextFormat { * result is coerced to a (signed) {@code long} when returned since Java has * no unsigned long type. */ - static long parseUInt64(String text) throws NumberFormatException { + static long parseUInt64(final String text) throws NumberFormatException { return parseInteger(text, false, true); } - private static long parseInteger(String text, - boolean isSigned, - boolean isLong) + private static long parseInteger(final String text, + final boolean isSigned, + final boolean isLong) throws NumberFormatException { int pos = 0; @@ -1224,7 +1256,7 @@ public final class TextFormat { radix = 8; } - String numberText = text.substring(pos); + final String numberText = text.substring(pos); long result = 0; if (numberText.length() < 16) { diff --git a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java index a1032caa..8743c120 100644 --- a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java +++ b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java @@ -30,12 +30,8 @@ package com.google.protobuf; -import com.google.protobuf.Descriptors.FieldDescriptor; - -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; /** * Thrown when attempting to build a protocol message that is missing required @@ -51,11 +47,15 @@ import java.util.Map; * @author kenton@google.com Kenton Varda */ public class UninitializedMessageException extends RuntimeException { - public UninitializedMessageException(Message message) { - this(findMissingFields(message)); + private static final long serialVersionUID = -7466929953374883507L; + + public UninitializedMessageException(final MessageLite message) { + super("Message was missing required fields. (Lite runtime could not " + + "determine which fields were missing)."); + missingFields = null; } - private UninitializedMessageException(List<String> missingFields) { + public UninitializedMessageException(final List<String> missingFields) { super(buildDescription(missingFields)); this.missingFields = missingFields; } @@ -65,6 +65,8 @@ public class UninitializedMessageException extends RuntimeException { /** * Get a list of human-readable names of required fields missing from this * message. Each name is a full path to a field, e.g. "foo.bar[5].baz". + * Returns null if the lite runtime was used, since it lacks the ability to + * find missing fields. */ public List<String> getMissingFields() { return Collections.unmodifiableList(missingFields); @@ -80,11 +82,11 @@ public class UninitializedMessageException extends RuntimeException { } /** Construct the description string for this exception. */ - private static String buildDescription(List<String> missingFields) { - StringBuilder description = + private static String buildDescription(final List<String> missingFields) { + final StringBuilder description = new StringBuilder("Message missing required fields: "); boolean first = true; - for (String field : missingFields) { + for (final String field : missingFields) { if (first) { first = false; } else { @@ -94,67 +96,4 @@ public class UninitializedMessageException extends RuntimeException { } return description.toString(); } - - /** - * Populates {@code this.missingFields} with the full "path" of each - * missing required field in the given message. - */ - private static List<String> findMissingFields(Message message) { - List<String> results = new ArrayList<String>(); - findMissingFields(message, "", results); - return results; - } - - /** Recursive helper implementing {@link #findMissingFields(Message)}. */ - private static void findMissingFields(Message message, String prefix, - List<String> results) { - for (FieldDescriptor field : message.getDescriptorForType().getFields()) { - if (field.isRequired() && !message.hasField(field)) { - results.add(prefix + field.getName()); - } - } - - for (Map.Entry<FieldDescriptor, Object> entry : - message.getAllFields().entrySet()) { - FieldDescriptor field = entry.getKey(); - Object value = entry.getValue(); - - if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - if (field.isRepeated()) { - int i = 0; - for (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(String prefix, - FieldDescriptor field, - int index) { - 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(); - } } diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java index 77fef323..239c2aca 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -30,15 +30,17 @@ package com.google.protobuf; -import java.io.InputStream; +import com.google.protobuf.Internal.ThreadLocalQuickQueue; + import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.TreeMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; /** * {@code UnknownFieldSet} is used to keep track of fields which were seen when @@ -48,56 +50,59 @@ import java.util.Map; * compiled before the new types were added. * * <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every - * {@link Message.Builder} contains an {@link UnknownFieldSet.Builder}). + * {@link Message.Builder} contains an {@link Builder}). * * <p>Most users will never need to use this class. * * @author kenton@google.com Kenton Varda */ -public final class UnknownFieldSet { +public final class UnknownFieldSet implements MessageLite { private UnknownFieldSet() {} - /** Create a new {@link UnknownFieldSet.Builder}. */ + /** Create a new {@link Builder}. */ public static Builder newBuilder() { - return new Builder(); + return Builder.create(); } /** - * Create a new {@link UnknownFieldSet.Builder} and initialize it to be a copy + * Create a new {@link Builder} and initialize it to be a copy * of {@code copyFrom}. */ - public static Builder newBuilder(UnknownFieldSet copyFrom) { - return new Builder().mergeFrom(copyFrom); + public static Builder newBuilder(final UnknownFieldSet copyFrom) { + return newBuilder().mergeFrom(copyFrom); } /** Get an empty {@code UnknownFieldSet}. */ public static UnknownFieldSet getDefaultInstance() { return defaultInstance; } - private static UnknownFieldSet defaultInstance = + public UnknownFieldSet getDefaultInstanceForType() { + return defaultInstance; + } + private static final UnknownFieldSet defaultInstance = new UnknownFieldSet(Collections.<Integer, Field>emptyMap()); /** * Construct an {@code UnknownFieldSet} around the given map. The map is * expected to be immutable. */ - private UnknownFieldSet(Map<Integer, Field> fields) { + private UnknownFieldSet(final Map<Integer, Field> fields) { this.fields = fields; } private Map<Integer, Field> fields; @Override - public boolean equals(Object other) { + public boolean equals(final Object other) { if (this == other) { return true; } return (other instanceof UnknownFieldSet) && - this.fields.equals(((UnknownFieldSet) other).fields); + fields.equals(((UnknownFieldSet) other).fields); } @Override public int hashCode() { - return this.fields.hashCode(); + return fields.hashCode(); } /** Get a map of fields in the set by number. */ @@ -106,7 +111,7 @@ public final class UnknownFieldSet { } /** Check if the given field number is present in the set. */ - public boolean hasField(int number) { + public boolean hasField(final int number) { return fields.containsKey(number); } @@ -114,14 +119,14 @@ public final class UnknownFieldSet { * Get a field by number. Returns an empty field if not present. Never * returns {@code null}. */ - public Field getField(int number) { - Field result = fields.get(number); + public Field getField(final int number) { + final Field result = fields.get(number); return (result == null) ? Field.getDefaultInstance() : result; } /** Serializes the set and writes it to {@code output}. */ - public void writeTo(CodedOutputStream output) throws IOException { - for (Map.Entry<Integer, Field> entry : fields.entrySet()) { + public void writeTo(final CodedOutputStream output) throws IOException { + for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { entry.getValue().writeTo(entry.getKey(), output); } } @@ -131,7 +136,8 @@ public final class UnknownFieldSet { * just a trivial wrapper around * {@link TextFormat#printToString(UnknownFieldSet)}. */ - public final String toString() { + @Override + public String toString() { return TextFormat.printToString(this); } @@ -139,13 +145,13 @@ public final class UnknownFieldSet { * Serializes the message to a {@code ByteString} and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ - public final ByteString toByteString() { + public ByteString toByteString() { try { - ByteString.CodedBuilder out = + final ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); writeTo(out.getCodedOutput()); return out.build(); - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException( "Serializing to a ByteString threw an IOException (should " + "never happen).", e); @@ -156,14 +162,14 @@ public final class UnknownFieldSet { * Serializes the message to a {@code byte} array and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ - public final byte[] toByteArray() { + public byte[] toByteArray() { try { - byte[] result = new byte[getSerializedSize()]; - CodedOutputStream output = CodedOutputStream.newInstance(result); + final byte[] result = new byte[getSerializedSize()]; + final CodedOutputStream output = CodedOutputStream.newInstance(result); writeTo(output); output.checkNoSpaceLeft(); return result; - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException( "Serializing to a byte array threw an IOException " + "(should never happen).", e); @@ -174,8 +180,15 @@ public final class UnknownFieldSet { * Serializes the message and writes it to {@code output}. This is just a * trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ - public final void writeTo(OutputStream output) throws IOException { - CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + public void writeTo(final OutputStream output) throws IOException { + final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + public void writeDelimitedTo(OutputStream output) throws IOException { + final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + codedOutput.writeRawVarint32(getSerializedSize()); writeTo(codedOutput); codedOutput.flush(); } @@ -183,7 +196,7 @@ public final class UnknownFieldSet { /** Get the number of bytes required to encode this set. */ public int getSerializedSize() { int result = 0; - for (Map.Entry<Integer, Field> entry : fields.entrySet()) { + for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { result += entry.getValue().getSerializedSize(entry.getKey()); } return result; @@ -193,9 +206,9 @@ public final class UnknownFieldSet { * Serializes the set and writes it to {@code output} using * {@code MessageSet} wire format. */ - public void writeAsMessageSetTo(CodedOutputStream output) + public void writeAsMessageSetTo(final CodedOutputStream output) throws IOException { - for (Map.Entry<Integer, Field> entry : fields.entrySet()) { + for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { entry.getValue().writeAsMessageSetExtensionTo( entry.getKey(), output); } @@ -207,37 +220,51 @@ public final class UnknownFieldSet { */ public int getSerializedSizeAsMessageSet() { int result = 0; - for (Map.Entry<Integer, Field> entry : fields.entrySet()) { + for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { result += entry.getValue().getSerializedSizeAsMessageSetExtension( entry.getKey()); } return result; } + public boolean isInitialized() { + // UnknownFieldSets do not have required fields, so they are always + // initialized. + return true; + } + /** Parse an {@code UnknownFieldSet} from the given input stream. */ - static public UnknownFieldSet parseFrom(CodedInputStream input) + public static UnknownFieldSet parseFrom(final CodedInputStream input) throws IOException { return newBuilder().mergeFrom(input).build(); } /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ - public static UnknownFieldSet parseFrom(ByteString data) + public static UnknownFieldSet parseFrom(final ByteString data) throws InvalidProtocolBufferException { return newBuilder().mergeFrom(data).build(); } /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ - public static UnknownFieldSet parseFrom(byte[] data) + public static UnknownFieldSet parseFrom(final byte[] data) throws InvalidProtocolBufferException { return newBuilder().mergeFrom(data).build(); } /** Parse an {@code UnknownFieldSet} from {@code input} and return it. */ - public static UnknownFieldSet parseFrom(InputStream input) + public static UnknownFieldSet parseFrom(final InputStream input) throws IOException { return newBuilder().mergeFrom(input).build(); } + public Builder newBuilderForType() { + return newBuilder(); + } + + public Builder toBuilder() { + return newBuilder().mergeFrom(this); + } + /** * Builder for {@link UnknownFieldSet}s. * @@ -250,21 +277,35 @@ public final class UnknownFieldSet { * * <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. */ - public static final class Builder { + public static final class Builder implements MessageLite.Builder { + private static ThreadLocalQuickQueue<Builder> builders = + new ThreadLocalQuickQueue<Builder>(); + + // This constructor should never be called directly (except from 'create'). private Builder() {} - private Map<Integer, Field> fields = new TreeMap<Integer, Field>(); + + private Map<Integer, Field> fields; // Optimization: We keep around a builder for the last field that was // modified so that we can efficiently add to it multiple times in a // row (important when parsing an unknown repeated field). - int lastFieldNumber = 0; - Field.Builder lastField = null; + private int lastFieldNumber; + private Field.Builder lastField; + + private static Builder create() { + Builder builder = builders.get().poll(); + if (builder == null) { + builder = new Builder(); + } + builder.reinitialize(); + return builder; + } /** * Get a field builder for the given field number which includes any * values that already exist. */ - private Field.Builder getFieldBuilder(int number) { + private Field.Builder getFieldBuilder(final int number) { if (lastField != null) { if (number == lastFieldNumber) { return lastField; @@ -275,7 +316,7 @@ public final class UnknownFieldSet { if (number == 0) { return null; } else { - Field existing = fields.get(number); + final Field existing = fields.get(number); lastFieldNumber = number; lastField = Field.newBuilder(); if (existing != null) { @@ -289,26 +330,48 @@ public final class UnknownFieldSet { * Build the {@link UnknownFieldSet} and return it. * * <p>Once {@code build()} has been called, the {@code Builder} will no - * longer be usable. Calling any method after {@code build()} will throw - * {@code NullPointerException}. + * longer be usable. Calling any method after {@code build()} will result + * in undefined behavior and can cause a {@code NullPointerException} to be + * thrown. */ public UnknownFieldSet build() { getFieldBuilder(0); // Force lastField to be built. - UnknownFieldSet result; + final UnknownFieldSet result; if (fields.isEmpty()) { result = getDefaultInstance(); } else { result = new UnknownFieldSet(Collections.unmodifiableMap(fields)); } fields = null; + builders.get().offer(this); return result; } - /** Reset the builder to an empty set. */ - public Builder clear() { - fields = new TreeMap<Integer, Field>(); + public UnknownFieldSet buildPartial() { + // No required fields, so this is the same as build(). + return build(); + } + + @Override + public Builder clone() { + getFieldBuilder(0); // Force lastField to be built. + return UnknownFieldSet.newBuilder().mergeFrom( + new UnknownFieldSet(fields)); + } + + public UnknownFieldSet getDefaultInstanceForType() { + return UnknownFieldSet.getDefaultInstance(); + } + + private void reinitialize() { + fields = Collections.emptyMap(); lastFieldNumber = 0; lastField = null; + } + + /** Reset the builder to an empty set. */ + public Builder clear() { + reinitialize(); return this; } @@ -317,9 +380,9 @@ public final class UnknownFieldSet { * exists in both sets, {@code other}'s values for that field will be * appended to the values in this set. */ - public Builder mergeFrom(UnknownFieldSet other) { + public Builder mergeFrom(final UnknownFieldSet other) { if (other != getDefaultInstance()) { - for (Map.Entry<Integer, Field> entry : other.fields.entrySet()) { + for (final Map.Entry<Integer, Field> entry : other.fields.entrySet()) { mergeField(entry.getKey(), entry.getValue()); } } @@ -330,7 +393,7 @@ public final class UnknownFieldSet { * Add a field to the {@code UnknownFieldSet}. If a field with the same * number already exists, the two are merged. */ - public Builder mergeField(int number, Field field) { + public Builder mergeField(final int number, final Field field) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } @@ -350,7 +413,7 @@ public final class UnknownFieldSet { * value. This is used in particular when an unknown enum value is * encountered. */ - public Builder mergeVarintField(int number, int value) { + public Builder mergeVarintField(final int number, final int value) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } @@ -359,7 +422,7 @@ public final class UnknownFieldSet { } /** Check if the given field number is present in the set. */ - public boolean hasField(int number) { + public boolean hasField(final int number) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } @@ -370,7 +433,7 @@ public final class UnknownFieldSet { * Add a field to the {@code UnknownFieldSet}. If a field with the same * number already exists, it is removed. */ - public Builder addField(int number, Field field) { + public Builder addField(final int number, final Field field) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } @@ -379,6 +442,9 @@ public final class UnknownFieldSet { lastField = null; lastFieldNumber = 0; } + if (fields.isEmpty()) { + fields = new TreeMap<Integer,Field>(); + } fields.put(number, field); return this; } @@ -396,9 +462,9 @@ public final class UnknownFieldSet { * Parse an entire message from {@code input} and merge its fields into * this set. */ - public Builder mergeFrom(CodedInputStream input) throws IOException { + public Builder mergeFrom(final CodedInputStream input) throws IOException { while (true) { - int tag = input.readTag(); + final int tag = input.readTag(); if (tag == 0 || !mergeFieldFrom(tag, input)) { break; } @@ -411,9 +477,9 @@ public final class UnknownFieldSet { * @param tag The field's tag number, which was already parsed. * @return {@code false} if the tag is an engroup tag. */ - public boolean mergeFieldFrom(int tag, CodedInputStream input) + public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { - int number = WireFormat.getTagFieldNumber(tag); + final int number = WireFormat.getTagFieldNumber(tag); switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: getFieldBuilder(number).addVarint(input.readInt64()); @@ -424,12 +490,12 @@ public final class UnknownFieldSet { case WireFormat.WIRETYPE_LENGTH_DELIMITED: getFieldBuilder(number).addLengthDelimited(input.readBytes()); return true; - case WireFormat.WIRETYPE_START_GROUP: { - UnknownFieldSet.Builder subBuilder = UnknownFieldSet.newBuilder(); - input.readUnknownGroup(number, subBuilder); + case WireFormat.WIRETYPE_START_GROUP: + final Builder subBuilder = newBuilder(); + input.readGroup(number, subBuilder, + ExtensionRegistry.getEmptyRegistry()); getFieldBuilder(number).addGroup(subBuilder.build()); return true; - } case WireFormat.WIRETYPE_END_GROUP: return false; case WireFormat.WIRETYPE_FIXED32: @@ -445,16 +511,16 @@ public final class UnknownFieldSet { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ - public Builder mergeFrom(ByteString data) + public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException { try { - CodedInputStream input = data.newCodedInput(); + final CodedInputStream input = data.newCodedInput(); mergeFrom(input); input.checkLastTagWas(0); return this; - } catch (InvalidProtocolBufferException e) { + } catch (final InvalidProtocolBufferException e) { throw e; - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException( "Reading from a ByteString threw an IOException (should " + "never happen).", e); @@ -466,16 +532,16 @@ public final class UnknownFieldSet { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ - public Builder mergeFrom(byte[] data) + public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException { try { - CodedInputStream input = CodedInputStream.newInstance(data); + final CodedInputStream input = CodedInputStream.newInstance(data); mergeFrom(input); input.checkLastTagWas(0); return this; - } catch (InvalidProtocolBufferException e) { + } catch (final InvalidProtocolBufferException e) { throw e; - } catch (IOException e) { + } catch (final IOException e) { throw new RuntimeException( "Reading from a byte array threw an IOException (should " + "never happen).", e); @@ -487,12 +553,88 @@ public final class UnknownFieldSet { * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ - public Builder mergeFrom(InputStream input) throws IOException { - CodedInputStream codedInput = CodedInputStream.newInstance(input); + public Builder mergeFrom(final InputStream input) throws IOException { + final CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput); codedInput.checkLastTagWas(0); return this; } + + public Builder mergeDelimitedFrom(InputStream input) + throws IOException { + final int size = CodedInputStream.readRawVarint32(input); + final InputStream limitedInput = + new AbstractMessage.Builder.LimitedInputStream(input, size); + return mergeFrom(limitedInput, null); + } + + public Builder mergeDelimitedFrom( + InputStream input, + ExtensionRegistryLite extensionRegistry) throws IOException { + // UnknownFieldSet has no extensions. + return mergeFrom(input); + } + + public Builder mergeFrom( + CodedInputStream input, + ExtensionRegistryLite extensionRegistry) throws IOException { + // UnknownFieldSet has no extensions. + return mergeFrom(input); + } + + public Builder mergeFrom( + ByteString data, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + // UnknownFieldSet has no extensions. + return mergeFrom(data); + } + + public Builder mergeFrom(byte[] data, int off, int len) + throws InvalidProtocolBufferException { + try { + final CodedInputStream input = + CodedInputStream.newInstance(data, off, len); + mergeFrom(input); + input.checkLastTagWas(0); + return this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public Builder mergeFrom( + byte[] data, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + // UnknownFieldSet has no extensions. + return mergeFrom(data); + } + + public Builder mergeFrom( + byte[] data, int off, int len, + ExtensionRegistryLite extensionRegistry) + throws InvalidProtocolBufferException { + // UnknownFieldSet has no extensions. + return mergeFrom(data, off, len); + } + + public Builder mergeFrom( + InputStream input, + ExtensionRegistryLite extensionRegistry) throws IOException { + // UnknownFieldSet has no extensions. + return mergeFrom(input); + } + + public boolean isInitialized() { + // UnknownFieldSets do not have required fields, so they are always + // initialized. + return true; + } } /** @@ -510,7 +652,7 @@ public final class UnknownFieldSet { * wire types. * * <p>{@code Field} is an immutable class. To construct one, you must use a - * {@link Field.Builder}. + * {@link Builder}. * * @see UnknownFieldSet */ @@ -519,22 +661,22 @@ public final class UnknownFieldSet { /** Construct a new {@link Builder}. */ public static Builder newBuilder() { - return new Builder(); + return Builder.create(); } /** * Construct a new {@link Builder} and initialize it to a copy of * {@code copyFrom}. */ - public static Builder newBuilder(Field copyFrom) { - return new Builder().mergeFrom(copyFrom); + public static Builder newBuilder(final Field copyFrom) { + return newBuilder().mergeFrom(copyFrom); } /** Get an empty {@code Field}. */ public static Field getDefaultInstance() { - return defaultInstance; + return fieldDefaultInstance; } - private static Field defaultInstance = newBuilder().build(); + private static final Field fieldDefaultInstance = newBuilder().build(); /** Get the list of varint values for this field. */ public List<Long> getVarintList() { return varint; } @@ -556,14 +698,14 @@ public final class UnknownFieldSet { public List<UnknownFieldSet> getGroupList() { return group; } @Override - public boolean equals(Object other) { + public boolean equals(final Object other) { if (this == other) { return true; } if (!(other instanceof Field)) { return false; } - return Arrays.equals(this.getIdentityArray(), + return Arrays.equals(getIdentityArray(), ((Field) other).getIdentityArray()); } @@ -574,37 +716,37 @@ public final class UnknownFieldSet { /** * Returns the array of objects to be used to uniquely identify this - * {@link UnknownFieldSet.Field} instance. + * {@link Field} instance. */ private Object[] getIdentityArray() { return new Object[] { - this.varint, - this.fixed32, - this.fixed64, - this.lengthDelimited, - this.group}; + varint, + fixed32, + fixed64, + lengthDelimited, + group}; } /** * Serializes the field, including field number, and writes it to * {@code output}. */ - public void writeTo(int fieldNumber, CodedOutputStream output) + public void writeTo(final int fieldNumber, final CodedOutputStream output) throws IOException { - for (long value : varint) { + for (final long value : varint) { output.writeUInt64(fieldNumber, value); } - for (int value : fixed32) { + for (final int value : fixed32) { output.writeFixed32(fieldNumber, value); } - for (long value : fixed64) { + for (final long value : fixed64) { output.writeFixed64(fieldNumber, value); } - for (ByteString value : lengthDelimited) { + for (final ByteString value : lengthDelimited) { output.writeBytes(fieldNumber, value); } - for (UnknownFieldSet value : group) { - output.writeUnknownGroup(fieldNumber, value); + for (final UnknownFieldSet value : group) { + output.writeGroup(fieldNumber, value); } } @@ -612,22 +754,22 @@ public final class UnknownFieldSet { * Get the number of bytes required to encode this field, including field * number. */ - public int getSerializedSize(int fieldNumber) { + public int getSerializedSize(final int fieldNumber) { int result = 0; - for (long value : varint) { + for (final long value : varint) { result += CodedOutputStream.computeUInt64Size(fieldNumber, value); } - for (int value : fixed32) { + for (final int value : fixed32) { result += CodedOutputStream.computeFixed32Size(fieldNumber, value); } - for (long value : fixed64) { + for (final long value : fixed64) { result += CodedOutputStream.computeFixed64Size(fieldNumber, value); } - for (ByteString value : lengthDelimited) { + for (final ByteString value : lengthDelimited) { result += CodedOutputStream.computeBytesSize(fieldNumber, value); } - for (UnknownFieldSet value : group) { - result += CodedOutputStream.computeUnknownGroupSize(fieldNumber, value); + for (final UnknownFieldSet value : group) { + result += CodedOutputStream.computeGroupSize(fieldNumber, value); } return result; } @@ -637,10 +779,10 @@ public final class UnknownFieldSet { * {@code output}, using {@code MessageSet} wire format. */ public void writeAsMessageSetExtensionTo( - int fieldNumber, - CodedOutputStream output) + final int fieldNumber, + final CodedOutputStream output) throws IOException { - for (ByteString value : lengthDelimited) { + for (final ByteString value : lengthDelimited) { output.writeRawMessageSetExtension(fieldNumber, value); } } @@ -649,9 +791,9 @@ public final class UnknownFieldSet { * Get the number of bytes required to encode this field, including field * number, using {@code MessageSet} wire format. */ - public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { + public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) { int result = 0; - for (ByteString value : lengthDelimited) { + for (final ByteString value : lengthDelimited) { result += CodedOutputStream.computeRawMessageSetExtensionSize( fieldNumber, value); } @@ -670,13 +812,29 @@ public final class UnknownFieldSet { * <p>Use {@link Field#newBuilder()} to construct a {@code Builder}. */ public static final class Builder { + private static ThreadLocalQuickQueue<Builder> builders = + new ThreadLocalQuickQueue<Builder>(); + + // This constructor should never be called directly (except from 'create'). private Builder() {} - private Field result = new Field(); + + private static Builder create() { + Builder builder = builders.get().poll(); + if (builder == null) { + builder = new Builder(); + } + + builder.result = new Field(); + return builder; + } + + private Field result; /** * Build the field. After {@code build()} has been called, the * {@code Builder} is no longer usable. Calling any other method will - * throw a {@code NullPointerException}. + * result in undefined behavior and can cause a + * {@code NullPointerException} to be thrown. */ public Field build() { if (result.varint == null) { @@ -706,8 +864,9 @@ public final class UnknownFieldSet { result.group = Collections.unmodifiableList(result.group); } - Field returnMe = result; + final Field returnMe = result; result = null; + builders.get().offer(this); return returnMe; } @@ -722,7 +881,7 @@ public final class UnknownFieldSet { * of values, {@code other}'s values are append to the ones in this * field. */ - public Builder mergeFrom(Field other) { + public Builder mergeFrom(final Field other) { if (!other.varint.isEmpty()) { if (result.varint == null) { result.varint = new ArrayList<Long>(); @@ -757,7 +916,7 @@ public final class UnknownFieldSet { } /** Add a varint value. */ - public Builder addVarint(long value) { + public Builder addVarint(final long value) { if (result.varint == null) { result.varint = new ArrayList<Long>(); } @@ -766,7 +925,7 @@ public final class UnknownFieldSet { } /** Add a fixed32 value. */ - public Builder addFixed32(int value) { + public Builder addFixed32(final int value) { if (result.fixed32 == null) { result.fixed32 = new ArrayList<Integer>(); } @@ -775,7 +934,7 @@ public final class UnknownFieldSet { } /** Add a fixed64 value. */ - public Builder addFixed64(long value) { + public Builder addFixed64(final long value) { if (result.fixed64 == null) { result.fixed64 = new ArrayList<Long>(); } @@ -784,7 +943,7 @@ public final class UnknownFieldSet { } /** Add a length-delimited value. */ - public Builder addLengthDelimited(ByteString value) { + public Builder addLengthDelimited(final ByteString value) { if (result.lengthDelimited == null) { result.lengthDelimited = new ArrayList<ByteString>(); } @@ -793,7 +952,7 @@ public final class UnknownFieldSet { } /** Add an embedded group. */ - public Builder addGroup(UnknownFieldSet value) { + public Builder addGroup(final UnknownFieldSet value) { if (result.group == null) { result.group = new ArrayList<UnknownFieldSet>(); } diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index 2faf2448..3b0bdcd0 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -56,54 +56,84 @@ public final class WireFormat { static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1; /** Given a tag value, determines the wire type (the lower 3 bits). */ - static int getTagWireType(int tag) { + static int getTagWireType(final int tag) { return tag & TAG_TYPE_MASK; } /** Given a tag value, determines the field number (the upper 29 bits). */ - public static int getTagFieldNumber(int tag) { + public static int getTagFieldNumber(final int tag) { return tag >>> TAG_TYPE_BITS; } /** Makes a tag value given a field number and wire type. */ - static int makeTag(int fieldNumber, int wireType) { + static int makeTag(final int fieldNumber, final int wireType) { return (fieldNumber << TAG_TYPE_BITS) | wireType; } - static int getWireFormatForFieldType(Descriptors.FieldDescriptor.Type type) { - switch (type) { - case DOUBLE : return WIRETYPE_FIXED64; - case FLOAT : return WIRETYPE_FIXED32; - case INT64 : return WIRETYPE_VARINT; - case UINT64 : return WIRETYPE_VARINT; - case INT32 : return WIRETYPE_VARINT; - case FIXED64 : return WIRETYPE_FIXED64; - case FIXED32 : return WIRETYPE_FIXED32; - case BOOL : return WIRETYPE_VARINT; - case STRING : return WIRETYPE_LENGTH_DELIMITED; - case GROUP : return WIRETYPE_START_GROUP; - case MESSAGE : return WIRETYPE_LENGTH_DELIMITED; - case BYTES : return WIRETYPE_LENGTH_DELIMITED; - case UINT32 : return WIRETYPE_VARINT; - case ENUM : return WIRETYPE_VARINT; - case SFIXED32: return WIRETYPE_FIXED32; - case SFIXED64: return WIRETYPE_FIXED64; - case SINT32 : return WIRETYPE_VARINT; - case SINT64 : return WIRETYPE_VARINT; + /** + * Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}. This is + * only here to support the lite runtime and should not be used by users. + */ + public enum JavaType { + INT(0), + LONG(0L), + FLOAT(0F), + DOUBLE(0D), + BOOLEAN(false), + STRING(""), + BYTE_STRING(ByteString.EMPTY), + ENUM(null), + MESSAGE(null); + + JavaType(final Object defaultDefault) { + this.defaultDefault = defaultDefault; + } + + /** + * The default default value for fields of this type, if it's a primitive + * type. + */ + Object getDefaultDefault() { + return defaultDefault; } - throw new RuntimeException( - "There is no way to get here, but the compiler thinks otherwise."); + private final Object defaultDefault; } - /** Given a field descriptor, returns the wire type. This differs from - * getWireFormatForFieldType for packed repeated fields. */ - static int getWireFormatForField(Descriptors.FieldDescriptor descriptor) { - if (descriptor.getOptions().getPacked()) { - return WIRETYPE_LENGTH_DELIMITED; - } else { - return getWireFormatForFieldType(descriptor.getType()); + /** + * Lite equivalent to {@link Descriptors.FieldDescriptor.Type}. This is + * only here to support the lite runtime and should not be used by users. + */ + public enum FieldType { + DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ), + FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ), + INT64 (JavaType.LONG , WIRETYPE_VARINT ), + UINT64 (JavaType.LONG , WIRETYPE_VARINT ), + INT32 (JavaType.INT , WIRETYPE_VARINT ), + FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ), + FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ), + BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ), + STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED), + GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ), + MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED), + BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED), + UINT32 (JavaType.INT , WIRETYPE_VARINT ), + ENUM (JavaType.ENUM , WIRETYPE_VARINT ), + SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ), + SFIXED64(JavaType.LONG , WIRETYPE_FIXED64 ), + SINT32 (JavaType.INT , WIRETYPE_VARINT ), + SINT64 (JavaType.LONG , WIRETYPE_VARINT ); + + FieldType(final JavaType javaType, final int wireType) { + this.javaType = javaType; + this.wireType = wireType; } + + private final JavaType javaType; + private final int wireType; + + public JavaType getJavaType() { return javaType; } + public int getWireType() { return wireType; } } // Field numbers for feilds in MessageSet wire format. |