/*
 * Copyright (C) 2018-2021 Lightbend Inc. <https://www.lightbend.com>
 */

// 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 akka.protobuf;

import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.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
    implements Serializable {
  private static final long serialVersionUID = 1L;

  protected GeneratedMessageLite() {
  }

  protected GeneratedMessageLite(Builder builder) {
  }

  public Parser<? extends MessageLite> getParserForType() {
    throw new UnsupportedOperationException(
        "This is supposed to be overridden by subclasses.");
  }

  /**
   * Called by subclasses to parse an unknown field.
   * @return {@code true} unless the tag is an end-group tag.
   */
  protected boolean parseUnknownField(
      CodedInputStream input,
      ExtensionRegistryLite extensionRegistry,
      int tag) throws IOException {
    return input.skipField(tag);
  }

  /**
   * Used by parsing constructors in generated classes.
   */
  protected void makeExtensionsImmutable() {
    // Noop for messages without extensions.
  }

  @SuppressWarnings("unchecked")
  public abstract static class Builder<MessageType extends GeneratedMessageLite,
                                       BuilderType extends Builder>
      extends AbstractMessageLite.Builder<BuilderType> {
    protected Builder() {}

    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public BuilderType clear() {
      return (BuilderType) this;
    }

    // This is implemented here only to work around an apparent bug in the
    // Java compiler and/or build system.  See bug #1898463.  The mere presence
    // of this dummy clone() implementation makes it go away.
    @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();

    /**
     * Called by subclasses to parse an unknown field.
     * @return {@code true} unless the tag is an end-group tag.
     */
    protected boolean parseUnknownField(
        CodedInputStream input,
        ExtensionRegistryLite extensionRegistry,
        int tag) throws IOException {
      return input.skipField(tag);
    }
  }

  // =================================================================
  // Extensions-related stuff

  /**
   * Lite equivalent of {@link akka.protobuf.GeneratedMessage.ExtendableMessageOrBuilder}.
   */
  public interface ExtendableMessageOrBuilder<
      MessageType extends ExtendableMessage> extends MessageLiteOrBuilder {

    /** Check if a singular extension is present. */
    <Type> boolean hasExtension(
        GeneratedExtension<MessageType, Type> extension);

    /** Get the number of elements in a repeated extension. */
    <Type> int getExtensionCount(
        GeneratedExtension<MessageType, List<Type>> extension);

    /** Get the value of an extension. */
    <Type> Type getExtension(GeneratedExtension<MessageType, Type> extension);

    /** Get one element of a repeated extension. */
    <Type> Type getExtension(
        GeneratedExtension<MessageType, List<Type>> extension,
        int index);
  }

  /**
   * Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
   */
  public abstract static class ExtendableMessage<
        MessageType extends ExtendableMessage<MessageType>>
      extends GeneratedMessageLite
      implements ExtendableMessageOrBuilder<MessageType> {

    private final FieldSet<ExtensionDescriptor> extensions;

    protected ExtendableMessage() {
      this.extensions = FieldSet.newFieldSet();
    }

    protected ExtendableMessage(ExtendableBuilder<MessageType, ?> builder) {
      this.extensions = builder.buildExtensions();
    }

    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. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public final <Type> boolean hasExtension(
        final GeneratedExtension<MessageType, Type> extension) {
      verifyExtensionContainingType(extension);
      return extensions.hasField(extension.descriptor);
    }

    /** Get the number of elements in a repeated extension. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public final <Type> int getExtensionCount(
        final GeneratedExtension<MessageType, List<Type>> extension) {
      verifyExtensionContainingType(extension);
      return extensions.getRepeatedFieldCount(extension.descriptor);
    }

    /** Get the value of an extension. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    @SuppressWarnings("unchecked")
    public final <Type> Type getExtension(
        final GeneratedExtension<MessageType, Type> extension) {
      verifyExtensionContainingType(extension);
      final Object value = extensions.getField(extension.descriptor);
      if (value == null) {
        return extension.defaultValue;
      } else {
        return (Type) value;
      }
    }

    /** Get one element of a repeated extension. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    @SuppressWarnings("unchecked")
    public final <Type> Type getExtension(
        final GeneratedExtension<MessageType, List<Type>> extension,
        final int index) {
      verifyExtensionContainingType(extension);
      return (Type) extensions.getRepeatedField(extension.descriptor, index);
    }

    /** Called by subclasses to check if all extensions are initialized. */
    protected boolean extensionsAreInitialized() {
      return extensions.isInitialized();
    }

    /**
     * Called by subclasses to parse an unknown field or an extension.
     * @return {@code true} unless the tag is an end-group tag.
     */
    @Override
    protected boolean parseUnknownField(
        CodedInputStream input,
        ExtensionRegistryLite extensionRegistry,
        int tag) throws IOException {
      return GeneratedMessageLite.parseUnknownField(
          extensions,
          getDefaultInstanceForType(),
          input,
          extensionRegistry,
          tag);
    }

    /**
     * Used by parsing constructors in generated classes.
     */
    @Override
    protected void makeExtensionsImmutable() {
      extensions.makeImmutable();
    }

    /**
     * Used by subclasses to serialize extensions.  Extension ranges may be
     * interleaved with field numbers, but we must write them in canonical
     * (sorted by field number) order.  ExtensionWriter helps us write
     * 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>
      implements ExtendableMessageOrBuilder<MessageType> {
    protected ExtendableBuilder() {}

    private FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
    private boolean extensionsIsMutable;

    @Override
    public BuilderType clear() {
      extensions.clear();
      extensionsIsMutable = false;
      return super.clear();
    }

    private void ensureExtensionsIsMutable() {
      if (!extensionsIsMutable) {
        extensions = extensions.clone();
        extensionsIsMutable = true;
      }
    }

    /**
     * Called by the build code path to create a copy of the extensions for
     * building the message.
     */
    private FieldSet<ExtensionDescriptor> buildExtensions() {
      extensions.makeImmutable();
      extensionsIsMutable = false;
      return extensions;
    }

    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. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public final <Type> boolean hasExtension(
        final GeneratedExtension<MessageType, Type> extension) {
      verifyExtensionContainingType(extension);
      return extensions.hasField(extension.descriptor);
    }

    /** Get the number of elements in a repeated extension. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public final <Type> int getExtensionCount(
        final GeneratedExtension<MessageType, List<Type>> extension) {
      verifyExtensionContainingType(extension);
      return extensions.getRepeatedFieldCount(extension.descriptor);
    }

    /** Get the value of an extension. */
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    @SuppressWarnings("unchecked")
    public final <Type> Type getExtension(
        final GeneratedExtension<MessageType, Type> extension) {
      verifyExtensionContainingType(extension);
      final Object value = extensions.getField(extension.descriptor);
      if (value == null) {
        return extension.defaultValue;
      } else {
        return (Type) value;
      }
    }

    /** Get one element of a repeated extension. */
    @SuppressWarnings("unchecked")
    //@Override (Java 1.6 override semantics, but we must support 1.5)
    public final <Type> Type getExtension(
        final GeneratedExtension<MessageType, List<Type>> extension,
        final int index) {
      verifyExtensionContainingType(extension);
      return (Type) extensions.getRepeatedField(extension.descriptor, index);
    }

    // This is implemented here only to work around an apparent bug in the
    // Java compiler and/or build system.  See bug #1898463.  The mere presence
    // of this dummy clone() implementation makes it go away.
    @Override
    public BuilderType clone() {
      throw new UnsupportedOperationException(
          "This is supposed to be overridden by subclasses.");
    }

    /** Set the value of an extension. */
    public final <Type> BuilderType setExtension(
        final GeneratedExtension<MessageType, Type> extension,
        final Type value) {
      verifyExtensionContainingType(extension);
      ensureExtensionsIsMutable();
      extensions.setField(extension.descriptor, 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) {
      verifyExtensionContainingType(extension);
      ensureExtensionsIsMutable();
      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) {
      verifyExtensionContainingType(extension);
      ensureExtensionsIsMutable();
      extensions.addRepeatedField(extension.descriptor, value);
      return (BuilderType) this;
    }

    /** Clear an extension. */
    public final <Type> BuilderType clearExtension(
        final GeneratedExtension<MessageType, ?> extension) {
      verifyExtensionContainingType(extension);
      ensureExtensionsIsMutable();
      extensions.clearField(extension.descriptor);
      return (BuilderType) this;
    }

    /** Called by subclasses to check if all extensions are initialized. */
    protected boolean extensionsAreInitialized() {
      return extensions.isInitialized();
    }

    /**
     * Called by subclasses to parse an unknown field or an extension.
     * @return {@code true} unless the tag is an end-group tag.
     */
    @Override
    protected boolean parseUnknownField(
        CodedInputStream input,
        ExtensionRegistryLite extensionRegistry,
        int tag) throws IOException {
      ensureExtensionsIsMutable();
      return GeneratedMessageLite.parseUnknownField(
          extensions,
          getDefaultInstanceForType(),
          input,
          extensionRegistry,
          tag);
    }

    protected final void mergeExtensionFields(final MessageType other) {
      ensureExtensionsIsMutable();
      extensions.mergeFrom(((ExtendableMessage) other).extensions);
    }
  }

  // -----------------------------------------------------------------

  /**
   * Parse an unknown field or an extension.
   * @return {@code true} unless the tag is an end-group tag.
   */
  private static <MessageType extends MessageLite>
      boolean parseUnknownField(
          FieldSet<ExtensionDescriptor> extensions,
          MessageType defaultInstance,
          CodedInputStream input,
          ExtensionRegistryLite extensionRegistry,
          int tag) throws IOException {
    int wireType = WireFormat.getTagWireType(tag);
    int fieldNumber = WireFormat.getTagFieldNumber(tag);

    GeneratedExtension<MessageType, ?> extension =
      extensionRegistry.findLiteExtensionByNumber(
          defaultInstance, fieldNumber);

    boolean unknown = false;
    boolean packed = false;
    if (extension == null) {
      unknown = true;  // Unknown field.
    } else if (wireType == FieldSet.getWireFormatForFieldType(
                 extension.descriptor.getLiteType(),
                 false  /* isPacked */)) {
      packed = false;  // Normal, unpacked value.
    } else if (extension.descriptor.isRepeated &&
               extension.descriptor.type.isPackable() &&
               wireType == FieldSet.getWireFormatForFieldType(
                 extension.descriptor.getLiteType(),
                 true  /* isPacked */)) {
      packed = true;  // Packed value.
    } else {
      unknown = true;  // Wrong wire type.
    }

    if (unknown) {  // Unknown field or wrong wire type.  Skip.
      return input.skipField(tag);
    }

    if (packed) {
      int length = input.readRawVarint32();
      int limit = input.pushLimit(length);
      if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) {
        while (input.getBytesUntilLimit() > 0) {
          int rawValue = input.readEnum();
          Object value =
              extension.descriptor.getEnumType().findValueByNumber(rawValue);
          if (value == null) {
            // If the number isn't recognized as a valid value for this
            // enum, drop it (don't even add it to unknownFields).
            return true;
          }
          extensions.addRepeatedField(extension.descriptor, value);
        }
      } else {
        while (input.getBytesUntilLimit() > 0) {
          Object value =
            FieldSet.readPrimitiveField(input,
                                        extension.descriptor.getLiteType());
          extensions.addRepeatedField(extension.descriptor, value);
        }
      }
      input.popLimit(limit);
    } else {
      Object value;
      switch (extension.descriptor.getLiteJavaType()) {
        case MESSAGE: {
          MessageLite.Builder subBuilder = null;
          if (!extension.descriptor.isRepeated()) {
            MessageLite existingValue =
                (MessageLite) extensions.getField(extension.descriptor);
            if (existingValue != null) {
              subBuilder = existingValue.toBuilder();
            }
          }
          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:
          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;
  }

  // -----------------------------------------------------------------

  /** For use by generated code only. */
  public static <ContainingType extends MessageLite, Type>
      GeneratedExtension<ContainingType, Type>
          newSingularGeneratedExtension(
              final ContainingType containingTypeDefaultInstance,
              final Type defaultValue,
              final MessageLite messageDefaultInstance,
              final Internal.EnumLiteMap<?> enumTypeMap,
              final int number,
              final WireFormat.FieldType type) {
    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, Type>
          newRepeatedGeneratedExtension(
              final ContainingType containingTypeDefaultInstance,
              final MessageLite messageDefaultInstance,
              final Internal.EnumLiteMap<?> enumTypeMap,
              final int number,
              final WireFormat.FieldType type,
              final boolean isPacked) {
    @SuppressWarnings("unchecked")  // Subclasses ensure Type is a List
    Type emptyList = (Type) Collections.emptyList();
    return new GeneratedExtension<ContainingType, Type>(
        containingTypeDefaultInstance,
        emptyList,
        messageDefaultInstance,
        new ExtensionDescriptor(
            enumTypeMap, number, type, true /* isRepeated */, isPacked));
  }

  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) {
      // Defensive checks to verify the correct initialization order of
      // GeneratedExtensions and their related GeneratedMessages.
      if (containingTypeDefaultInstance == null) {
        throw new IllegalArgumentException(
            "Null containingTypeDefaultInstance");
      }
      if (descriptor.getLiteType() == WireFormat.FieldType.MESSAGE &&
          messageDefaultInstance == null) {
        throw new IllegalArgumentException(
            "Null messageDefaultInstance");
      }
      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;
    }
  }

  /**
   * A serialized (serializable) form of the generated message.  Stores the
   * message as a class name and a byte array.
   */
  static final class SerializedForm implements Serializable {
    private static final long serialVersionUID = 0L;

    private String messageClassName;
    private byte[] asBytes;

    /**
     * Creates the serialized form by calling {@link akka.protobuf.MessageLite#toByteArray}.
     * @param regularForm the message to serialize
     */
    SerializedForm(MessageLite regularForm) {
      messageClassName = regularForm.getClass().getName();
      asBytes = regularForm.toByteArray();
    }

    /**
     * When read from an ObjectInputStream, this method converts this object
     * back to the regular form.  Part of Java's serialization magic.
     * @return a GeneratedMessage of the type that was serialized
     */
    @SuppressWarnings("unchecked")
    protected Object readResolve() throws ObjectStreamException {
      try {
        Class messageClass = Class.forName(messageClassName);
        Method newBuilder = messageClass.getMethod("newBuilder");
        MessageLite.Builder builder =
            (MessageLite.Builder) newBuilder.invoke(null);
        builder.mergeFrom(asBytes);
        return builder.buildPartial();
      } catch (ClassNotFoundException e) {
        throw new RuntimeException("Unable to find proto buffer class", e);
      } catch (NoSuchMethodException e) {
        throw new RuntimeException("Unable to find newBuilder method", e);
      } catch (IllegalAccessException e) {
        throw new RuntimeException("Unable to call newBuilder method", e);
      } catch (InvocationTargetException e) {
        throw new RuntimeException("Error calling newBuilder", e.getCause());
      } catch (InvalidProtocolBufferException e) {
        throw new RuntimeException("Unable to understand proto buffer", e);
      }
    }
  }

  /**
   * Replaces this object in the output stream with a serialized form.
   * Part of Java's serialization magic.  Generated sub-classes must override
   * this method by calling {@code return super.writeReplace();}
   * @return a SerializedForm of this message
   */
  protected Object writeReplace() throws ObjectStreamException {
    return new SerializedForm(this);
  }
}
