// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; import java.util.Collections; import java.util.Map; import java.util.TreeMap; /** * Implements MapEntry messages. * * In reflection API, map fields will be treated as repeated message fields and * each map entry is accessed as a message. This MapEntry class is used to * represent these map entry messages in reflection API. * * Protobuf internal. Users shouldn't use this class. */ public final class MapEntry extends AbstractMessage { private static final class Metadata extends MapEntryLite.Metadata { public final Descriptor descriptor; public final Parser> parser; public Metadata( Descriptor descriptor, MapEntry defaultInstance, WireFormat.FieldType keyType, WireFormat.FieldType valueType) { super(keyType, defaultInstance.key, valueType, defaultInstance.value); this.descriptor = descriptor; this.parser = new AbstractParser>() { @Override public MapEntry parsePartialFrom( CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { return new MapEntry(Metadata.this, input, extensionRegistry); } }; } } private final K key; private final V value; private final Metadata metadata; /** Create a default MapEntry instance. */ private MapEntry( Descriptor descriptor, WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { this.key = defaultKey; this.value = defaultValue; this.metadata = new Metadata(descriptor, this, keyType, valueType); } /** Create a MapEntry with the provided key and value. */ @SuppressWarnings("unchecked") private MapEntry(Metadata metadata, K key, V value) { this.key = key; this.value = value; this.metadata = metadata; } /** Parsing constructor. */ private MapEntry( Metadata metadata, CodedInputStream input, ExtensionRegistryLite extensionRegistry) throws InvalidProtocolBufferException { try { this.metadata = metadata; Map.Entry entry = MapEntryLite.parseEntry(input, metadata, extensionRegistry); this.key = entry.getKey(); this.value = entry.getValue(); } catch (InvalidProtocolBufferException e) { throw e.setUnfinishedMessage(this); } catch (IOException e) { throw new InvalidProtocolBufferException(e).setUnfinishedMessage(this); } } /** * Create a default MapEntry instance. A default MapEntry instance should be * created only once for each map entry message type. Generated code should * store the created default instance and use it later to create new MapEntry * messages of the same type. */ public static MapEntry newDefaultInstance( Descriptor descriptor, WireFormat.FieldType keyType, K defaultKey, WireFormat.FieldType valueType, V defaultValue) { return new MapEntry( descriptor, keyType, defaultKey, valueType, defaultValue); } public K getKey() { return key; } public V getValue() { return value; } private volatile int cachedSerializedSize = -1; @Override public int getSerializedSize() { if (cachedSerializedSize != -1) { return cachedSerializedSize; } int size = MapEntryLite.computeSerializedSize(metadata, key, value); cachedSerializedSize = size; return size; } @Override public void writeTo(CodedOutputStream output) throws IOException { MapEntryLite.writeTo(output, metadata, key, value); } @Override public boolean isInitialized() { return isInitialized(metadata, value); } @Override public Parser> getParserForType() { return metadata.parser; } @Override public Builder newBuilderForType() { return new Builder(metadata); } @Override public Builder toBuilder() { return new Builder(metadata, key, value); } @Override public MapEntry getDefaultInstanceForType() { return new MapEntry(metadata, metadata.defaultKey, metadata.defaultValue); } @Override public Descriptor getDescriptorForType() { return metadata.descriptor; } @Override public Map getAllFields() { TreeMap result = new TreeMap(); for (final FieldDescriptor field : metadata.descriptor.getFields()) { if (hasField(field)) { result.put(field, getField(field)); } } return Collections.unmodifiableMap(result); } private void checkFieldDescriptor(FieldDescriptor field) { if (field.getContainingType() != metadata.descriptor) { throw new RuntimeException( "Wrong FieldDescriptor \"" + field.getFullName() + "\" used in message \"" + metadata.descriptor.getFullName()); } } @Override public boolean hasField(FieldDescriptor field) { checkFieldDescriptor(field);; // A MapEntry always contains two fields. return true; } @Override public Object getField(FieldDescriptor field) { checkFieldDescriptor(field); Object result = field.getNumber() == 1 ? getKey() : getValue(); // Convert enums to EnumValueDescriptor. if (field.getType() == FieldDescriptor.Type.ENUM) { result = field.getEnumType().findValueByNumberCreatingIfUnknown( (java.lang.Integer) result); } return result; } @Override public int getRepeatedFieldCount(FieldDescriptor field) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public Object getRepeatedField(FieldDescriptor field, int index) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public UnknownFieldSet getUnknownFields() { return UnknownFieldSet.getDefaultInstance(); } /** * Builder to create {@link MapEntry} messages. */ public static class Builder extends AbstractMessage.Builder> { private final Metadata metadata; private K key; private V value; private Builder(Metadata metadata) { this(metadata, metadata.defaultKey, metadata.defaultValue); } private Builder(Metadata metadata, K key, V value) { this.metadata = metadata; this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } public Builder setKey(K key) { this.key = key; return this; } public Builder clearKey() { this.key = metadata.defaultKey; return this; } public Builder setValue(V value) { this.value = value; return this; } public Builder clearValue() { this.value = metadata.defaultValue; return this; } @Override public MapEntry build() { MapEntry result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public MapEntry buildPartial() { return new MapEntry(metadata, key, value); } @Override public Descriptor getDescriptorForType() { return metadata.descriptor; } private void checkFieldDescriptor(FieldDescriptor field) { if (field.getContainingType() != metadata.descriptor) { throw new RuntimeException( "Wrong FieldDescriptor \"" + field.getFullName() + "\" used in message \"" + metadata.descriptor.getFullName()); } } @Override public Message.Builder newBuilderForField(FieldDescriptor field) { checkFieldDescriptor(field);; // This method should be called for message fields and in a MapEntry // message only the value field can possibly be a message field. if (field.getNumber() != 2 || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { throw new RuntimeException( "\"" + field.getFullName() + "\" is not a message value field."); } return ((Message) value).newBuilderForType(); } @SuppressWarnings("unchecked") @Override public Builder setField(FieldDescriptor field, Object value) { checkFieldDescriptor(field); if (field.getNumber() == 1) { setKey((K) value); } else { if (field.getType() == FieldDescriptor.Type.ENUM) { value = ((EnumValueDescriptor) value).getNumber(); } else if (field.getType() == FieldDescriptor.Type.MESSAGE) { if (value != null && !metadata.defaultValue.getClass().isInstance(value)) { // The value is not the exact right message type. However, if it // is an alternative implementation of the same type -- e.g. a // DynamicMessage -- we should accept it. In this case we can make // a copy of the message. value = ((Message) metadata.defaultValue).toBuilder().mergeFrom((Message) value).build(); } } setValue((V) value); } return this; } @Override public Builder clearField(FieldDescriptor field) { checkFieldDescriptor(field); if (field.getNumber() == 1) { clearKey(); } else { clearValue(); } return this; } @Override public Builder setRepeatedField(FieldDescriptor field, int index, Object value) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public Builder addRepeatedField(FieldDescriptor field, Object value) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public Builder setUnknownFields(UnknownFieldSet unknownFields) { // Unknown fields are discarded for MapEntry message. return this; } @Override public MapEntry getDefaultInstanceForType() { return new MapEntry(metadata, metadata.defaultKey, metadata.defaultValue); } @Override public boolean isInitialized() { return MapEntry.isInitialized(metadata, value); } @Override public Map getAllFields() { final TreeMap result = new TreeMap(); for (final FieldDescriptor field : metadata.descriptor.getFields()) { if (hasField(field)) { result.put(field, getField(field)); } } return Collections.unmodifiableMap(result); } @Override public boolean hasField(FieldDescriptor field) { checkFieldDescriptor(field); return true; } @Override public Object getField(FieldDescriptor field) { checkFieldDescriptor(field); Object result = field.getNumber() == 1 ? getKey() : getValue(); // Convert enums to EnumValueDescriptor. if (field.getType() == FieldDescriptor.Type.ENUM) { result = field.getEnumType().findValueByNumberCreatingIfUnknown((Integer) result); } return result; } @Override public int getRepeatedFieldCount(FieldDescriptor field) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public Object getRepeatedField(FieldDescriptor field, int index) { throw new RuntimeException( "There is no repeated field in a map entry message."); } @Override public UnknownFieldSet getUnknownFields() { return UnknownFieldSet.getDefaultInstance(); } @Override @SuppressWarnings("unchecked") public Builder clone() { return new Builder(metadata, key, value); } } private static boolean isInitialized(Metadata metadata, V value) { if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) { return ((MessageLite) value).isInitialized(); } return true; } }