class CPEComponent(object):
    """
    Represents a generic component of CPE Name,
    compatible with the components of all versions of CPE specification.
    """

    ###############
    #  CONSTANTS  #
    ###############

    # Constants of possible versions of CPE components

    #: Version 1.1 of CPE component
    COMP_1_1 = "1.1"

    #: Version 2.2 of CPE component
    COMP_2_2 = "2.2"

    #: Version 2.3 with WFN style of CPE component
    COMP_2_3_WFN = "2.3_wfn"

    #: Version 2.3 with URI style of CPE component
    COMP_2_3_URI = "2.3_uri"

    #: Version 2.3 with formatted string style of CPE component
    COMP_2_3_FS = "2.3_fs"

    # Attributes associated with components of all versions of CPE

    #: Part attribute of CPE Name that indicates the type of system
    #: associated with the product
    ATT_PART = "part"

    #: Vendor attribute of CPE Name that describes or identify the person or
    #: organization that manufactured or created the product
    ATT_VENDOR = "vendor"

    #: Product attribute of CPE Name that describes or identify the most common
    #: and recognizable title or name of the product
    ATT_PRODUCT = "product"

    #: Version attribute of CPE Name that indicates vendor-specific
    #: alphanumeric strings characterizing the particular release version
    #: of the product
    ATT_VERSION = "version"

    #: Version attribute of CPE Name that indicates vendor-specific
    #: alphanumeric strings characterizing the particular update,
    #: service pack, or point release of the product
    ATT_UPDATE = "update"

    #: Edition attribute of CPE Name that captures the edition-related terms
    #: applied by the vendor to the product
    ATT_EDITION = "edition"

    #: Language attribute of CPE Name that defines the language supported
    #: in the user interface of the product being described
    ATT_LANGUAGE = "language"

    # Attributes associated with components of version 2.3 of CPE

    #: SW_edition attribute of version 2.3 of CPE Name that characterizes
    #: how the product is tailored to a particular market or class of
    #: end users
    ATT_SW_EDITION = "sw_edition"

    #: Target_SW attribute of version 2.3 of CPE Name that characterizes the
    #: software computing environment within which the product operates
    ATT_TARGET_SW = "target_sw"

    #: Target_HW attribute of version 2.3 of CPE Name that characterizes the
    #: instruction set architecture (e.g., x86) on which the product being
    #: described or identified by the WFN operates
    ATT_TARGET_HW = "target_hw"

    #: Other attribute of version 2.3 of CPE Name that capture any other
    #: general descriptive or identifying information which is vendor-
    #: or product-specific and which does not logically fit in any other
    #: attribute value
    ATT_OTHER = "other"

    #: List of attribute names associated with CPE Name components
    #: (versions 1.1 and 2.2 of CPE specification)
    CPE_COMP_KEYS = (ATT_PART,
                     ATT_VENDOR,
                     ATT_PRODUCT,
                     ATT_VERSION,
                     ATT_UPDATE,
                     ATT_EDITION,
                     ATT_LANGUAGE)

    #: List of attribute names associated with CPE Name components
    #: of version 2.3
    CPE_COMP_KEYS_EXTENDED = (ATT_PART,
                              ATT_VENDOR,
                              ATT_PRODUCT,
                              ATT_VERSION,
                              ATT_UPDATE,
                              ATT_EDITION,
                              ATT_LANGUAGE,
                              ATT_SW_EDITION,
                              ATT_TARGET_SW,
                              ATT_TARGET_HW,
                              ATT_OTHER)

    # Possible values of "part" attribute of CPE (type of system)

    #: Value of part attribute associated with a hardware system
    VALUE_PART_HW = "h"

    #: Value of part attribute associated with an operating system
    VALUE_PART_OS = "o"

    #: Value of part attribute associated with an application
    VALUE_PART_APP = "a"

    #: Value of part attribute that indicates a CPE Name with
    #: undefined type of system
    VALUE_PART_UNDEFINED = "u"

    #: Possible values of a type of system in CPE specification:
    #: hardware, operating system, software and undefined
    SYSTEM_VALUES = (VALUE_PART_HW,
                     VALUE_PART_OS,
                     VALUE_PART_APP,
                     VALUE_PART_UNDEFINED)

    ###############
    #  VARIABLES  #
    ###############

    #: Order of attributes of CPE Name components
    ordered_comp_parts = {0: ATT_PART,
                          1: ATT_VENDOR,
                          2: ATT_PRODUCT,
                          3: ATT_VERSION,
                          4: ATT_UPDATE,
                          5: ATT_EDITION,
                          6: ATT_LANGUAGE,
                          7: ATT_SW_EDITION,
                          8: ATT_TARGET_SW,
                          9: ATT_TARGET_HW,
                          10: ATT_OTHER}

    ###################
    #  CLASS METHODS  #
    ###################

    @classmethod
    def is_valid_attribute(cls, att_name):
        """
        Check if input attribute name is correct.

        :param string att_name: attribute name to check
        :returns: True is attribute name is valid, otherwise False
        :rtype: boolean

        TEST: a wrong attribute

        >>> from .cpecomp import CPEComponent
        >>> att = CPEComponent.ATT_PRODUCT
        >>> CPEComponent.is_valid_attribute(att)
        True
        """

        return att_name in CPEComponent.CPE_COMP_KEYS_EXTENDED

    ####################
    #  OBJECT METHODS  #
    ####################

    def __contains__(self, item):
        """
        Returns True if item is included in set of values of self.

        :param CPEComponent item: component to find in self
        :returns: True if item is included in set of self, otherwise False
        :rtype: boolean
        """

        from .cpecomp_undefined import CPEComponentUndefined
        from .cpecomp_empty import CPEComponentEmpty
        from .cpecomp_anyvalue import CPEComponentAnyValue

        if ((self == item) or
           isinstance(self, CPEComponentUndefined) or
           isinstance(self, CPEComponentEmpty) or
           isinstance(self, CPEComponentAnyValue)):

            return True

        return False

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        :rtype: boolean
        """

        len_self = len(self._standard_value)
        len_other = len(other._standard_value)

        if isinstance(self._standard_value, list):
            # Self is version 1.1 of CPE Name
            if isinstance(other._standard_value, list):
                # Other is version 1.1 of CPE Name
                value_self = self._standard_value
                value_other = other._standard_value

            # Other is higher version than to 1.1 of CPE Name
            elif len_self == 1:
                value_self = self._standard_value[0]
                value_other = other._standard_value

            else:
                # The comparation between components is impossible
                return False
        else:
            # Self is higher version than 1.1 of CPE Name
            if isinstance(other._standard_value, list):
                # Other is version 1.1 of CPE Name
                if len_other == 1:
                    value_self = self._standard_value
                    value_other = other._standard_value[0]

                else:
                    # The comparation between components is impossible
                    return False
            else:
                value_self = self._standard_value
                value_other = other._standard_value

        return ((value_self == value_other) and
               (self._is_negated == other._is_negated))

    def __init__(self, comp_str):
        """
        Store the value of component.

        :param string comp_str: value of component value
        :returns: None
        """

        self._is_negated = False
        self._encoded_value = comp_str
        self._standard_value = [comp_str]

    def __ne__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are not equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other != self, False otherwise
        :rtype: boolean
        """

        return not (self == other)

    def __repr__(self):
        """
        Returns a unambiguous representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        """

        return "{0}()".format(self.__class__.__name__) class CPEComponent2_3_URI(CPEComponent2_3):
    """
    Represents a component of version 2.3 of CPE specification with URI style.
    """

    ###############
    #  CONSTANTS  #
    ###############

    # Patterns used to check the value of component
    _PCT_ENCODED = "%21|%22|%23|%24|%25|%26|%27|%28|%29|%2a|%2b|%2c|%2f|%3a|%3b|%3c|%3d|%3e|%3f|%40|%5b|%5c|%5d|%5e|%60|%7b|%7c|%7d|%7e"
    _SPEC_CHRS = "((%01)+|%02)"
    _UNRESERVED = "[\w\-\.]"

    #: Separator of components of CPE name with URI style
    SEPARATOR_COMP = ":"

    #: Separator of language parts: language and region
    SEPARATOR_LANG = "-"

    #: Separator of edition part components in CPE uri
    SEPARATOR_PACKED_EDITION = "~"

    # Logical values in string format

    #: Logical value associated with a any value logical value
    VALUE_ANY = ""

    #: Logical value associated with a not applicable logical value
    VALUE_NA = "-"

    #: Logical value associated with a component without value
    VALUE_EMPTY = ""

    #: Logical value associated with a undefined component
    VALUE_UNDEFINED = None

    #: Constant associated with wildcard to indicate a sequence of characters
    WILDCARD_MULTI = "%02"
    #: Constant associated with wildcard to indicate a character
    WILDCARD_ONE = "%01"

    ###############
    #  VARIABLES  #
    ###############

    # Variable used to check the value of component
    _str_w_special = "({0}?({1}|{2})+{3}?)".format(_SPEC_CHRS, _UNRESERVED,
                                                   _PCT_ENCODED, _SPEC_CHRS)
    _str_wo_special = "({0}|{1})*".format(_UNRESERVED, _PCT_ENCODED)
    _string = "({0}|{1})".format(_str_wo_special, _str_w_special)

    # Compilation of regular expression associated with value of component
    _value_pattern = "^{0}$".format(_string)
    _value_rxc = re.compile(_value_pattern)

    #: Characters to convert to percent-encoded characters
    char_to_pce = {
        '!': "%21",
        '"': "%22",
        '#': "%23",
        '$': "%24",
        '%': "%25",
        '&': "%26",
        '\'': "%27",
        '(': "%28",
        ')': "%29",
        '*': "%2a",
        '+': "%2b",
        ',': "%2c",
        '/': "%2f",
        ':': "%3a",
        ';': "%3b",
        '<': "%3c",
        '=': "%3d",
        '>': "%3e",
        '?': "%3f",
        '@': "%40",
        '[': "%5b",
        '\\': "%5c",
        ']': "%5d",
        '^': "%5e",
        '`': "%60",
        '{': "%7b",
        '|': "%7c",
        '}': "%7d",
        '~': "%7e"}

    #: Percent-encoded characters to decode
    pce_char_to_decode = {
        "%21": '\\!',
        "%22": '\\\"',
        "%23": '\\#',
        "%24": '\\$',
        "%25": '\\%',
        "%26": '\\&',
        "%27": '\\\'',
        "%28": '\\(',
        "%29": '\\)',
        "%2a": '\\*',
        "%2b": '\\+',
        "%2c": '\\,',
        "%2f": '\\/',
        "%3a": '\\:',
        "%3b": '\\;',
        "%3c": '\\<',
        "%3d": '\\=',
        "%3e": '\\>',
        "%3f": '\\?',
        "%40": '\\@',
        "%5b": '\\[',
        "%5c": '\\\\',
        "%5d": '\\]',
        "%5e": '\\^',
        "%60": '\\`',
        "%7b": '\\{',
        "%7c": '\\|',
        "%7d": '\\}',
        "%7e": '\\~'}

    ####################
    #  OBJECT METHODS  #
    ####################

    def _decode(self):
        """
        Convert the characters of character in value of component to standard
        value (WFN value).
        This function scans the value of component and returns a copy
        with all percent-encoded characters decoded.

        :exception: ValueError - invalid character in value of component
        """

        result = []
        idx = 0
        s = self._encoded_value
        embedded = False

        errmsg = []
        errmsg.append("Invalid value: ")

        while (idx < len(s)):
            errmsg.append(s)
            errmsg_str = "".join(errmsg)

            # Get the idx'th character of s
            c = s[idx]

            # Deal with dot, hyphen and tilde: decode with quoting
            if ((c == '.') or (c == '-') or (c == '~')):
                result.append("\\")
                result.append(c)
                idx += 1
                embedded = True  # a non-%01 encountered
                continue

            if (c != '%'):
                result.append(c)
                idx += 1
                embedded = True  # a non-%01 encountered
                continue

            # we get here if we have a substring starting w/ '%'
            form = s[idx: idx + 3]  # get the three-char sequence

            if form == CPEComponent2_3_URI.WILDCARD_ONE:
                # If %01 legal at beginning or end
                # embedded is false, so must be preceded by %01
                # embedded is true, so must be followed by %01
                if (((idx == 0) or (idx == (len(s)-3))) or
                    ((not embedded) and (s[idx - 3:idx] == CPEComponent2_3_URI.WILDCARD_ONE)) or
                    (embedded and (len(s) >= idx + 6) and (s[idx + 3:idx + 6] == CPEComponent2_3_URI.WILDCARD_ONE))):

                    # A percent-encoded question mark is found
                    # at the beginning or the end of the string,
                    # or embedded in sequence as required.
                    # Decode to unquoted form.
                    result.append(CPEComponent2_3_WFN.WILDCARD_ONE)
                    idx += 3
                    continue
                else:
                    raise ValueError(errmsg_str)

            elif form == CPEComponent2_3_URI.WILDCARD_MULTI:
                if ((idx == 0) or (idx == (len(s) - 3))):
                    # Percent-encoded asterisk is at the beginning
                    # or the end of the string, as required.
                    # Decode to unquoted form.
                    result.append(CPEComponent2_3_WFN.WILDCARD_MULTI)
                else:
                    raise ValueError(errmsg_str)

            elif form in CPEComponent2_3_URI.pce_char_to_decode.keys():
                value = CPEComponent2_3_URI.pce_char_to_decode[form]
                result.append(value)

            else:
                errmsg.append("Invalid percent-encoded character: ")
                errmsg.append(s)
                raise ValueError("".join(errmsg))

            idx += 3
            embedded = True  # a non-%01 encountered.

        self._standard_value = "".join(result)

    def _is_valid_edition(self):
        """
        Return True if the input value of attribute "edition" is valid,
        and otherwise False.

        :returns: True if value is valid, False otherwise
        :rtype: boolean
        """

        comp_str = self._standard_value[0]

        packed = []
        packed.append("(")
        packed.append(CPEComponent2_3_URI.SEPARATOR_PACKED_EDITION)
        packed.append(CPEComponent2_3_URI._string)
        packed.append("){5}")

        value_pattern = []
        value_pattern.append("^(")
        value_pattern.append(CPEComponent2_3_URI._string)
        value_pattern.append("|")
        value_pattern.append("".join(packed))
        value_pattern.append(")$")

        value_rxc = re.compile("".join(value_pattern))
        return value_rxc.match(comp_str) is not None

    def _is_valid_value(self):
        """
        Return True if the input value CPE name attribute is valid,
        and otherwise False.

        :returns: True if value is valid, False otherwise
        :rtype: boolean
        """

        comp_str = self._standard_value[0]
        return CPEComponent2_3_URI._value_rxc.match(comp_str) is not None class CPEComponent2_3_WFN(CPEComponent2_3):
    """
    Represents a component of version 2.3 of CPE specification with WFN style.
    """

    ###############
    #  CONSTANTS  #
    ###############

    # Patterns used to check the value of component
    _ESCAPE = r"\\"
    _PUNC_NO_DASH = "\!|\"|\;|\#|\$|\%|\&|\'|\(|\)|\+|\,|\.|\/|\:|\<|\=|\>|\@|\[|\]|\^|\`|\{|\||\}|\~"

    #: Separator of components of CPE name with URI style
    SEPARATOR_COMP = ", "

    #: Separator of attribute and value in pairs of component
    SEPARATOR_PAIR = "="

    #: Separator of language parts: language and region
    SEPARATOR_LANG = r"\-"

    # Logical values in string format

    #: Logical value associated with a any value logical value
    VALUE_ANY = "ANY"

    #: Logical value associated with a not applicable logical value
    VALUE_NA = "NA"

    #: Constant associated with wildcard to indicate a sequence of characters
    WILDCARD_MULTI = "*"
    #: Constant associated with wildcard to indicate a character
    WILDCARD_ONE = "?"

    ###############
    #  VARIABLES  #
    ###############

    _spec1 = "\{0}".format(WILDCARD_ONE)
    _spec2 = "\{0}".format(WILDCARD_MULTI)
    _spec_chrs = "{0}+|{1}".format(_spec1, _spec2)
    _special = "{0}|{1}".format(_spec1, _spec2)
    _punc_w_dash = "{0}|-".format(_PUNC_NO_DASH)
    _quoted1 = "{0}({1}|{2}|{3})".format(_ESCAPE, _ESCAPE, _special,
                                         _PUNC_NO_DASH)
    _quoted2 = "{0}({1}|{2}|{3})".format(_ESCAPE, _ESCAPE,
                                         _special, _punc_w_dash)
    _body1 = "\w|{0}".format(_quoted1)
    _body2 = "\w|{0}".format(_quoted2)
    _body = "(({0})({1})*)|{2}({3})+".format(_body1, _body2, _body2, _body2)
    _avstring_pattern = "^((({0})|(({1})({2})*))({3})?)$".format(_body,
                                                                 _spec_chrs,
                                                                 _body2,
                                                                 _spec_chrs)

    _value_rxc = re.compile(_avstring_pattern)

    ####################
    #  OBJECT METHODS  #
    ####################

    def _decode(self):
        """
        Convert the encoded value of component to standard value (WFN value) is
        not necessary in this case because the internal value is in WFN
        already.
        """

        pass

    def _is_valid_value(self):
        """
        Return True if the value of component in generic attribute is valid,
        and otherwise False.

        :returns: True if value is valid, False otherwise
        :rtype: boolean
        """

        comp_str = self._standard_value
        return CPEComponent2_3_WFN._value_rxc.match(comp_str) is not None

    def get_value(self):
        """
        Returns the encoded value of component.

        :returns: encoded value of component
        :rtype: string
        """

        # Add double quotes
        return '"{0}"'.format(super(CPEComponent2_3_WFN, self).get_value())

    def set_value(self, comp_str, comp_att):
        """
        Set the value of component.

        :param string comp_str: value of component
        :param string comp_att: attribute associated with comp_str
        :returns: None
        :exception: ValueError - incorrect value of component
        """

        # Del double quotes of value
        str = comp_str[1:-1]
        self._standard_value = str

        # Parse the value
        super(CPEComponent2_3_WFN, self).set_value(str, comp_att) class CPEComponent2_3_FS(CPEComponent2_3):
    """
    Represents a component of version 2.3 of CPE specification with URI style.
    """

    ###############
    #  CONSTANTS  #
    ###############

    # Patterns used to check the value of component
    _UNRESERVED = "\w|\.|\-"
    _PUNC = "\!|\"|\;|\#|\$|\%|\&|\'|\(|\)|\+|\,|\/|\:|\<|\=|\>|\@|\[|\]|\^|\`|\{|\||\}|\~|\-"

    #: Separator of components of CPE name with URI style
    SEPARATOR_COMP = ":"

    #: Separator of language parts: language and region
    SEPARATOR_LANG = "-"

    # Logical values in string format

    #: Logical value associated with a any value logical value
    VALUE_ANY = "*"

    #: Logical value associated with a not applicable logical value
    VALUE_NA = "-"

    #: Constant associated with wildcard to indicate a sequence of characters
    WILDCARD_MULTI = CPEComponent2_3_WFN.WILDCARD_MULTI
    #: Constant associated with wildcard to indicate a character
    WILDCARD_ONE = CPEComponent2_3_WFN.WILDCARD_ONE

    ###############
    #  VARIABLES  #
    ###############

    # Compilation of regular expression associated with value of CPE part
    _logical = "(\{0}|{1})".format(VALUE_ANY, VALUE_NA)
    _quest = "\{0}".format(WILDCARD_ONE)
    _asterisk = "\{0}".format(WILDCARD_MULTI)
    _special = "{0}|{1}".format(_quest, _asterisk)
    _spec_chrs = "{0}+|{1}".format(_quest, _asterisk)
    _quoted = r"\\(\\" + "|{0}|{1})".format(_special, _PUNC)
    _avstring = "{0}|{1}".format(_UNRESERVED, _quoted)
    _value_string_pattern = "^(({0}+|{1}*({2})+|{3}({4})+)({5})?|{6})$".format(
        _quest, _quest, _avstring, _asterisk, _avstring, _spec_chrs, _logical)

    _part_value_rxc = re.compile(_value_string_pattern)

    ####################
    #  OBJECT METHODS  #
    ####################

    def _decode(self):
        """
        Convert the characters of string s to standard value (WFN value).
        Inspect each character in value of component. Copy quoted characters,
        with their escaping, into the result. Look for unquoted non
        alphanumerics and if not "*" or "?", add escaping.

        :exception: ValueError - invalid character in value of component
        """

        result = []
        idx = 0
        s = self._encoded_value
        embedded = False

        errmsg = []
        errmsg.append("Invalid character '")

        while (idx < len(s)):
            c = s[idx]  # get the idx'th character of s
            errmsg.append(c)
            errmsg.append("'")
            errmsg_str = "".join(errmsg)

            if (CPEComponentSimple._is_alphanum(c)):
                # Alphanumeric characters pass untouched
                result.append(c)
                idx += 1
                embedded = True
                continue

            if c == "\\":
                # Anything quoted in the bound string stays quoted
                # in the unbound string.
                result.append(s[idx: idx + 2])
                idx += 2
                embedded = True
                continue

            if (c == CPEComponent2_3_FS.WILDCARD_MULTI):
                # An unquoted asterisk must appear at the beginning or
                # end of the string.
                if (idx == 0) or (idx == (len(s) - 1)):
                    result.append(c)
                    idx += 1
                    embedded = True
                    continue
                else:
                    raise ValueError(errmsg_str)

            if (c == CPEComponent2_3_FS.WILDCARD_ONE):
                # An unquoted question mark must appear at the beginning or
                # end of the string, or in a leading or trailing sequence:
                # - ? legal at beginning or end
                # - embedded is false, so must be preceded by ?
                # - embedded is true, so must be followed by ?
                if (((idx == 0) or (idx == (len(s) - 1))) or
                   ((not embedded) and (s[idx - 1] == CPEComponent2_3_FS.WILDCARD_ONE)) or
                   (embedded and (s[idx + 1] == CPEComponent2_3_FS.WILDCARD_ONE))):
                    result.append(c)
                    idx += 1
                    embedded = False
                    continue
                else:
                    raise ValueError(errmsg_str)

            # all other characters must be quoted
            result.append("\\")
            result.append(c)
            idx += 1
            embedded = True

        self._standard_value = "".join(result)

    def _is_valid_value(self):
        """
        Return True if the value of component in generic attribute is valid,
        and otherwise False.

        :returns: True if value is valid, False otherwise
        :rtype: boolean
        """

        comp_str = self._standard_value[0]
        return CPEComponent2_3_FS._part_value_rxc.match(comp_str) is not None class CPEComponent2_3_URI_edpacked(CPEComponent2_3_URI):
    """
    Represents a packd edition component of version 2.3 of CPE
    specification with URI style.
    """

    ###############
    #  CONSTANTS  #
    ###############

    #: Separator of components of an edition attribute packed
    SEPARATOR_COMP = "~"

    ####################
    #  OBJECT METHODS  #
    ####################

    def __init__(self, comp_str):
        """
        Store the value of component.

        :param string comp_str: value of component value
        :returns: None
        :exception: ValueError - incorrect value of component
        """

        self.set_value(comp_str)

    def _decode(self):
        """
        The decoding of the value of this type of component is not necesaary.
        """

        pass

    def _is_valid_edition(self):
        """
        This function is not necesaary in this component.

        :returns: True
        :rtype: boolean
        """

        True

    def _is_valid_value(self):
        """
        This function is not necesaary in this component.

        :returns: True
        :rtype: boolean
        """

        True

    def set_value(self, comp_str):
        """
        Set the value of component.

        :param string comp_str: value of component
        :returns: None
        :exception: ValueError - incorrect value of component
        """

        self._is_negated = False
        self._encoded_value = comp_str
        self._standard_value = super(
            CPEComponent2_3_URI_edpacked, self)._decode() class CPEComponentLogical(CPEComponent):
    """
    Represents a generic logical component of CPE name,
    compatible with the components of all versions of CPE specification.
    """

    ###############
    #  CONSTANTS  #
    ###############

    # Logical values in integer format

    #: Value of an undefined component. For example, edition attribute in the
    #: CPE name cpe:/cisco::2345 of version 1.1 of CPE specification
    _VALUE_INT_UNDEFINED = -1

    #: Value of an empty component. For example, product attribute in the
    #: CPE name cpe:/cisco::2345 of version 1.1 of CPE specification
    _VALUE_INT_EMPTY = 0

    #: Value of a component "any value". For example, product attribute in the
    #: CPE name cpe:/h:cisco:*:2345 of version 2.3 of CPE specification
    _VALUE_INT_ANY = 1

    #: Value of a not applicable component. For example, product attribute in
    #: the CPE name cpe:/h:cisco:-:2345 of version 2.3 of CPE specification
    _VALUE_INT_NA = 2

    ####################
    #  OBJECT METHODS  #
    ####################

    def __contains__(self, item):
        """
        Returns True if item is included in set of values of self.

        :param CPEComponent item: component to find in self
        :returns: True if item is included in set of self
        :rtype: boolean
        """

        return True

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        :rtype: boolean
        :exception: NotImplementedError - class method not implemented
        """

        errmsg = "Class method not implemented. Use the method of some child class"
        raise NotImplementedError(errmsg)

    def __str__(self):
        """
        Returns a human-readable representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        :exception: NotImplementedError - class method not implemented
        """

        errmsg = "Class method not implemented. Use the method of some child class"
        raise NotImplementedError(errmsg) class CPEComponentEmpty(CPEComponentLogical):
    """
    Represents an empty component of CPE name,
    compatible with the components of all versions of CPE specification.

    For example, in version 1.1 of CPE specification, an empty component
    is version attribute in CPE name cpe:/microsft:windows::sp2.
    """

    ####################
    #  OBJECT METHODS  #
    ####################

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        :rtype: boolean
        """

        from .cpecomp_anyvalue import CPEComponentAnyValue
        from .cpecomp_undefined import CPEComponentUndefined

        return (isinstance(other, CPEComponentEmpty) or
                isinstance(other, CPEComponentUndefined) or
                isinstance(other, CPEComponentAnyValue))

    def __init__(self):
        """
        Initializes the component.
        """

        super(CPEComponentEmpty, self).__init__(
            CPEComponentLogical._VALUE_INT_EMPTY)

    def __str__(self):
        """
        Returns a human-readable representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        """

        return "<EMPTY>" class CPEComponentAnyValue(CPEComponentLogical):
    """
    Represents a component of CPE name without a particular value,
    compatible with the components of all versions of CPE specification.

    For example, in version 2.3 of CPE specification, an component "any value"
    is other attribute in CPE name
    cpe:2.3:a:microsft:windows:xp:\*:\*:\*:\*:\*:\*:\*.
    """

    ####################
    #  OBJECT METHODS  #
    ####################

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        :rtype: boolean
        """

        from .cpecomp_empty import CPEComponentEmpty
        from .cpecomp_undefined import CPEComponentUndefined

        return (isinstance(other, CPEComponentUndefined) or
                isinstance(other, CPEComponentEmpty) or
                isinstance(other, CPEComponentAnyValue))

    def __init__(self):
        """
        Initializes the component.
        """

        super(CPEComponentAnyValue, self).__init__(
            CPEComponentLogical._VALUE_INT_ANY)

    def __str__(self):
        """
        Returns a human-readable representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        """

        return "<ANY>" class CPEComponentUndefined(CPEComponentLogical):
    """
    Represents an undefined component of CPE name,
    compatible with the components of all versions of CPE specification.

    For example, in version 1.1 of CPE specification, an undefined component
    is edition attribute in CPE name cpe:/microsft:windows:xp.
    """

    ####################
    #  OBJECT METHODS  #
    ####################

    def __contains__(self, item):
        """
        Returns True if item is included in set of values of self.

        :param CPEComponent item: component to find in self
        :returns: True if item is included in set of self
        :rtype: boolean
        """

        return super(CPEComponentUndefined, self).__contains__(item)

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        """

        from .cpecomp_anyvalue import CPEComponentAnyValue
        from .cpecomp_empty import CPEComponentEmpty

        return (isinstance(other, CPEComponentUndefined) or
                isinstance(other, CPEComponentEmpty) or
                isinstance(other, CPEComponentAnyValue))

    def __init__(self):
        """
        Initializes the component.
        """

        super(CPEComponentUndefined, self).__init__(
            CPEComponentLogical._VALUE_INT_UNDEFINED)

    def __str__(self):
        """
        Returns a human-readable representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        """

        return "<UNDEFINED>" class CPEComponentNotApplicable(CPEComponentLogical):
    """
    Represents a component of CPE name with a not applicable value,
    compatible with the components of all versions of CPE specification.

    For example, in version 2.3 of CPE specification, an component "not
    applicable" is update attribute in CPE name cpe:/a:microsft:windows:me:-.
    """

    ####################
    #  OBJECT METHODS  #
    ####################

    def __contains__(self, item):
        """
        Returns True if item is included in set of values of self.

        :param CPEComponent item: component to find in self
        :returns: True if item is included in set of self
        :rtype: boolean
        """

        return (self == item)

    def __eq__(self, other):
        """
        Returns True if other (first element of operation) and
        self (second element of operation) are equal components,
        false otherwise.

        :param CPEComponent other: component to compare
        :returns: True if other == self, False otherwise
        :rtype: boolean
        """

        return isinstance(other, CPEComponentNotApplicable)

    def __init__(self):
        """
        Initializes the component.
        """

        super(CPEComponentNotApplicable, self).__init__(
            CPEComponentLogical._VALUE_INT_NA)

    def __str__(self):
        """
        Returns a human-readable representation of CPE component.

        :returns: Representation of CPE component as string
        :rtype: string
        """

        return "<NA>"
from collections import OrderedDict
from .comp.cpecomp import CPEComponent
from .comp.cpecomp2_3_uri import CPEComponent2_3_URI
from .comp.cpecomp2_3_wfn import CPEComponent2_3_WFN
from .comp.cpecomp2_3_fs import CPEComponent2_3_FS
from .comp.cpecomp2_3_uri_edpacked import CPEComponent2_3_URI_edpacked
from .comp.cpecomp_logical import CPEComponentLogical
from .comp.cpecomp_empty import CPEComponentEmpty
from .comp.cpecomp_anyvalue import CPEComponentAnyValue
from .comp.cpecomp_undefined import CPEComponentUndefined
from .comp.cpecomp_notapplicable import CPEComponentNotApplicable
        from .cpe1_1 import CPE1_1
        from .cpe2_2 import CPE2_2
        from .cpe2_3 import CPE2_3
        from .cpe2_3_wfn import CPE2_3_WFN
    import doctest
                    # Get the value of component encoded in URI
                    v = comp.as_uri_2_3()

            # Append v to the URI and add a separator
            uri.append(v)
            uri.append(CPEComponent2_3_URI.SEPARATOR_COMP)

            if set_prev_comp:
                # Set the previous attribute as logical value any
                v = CPEComponent2_3_URI.VALUE_ANY
                pos_ini = max(len(uri) - len(prev_comp_list) - 1, 1)
                increment = 2  # Count of inserted values

                for p, val in enumerate(prev_comp_list):
                    pos = pos_ini + (p * increment)
                    uri.insert(pos, v)
                    uri.insert(pos + 1, CPEComponent2_3_URI.SEPARATOR_COMP)

                set_prev_comp = False
                prev_comp_list = []

        # Return the URI string, with trailing separator trimmed
        return CPE._trim("".join(uri[:-1]))

    def as_wfn(self):
        """
        Returns the CPE Name as Well-Formed Name string of version 2.3.

        :return: CPE Name as WFN string
        :rtype: string
        :exception: TypeError - incompatible version
        """


        wfn = []
        wfn.append(CPE2_3_WFN.CPE_PREFIX)

        for i in range(0, len(CPEComponent.ordered_comp_parts)):
            ck = CPEComponent.ordered_comp_parts[i]
            lc = self._get_attribute_components(ck)

            if len(lc) > 1:
                # Incompatible version 1.1, there are two or more elements
                # in CPE Name
                errmsg = "Incompatible version {0} with WFN".format(
                    self.VERSION)
                raise TypeError(errmsg)

            else:
                comp = lc[0]

                v = []
                v.append(ck)
                v.append("=")

                if isinstance(comp, CPEComponentAnyValue):

                    # Logical value any
                    v.append(CPEComponent2_3_WFN.VALUE_ANY)

                elif isinstance(comp, CPEComponentNotApplicable):

                    # Logical value not applicable
                    v.append(CPEComponent2_3_WFN.VALUE_NA)

                elif (isinstance(comp, CPEComponentUndefined) or
                      isinstance(comp, CPEComponentEmpty)):
                    # Do not set the attribute
                    continue
                else:
                    # Get the simple value of WFN of component
                    v.append('"')
                    v.append(comp.as_wfn())
                    v.append('"')

                # Append v to the WFN and add a separator
                wfn.append("".join(v))
                wfn.append(CPEComponent2_3_WFN.SEPARATOR_COMP)

        # Del the last separator
        wfn = wfn[:-1]

        # Return the WFN string
        wfn.append(CPE2_3_WFN.CPE_SUFFIX)

        return "".join(wfn)

    def as_fs(self):
        """
        Returns the CPE Name as formatted string of version 2.3.

        :returns: CPE Name as formatted string
        :rtype: string
        :exception: TypeError - incompatible version
        """

        fs = []
        fs.append("cpe:2.3:")

        for i in range(0, len(CPEComponent.ordered_comp_parts)):
            ck = CPEComponent.ordered_comp_parts[i]
            lc = self._get_attribute_components(ck)

            if len(lc) > 1:
                # Incompatible version 1.1, there are two or more elements
                # in CPE Name
                errmsg = "Incompatible version {0} with formatted string".format(
                    self.VERSION)
                raise TypeError(errmsg)

            else:
                comp = lc[0]

                if (isinstance(comp, CPEComponentUndefined) or
                   isinstance(comp, CPEComponentEmpty) or
                   isinstance(comp, CPEComponentAnyValue)):

                    # Logical value any
                    v = CPEComponent2_3_FS.VALUE_ANY


                elif isinstance(comp, CPEComponentNotApplicable):