<commit_before># Copyright (c) 2015, The MITRE Corporation. All rights reserved.
# See LICENSE.txt for complete terms.
"""
Utilities for dealing with XML namespaces.
"""

# stdlib
import collections
import copy

# external
from ordered_set import OrderedSet

# internal
from mixbox.vendor import six


_NamespaceTuple = collections.namedtuple("Namespace",
                                         "name prefix schema_location")

class Namespace(_NamespaceTuple):
    """A convenience class which represents simplified XML namespace info,
    consisting of exactly one namespace URI, and an optional prefix and schema
    location URI.  This is handy for building up big tables of namespace data.
    """
    def __new__(cls, name, prefix, schema_location=None):
        """This essentially enables parameter defaulting behavior on a
        namedtuple.  Here, schema location need not be given when creating a
        Namespace, and it will default to None."""
        return super(Namespace, cls).__new__(cls, name, prefix, schema_location)


class DuplicatePrefixError(Exception):
    """Indicates an attempt to map a prefix to two different namespaces."""
    def __init__(self, prefix, *namespaces):
        super(DuplicatePrefixError, self).__init__(
            "Can't map prefix '{0}' to different namespaces: {1}".format(
                prefix, namespaces
            )
        )
        self.prefix = prefix
        self.namespaces = namespaces


class ConflictingSchemaLocationError(Exception):
    """Indicates an attempt to associated an XML namespace URI with two
    different schema location URIs."""
    def __init__(self, ns_uri, *schemalocs):
        super(ConflictingSchemaLocationError, self).__init__(
            "Can't map namespace '{0}' to different schema locations: {1}".format(
                ns_uri, schemalocs
            )
        )
        self.ns_uri = ns_uri
        self.schemalocs = schemalocs


class NamespaceNotFoundError(Exception):
    """Thrown when a namespace is not found.  The URI identifying the namespace
    is available from the "ns_uri" attribute."""
    def __init__(self, ns_uri):
        super(NamespaceNotFoundError, self).__init__(
            "Namespace not found: {0}".format(ns_uri)
        )
        self.ns_uri = ns_uri


class PrefixNotFoundError(Exception):
    """Thrown when a namespace prefix is not found.  The prefix is available
    from the "prefix" attribute."""
    def __init__(self, prefix):
        super(PrefixNotFoundError, self).__init__(
            "Prefix not found: {0}".format(prefix)
        )
        self.prefix = prefix


class TooManyDefaultNamespacesError(Exception):
    """Raised when creating xmlns attributes.  You can't have more than one
    default namespace in an XML document.  If a second preferred default is
    encountered, an attempt is made to choose a prefix from the set of
    registered prefixes.  If none are registered, this exception is raised."""
    def __init__(self, ns_uri):
        super(TooManyDefaultNamespacesError, self).__init__(
            "Too many default namespaces: needed a prefix for namespace '{0}' "
            "but none are defined!".format(ns_uri)
        )


class NoPrefixesError(Exception):
    """Thrown when prefixes are required for a namespace, but none are
    registered."""
    def __init__(self, ns_uri):
        super(NoPrefixesError, self).__init__(
            "Namespace '{0}' has no prefixes!".format(ns_uri)
        )


class _NamespaceInfo(object):
    """**This class is an implementation detail of :class:`NamespaceSet`.
    Others must not use this class.**

    Holds all info about a single XML namespace, including its URI, a
    set of prefixes, and a schema location URI.

    This is an internal class.  Some invariants must be maintained:
    preferred_prefix is a member of prefixes, or is None (meaning the
    preferred thing to do is use it as an XML default namespace).
    There must be no more than one instance per namespace URI.  Other users
    may mess up the invariants, which is why this is "hidden".  The
    NamespaceSet public interface never uses this class.
    """

    def __init__(self, *args):
        """If a Namespace object is passed, this object is constructed from
        it.  If one to three strings are passed, they are treated as
        individual namespace components in the following order: URI, prefix,
        schema location.  Either way, the given prefix will become the
        preferred prefix.
        """
        iterargs = iter(args)
        ns       = next(iterargs, None)

        if not args:
            self.__default_construct()
        elif isinstance(ns, Namespace):
            self.__construct_from_namespace(ns)
        else:
            prefix    = next(iterargs, None)
            schemaloc = next(iterargs, None)
            self.__construct_from_components(ns, prefix, schemaloc)

    def __default_construct(self):
        """Default-construct this object.
        Internal use only; it constructs an invalid object.  Further
        initialization is required to make it valid.
        """
        self.uri = None
        self.schema_location = None
        self.prefixes = OrderedSet()
        self.preferred_prefix = None

    def __construct_from_namespace(self, ns):
        """Initialize this instance from a given Namespace object."""
        assert isinstance(ns, Namespace)  # TODO (bworrell): Change this to an if not isinstance(...): raise TypeError?
        self.__construct_from_components(*ns)

    def __construct_from_components(self, ns_uri, prefix=None, schema_location=None):
        """Initialize this instance from a namespace URI, and optional
        prefix and schema location URI."""

        assert ns_uri  # other fields are optional

        self.uri = ns_uri
        self.schema_location = schema_location or None
        self.prefixes = set()

        if prefix:
            self.prefixes.add(prefix)
        self.preferred_prefix = prefix or None

    def __eq__(self, other):
        if type(other) != type(self):
            return False
        return vars(other) == vars(self)

    def __ne__(self, other):
        """Python2 apparently needs this; python3 has a suitable default
        which delegates to __eq__.
        """
        return not self.__eq__(other)

    def __str__(self):
        """for debugging"""
        if self.preferred_prefix:
            preferred_prefix = self.preferred_prefix
        else:
            preferred_prefix = "(default)"

        s = "{0.uri}\n  {0.prefixes}\n  preferred: {1}\n  {0.schema_location}"
        return s.format(self, preferred_prefix)<commit_msg>Bryan had begun to change _NamespaceInfo's prefixes set to use an OrderedSet instead of the built-in python set.  I think he didn't finish: he changed __default_construct() but never changed __construct_from_components().  So I updated the latter method.<commit_after># Copyright (c) 2015, The MITRE Corporation. All rights reserved.
# See LICENSE.txt for complete terms.
"""
Utilities for dealing with XML namespaces.
"""

# stdlib
import collections
import copy

# external
from ordered_set import OrderedSet

# internal
from mixbox.vendor import six


_NamespaceTuple = collections.namedtuple("Namespace",
                                         "name prefix schema_location")

class Namespace(_NamespaceTuple):
    """A convenience class which represents simplified XML namespace info,
    consisting of exactly one namespace URI, and an optional prefix and schema
    location URI.  This is handy for building up big tables of namespace data.
    """
    def __new__(cls, name, prefix, schema_location=None):
        """This essentially enables parameter defaulting behavior on a
        namedtuple.  Here, schema location need not be given when creating a
        Namespace, and it will default to None."""
        return super(Namespace, cls).__new__(cls, name, prefix, schema_location)


class DuplicatePrefixError(Exception):
    """Indicates an attempt to map a prefix to two different namespaces."""
    def __init__(self, prefix, *namespaces):
        super(DuplicatePrefixError, self).__init__(
            "Can't map prefix '{0}' to different namespaces: {1}".format(
                prefix, namespaces
            )
        )
        self.prefix = prefix
        self.namespaces = namespaces


class ConflictingSchemaLocationError(Exception):
    """Indicates an attempt to associated an XML namespace URI with two
    different schema location URIs."""
    def __init__(self, ns_uri, *schemalocs):
        super(ConflictingSchemaLocationError, self).__init__(
            "Can't map namespace '{0}' to different schema locations: {1}".format(
                ns_uri, schemalocs
            )
        )
        self.ns_uri = ns_uri
        self.schemalocs = schemalocs


class NamespaceNotFoundError(Exception):
    """Thrown when a namespace is not found.  The URI identifying the namespace
    is available from the "ns_uri" attribute."""
    def __init__(self, ns_uri):
        super(NamespaceNotFoundError, self).__init__(
            "Namespace not found: {0}".format(ns_uri)
        )
        self.ns_uri = ns_uri


class PrefixNotFoundError(Exception):
    """Thrown when a namespace prefix is not found.  The prefix is available
    from the "prefix" attribute."""
    def __init__(self, prefix):
        super(PrefixNotFoundError, self).__init__(
            "Prefix not found: {0}".format(prefix)
        )
        self.prefix = prefix


class TooManyDefaultNamespacesError(Exception):
    """Raised when creating xmlns attributes.  You can't have more than one
    default namespace in an XML document.  If a second preferred default is
    encountered, an attempt is made to choose a prefix from the set of
    registered prefixes.  If none are registered, this exception is raised."""
    def __init__(self, ns_uri):
        super(TooManyDefaultNamespacesError, self).__init__(
            "Too many default namespaces: needed a prefix for namespace '{0}' "
            "but none are defined!".format(ns_uri)
        )


class NoPrefixesError(Exception):
    """Thrown when prefixes are required for a namespace, but none are
    registered."""
    def __init__(self, ns_uri):
        super(NoPrefixesError, self).__init__(
            "Namespace '{0}' has no prefixes!".format(ns_uri)
        )


class _NamespaceInfo(object):
    """**This class is an implementation detail of :class:`NamespaceSet`.
    Others must not use this class.**

    Holds all info about a single XML namespace, including its URI, a
    set of prefixes, and a schema location URI.

    This is an internal class.  Some invariants must be maintained:
    preferred_prefix is a member of prefixes, or is None (meaning the
    preferred thing to do is use it as an XML default namespace).
    There must be no more than one instance per namespace URI.  Other users
    may mess up the invariants, which is why this is "hidden".  The
    NamespaceSet public interface never uses this class.
    """

    def __init__(self, *args):
        """If a Namespace object is passed, this object is constructed from
        it.  If one to three strings are passed, they are treated as
        individual namespace components in the following order: URI, prefix,
        schema location.  Either way, the given prefix will become the
        preferred prefix.
        """
        iterargs = iter(args)
        ns       = next(iterargs, None)

        if not args:
            self.__default_construct()
        elif isinstance(ns, Namespace):
            self.__construct_from_namespace(ns)
        else:
            prefix    = next(iterargs, None)
            schemaloc = next(iterargs, None)
            self.__construct_from_components(ns, prefix, schemaloc)

    def __default_construct(self):
        """Default-construct this object.
        Internal use only; it constructs an invalid object.  Further
        initialization is required to make it valid.
        """
        self.uri = None
        self.schema_location = None
        self.prefixes = OrderedSet()
        self.preferred_prefix = None

    def __construct_from_namespace(self, ns):
        """Initialize this instance from a given Namespace object."""
        assert isinstance(ns, Namespace)  # TODO (bworrell): Change this to an if not isinstance(...): raise TypeError?
        self.__construct_from_components(*ns)

    def __construct_from_components(self, ns_uri, prefix=None, schema_location=None):
        """Initialize this instance from a namespace URI, and optional
        prefix and schema location URI."""

        assert ns_uri  # other fields are optional

        self.uri = ns_uri
        self.schema_location = schema_location or None
        self.prefixes = OrderedSet()

        if prefix:
            self.prefixes.add(prefix)
        self.preferred_prefix = prefix or None

    def __eq__(self, other):
        if type(other) != type(self):
            return False
        return vars(other) == vars(self)

    def __ne__(self, other):
        """Python2 apparently needs this; python3 has a suitable default
        which delegates to __eq__.
        """
        return not self.__eq__(other)

    def __str__(self):
        """for debugging"""
        if self.preferred_prefix:
            preferred_prefix = self.preferred_prefix
        else:
            preferred_prefix = "(default)"

        s = "{0.uri}\n  {0.prefixes}\n  preferred: {1}\n  {0.schema_location}"
        return s.format(self, preferred_prefix)
{'fraction_non_alphanumeric': 0.0792422232050715, 'fraction_numerical': 0.0026536930561698365, 'mean_word_length': 2.7149507119386636, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1162077', 'n_tokens_mistral': 3513, 'n_tokens_neox': 3436, 'n_words': 1338}