text: #
  # This file is part of pyasn1 software.
  #
  # Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
  # License: http://snmplabs.com/pyasn1/license.html
  #
  import sys
  
  from pyasn1 import error
  from pyasn1.compat import calling
  from pyasn1.type import constraint
  from pyasn1.type import tag
  from pyasn1.type import tagmap
  
  __all__ = ['Asn1Item', 'Asn1Type', 'SimpleAsn1Type',
             'ConstructedAsn1Type']
  
  
  class Asn1Item(object):
      @classmethod
      def getTypeId(cls, increment=1):
          try:
              Asn1Item._typeCounter += increment
          except AttributeError:
              Asn1Item._typeCounter = increment
          return Asn1Item._typeCounter
  
  
  class Asn1Type(Asn1Item):
      """Base class for all classes representing ASN.1 types.
  
      In the user code, |ASN.1| class is normally used only for telling
      ASN.1 objects from others.
  
      Note
      ----
      For as long as ASN.1 is concerned, a way to compare ASN.1 types
      is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
      """
      #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
      #: ASN.1 tag(s) associated with |ASN.1| type.
      tagSet = tag.TagSet()
  
      #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
      #: object imposing constraints on initialization values.
      subtypeSpec = constraint.ConstraintsIntersection()
  
      # Disambiguation ASN.1 types identification
      typeId = None
  
      def __init__(self, **kwargs):
          readOnly = {
              'tagSet': self.tagSet,
              'subtypeSpec': self.subtypeSpec
          }
  
          readOnly.update(kwargs)
  
          self.__dict__.update(readOnly)
  
          self._readOnly = readOnly
  
      def __setattr__(self, name, value):
          if name[0] != '_' and name in self._readOnly:
              raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
  
          self.__dict__[name] = value
  
      def __str__(self):
          return self.prettyPrint()
  
      @property
      def readOnly(self):
          return self._readOnly
  
      @property
      def effectiveTagSet(self):
          """For |ASN.1| type is equivalent to *tagSet*
          """
          return self.tagSet  # used by untagged types
  
      @property
      def tagMap(self):
          """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
          """
          return tagmap.TagMap({self.tagSet: self})
  
      def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
          """Examine |ASN.1| type for equality with other ASN.1 type.
  
          ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
          (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
          out ASN.1 types comparison.
  
          Python class inheritance relationship is NOT considered.
  
          Parameters
          ----------
          other: a pyasn1 type object
              Class instance representing ASN.1 type.
  
          Returns
          -------
          : :class:`bool`
              :obj:`True` if *other* is |ASN.1| type,
              :obj:`False` otherwise.
          """
          return (self is other or
                  (not matchTags or self.tagSet == other.tagSet) and
                  (not matchConstraints or self.subtypeSpec == other.subtypeSpec))
  
      def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
          """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
  
          ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
          (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
          out ASN.1 types comparison.
  
          Python class inheritance relationship is NOT considered.
  
          Parameters
          ----------
              other: a pyasn1 type object
                  Class instance representing ASN.1 type.
  
          Returns
          -------
              : :class:`bool`
                  :obj:`True` if *other* is a subtype of |ASN.1| type,
                  :obj:`False` otherwise.
          """
          return (not matchTags or
                  (self.tagSet.isSuperTagSetOf(other.tagSet)) and
                   (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
  
      @staticmethod
      def isNoValue(*values):
          for value in values:
              if value is not noValue:
                  return False
          return True
  
      def prettyPrint(self, scope=0):
          raise NotImplementedError()
  
      # backward compatibility
  
      def getTagSet(self):
          return self.tagSet
  
      def getEffectiveTagSet(self):
          return self.effectiveTagSet
  
      def getTagMap(self):
          return self.tagMap
  
      def getSubtypeSpec(self):
          return self.subtypeSpec
  
      # backward compatibility
      def hasValue(self):
          return self.isValue
  
  # Backward compatibility
  Asn1ItemBase = Asn1Type
  
  
  class NoValue(object):
      """Create a singleton instance of NoValue class.
  
      The *NoValue* sentinel object represents an instance of ASN.1 schema
      object as opposed to ASN.1 value object.
  
      Only ASN.1 schema-related operations can be performed on ASN.1
      schema objects.
  
      Warning
      -------
      Any operation attempted on the *noValue* object will raise the
      *PyAsn1Error* exception.
      """
      skipMethods = set(
          ('__slots__',
           # attributes
           '__getattribute__',
           '__getattr__',
           '__setattr__',
           '__delattr__',
           # class instance
           '__class__',
           '__init__',
           '__del__',
           '__new__',
           '__repr__',
           '__qualname__',
           '__objclass__',
           'im_class',
           '__sizeof__',
           # pickle protocol
           '__reduce__',
           '__reduce_ex__',
           '__getnewargs__',
           '__getinitargs__',
           '__getstate__',
           '__setstate__')
      )
  
      _instance = None
  
      def __new__(cls):
          if cls._instance is None:
              def getPlug(name):
                  def plug(self, *args, **kw):
                      raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % name)
                  return plug
  
              op_names = [name
                          for typ in (str, int, list, dict)
                          for name in dir(typ)
                          if (name not in cls.skipMethods and
                              name.startswith('__') and
                              name.endswith('__') and
                              calling.callable(getattr(typ, name)))]
  
              for name in set(op_names):
                  setattr(cls, name, getPlug(name))
  
              cls._instance = object.__new__(cls)
  
          return cls._instance
  
      def __getattr__(self, attr):
          if attr in self.skipMethods:
              raise AttributeError('Attribute %s not present' % attr)
  
          raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr)
  
      def __repr__(self):
          return '<%s object>' % self.__class__.__name__
  
  
  noValue = NoValue()
  
  
  class SimpleAsn1Type(Asn1Type):
      """Base class for all simple classes representing ASN.1 types.
  
      ASN.1 distinguishes types by their ability to hold other objects.
      Scalar types are known as *simple* in ASN.1.
  
      In the user code, |ASN.1| class is normally used only for telling
      ASN.1 objects from others.
  
      Note
      ----
      For as long as ASN.1 is concerned, a way to compare ASN.1 types
      is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
      """
      #: Default payload value
      defaultValue = noValue
  
      def __init__(self, value=noValue, **kwargs):
          Asn1Type.__init__(self, **kwargs)
          if value is noValue:
              value = self.defaultValue
          else:
              value = self.prettyIn(value)
              try:
                  self.subtypeSpec(value)
  
              except error.PyAsn1Error:
                  exType, exValue, exTb = sys.exc_info()
                  raise exType('%s at %s' % (exValue, self.__class__.__name__))
  
          self._value = value
  
      def __repr__(self):
          representation = '%s %s object' % (
              self.__class__.__name__, self.isValue and 'value' or 'schema')
  
          for attr, value in self.readOnly.items():
              if value:
                  representation += ', %s %s' % (attr, value)
  
          if self.isValue:
              value = self.prettyPrint()
              if len(value) > 32:
                  value = value[:16] + '...' + value[-16:]
              representation += ', payload [%s]' % value
  
          return '<%s>' % representation
  
      def __eq__(self, other):
          return self is other and True or self._value == other
  
      def __ne__(self, other):
          return self._value != other
  
      def __lt__(self, other):
          return self._value < other
  
      def __le__(self, other):
          return self._value <= other
  
      def __gt__(self, other):
          return self._value > other
  
      def __ge__(self, other):
          return self._value >= other
  
      if sys.version_info[0] <= 2:
          def __nonzero__(self):
              return self._value and True or False
      else:
          def __bool__(self):
              return self._value and True or False
  
      def __hash__(self):
          return hash(self._value)
  
      @property
      def isValue(self):
          """Indicate that |ASN.1| object represents ASN.1 value.
  
          If *isValue* is :obj:`False` then this object represents just
          ASN.1 schema.
  
          If *isValue* is :obj:`True` then, in addition to its ASN.1 schema
          features, this object can also be used like a Python built-in object
          (e.g. :class:`int`, :class:`str`, :class:`dict` etc.).
  
          Returns
          -------
          : :class:`bool`
              :obj:`False` if object represents just ASN.1 schema.
              :obj:`True` if object represents ASN.1 schema and can be used as a normal value.
  
          Note
          ----
          There is an important distinction between PyASN1 schema and value objects.
          The PyASN1 schema objects can only participate in ASN.1 schema-related
          operations (e.g. defining or testing the structure of the data). Most
          obvious uses of ASN.1 schema is to guide serialisation codecs whilst
          encoding/decoding serialised ASN.1 contents.
  
          The PyASN1 value objects can **additionally** participate in many operations
          involving regular Python objects (e.g. arithmetic, comprehension etc).
          """
          return self._value is not noValue
  
      def clone(self, value=noValue, **kwargs):
          """Create a modified version of |ASN.1| schema or value object.
  
          The `clone()` method accepts the same set arguments as |ASN.1|
          class takes on instantiation except that all arguments
          of the `clone()` method are optional.
  
          Whatever arguments are supplied, they are used to create a copy
          of `self` taking precedence over the ones used to instantiate `self`.
  
          Note
          ----
          Due to the immutable nature of the |ASN.1| object, if no arguments
          are supplied, no new |ASN.1| object will be created and `self` will
          be returned instead.
          """
          if value is noValue:
              if not kwargs:
                  return self
  
              value = self._value
  
          initializers = self.readOnly.copy()
          initializers.update(kwargs)
  
          return self.__class__(value, **initializers)
  
      def subtype(self, value=noValue, **kwargs):
          """Create a specialization of |ASN.1| schema or value object.
  
          The subtype relationship between ASN.1 types has no correlation with
          subtype relationship between Python types. ASN.1 type is mainly identified
          by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
          constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
          These ASN.1 type properties are implemented as |ASN.1| attributes.  
  
          The `subtype()` method accepts the same set arguments as |ASN.1|
          class takes on instantiation except that all parameters
          of the `subtype()` method are optional.
  
          With the exception of the arguments described below, the rest of
          supplied arguments they are used to create a copy of `self` taking
          precedence over the ones used to instantiate `self`.
  
          The following arguments to `subtype()` create a ASN.1 subtype out of
          |ASN.1| type:
  
          Other Parameters
          ----------------
          implicitTag: :py:class:`~pyasn1.type.tag.Tag`
              Implicitly apply given ASN.1 tag object to `self`'s
              :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
              new object's ASN.1 tag(s).
  
          explicitTag: :py:class:`~pyasn1.type.tag.Tag`
              Explicitly apply given ASN.1 tag object to `self`'s
              :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
              new object's ASN.1 tag(s).
  
          subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
              Add ASN.1 constraints object to one of the `self`'s, then
              use the result as new object's ASN.1 constraints.
  
          Returns
          -------
          :
              new instance of |ASN.1| schema or value object
  
          Note
          ----
          Due to the immutable nature of the |ASN.1| object, if no arguments
          are supplied, no new |ASN.1| object will be created and `self` will
          be returned instead.
          """
          if value is noValue:
              if not kwargs:
                  return self
  
              value = self._value
  
          initializers = self.readOnly.copy()
  
          implicitTag = kwargs.pop('implicitTag', None)
          if implicitTag is not None:
              initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
  
          explicitTag = kwargs.pop('explicitTag', None)
          if explicitTag is not None:
              initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
  
          for arg, option in kwargs.items():
              initializers[arg] += option
  
          return self.__class__(value, **initializers)
  
      def prettyIn(self, value):
          return value
  
      def prettyOut(self, value):
          return str(value)
  
      def prettyPrint(self, scope=0):
          return self.prettyOut(self._value)
  
      def prettyPrintType(self, scope=0):
          return '%s -> %s' % (self.tagSet, self.__class__.__name__)
  
  # Backward compatibility
  AbstractSimpleAsn1Item = SimpleAsn1Type
  
  #
  # Constructed types:
  # * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
  # * ASN1 types and values are represened by Python class instances
  # * Value initialization is made for defaulted components only
  # * Primary method of component addressing is by-position. Data model for base
  #   type is Python sequence. Additional type-specific addressing methods
  #   may be implemented for particular types.
  # * SequenceOf and SetOf types do not implement any additional methods
  # * Sequence, Set and Choice types also implement by-identifier addressing
  # * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
  # * Sequence and Set types may include optional and defaulted
  #   components
  # * Constructed types hold a reference to component types used for value
  #   verification and ordering.
  # * Component type is a scalar type for SequenceOf/SetOf types and a list
  #   of types for Sequence/Set/Choice.
  #
  
  
  class ConstructedAsn1Type(Asn1Type):
      """Base class for all constructed classes representing ASN.1 types.
  
      ASN.1 distinguishes types by their ability to hold other objects.
      Those "nesting" types are known as *constructed* in ASN.1.
  
      In the user code, |ASN.1| class is normally used only for telling
      ASN.1 objects from others.
  
      Note
      ----
      For as long as ASN.1 is concerned, a way to compare ASN.1 types
      is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
      """
  
      #: If :obj:`True`, requires exact component type matching,
      #: otherwise subtype relation is only enforced
      strictConstraints = False
  
      componentType = None
  
      # backward compatibility, unused
      sizeSpec = constraint.ConstraintsIntersection()
  
      def __init__(self, **kwargs):
          readOnly = {
              'componentType': self.componentType,
              # backward compatibility, unused
              'sizeSpec': self.sizeSpec
          }
  
          # backward compatibility: preserve legacy sizeSpec support
          kwargs = self._moveSizeSpec(**kwargs)
  
          readOnly.update(kwargs)
  
          Asn1Type.__init__(self, **readOnly)
  
      def _moveSizeSpec(self, **kwargs):
          # backward compatibility, unused
          sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec)
          if sizeSpec:
              subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec)
              if subtypeSpec:
                  subtypeSpec = sizeSpec
  
              else:
                  subtypeSpec += sizeSpec
  
              kwargs['subtypeSpec'] = subtypeSpec
  
          return kwargs
  
      def __repr__(self):
          representation = '%s %s object' % (
              self.__class__.__name__, self.isValue and 'value' or 'schema'
          )
  
          for attr, value in self.readOnly.items():
              if value is not noValue:
                  representation += ', %s=%r' % (attr, value)
  
          if self.isValue and self.components:
              representation += ', payload [%s]' % ', '.join(
                  [repr(x) for x in self.components])
  
          return '<%s>' % representation
  
      def __eq__(self, other):
          return self is other or self.components == other
  
      def __ne__(self, other):
          return self.components != other
  
      def __lt__(self, other):
          return self.components < other
  
      def __le__(self, other):
          return self.components <= other
  
      def __gt__(self, other):
          return self.components > other
  
      def __ge__(self, other):
          return self.components >= other
  
      if sys.version_info[0] <= 2:
          def __nonzero__(self):
              return bool(self.components)
      else:
          def __bool__(self):
              return bool(self.components)
  
      @property
      def components(self):
          raise error.PyAsn1Error('Method not implemented')
  
      def _cloneComponentValues(self, myClone, cloneValueFlag):
          pass
  
      def clone(self, **kwargs):
          """Create a modified version of |ASN.1| schema object.
  
          The `clone()` method accepts the same set arguments as |ASN.1|
          class takes on instantiation except that all arguments
          of the `clone()` method are optional.
  
          Whatever arguments are supplied, they are used to create a copy
          of `self` taking precedence over the ones used to instantiate `self`.
  
          Possible values of `self` are never copied over thus `clone()` can
          only create a new schema object.
  
          Returns
          -------
          :
              new instance of |ASN.1| type/value
  
          Note
          ----
          Due to the mutable nature of the |ASN.1| object, even if no arguments
          are supplied, a new |ASN.1| object will be created and returned.
          """
          cloneValueFlag = kwargs.pop('cloneValueFlag', False)
  
          initializers = self.readOnly.copy()
          initializers.update(kwargs)
  
          clone = self.__class__(**initializers)
  
          if cloneValueFlag:
              self._cloneComponentValues(clone, cloneValueFlag)
  
          return clone
  
      def subtype(self, **kwargs):
          """Create a specialization of |ASN.1| schema object.
  
          The `subtype()` method accepts the same set arguments as |ASN.1|
          class takes on instantiation except that all parameters
          of the `subtype()` method are optional.
  
          With the exception of the arguments described below, the rest of
          supplied arguments they are used to create a copy of `self` taking
          precedence over the ones used to instantiate `self`.
  
          The following arguments to `subtype()` create a ASN.1 subtype out of
          |ASN.1| type.
  
          Other Parameters
          ----------------
          implicitTag: :py:class:`~pyasn1.type.tag.Tag`
              Implicitly apply given ASN.1 tag object to `self`'s
              :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
              new object's ASN.1 tag(s).
  
          explicitTag: :py:class:`~pyasn1.type.tag.Tag`
              Explicitly apply given ASN.1 tag object to `self`'s
              :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
              new object's ASN.1 tag(s).
  
          subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
              Add ASN.1 constraints object to one of the `self`'s, then
              use the result as new object's ASN.1 constraints.
  
  
          Returns
          -------
          :
              new instance of |ASN.1| type/value
  
          Note
          ----
          Due to the mutable nature of the |ASN.1| object, even if no arguments
          are supplied, a new |ASN.1| object will be created and returned.
          """
  
          initializers = self.readOnly.copy()
  
          cloneValueFlag = kwargs.pop('cloneValueFlag', False)
  
          implicitTag = kwargs.pop('implicitTag', None)
          if implicitTag is not None:
              initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
  
          explicitTag = kwargs.pop('explicitTag', None)
          if explicitTag is not None:
              initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
  
          for arg, option in kwargs.items():
              initializers[arg] += option
  
          clone = self.__class__(**initializers)
  
          if cloneValueFlag:
              self._cloneComponentValues(clone, cloneValueFlag)
  
          return clone
  
      def getComponentByPosition(self, idx):
          raise error.PyAsn1Error('Method not implemented')
  
      def setComponentByPosition(self, idx, value, verifyConstraints=True):
          raise error.PyAsn1Error('Method not implemented')
  
      def setComponents(self, *args, **kwargs):
          for idx, value in enumerate(args):
              self[idx] = value
          for k in kwargs:
              self[k] = kwargs[k]
          return self
  
      # backward compatibility
  
      def setDefaultComponents(self):
          pass
  
      def getComponentType(self):
          return self.componentType
  
      # backward compatibility, unused
      def verifySizeSpec(self):
          self.subtypeSpec(self)
  
  
          # Backward compatibility
  AbstractConstructedAsn1Item = ConstructedAsn1Type
  