diff options
Diffstat (limited to 'java/src/main/java/com/google/protobuf/GeneratedMessage.java')
-rw-r--r-- | java/src/main/java/com/google/protobuf/GeneratedMessage.java | 1219 |
1 files changed, 1219 insertions, 0 deletions
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java new file mode 100644 index 00000000..957965b7 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -0,0 +1,1219 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * All generated protocol message classes extend this class. This class + * implements most of the Message and Builder interfaces using Java reflection. + * Users can ignore this class and pretend that generated messages implement + * the Message interface directly. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class GeneratedMessage extends AbstractMessage { + protected GeneratedMessage() {} + + private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + + public Descriptor getDescriptorForType() { + return internalGetFieldAccessorTable().descriptor; + } + + /** Internal helper which returns a mutable map. */ + private final Map<FieldDescriptor, Object> getAllFieldsMutable() { + TreeMap<FieldDescriptor, Object> result = + new TreeMap<FieldDescriptor, Object>(); + Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + for (FieldDescriptor field : descriptor.getFields()) { + if (field.isRepeated()) { + List value = (List)getField(field); + if (!value.isEmpty()) { + result.put(field, value); + } + } else { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + } + return result; + } + + public Map<FieldDescriptor, Object> getAllFields() { + return Collections.unmodifiableMap(getAllFieldsMutable()); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).has(this); + } + + public Object getField(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).get(this); + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field) + .getRepeatedCount(this); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + return internalGetFieldAccessorTable().getField(field) + .getRepeated(this, index); + } + + public final UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + @SuppressWarnings("unchecked") + public abstract static class Builder <BuilderType extends Builder> + extends AbstractMessage.Builder<BuilderType> { + protected Builder() {} + + /** + * Get the message being built. We don't just pass this to the + * constructor because it becomes null when build() is called. + */ + protected abstract GeneratedMessage internalGetResult(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + private FieldAccessorTable internalGetFieldAccessorTable() { + 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() { + return internalGetResult().getAllFields(); + } + + public Message.Builder newBuilderForField( + Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).newBuilder(); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetResult().hasField(field); + } + + public Object getField(Descriptors.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. + return Collections.unmodifiableList( + (List)internalGetResult().getField(field)); + } else { + return internalGetResult().getField(field); + } + } + + public BuilderType setField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).set(this, value); + return (BuilderType)this; + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + internalGetFieldAccessorTable().getField(field).clear(this); + return (BuilderType)this; + } + + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return internalGetResult().getRepeatedFieldCount(field); + } + + public Object getRepeatedField(Descriptors.FieldDescriptor field, + int index) { + return internalGetResult().getRepeatedField(field, index); + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + internalGetFieldAccessorTable().getField(field) + .setRepeated(this, index, value); + return (BuilderType)this; + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).addRepeated(this, value); + return (BuilderType)this; + } + + public final UnknownFieldSet getUnknownFields() { + return internalGetResult().unknownFields; + } + + public final BuilderType setUnknownFields(UnknownFieldSet unknownFields) { + internalGetResult().unknownFields = unknownFields; + return (BuilderType)this; + } + + public final BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + GeneratedMessage result = internalGetResult(); + result.unknownFields = + UnknownFieldSet.newBuilder(result.unknownFields) + .mergeFrom(unknownFields) + .build(); + return (BuilderType)this; + } + + public boolean isInitialized() { + return internalGetResult().isInitialized(); + } + + /** + * 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 { + return unknownFields.mergeFieldFrom(tag, input); + } + + protected <T> void addAll(Iterable<T> values, Collection<? super T> list) { + if (values instanceof Collection) { + @SuppressWarnings("unsafe") + Collection<T> collection = (Collection<T>) values; + list.addAll(collection); + } else { + for (T value : values) { + list.add(value); + } + } + } + } + + // ================================================================= + // Extensions-related stuff + + /** + * Generated message classes for message types that contain extension ranges + * subclass this. + * + * <p>This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "has", "get", and "getCount" -- but for extensions. The extensions + * are identified using instances of the class {@link GeneratedExtension}; + * the protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + * <p>For example, imagine you have the {@code .proto} file: + * + * <pre> + * option java_class = "MyProto"; + * + * message Foo { + * extensions 1000 to max; + * } + * + * extend Foo { + * optional int32 bar; + * } + * </pre> + * + * <p>Then you might write code like: + * + * <pre> + * MyProto.Foo foo = getFoo(); + * int i = foo.getExtension(MyProto.bar); + * </pre> + * + * <p>See also {@link ExtendableBuilder}. + */ + public abstract static class ExtendableMessage< + MessageType extends ExtendableMessage> + extends GeneratedMessage { + protected ExtendableMessage() {} + private final FieldSet extensions = FieldSet.newFieldSet(); + + private final void verifyExtensionContainingType( + GeneratedExtension<MessageType, ?> extension) { + if (extension.getDescriptor().getContainingType() != + getDescriptorForType()) { + // This can only happen if someone uses unchecked operations. + throw new IllegalArgumentException( + "Extension is for type \"" + + extension.getDescriptor().getContainingType().getFullName() + + "\" which does not match message type \"" + + getDescriptorForType().getFullName() + "\"."); + } + } + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + 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) { + verifyExtensionContainingType(extension); + return extensions.getRepeatedFieldCount(extension.getDescriptor()); + } + + /** Get the value of an extension. */ + @SuppressWarnings("unchecked") + public final <Type> Type getExtension( + GeneratedExtension<MessageType, Type> extension) { + verifyExtensionContainingType(extension); + Object value = extensions.getField(extension.getDescriptor()); + if (value == null) { + return (Type)extension.getMessageDefaultInstance(); + } else { + return (Type)extension.fromReflectionType(value); + } + } + + /** Get one element of a repeated extension. */ + @SuppressWarnings("unchecked") + public final <Type> Type getExtension( + GeneratedExtension<MessageType, List<Type>> extension, int index) { + verifyExtensionContainingType(extension); + return (Type)extension.singularFromReflectionType( + extensions.getRepeatedField(extension.getDescriptor(), 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. + + final Iterator<Map.Entry<FieldDescriptor, Object>> iter = + extensions.iterator(); + Map.Entry<FieldDescriptor, Object> next = null; + + private ExtensionWriter() { + if (iter.hasNext()) { + next = iter.next(); + } + } + + public void writeUntil(int end, CodedOutputStream output) + throws IOException { + while (next != null && next.getKey().getNumber() < end) { + extensions.writeField(next.getKey(), next.getValue(), output); + if (iter.hasNext()) { + next = iter.next(); + } else { + next = null; + } + } + } + } + + protected ExtensionWriter newExtensionWriter() { + return new ExtensionWriter(); + } + + /** Called by subclasses to compute the size of extensions. */ + protected int extensionsSerializedSize() { + return extensions.getSerializedSize(); + } + + // --------------------------------------------------------------- + // Reflection + + public Map<Descriptors.FieldDescriptor, Object> getAllFields() { + Map<FieldDescriptor, Object> result = super.getAllFieldsMutable(); + result.putAll(extensions.getAllFields()); + return Collections.unmodifiableMap(result); + } + + public boolean hasField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.hasField(field); + } else { + return super.hasField(field); + } + } + + public Object getField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + 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()); + } else { + return value; + } + } else { + return super.getField(field); + } + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedFieldCount(field); + } else { + return super.getRepeatedFieldCount(field); + } + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedField(field, index); + } else { + return super.getRepeatedField(field, index); + } + } + + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + } + + /** + * Generated message builders for message types that contain extension ranges + * subclass this. + * + * <p>This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "get", "set", and "add" -- but for extensions. The extensions are + * identified using instances of the class {@link GeneratedExtension}; the + * protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + * <p>For example, imagine you have the {@code .proto} file: + * + * <pre> + * option java_class = "MyProto"; + * + * message Foo { + * extensions 1000 to max; + * } + * + * extend Foo { + * optional int32 bar; + * } + * </pre> + * + * <p>Then you might write code like: + * + * <pre> + * MyProto.Foo foo = + * MyProto.Foo.newBuilder() + * .setExtension(MyProto.bar, 123) + * .build(); + * </pre> + * + * <p>See also {@link ExtendableMessage}. + */ + @SuppressWarnings("unchecked") + public abstract static class ExtendableBuilder< + MessageType extends ExtendableMessage, + BuilderType extends ExtendableBuilder> + extends GeneratedMessage.Builder<BuilderType> { + protected ExtendableBuilder() {} + protected abstract ExtendableMessage<MessageType> internalGetResult(); + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + 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) { + return internalGetResult().getExtensionCount(extension); + } + + /** Get the value of an extension. */ + public final <Type> Type getExtension( + 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) { + 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(); + message.verifyExtensionContainingType(extension); + message.extensions.setField(extension.getDescriptor(), + 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(); + message.verifyExtensionContainingType(extension); + message.extensions.setRepeatedField( + extension.getDescriptor(), 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(); + message.verifyExtensionContainingType(extension); + message.extensions.addRepeatedField( + extension.getDescriptor(), extension.singularToReflectionType(value)); + return (BuilderType)this; + } + + /** Clear an extension. */ + public final <Type> BuilderType clearExtension( + GeneratedExtension<MessageType, ?> extension) { + ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.getDescriptor()); + 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. + */ + protected boolean parseUnknownField(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + int tag) + throws IOException { + ExtendableMessage<MessageType> message = internalGetResult(); + return message.extensions.mergeFieldFrom( + input, unknownFields, extensionRegistry, this, tag); + } + + // --------------------------------------------------------------- + // Reflection + + // 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) { + if (field.isExtension()) { + ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setField(field, value); + return (BuilderType)this; + } else { + return super.setField(field, value); + } + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + if (field.isExtension()) { + ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.clearField(field); + return (BuilderType)this; + } else { + return super.clearField(field); + } + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + if (field.isExtension()) { + ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setRepeatedField(field, index, value); + return (BuilderType)this; + } else { + return super.setRepeatedField(field, index, value); + } + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + if (field.isExtension()) { + ExtendableMessage<MessageType> message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.addRepeatedField(field, value); + return (BuilderType)this; + } else { + return super.addRepeatedField(field, value); + } + } + } + + // ----------------------------------------------------------------- + + /** For use by generated code only. */ + public static <ContainingType extends Message, Type> + GeneratedExtension<ContainingType, Type> + newGeneratedExtension(FieldDescriptor descriptor, Class<Type> type) { + if (descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newRepeatedGeneratedExtension() for repeated types."); + } + return new GeneratedExtension<ContainingType, Type>(descriptor, type); + } + + /** For use by generated code only. */ + public static <ContainingType extends Message, Type> + GeneratedExtension<ContainingType, List<Type>> + newRepeatedGeneratedExtension( + FieldDescriptor descriptor, Class<Type> type) { + if (!descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newGeneratedExtension() for non-repeated types."); + } + return new GeneratedExtension<ContainingType, List<Type>>(descriptor, type); + } + + /** + * Type used to represent generated extensions. The protocol compiler + * generates a static singleton instance of this class for each extension. + * + * <p>For example, imagine you have the {@code .proto} file: + * + * <pre> + * option java_class = "MyProto"; + * + * message Foo { + * extensions 1000 to max; + * } + * + * extend Foo { + * optional int32 bar; + * } + * </pre> + * + * <p>Then, {@code MyProto.Foo.bar} has type + * {@code GeneratedExtension<MyProto.Foo, Integer>}. + * + * <p>In general, users should ignore the details of this type, and simply use + * these static singletons as parameters to the extension accessors defined + * in {@link ExtendableMessage} and {@link ExtendableBuilder}. + */ + public static final class GeneratedExtension< + ContainingType extends Message, Type> { + // TODO(kenton): Find ways to avoid using Java reflection within this + // class. Also try to avoid suppressing unchecked warnings. + + private GeneratedExtension(FieldDescriptor descriptor, Class type) { + if (!descriptor.isExtension()) { + throw new IllegalArgumentException( + "GeneratedExtension given a regular (non-extension) field."); + } + + this.descriptor = descriptor; + this.type = type; + + switch (descriptor.getJavaType()) { + case MESSAGE: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = + (Message)invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), + null); + break; + case ENUM: + enumValueOf = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor"); + messageDefaultInstance = null; + break; + default: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = null; + break; + } + } + + private final FieldDescriptor descriptor; + private final Class type; + private final Method enumValueOf; + private final Method enumGetValueDescriptor; + private final Message messageDefaultInstance; + + public FieldDescriptor getDescriptor() { return descriptor; } + + /** + * If the extension is an embedded message or group, returns the default + * instance of the message. + */ + @SuppressWarnings("unchecked") + public Message getMessageDefaultInstance() { + return messageDefaultInstance; + } + + /** + * Convert from the type used by the reflection accessors to the type used + * by native accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object fromReflectionType(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) { + result.add(singularFromReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularFromReflectionType(value); + } + } + + /** + * Like {@link #fromReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularFromReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case MESSAGE: + if (type.isInstance(value)) { + return value; + } else { + // It seems the copy of the embedded message stored inside the + // extended message is not of the exact type the user was + // expecting. This can happen if a user defines a + // GeneratedExtension manually and gives it a different type. + // This should not happen in normal use. But, to be nice, we'll + // copy the message to whatever type the caller was expecting. + return messageDefaultInstance.newBuilderForType() + .mergeFrom((Message)value).build(); + } + case ENUM: + return invokeOrDie(enumValueOf, null, (EnumValueDescriptor)value); + default: + return value; + } + } + + /** + * Convert from the type used by the native accessors to the type used + * by reflection accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object toReflectionType(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) { + result.add(singularToReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularToReflectionType(value); + } + } + + /** + * Like {@link #toReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularToReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case ENUM: + return invokeOrDie(enumGetValueDescriptor, value); + default: + return value; + } + } + } + + // ================================================================= + + /** Calls Class.getMethod and throws a RuntimeException if it fails. */ + @SuppressWarnings("unchecked") + private static Method getMethodOrDie( + Class clazz, String name, Class... params) { + try { + return clazz.getMethod(name, params); + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Generated message class \"" + clazz.getName() + + "\" missing method \"" + name + "\".", e); + } + } + + /** Calls invoke and throws a RuntimeException if it fails. */ + private static Object invokeOrDie( + Method method, Object object, Object... params) { + try { + return method.invoke(object, params); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "Couldn't use Java reflection to implement protocol message " + + "reflection.", e); + } catch (java.lang.reflect.InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else if (cause instanceof Error) { + throw (Error)cause; + } else { + throw new RuntimeException( + "Unexpected exception thrown by generated accessor method.", cause); + } + } + } + + /** + * Users should ignore this class. This class provides the implementation + * with access to the fields of a message object using Java reflection. + */ + public static final class FieldAccessorTable { + /** + * Construct a FieldAccessorTable for a particular message class. Only + * one FieldAccessorTable should ever be constructed per class. + * + * @param descriptor The type's descriptor. + * @param camelCaseNames The camelcase names of all fields in the message. + * These are used to derive the accessor method names. + * @param messageClass The message type. + * @param builderClass The builder type. + */ + public FieldAccessorTable( + Descriptor descriptor, + String[] camelCaseNames, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.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); + if (field.isRepeated()) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new RepeatedMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new RepeatedEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new RepeatedFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } else { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new SingularMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new SingularEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new SingularFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } + } + } + + private final Descriptor descriptor; + private final FieldAccessor[] fields; + + /** Get the FieldAccessor for a particular field. */ + private FieldAccessor getField(FieldDescriptor field) { + if (field.getContainingType() != descriptor) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } else if (field.isExtension()) { + // If this type had extensions, it would subclass ExtendableMessage, + // which overrides the reflection interface to handle extensions. + throw new IllegalArgumentException( + "This type does not have extensions."); + } + return fields[field.getIndex()]; + } + + /** + * Abstract interface that provides access to a single field. This is + * implemented differently depending on the field type and cardinality. + */ + private static interface FieldAccessor { + Object get(GeneratedMessage message); + void set(GeneratedMessage.Builder builder, Object value); + Object getRepeated(GeneratedMessage message, int index); + void setRepeated(GeneratedMessage.Builder builder, + int index, Object value); + void addRepeated(GeneratedMessage.Builder builder, Object value); + boolean has(GeneratedMessage message); + int getRepeatedCount(GeneratedMessage message); + void clear(GeneratedMessage.Builder builder); + Message.Builder newBuilder(); + } + + // --------------------------------------------------------------- + + private static class SingularFieldAccessor implements FieldAccessor { + SingularFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.Builder> builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); + type = getMethod.getReturnType(); + setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); + hasMethod = + getMethodOrDie(messageClass, "has" + camelCaseName); + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + // 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; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(setMethod, builder, value); + } + public Object getRepeated(GeneratedMessage message, int index) { + throw new UnsupportedOperationException( + "getRepeatedField() called on a singular field."); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + throw new UnsupportedOperationException( + "setRepeatedField() called on a singular field."); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + throw new UnsupportedOperationException( + "addRepeatedField() called on a singular field."); + } + public boolean has(GeneratedMessage message) { + return (Boolean)invokeOrDie(hasMethod, message); + } + public int getRepeatedCount(GeneratedMessage message) { + throw new UnsupportedOperationException( + "getRepeatedFieldSize() called on a singular field."); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + 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"); + + getRepeatedMethod = + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + type = getRepeatedMethod.getReturnType(); + setRepeatedMethod = + getMethodOrDie(builderClass, "set" + camelCaseName, + Integer.TYPE, type); + addRepeatedMethod = + getMethodOrDie(builderClass, "add" + camelCaseName, type); + getCountMethod = + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + Class type; + Method getMethod; + Method getRepeatedMethod; + Method setRepeatedMethod; + Method addRepeatedMethod; + Method getCountMethod; + Method clearMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, 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) { + addRepeated(builder, element); + } + } + public Object getRepeated(GeneratedMessage message, int index) { + return invokeOrDie(getRepeatedMethod, message, index); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + invokeOrDie(setRepeatedMethod, builder, index, value); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(addRepeatedMethod, builder, value); + } + public boolean has(GeneratedMessage message) { + throw new UnsupportedOperationException( + "hasField() called on a singular field."); + } + public int getRepeatedCount(GeneratedMessage message) { + return (Integer)invokeOrDie(getCountMethod, message); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + // --------------------------------------------------------------- + + private static final class SingularEnumFieldAccessor + extends SingularFieldAccessor { + SingularEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getValueDescriptorMethod, super.get(message)); + } + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + private static final class RepeatedEnumFieldAccessor + extends RepeatedFieldAccessor { + RepeatedEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + @SuppressWarnings("unchecked") + public Object get(GeneratedMessage message) { + List newList = new ArrayList(); + for (Object element : (List)super.get(message)) { + newList.add(invokeOrDie(getValueDescriptorMethod, element)); + } + return Collections.unmodifiableList(newList); + } + public Object getRepeated(GeneratedMessage message, 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)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + // --------------------------------------------------------------- + + private static final class SingularMessageFieldAccessor + extends SingularFieldAccessor { + SingularMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // 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. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + + private static final class RepeatedMessageFieldAccessor + extends RepeatedFieldAccessor { + RepeatedMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class<? extends GeneratedMessage> messageClass, + Class<? extends GeneratedMessage.Builder> builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // 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. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + super.setRepeated(builder, index, coerceType(value)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + } +} |