from handler import HTTPError
import urllib

class Collection(object):
    """ an HTTP collection
    
        essentially this is a container object that has a path that ends on a
        slash, and support for PATH_INFO (so can have (virtual or not)
        children)

        children are callable attributes of ourselves that have an 'exposed'
        attribute themselves, that accept 3 arguments: 'handler', a reference
        to the BaseHTTPHandler that handles the request (XXX should be
        abstracted?), 'path', the requested path to the object, and 'query',
        the (unparsed!) GET query string (without a preceding ?)
    """

    def traverse(self, path, orgpath):
        """ traverse path relative to self

            'path' is the path requested by the client, split on '/', but
            relative from the current object: parent Collection items may have
            removed items (they will have, actually, unless 'self' is the root
            of the website) from the beginning on traversal to 'self'

            path is split on '/', the first item is removed and used to do
            a lookup on self, if that fails a 404 is raised, if successful
            the item is used to continue traversal (if the object found is
            a Collection type) or to handle the request (if the object found
            is a callable with .exposed set to True)

            if path equals '', a lookup for 'index' is done

            can be overridden in subclasses to implement different path
            handling (PATH_INFO-like stuff)
        """
        name = urllib.unquote_plus(path.pop(0))
        if name == '':
            name = 'index'
        resource = getattr(self, name, None)
        if (resource is None or (not isinstance(resource, Collection) and
                (not callable(resource) or
                    not getattr(resource, 'exposed', True)))):
            raise HTTPError(404)
        if path:
            if not isinstance(resource, Collection):
                raise HTTPError(500, 'PATH_INFO is only allowed on Collection')
            return resource.traverse(path, orgpath)
        else:
            if isinstance(resource, Collection):
                # targeting a collection directly: redirect to its 'index'
                raise HTTPError(301, orgpath + '/')
            if not getattr(resource, 'exposed', False):
                # don't reveal what is not accessible...
                raise HTTPError(404)
            return resource


