// 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.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream; /** * {@code UnknownFieldSet} is used to keep track of fields which were seen when * parsing a protocol message but whose field numbers or types are unrecognized. * This most frequently occurs when new fields are added to a message type * and then messages containing those feilds are read by old software that was * compiled before the new types were added. * *

Every {@link Message} contains an {@code UnknownFieldSet} (and every * {@link Message.Builder} contains an {@link Builder}). * *

Most users will never need to use this class. * * @author kenton@google.com Kenton Varda */ public final class UnknownFieldSet implements MessageLite { private UnknownFieldSet() {} /** Create a new {@link Builder}. */ public static Builder newBuilder() { return Builder.create(); } /** * Create a new {@link Builder} and initialize it to be a copy * of {@code copyFrom}. */ public static Builder newBuilder(final UnknownFieldSet copyFrom) { return newBuilder().mergeFrom(copyFrom); } /** Get an empty {@code UnknownFieldSet}. */ public static UnknownFieldSet getDefaultInstance() { return defaultInstance; } @Override public UnknownFieldSet getDefaultInstanceForType() { return defaultInstance; } private static final UnknownFieldSet defaultInstance = new UnknownFieldSet(Collections.emptyMap()); /** * Construct an {@code UnknownFieldSet} around the given map. The map is * expected to be immutable. */ private UnknownFieldSet(final Map fields) { this.fields = fields; } private Map fields; @Override public boolean equals(final Object other) { if (this == other) { return true; } return (other instanceof UnknownFieldSet) && fields.equals(((UnknownFieldSet) other).fields); } @Override public int hashCode() { return fields.hashCode(); } /** Get a map of fields in the set by number. */ public Map asMap() { return fields; } /** Check if the given field number is present in the set. */ public boolean hasField(final int number) { return fields.containsKey(number); } /** * Get a field by number. Returns an empty field if not present. Never * returns {@code null}. */ 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}. */ @Override public void writeTo(final CodedOutputStream output) throws IOException { for (final Map.Entry entry : fields.entrySet()) { entry.getValue().writeTo(entry.getKey(), output); } } /** * Converts the set to a string in protocol buffer text format. This is * just a trivial wrapper around * {@link TextFormat#printToString(UnknownFieldSet)}. */ @Override public String toString() { return TextFormat.printToString(this); } /** * Serializes the message to a {@code ByteString} and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ @Override public ByteString toByteString() { try { final ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); writeTo(out.getCodedOutput()); return out.build(); } catch (final IOException e) { throw new RuntimeException( "Serializing to a ByteString threw an IOException (should " + "never happen).", e); } } /** * Serializes the message to a {@code byte} array and returns it. This is * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ @Override public byte[] toByteArray() { try { final byte[] result = new byte[getSerializedSize()]; final CodedOutputStream output = CodedOutputStream.newInstance(result); writeTo(output); output.checkNoSpaceLeft(); return result; } catch (final IOException e) { throw new RuntimeException( "Serializing to a byte array threw an IOException " + "(should never happen).", e); } } /** * Serializes the message and writes it to {@code output}. This is just a * trivial wrapper around {@link #writeTo(CodedOutputStream)}. */ @Override public void writeTo(final OutputStream output) throws IOException { final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); writeTo(codedOutput); codedOutput.flush(); } @Override public void writeDelimitedTo(OutputStream output) throws IOException { final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); codedOutput.writeRawVarint32(getSerializedSize()); writeTo(codedOutput); codedOutput.flush(); } /** Get the number of bytes required to encode this set. */ @Override public int getSerializedSize() { int result = 0; for (final Map.Entry entry : fields.entrySet()) { result += entry.getValue().getSerializedSize(entry.getKey()); } return result; } /** * Serializes the set and writes it to {@code output} using * {@code MessageSet} wire format. */ public void writeAsMessageSetTo(final CodedOutputStream output) throws IOException { for (final Map.Entry entry : fields.entrySet()) { entry.getValue().writeAsMessageSetExtensionTo( entry.getKey(), output); } } /** * Get the number of bytes required to encode this set using * {@code MessageSet} wire format. */ public int getSerializedSizeAsMessageSet() { int result = 0; for (final Map.Entry entry : fields.entrySet()) { result += entry.getValue().getSerializedSizeAsMessageSetExtension( entry.getKey()); } return result; } @Override 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. */ 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(final ByteString data) throws InvalidProtocolBufferException { return newBuilder().mergeFrom(data).build(); } /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ 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(final InputStream input) throws IOException { return newBuilder().mergeFrom(input).build(); } @Override public Builder newBuilderForType() { return newBuilder(); } @Override public Builder toBuilder() { return newBuilder().mergeFrom(this); } /** * Builder for {@link UnknownFieldSet}s. * *

Note that this class maintains {@link Field.Builder}s for all fields * in the set. Thus, adding one element to an existing {@link Field} does not * require making a copy. This is important for efficient parsing of * unknown repeated fields. However, it implies that {@link Field}s cannot * be constructed independently, nor can two {@link UnknownFieldSet}s share * the same {@code Field} object. * *

Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. */ public static final class Builder implements MessageLite.Builder { // This constructor should never be called directly (except from 'create'). private Builder() {} private Map 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). private int lastFieldNumber; private Field.Builder lastField; private static Builder create() { Builder 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(final int number) { if (lastField != null) { if (number == lastFieldNumber) { return lastField; } // Note: addField() will reset lastField and lastFieldNumber. addField(lastFieldNumber, lastField.build()); } if (number == 0) { return null; } else { final Field existing = fields.get(number); lastFieldNumber = number; lastField = Field.newBuilder(); if (existing != null) { lastField.mergeFrom(existing); } return lastField; } } /** * Build the {@link UnknownFieldSet} and return it. * *

Once {@code build()} has been called, the {@code Builder} will no * longer be usable. Calling any method after {@code build()} will result * in undefined behavior and can cause a {@code NullPointerException} to be * thrown. */ @Override public UnknownFieldSet build() { getFieldBuilder(0); // Force lastField to be built. final UnknownFieldSet result; if (fields.isEmpty()) { result = getDefaultInstance(); } else { result = new UnknownFieldSet(Collections.unmodifiableMap(fields)); } fields = null; return result; } @Override 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)); } @Override public UnknownFieldSet getDefaultInstanceForType() { return UnknownFieldSet.getDefaultInstance(); } private void reinitialize() { fields = Collections.emptyMap(); lastFieldNumber = 0; lastField = null; } /** Reset the builder to an empty set. */ @Override public Builder clear() { reinitialize(); return this; } /** * Merge the fields from {@code other} into this set. If a field number * exists in both sets, {@code other}'s values for that field will be * appended to the values in this set. */ public Builder mergeFrom(final UnknownFieldSet other) { if (other != getDefaultInstance()) { for (final Map.Entry entry : other.fields.entrySet()) { mergeField(entry.getKey(), entry.getValue()); } } return this; } /** * Add a field to the {@code UnknownFieldSet}. If a field with the same * number already exists, the two are merged. */ public Builder mergeField(final int number, final Field field) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } if (hasField(number)) { getFieldBuilder(number).mergeFrom(field); } else { // Optimization: We could call getFieldBuilder(number).mergeFrom(field) // in this case, but that would create a copy of the Field object. // We'd rather reuse the one passed to us, so call addField() instead. addField(number, field); } return this; } /** * Convenience method for merging a new field containing a single varint * value. This is used in particular when an unknown enum value is * encountered. */ public Builder mergeVarintField(final int number, final int value) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } getFieldBuilder(number).addVarint(value); return this; } /** Check if the given field number is present in the set. */ public boolean hasField(final int number) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } return number == lastFieldNumber || fields.containsKey(number); } /** * Add a field to the {@code UnknownFieldSet}. If a field with the same * number already exists, it is removed. */ public Builder addField(final int number, final Field field) { if (number == 0) { throw new IllegalArgumentException("Zero is not a valid field number."); } if (lastField != null && lastFieldNumber == number) { // Discard this. lastField = null; lastFieldNumber = 0; } if (fields.isEmpty()) { fields = new TreeMap(); } fields.put(number, field); return this; } /** * Get all present {@code Field}s as an immutable {@code Map}. If more * fields are added, the changes may or may not be reflected in this map. */ public Map asMap() { getFieldBuilder(0); // Force lastField to be built. return Collections.unmodifiableMap(fields); } /** * Parse an entire message from {@code input} and merge its fields into * this set. */ @Override public Builder mergeFrom(final CodedInputStream input) throws IOException { while (true) { final int tag = input.readTag(); if (tag == 0 || !mergeFieldFrom(tag, input)) { break; } } return this; } /** * Parse a single field from {@code input} and merge it into this set. * @param tag The field's tag number, which was already parsed. * @return {@code false} if the tag is an engroup tag. */ public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { final int number = WireFormat.getTagFieldNumber(tag); switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: getFieldBuilder(number).addVarint(input.readInt64()); return true; case WireFormat.WIRETYPE_FIXED64: getFieldBuilder(number).addFixed64(input.readFixed64()); return true; case WireFormat.WIRETYPE_LENGTH_DELIMITED: getFieldBuilder(number).addLengthDelimited(input.readBytes()); return true; 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: getFieldBuilder(number).addFixed32(input.readFixed32()); return true; default: throw InvalidProtocolBufferException.invalidWireType(); } } /** * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ @Override public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException { try { final CodedInputStream input = data.newCodedInput(); mergeFrom(input); input.checkLastTagWas(0); return this; } catch (final InvalidProtocolBufferException e) { throw e; } catch (final IOException e) { throw new RuntimeException( "Reading from a ByteString threw an IOException (should " + "never happen).", e); } } /** * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ @Override public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException { try { final CodedInputStream input = CodedInputStream.newInstance(data); mergeFrom(input); input.checkLastTagWas(0); return this; } catch (final InvalidProtocolBufferException e) { throw e; } catch (final IOException e) { throw new RuntimeException( "Reading from a byte array threw an IOException (should " + "never happen).", e); } } /** * Parse an {@code UnknownFieldSet} from {@code input} and merge it with the * set being built. This is just a small wrapper around * {@link #mergeFrom(CodedInputStream)}. */ @Override public Builder mergeFrom(final InputStream input) throws IOException { final CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput); codedInput.checkLastTagWas(0); return this; } @Override public boolean mergeDelimitedFrom(InputStream input) throws IOException { final int firstByte = input.read(); if (firstByte == -1) { return false; } final int size = CodedInputStream.readRawVarint32(firstByte, input); final InputStream limitedInput = new LimitedInputStream(input, size); mergeFrom(limitedInput); return true; } @Override public boolean mergeDelimitedFrom( InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException { // UnknownFieldSet has no extensions. return mergeDelimitedFrom(input); } @Override public Builder mergeFrom( CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws IOException { // UnknownFieldSet has no extensions. return mergeFrom(input); } @Override public Builder mergeFrom( ByteString data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data); } @Override 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); } } @Override public Builder mergeFrom( byte[] data, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data); } @Override public Builder mergeFrom( byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { // UnknownFieldSet has no extensions. return mergeFrom(data, off, len); } @Override public Builder mergeFrom( InputStream input, ExtensionRegistryLite extensionRegistry) throws IOException { // UnknownFieldSet has no extensions. return mergeFrom(input); } @Override public boolean isInitialized() { // UnknownFieldSets do not have required fields, so they are always // initialized. return true; } } /** * Represents a single field in an {@code UnknownFieldSet}. * *

A {@code Field} consists of five lists of values. The lists correspond * to the five "wire types" used in the protocol buffer binary format. * The wire type of each field can be determined from the encoded form alone, * without knowing the field's declared type. So, we are able to parse * unknown values at least this far and separate them. Normally, only one * of the five lists will contain any values, since it is impossible to * define a valid message type that declares two different types for the * same field number. However, the code is designed to allow for the case * where the same unknown field number is encountered using multiple different * wire types. * *

{@code Field} is an immutable class. To construct one, you must use a * {@link Builder}. * * @see UnknownFieldSet */ public static final class Field { private Field() {} /** Construct a new {@link Builder}. */ public static Builder newBuilder() { return Builder.create(); } /** * Construct a new {@link Builder} and initialize it to a copy of * {@code copyFrom}. */ public static Builder newBuilder(final Field copyFrom) { return newBuilder().mergeFrom(copyFrom); } /** Get an empty {@code Field}. */ public static Field getDefaultInstance() { return fieldDefaultInstance; } private static final Field fieldDefaultInstance = newBuilder().build(); /** Get the list of varint values for this field. */ public List getVarintList() { return varint; } /** Get the list of fixed32 values for this field. */ public List getFixed32List() { return fixed32; } /** Get the list of fixed64 values for this field. */ public List getFixed64List() { return fixed64; } /** Get the list of length-delimited values for this field. */ public List getLengthDelimitedList() { return lengthDelimited; } /** * Get the list of embedded group values for this field. These are * represented using {@link UnknownFieldSet}s rather than {@link Message}s * since the group's type is presumably unknown. */ public List getGroupList() { return group; } @Override public boolean equals(final Object other) { if (this == other) { return true; } if (!(other instanceof Field)) { return false; } return Arrays.equals(getIdentityArray(), ((Field) other).getIdentityArray()); } @Override public int hashCode() { return Arrays.hashCode(getIdentityArray()); } /** * Returns the array of objects to be used to uniquely identify this * {@link Field} instance. */ private Object[] getIdentityArray() { return new Object[] { varint, fixed32, fixed64, lengthDelimited, group}; } /** * Serializes the field, including field number, and writes it to * {@code output}. */ public void writeTo(final int fieldNumber, final CodedOutputStream output) throws IOException { for (final long value : varint) { output.writeUInt64(fieldNumber, value); } for (final int value : fixed32) { output.writeFixed32(fieldNumber, value); } for (final long value : fixed64) { output.writeFixed64(fieldNumber, value); } for (final ByteString value : lengthDelimited) { output.writeBytes(fieldNumber, value); } for (final UnknownFieldSet value : group) { output.writeGroup(fieldNumber, value); } } /** * Get the number of bytes required to encode this field, including field * number. */ public int getSerializedSize(final int fieldNumber) { int result = 0; for (final long value : varint) { result += CodedOutputStream.computeUInt64Size(fieldNumber, value); } for (final int value : fixed32) { result += CodedOutputStream.computeFixed32Size(fieldNumber, value); } for (final long value : fixed64) { result += CodedOutputStream.computeFixed64Size(fieldNumber, value); } for (final ByteString value : lengthDelimited) { result += CodedOutputStream.computeBytesSize(fieldNumber, value); } for (final UnknownFieldSet value : group) { result += CodedOutputStream.computeGroupSize(fieldNumber, value); } return result; } /** * Serializes the field, including field number, and writes it to * {@code output}, using {@code MessageSet} wire format. */ public void writeAsMessageSetExtensionTo( final int fieldNumber, final CodedOutputStream output) throws IOException { for (final ByteString value : lengthDelimited) { output.writeRawMessageSetExtension(fieldNumber, value); } } /** * Get the number of bytes required to encode this field, including field * number, using {@code MessageSet} wire format. */ public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) { int result = 0; for (final ByteString value : lengthDelimited) { result += CodedOutputStream.computeRawMessageSetExtensionSize( fieldNumber, value); } return result; } private List varint; private List fixed32; private List fixed64; private List lengthDelimited; private List group; /** * Used to build a {@link Field} within an {@link UnknownFieldSet}. * *

Use {@link Field#newBuilder()} to construct a {@code Builder}. */ public static final class Builder { // This constructor should never be called directly (except from 'create'). private Builder() {} private static Builder create() { Builder 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 * result in undefined behavior and can cause a * {@code NullPointerException} to be thrown. */ public Field build() { if (result.varint == null) { result.varint = Collections.emptyList(); } else { result.varint = Collections.unmodifiableList(result.varint); } if (result.fixed32 == null) { result.fixed32 = Collections.emptyList(); } else { result.fixed32 = Collections.unmodifiableList(result.fixed32); } if (result.fixed64 == null) { result.fixed64 = Collections.emptyList(); } else { result.fixed64 = Collections.unmodifiableList(result.fixed64); } if (result.lengthDelimited == null) { result.lengthDelimited = Collections.emptyList(); } else { result.lengthDelimited = Collections.unmodifiableList(result.lengthDelimited); } if (result.group == null) { result.group = Collections.emptyList(); } else { result.group = Collections.unmodifiableList(result.group); } final Field returnMe = result; result = null; return returnMe; } /** Discard the field's contents. */ public Builder clear() { result = new Field(); return this; } /** * Merge the values in {@code other} into this field. For each list * of values, {@code other}'s values are append to the ones in this * field. */ public Builder mergeFrom(final Field other) { if (!other.varint.isEmpty()) { if (result.varint == null) { result.varint = new ArrayList(); } result.varint.addAll(other.varint); } if (!other.fixed32.isEmpty()) { if (result.fixed32 == null) { result.fixed32 = new ArrayList(); } result.fixed32.addAll(other.fixed32); } if (!other.fixed64.isEmpty()) { if (result.fixed64 == null) { result.fixed64 = new ArrayList(); } result.fixed64.addAll(other.fixed64); } if (!other.lengthDelimited.isEmpty()) { if (result.lengthDelimited == null) { result.lengthDelimited = new ArrayList(); } result.lengthDelimited.addAll(other.lengthDelimited); } if (!other.group.isEmpty()) { if (result.group == null) { result.group = new ArrayList(); } result.group.addAll(other.group); } return this; } /** Add a varint value. */ public Builder addVarint(final long value) { if (result.varint == null) { result.varint = new ArrayList(); } result.varint.add(value); return this; } /** Add a fixed32 value. */ public Builder addFixed32(final int value) { if (result.fixed32 == null) { result.fixed32 = new ArrayList(); } result.fixed32.add(value); return this; } /** Add a fixed64 value. */ public Builder addFixed64(final long value) { if (result.fixed64 == null) { result.fixed64 = new ArrayList(); } result.fixed64.add(value); return this; } /** Add a length-delimited value. */ public Builder addLengthDelimited(final ByteString value) { if (result.lengthDelimited == null) { result.lengthDelimited = new ArrayList(); } result.lengthDelimited.add(value); return this; } /** Add an embedded group. */ public Builder addGroup(final UnknownFieldSet value) { if (result.group == null) { result.group = new ArrayList(); } result.group.add(value); return this; } } } }