"""ResourceSync Capability List object
An Capability List is a set of capabilitys with some metadata for 
each capability. The Capability List object may also contain metadata 
and links like other lists.
import collections
from resource import Resource
from resource_set import ResourceSet
from list_base import ListBase
from sitemap import Sitemap
class CapabilitySet(ResourceSet):
    """Class for storage of resources in a Capability List
    Extends the ResourceSet to add checks to ensure that there are 
    never two entries for the same resource, and that values are 
    returned in the canonical order.
    def __init__(self):
        self.order = [ 'resourcelist', 'resourcedump',
                       'changelist', 'changedump',
                       'resourcelist-archive', 'resourcedump-archive',
                       'changelist-archive', 'changedump-archive' ]
    def __iter__(self):
        """Iterator over all the resources in capability order
        Deals with the case of unknown capabilities or duplicate entries
        by using uri order for duplicates and adding any unknown ones 
        at the end
        self._iter_next_list = []
        # look through all resources and build capability to uri index
        uris = {}
        for uri in self.keys():
            cap = self[uri].capability
            if (cap not in uris):
        # build list or uris in defined order for iterator
        for cap in self.order:
            if (cap in uris):
                for uri in sorted(uris[cap]):
                del uris[cap]
        # add any left over capabilities we don't know about in alphabetical order
        for cap in uris:
            for uri in sorted(uris[cap]):
        return(iter(self._iter_next, None))
    def _iter_next(self):
        if (len(self._iter_next_list)>0):
class CapabilityList(ListBase):
    """Class representing a Capability List
    An Capability List will admit only one resource with any given 
    URI. The iterator over resources is expected to return them in
    canonical order of capability names as defined in main specification
    section 7 and archives specification section 6.
    def __init__(self, resources=None, md=None, ln=None, uri=None):
        if (resources is None):
            resources = CapabilitySet()
        super(CapabilityList, self).__init__(resources=resources, md=md, ln=ln, uri=uri,
    def add(self, resource, replace=False):
        """Add a resource or an iterable collection of resources
        Will throw a ValueError if the resource (ie. same uri) already
        exists in the capability_list, unless replace=True.
        if isinstance(resource, collections.Iterable):
            for r in resource:
    def add_capability(self,capability=None,uri=None,name=None):
        """Specific add function for capabilities
        Takes either:
        - a capability object (derived from ListBase) as the first argument 
          from which the capability name is extracted, and the URI if given
        - or a plain name string
        - the URI of the capability
        if (capability is not None):
            name = capability.capability_name
            if (capability.uri is not None):
        self.add( Resource(uri=uri,capability=name) )
    def has_capability(self,name=None):
        """True if the Capability List includes the named capability"""
        return( self.capability(name) is not None )
    def capability(self,name=None):
        """Return information about the requested capability from this list
        Will return None if there is no information about the requested capability
        for r in self.resources:
            if (r.capability == name):