content: /* Copyright (C) 2016-2018 Alibaba Group Holding Limited
  
  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.
  ==============================================================================*/
  
  // 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.FieldDescriptor;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * A table of known extensions, searchable by name or field number.  When
   * parsing a protocol message that might have extensions, you must provide
   * an {@code ExtensionRegistry} in which you have registered any extensions
   * that you want to be able to parse.  Otherwise, those extensions will just
   * be treated like unknown fields.
   *
   * <p>For example, if you had the {@code .proto} file:
   *
   * <pre>
   * option java_class = "MyProto";
   *
   * message Foo {
   *   extensions 1000 to max;
   * }
   *
   * extend Foo {
   *   optional int32 bar;
   * }
   * </pre>
   *
   * Then you might write code like:
   *
   * <pre>
   * ExtensionRegistry registry = ExtensionRegistry.newInstance();
   * registry.add(MyProto.bar);
   * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
   * </pre>
   *
   * <p>Background:
   *
   * <p>You might wonder why this is necessary.  Two alternatives might come to
   * mind.  First, you might imagine a system where generated extensions are
   * automatically registered when their containing classes are loaded.  This
   * is a popular technique, but is bad design; among other things, it creates a
   * situation where behavior can change depending on what classes happen to be
   * loaded.  It also introduces a security vulnerability, because an
   * unprivileged class could cause its code to be called unexpectedly from a
   * privileged class by registering itself as an extension of the right type.
   *
   * <p>Another option you might consider is lazy parsing: do not parse an
   * extension until it is first requested, at which point the caller must
   * provide a type to use.  This introduces a different set of problems.  First,
   * it would require a mutex lock any time an extension was accessed, which
   * would be slow.  Second, corrupt data would not be detected until first
   * access, at which point it would be much harder to deal with it.  Third, it
   * could violate the expectation that message objects are immutable, since the
   * type provided could be any arbitrary message class.  An unprivileged user
   * could take advantage of this to inject a mutable object into a message
   * belonging to privileged code and create mischief.
   *
   * @author kenton@google.com Kenton Varda
   */
  public class ExtensionRegistry extends ExtensionRegistryLite {
    /** Construct a new, empty instance. */
    public static ExtensionRegistry newInstance() {
      return new ExtensionRegistry();
    }
  
    /** Get the unmodifiable singleton empty instance. */
    public static ExtensionRegistry getEmptyRegistry() {
      return EMPTY_REGISTRY;
    }
  
  
    /** Returns an unmodifiable view of the registry. */
    @Override
    public ExtensionRegistry getUnmodifiable() {
      return new ExtensionRegistry(this);
    }
  
    /** A (Descriptor, Message) pair, returned by lookup methods. */
    public static final class ExtensionInfo {
      /** The extension's descriptor. */
      public final FieldDescriptor descriptor;
  
      /**
       * A default instance of the extension's type, if it has a message type.
       * Otherwise, {@code null}.
       */
      public final Message defaultInstance;
  
      private ExtensionInfo(final FieldDescriptor descriptor) {
        this.descriptor = descriptor;
        defaultInstance = null;
      }
      private ExtensionInfo(final FieldDescriptor descriptor,
                            final Message defaultInstance) {
        this.descriptor = descriptor;
        this.defaultInstance = defaultInstance;
      }
    }
  
    /**
     * Deprecated. Use {@link #findImmutableExtensionByName(String)} instead.
     */
    public ExtensionInfo findExtensionByName(final String fullName) {
      return findImmutableExtensionByName(fullName);
    }
  
    /**
     * Find an extension for immutable APIs by fully-qualified field name,
     * in the proto namespace. i.e. {@code result.descriptor.fullName()} will
     * match {@code fullName} if a match is found.
     *
     * @return Information about the extension if found, or {@code null}
     *         otherwise.
     */
    public ExtensionInfo findImmutableExtensionByName(final String fullName) {
      return immutableExtensionsByName.get(fullName);
    }
  
    /**
     * Find an extension for mutable APIs by fully-qualified field name,
     * in the proto namespace. i.e. {@code result.descriptor.fullName()} will
     * match {@code fullName} if a match is found.
     *
     * @return Information about the extension if found, or {@code null}
     *         otherwise.
     */
    public ExtensionInfo findMutableExtensionByName(final String fullName) {
      return mutableExtensionsByName.get(fullName);
    }
  
    /**
     * Deprecated. Use {@link #findImmutableExtensionByNumber(
     * Descriptors.Descriptor, int)}
     */
    public ExtensionInfo findExtensionByNumber(
        final Descriptor containingType, final int fieldNumber) {
      return findImmutableExtensionByNumber(containingType, fieldNumber);
    }
  
    /**
     * Find an extension by containing type and field number for immutable APIs.
     *
     * @return Information about the extension if found, or {@code null}
     *         otherwise.
     */
    public ExtensionInfo findImmutableExtensionByNumber(
        final Descriptor containingType, final int fieldNumber) {
      return immutableExtensionsByNumber.get(
        new DescriptorIntPair(containingType, fieldNumber));
    }
  
    /**
     * Find an extension by containing type and field number for mutable APIs.
     *
     * @return Information about the extension if found, or {@code null}
     *         otherwise.
     */
    public ExtensionInfo findMutableExtensionByNumber(
        final Descriptor containingType, final int fieldNumber) {
      return mutableExtensionsByNumber.get(
          new DescriptorIntPair(containingType, fieldNumber));
    }
  
    /**
     * Find all extensions for mutable APIs by fully-qualified name of
     * extended class. Note that this method is more computationally expensive
     * than getting a single extension by name or number.
     *
     * @return Information about the extensions found, or {@code null} if there
     *     are none.
     */
    public Set<ExtensionInfo> getAllMutableExtensionsByExtendedType(final String fullName) {
      HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
      for (DescriptorIntPair pair : mutableExtensionsByNumber.keySet()) {
        if (pair.descriptor.getFullName().equals(fullName)) {
          extensions.add(mutableExtensionsByNumber.get(pair));
        }
      }
      return extensions;
    }
  
    /**
     * Find all extensions for immutable APIs by fully-qualified name of
     * extended class. Note that this method is more computationally expensive
     * than getting a single extension by name or number.
     *
     * @return Information about the extensions found, or {@code null} if there
     *     are none.
     */
    public Set<ExtensionInfo> getAllImmutableExtensionsByExtendedType(final String fullName) {
      HashSet<ExtensionInfo> extensions = new HashSet<ExtensionInfo>();
      for (DescriptorIntPair pair : immutableExtensionsByNumber.keySet()) {
        if (pair.descriptor.getFullName().equals(fullName)) {
          extensions.add(immutableExtensionsByNumber.get(pair));
        }
      }
      return extensions;
    }
  
    /** Add an extension from a generated file to the registry. */
    public void add(final Extension<?, ?> extension) {
      if (extension.getExtensionType() != Extension.ExtensionType.IMMUTABLE &&
          extension.getExtensionType() != Extension.ExtensionType.MUTABLE) {
        // do not support other extension types. ignore
        return;
      }
      add(newExtensionInfo(extension), extension.getExtensionType());
    }
  
    /** Add an extension from a generated file to the registry. */
    public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
      add((Extension<?, ?>) extension);
    }
  
    static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
      if (extension.getDescriptor().getJavaType() ==
          FieldDescriptor.JavaType.MESSAGE) {
        if (extension.getMessageDefaultInstance() == null) {
          throw new IllegalStateException(
              "Registered message-type extension had null default instance: " +
                  extension.getDescriptor().getFullName());
        }
        return new ExtensionInfo(extension.getDescriptor(),
            (Message) extension.getMessageDefaultInstance());
      } else {
        return new ExtensionInfo(extension.getDescriptor(), null);
      }
    }
  
    /** Add a non-message-type extension to the registry by descriptor. */
    public void add(final FieldDescriptor type) {
      if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
        throw new IllegalArgumentException(
          "ExtensionRegistry.add() must be provided a default instance when " +
          "adding an embedded message extension.");
      }
      ExtensionInfo info = new ExtensionInfo(type, null);
      add(info, Extension.ExtensionType.IMMUTABLE);
      add(info, Extension.ExtensionType.MUTABLE);
    }
  
    /** Add a message-type extension to the registry by descriptor. */
    public void add(final FieldDescriptor type, final Message defaultInstance) {
      if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
        throw new IllegalArgumentException(
          "ExtensionRegistry.add() provided a default instance for a " +
          "non-message extension.");
      }
        add(new ExtensionInfo(type, defaultInstance),
            Extension.ExtensionType.IMMUTABLE);
    }
  
    // =================================================================
    // Private stuff.
  
    private ExtensionRegistry() {
      this.immutableExtensionsByName = new HashMap<String, ExtensionInfo>();
      this.mutableExtensionsByName = new HashMap<String, ExtensionInfo>();
      this.immutableExtensionsByNumber =
          new HashMap<DescriptorIntPair, ExtensionInfo>();
      this.mutableExtensionsByNumber =
          new HashMap<DescriptorIntPair, ExtensionInfo>();
    }
  
    private ExtensionRegistry(ExtensionRegistry other) {
      super(other);
      this.immutableExtensionsByName =
          Collections.unmodifiableMap(other.immutableExtensionsByName);
      this.mutableExtensionsByName =
          Collections.unmodifiableMap(other.mutableExtensionsByName);
      this.immutableExtensionsByNumber =
          Collections.unmodifiableMap(other.immutableExtensionsByNumber);
      this.mutableExtensionsByNumber =
              Collections.unmodifiableMap(other.mutableExtensionsByNumber);
    }
  
    private final Map<String, ExtensionInfo> immutableExtensionsByName;
    private final Map<String, ExtensionInfo> mutableExtensionsByName;
    private final Map<DescriptorIntPair, ExtensionInfo> immutableExtensionsByNumber;
    private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
  
    ExtensionRegistry(boolean empty) {
      super(EMPTY_REGISTRY_LITE);
      this.immutableExtensionsByName =
          Collections.<String, ExtensionInfo>emptyMap();
      this.mutableExtensionsByName =
          Collections.<String, ExtensionInfo>emptyMap();
      this.immutableExtensionsByNumber =
          Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
      this.mutableExtensionsByNumber =
              Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
    }
    static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
  
    private void add(
        final ExtensionInfo extension,
        final Extension.ExtensionType extensionType) {
      if (!extension.descriptor.isExtension()) {
        throw new IllegalArgumentException(
            "ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
                "(non-extension) field.");
      }
  
      Map<String, ExtensionInfo> extensionsByName;
      Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
      switch (extensionType) {
        case IMMUTABLE:
          extensionsByName = immutableExtensionsByName;
          extensionsByNumber = immutableExtensionsByNumber;
          break;
        case MUTABLE:
          extensionsByName = mutableExtensionsByName;
          extensionsByNumber = mutableExtensionsByNumber;
          break;
        default:
          // Ignore the unknown supported type.
          return;
      }
  
      extensionsByName.put(extension.descriptor.getFullName(), extension);
      extensionsByNumber.put(
        new DescriptorIntPair(extension.descriptor.getContainingType(),
                              extension.descriptor.getNumber()),
        extension);
  
      final FieldDescriptor field = extension.descriptor;
      if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
          field.getType() == FieldDescriptor.Type.MESSAGE &&
          field.isOptional() &&
          field.getExtensionScope() == field.getMessageType()) {
        // This is an extension of a MessageSet type defined within the extension
        // type's own scope.  For backwards-compatibility, allow it to be looked
        // up by type name.
        extensionsByName.put(field.getMessageType().getFullName(), extension);
      }
    }
  
    /** A (GenericDescriptor, int) pair, used as a map key. */
    private static final class DescriptorIntPair {
      private final Descriptor descriptor;
      private final int number;
  
      DescriptorIntPair(final Descriptor descriptor, final int number) {
        this.descriptor = descriptor;
        this.number = number;
      }
  
      @Override
      public int hashCode() {
        return descriptor.hashCode() * ((1 << 16) - 1) + number;
      }
      @Override
      public boolean equals(final Object obj) {
        if (!(obj instanceof DescriptorIntPair)) {
          return false;
        }
        final DescriptorIntPair other = (DescriptorIntPair)obj;
        return descriptor == other.descriptor && number == other.number;
      }
    }
  }
  