Beispiel #1
0
    def save(self, name=None):
        """Save the Package in the specified file.

        We expect that the name is a unicode string.
        """
        if name is None:
            name=self.__uri

        name = uri2path(name)
        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z=ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml (using binary mode since serialize is handling encoding)
            stream = open (self.__zip.getContentsFile(), "wb")
            self.serialize(stream)
            stream.close ()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open (name, "wb")
            self.serialize(stream)
            stream.close ()
Beispiel #2
0
    def save(self, name=None):
        """Save the Package in the specified file.

        We expect that the name is a unicode string.
        """
        if name is None:
            name=self.__uri

        name = uri2path(name)
        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z=ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml (using binary mode since serialize is handling encoding)
            stream = open (self.__zip.getContentsFile(), "wb")
            self.serialize(stream)
            stream.close ()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open (name, "wb")
            self.serialize(stream)
            stream.close ()
Beispiel #3
0
    def save(self, name=None):
        """Save the Package in the specified file"""
        if name is None:
            name=self.__uri
        if name.startswith('file:///'):
            name = name[7:]

        if re.match('|/', name):
            # Windows drive: notation. Convert it from
            # a more URI-compatible syntax
            name=urllib.url2pathname(name)

        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z=ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml
            stream = open (self.__zip.getContentsFile(), "w")
            self.serialize(stream)
            stream.close ()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open (name, "w")
            self.serialize(stream)
            stream.close ()
Beispiel #4
0
    def save(self, name=None):
        """Save the Package in the specified file"""
        if name is None:
            name = self.__uri
        if name.startswith('file:///'):
            name = name[7:]

        if re.match('|/', name):
            # Windows drive: notation. Convert it from
            # a more URI-compatible syntax
            name = urllib.url2pathname(name)

        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z = ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml
            stream = open(self.__zip.getContentsFile(), "w")
            self.serialize(stream)
            stream.close()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open(name, "w")
            self.serialize(stream)
            stream.close()
Beispiel #5
0
    def __init__(self, uri, source=_get_from_uri, importer=None):
        """Calling the constructor with just a URI tries to read the package
           from this URI. This can be overidden by providing explicitly the
           source parameter (a URL or a stream).
           Providing None for the source parameter creates a new Package.
        """
        self.meta_cache={}
        if isinstance(uri, unicode):
            uri=uri.encode(sys.getfilesystemencoding())
        if re.match('[a-zA-Z]:', uri):
            # Windows drive: notation. Convert it to
            # a more URI-compatible syntax
            uri=urllib.pathname2url(uri)
        self.__uri = uri
        self.__importer = importer
        # Possible container
        self.__zip = None
        abs_uri = self.getUri (absolute=True)

        if importer:
            importer.__pkg_cache[abs_uri] = self
            self.__pkg_cache = importer.__pkg_cache
        else:
            self.__pkg_cache = {abs_uri:self}

        element = None
        if source is None:
            element = self._make_model()
        else:
            reader = PyExpat.Reader()
            if source is _get_from_uri:
                # Determine the package format (plain XML or AZP)
                # FIXME: should be done by content rather than extension
                if abs_uri.lower().endswith('.azp') or abs_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(abs_uri)
                    f=urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(abs_uri).documentElement
            elif hasattr(source, 'read'):
                element = reader.fromStream(source).documentElement
            else:
                if re.match('[a-zA-Z]:', source):
                    # Windows drive: notation. Convert it to
                    # a more URI-compatible syntax
                    source=urllib.pathname2url(source)
                source_uri = util.uri.urljoin (
                    'file:%s/' % urllib.pathname2url (os.getcwd ()),
                     str(source)
                )

                if source_uri.lower().endswith('.azp') or source_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(source_uri)
                    f=urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(source_uri).documentElement

        modeled.Modeled.__init__(self, element, None)

        self.__imports = None
        self.__annotations = None
        self.__queries = None
        self.__relations = None
        self.__schemas = None
        self.__views = None
Beispiel #6
0
class Package(modeled.Modeled, viewable.Viewable.withClass('package'),
             _impl.Authored, _impl.Dated, _impl.Titled,
             annotation.AnnotationFactory,
             annotation.RelationFactory,
             schema.SchemaFactory,
             query.QueryFactory,
             view.ViewFactory,
             ):

    """A package is the container of all the elements of an annotation
    (schemas, types, annotations, relations, views, queries). It
    provides factory methods to create attached annotations, views, ..."""

    __metaclass__ = auto_properties

    def __init__(self, uri, source=_get_from_uri, importer=None):
        """Calling the constructor with just a URI tries to read the package
           from this URI. This can be overidden by providing explicitly the
           source parameter (a URL or a stream).
           Providing None for the source parameter creates a new Package.
        """
        self.meta_cache={}
        if isinstance(uri, unicode):
            uri=uri.encode(sys.getfilesystemencoding())
        if re.match('[a-zA-Z]:', uri):
            # Windows drive: notation. Convert it to
            # a more URI-compatible syntax
            uri=urllib.pathname2url(uri)
        self.__uri = uri
        self.__importer = importer
        # Possible container
        self.__zip = None
        abs_uri = self.getUri (absolute=True)

        if importer:
            importer.__pkg_cache[abs_uri] = self
            self.__pkg_cache = importer.__pkg_cache
        else:
            self.__pkg_cache = {abs_uri:self}

        element = None
        if source is None:
            element = self._make_model()
        else:
            reader = PyExpat.Reader()
            if source is _get_from_uri:
                # Determine the package format (plain XML or AZP)
                # FIXME: should be done by content rather than extension
                if abs_uri.lower().endswith('.azp') or abs_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(abs_uri)
                    f=urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(abs_uri).documentElement
            elif hasattr(source, 'read'):
                element = reader.fromStream(source).documentElement
            else:
                if re.match('[a-zA-Z]:', source):
                    # Windows drive: notation. Convert it to
                    # a more URI-compatible syntax
                    source=urllib.pathname2url(source)
                source_uri = util.uri.urljoin (
                    'file:%s/' % urllib.pathname2url (os.getcwd ()),
                     str(source)
                )

                if source_uri.lower().endswith('.azp') or source_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(source_uri)
                    f=urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(source_uri).documentElement

        modeled.Modeled.__init__(self, element, None)

        self.__imports = None
        self.__annotations = None
        self.__queries = None
        self.__relations = None
        self.__schemas = None
        self.__views = None

    def close(self):
        if self.__zip:
            self.__zip.close()
        return

    def __str__(self):
        """Return a nice string representation of the object."""
        return "Package (%s)" % self.__uri

    def _make_model(self):
        """Build a new empty annotation model"""
        di = xml.dom.getDOMImplementation()
        doc = di.createDocument(adveneNS, "package", None)

        elt = doc.documentElement
        elt.setAttributeNS(xmlNS,   "xml:base", unicode(self.__uri))
        elt.setAttributeNS(xmlnsNS, "xmlns", adveneNS)
        elt.setAttributeNS(xmlnsNS, "xmlns:xlink", xlinkNS)
        elt.setAttributeNS(xmlnsNS, "xmlns:dc", dcNS)
        elt.setAttributeNS(dcNS,    "dc:creator", "")

        elt.appendChild(doc.createElementNS(adveneNS, "imports"))
        elt.appendChild(doc.createElementNS(adveneNS, "annotations"))
        elt.appendChild(doc.createElementNS(adveneNS, "queries"))
        elt.appendChild(doc.createElementNS(adveneNS, "schemas"))
        elt.appendChild(doc.createElementNS(adveneNS, "views"))
        return elt

    def _get_cached(self, uri):
        """Return a cached version of the package designated by "uri" """
        return self.__pkg_cache.get(uri, None)

    def getOwnerPackage(self):
        """Return this package. Used for breaking recursivity in the parenthood tree."""
        return self

    def getAccessPath(self):
        if self.__importer:
            r = list(self.__importer.getAccessPath())
            r.append(self)
            return tuple(r)
        else:
            return (self,)

    def getRootPackage(self):
        if self.__importer:
            return self.__importer.getRootPackage()
        else:
            return self

    def getUri(self, absolute=True, context=None):
        """
        Return the URI of the package.

        Parameter if _absolute_ is _True_, the URI will be forced absolute.
        If not, and if _context_ is _None_, the URI will be resolved with
        respect to the root package URI, whatever its stored form (absolute or
        relative).
        If context is given and is a (direct or indirect) importer package,
        the URI will be resolved with respect to the context URI, whatever its
        stored form (absolute or relative).

        You would probably rather use the uri read-only property, unless you
        want to set the parameter _absolute_.
        """
        uri = self.__uri

        if not absolute and context is self:
            return ''

        importer = self.__importer
        if importer is not None:
            uri = util.uri.urljoin (importer.getUri (absolute, context), uri)

        if absolute:
            base_uri = 'file:%s/' % urllib.pathname2url (os.getcwd ())
            uri = util.uri.urljoin(base_uri, uri)

        return uri

    def getImports (self):
        """Return a collection of this package's imports"""
        if self.__imports is None:
            e = self._getChild ( (adveneNS, "imports") )
            self.__imports = InverseDictBundle (self, e, Import, Import.getAlias)
        return self.__imports

    def getAnnotations(self):
        """Return a collection of this package's annotations"""
        if self.__annotations is None:
            e = self._getChild((adveneNS, "annotations"))
            self.__annotations = StandardXmlBundle(self, e, annotation.Annotation)
        return self.__annotations

    def getRelations(self):
        """Return a collection of this package's relations"""
        if self.__relations is None:
            e = self._getChild((adveneNS, "annotations"))
            # yes, "annotations"!
            #relations are under the same element as annotations
            # FIXME: is this always the case ?
            self.__relations = StandardXmlBundle(self, e, annotation.Relation)
        return self.__relations

    def getSchemas(self):
        """Return a collection of this package's schemas"""
        if self.__schemas is None:
            e = self._getChild((adveneNS, "schemas"))
            self.__schemas = ImportBundle(self, e, schema.Schema)
        return self.__schemas

    def getViews(self):
        """Return a collection of this package's view"""
        if self.__views is None:
            e = self._getChild((adveneNS, "views"))
            self.__views = ImportBundle(self, e, view.View)
        return self.__views

    def getQueries(self):
        """Return a collection of this package's queries"""
        if self.__queries is None:
            e = self._getChild((adveneNS, "queries"))
            self.__queries = ImportBundle(self, e, query.Query)
        return self.__queries

    def getAnnotationTypes (self):
        """Return a collection of this package's annotation types"""
        r = SumBundle ()
        for s in self.getSchemas ():
            r += s.getAnnotationTypes ()
        return r

    def getRelationTypes(self):
        """Return a collection of this package's relation types"""
        r = SumBundle ()
        for s in self.getSchemas ():
            r += s.getRelationTypes ()
        return r

    def getResources(self):
        if self.__zip is None:
            return None
        else:
            return self.__zip.getResources(package=self)

    def get_element_by_id(self, i):
        if not i:
            return None
        uri=self.uri
        for m in (self.getSchemas, self.getViews, self.getAnnotationTypes,
                  self.getRelationTypes, self.getAnnotations, self.getQueries,
                  self.getRelations):
            el=m().get( '#'.join( (uri, i) ), None )
            if el is not None:
                return el
        return None

    def generate_statistics(self):
        """Generate the statistics.xml file.
        """
        out=u"""<?xml version="1.0" encoding="UTF-8"?>
    <statistics:statistics xmlns:statistics="urn:advene:names:tc:opendocument:xmlns:manifest:1.0">
    """
        # Note: we do not use urllib.quote, since it chokes on non-ASCII characters (unicode)
        out += u"""<statistics:title value="%s" />""" % ((self.title or '').replace('"', '%22') or "")
        out += u"""<statistics:description value="%s" />""" % ((self.getMetaData(config.data.namespace_prefix['dc'], 'description') or "").replace('"', '%22') or "")
        for n, l in ( ('schema', len(self.schemas)),
                      ('annotation', len(self.annotations)),
                      ('annotation_type', len(self.annotationTypes)),
                      ('relation', len(self.relations)),
                      ('relation_type', len(self.relationTypes)),
                      ('query', len(self.queries)),
                      ('view', len(self.views)) ):
            out += u"""<statistics:item name="%s" value="%d" />""" % (n, l)
        out += u"""</statistics:statistics>"""
        return out

    def serialize(self, stream=sys.stdout):
        """Serialize the Package on the specified stream"""
        stream.write(self._getModel().toxml(encoding='utf8'))

    def save(self, name=None):
        """Save the Package in the specified file"""
        if name is None:
            name=self.__uri
        if name.startswith('file:///'):
            name = name[7:]

        if re.match('|/', name):
            # Windows drive: notation. Convert it from
            # a more URI-compatible syntax
            name=urllib.url2pathname(name)

        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z=ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml
            stream = open (self.__zip.getContentsFile(), "w")
            self.serialize(stream)
            stream.close ()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open (name, "w")
            self.serialize(stream)
            stream.close ()

    def _recursive_save (self):
        """Save recursively this packages with all its imported packages"""
        self.save ()
        for i in self.getImports ():
            i.getPackage ()._recursive_save ()

    def getQnamePrefix (self, item):
        if self == item:
            return None
        if isinstance (item, Package):
            try:
                i=self.getImports()[item.uri]
                return i.getAlias()
            except KeyError:
                raise AdveneException ("item %s has no QName prefix in %s" %
                                       (item, self))
        else:
            return self.getQnamePrefix(item._getParent())
Beispiel #7
0
    def __init__(self, uri, source=_get_from_uri, importer=None):
        """Calling the constructor with just a URI tries to read the package
           from this URI. This can be overidden by providing explicitly the
           source parameter (a URL or a stream).
           Providing None for the source parameter creates a new Package.
        """
        self.meta_cache = {}
        if isinstance(uri, unicode):
            uri = uri.encode(sys.getfilesystemencoding())
        if re.match('[a-zA-Z]:', uri):
            # Windows drive: notation. Convert it to
            # a more URI-compatible syntax
            uri = urllib.pathname2url(uri)
        self.__uri = uri
        self.__importer = importer
        # Possible container
        self.__zip = None
        abs_uri = self.getUri(absolute=True)

        if importer:
            importer.__pkg_cache[abs_uri] = self
            self.__pkg_cache = importer.__pkg_cache
        else:
            self.__pkg_cache = {abs_uri: self}

        element = None
        if source is None:
            element = self._make_model()
        else:
            reader = PyExpat.Reader()
            if source is _get_from_uri:
                # Determine the package format (plain XML or AZP)
                # FIXME: should be done by content rather than extension
                if abs_uri.lower().endswith('.azp') or abs_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(abs_uri)
                    f = urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(abs_uri).documentElement
            elif hasattr(source, 'read'):
                element = reader.fromStream(source).documentElement
            else:
                if re.match('[a-zA-Z]:', source):
                    # Windows drive: notation. Convert it to
                    # a more URI-compatible syntax
                    source = urllib.pathname2url(source)
                source_uri = util.uri.urljoin(
                    'file:%s/' % urllib.pathname2url(os.getcwd()), str(source))

                if source_uri.lower().endswith('.azp') or source_uri.endswith(
                        '/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(source_uri)
                    f = urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(source_uri).documentElement

        modeled.Modeled.__init__(self, element, None)

        self.__imports = None
        self.__annotations = None
        self.__queries = None
        self.__relations = None
        self.__schemas = None
        self.__views = None
Beispiel #8
0
class Package(
        modeled.Modeled,
        viewable.Viewable.withClass('package'),
        _impl.Authored,
        _impl.Dated,
        _impl.Titled,
        annotation.AnnotationFactory,
        annotation.RelationFactory,
        schema.SchemaFactory,
        query.QueryFactory,
        view.ViewFactory,
):
    """A package is the container of all the elements of an annotation
    (schemas, types, annotations, relations, views, queries). It
    provides factory methods to create attached annotations, views, ..."""

    __metaclass__ = auto_properties

    def __init__(self, uri, source=_get_from_uri, importer=None):
        """Calling the constructor with just a URI tries to read the package
           from this URI. This can be overidden by providing explicitly the
           source parameter (a URL or a stream).
           Providing None for the source parameter creates a new Package.
        """
        self.meta_cache = {}
        if isinstance(uri, unicode):
            uri = uri.encode(sys.getfilesystemencoding())
        if re.match('[a-zA-Z]:', uri):
            # Windows drive: notation. Convert it to
            # a more URI-compatible syntax
            uri = urllib.pathname2url(uri)
        self.__uri = uri
        self.__importer = importer
        # Possible container
        self.__zip = None
        abs_uri = self.getUri(absolute=True)

        if importer:
            importer.__pkg_cache[abs_uri] = self
            self.__pkg_cache = importer.__pkg_cache
        else:
            self.__pkg_cache = {abs_uri: self}

        element = None
        if source is None:
            element = self._make_model()
        else:
            reader = PyExpat.Reader()
            if source is _get_from_uri:
                # Determine the package format (plain XML or AZP)
                # FIXME: should be done by content rather than extension
                if abs_uri.lower().endswith('.azp') or abs_uri.endswith('/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(abs_uri)
                    f = urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(abs_uri).documentElement
            elif hasattr(source, 'read'):
                element = reader.fromStream(source).documentElement
            else:
                if re.match('[a-zA-Z]:', source):
                    # Windows drive: notation. Convert it to
                    # a more URI-compatible syntax
                    source = urllib.pathname2url(source)
                source_uri = util.uri.urljoin(
                    'file:%s/' % urllib.pathname2url(os.getcwd()), str(source))

                if source_uri.lower().endswith('.azp') or source_uri.endswith(
                        '/'):
                    # Advene Zip Package. Do some magic.
                    self.__zip = ZipPackage(source_uri)
                    f = urllib.pathname2url(self.__zip.getContentsFile())
                    element = reader.fromUri("file://" + f).documentElement
                else:
                    element = reader.fromUri(source_uri).documentElement

        modeled.Modeled.__init__(self, element, None)

        self.__imports = None
        self.__annotations = None
        self.__queries = None
        self.__relations = None
        self.__schemas = None
        self.__views = None

    def close(self):
        if self.__zip:
            self.__zip.close()
        return

    def __str__(self):
        """Return a nice string representation of the object."""
        return "Package (%s)" % self.__uri

    def _make_model(self):
        """Build a new empty annotation model"""
        di = xml.dom.getDOMImplementation()
        doc = di.createDocument(adveneNS, "package", None)

        elt = doc.documentElement
        elt.setAttributeNS(xmlNS, "xml:base", unicode(self.__uri))
        elt.setAttributeNS(xmlnsNS, "xmlns", adveneNS)
        elt.setAttributeNS(xmlnsNS, "xmlns:xlink", xlinkNS)
        elt.setAttributeNS(xmlnsNS, "xmlns:dc", dcNS)
        elt.setAttributeNS(dcNS, "dc:creator", "")

        elt.appendChild(doc.createElementNS(adveneNS, "imports"))
        elt.appendChild(doc.createElementNS(adveneNS, "annotations"))
        elt.appendChild(doc.createElementNS(adveneNS, "queries"))
        elt.appendChild(doc.createElementNS(adveneNS, "schemas"))
        elt.appendChild(doc.createElementNS(adveneNS, "views"))
        return elt

    def _get_cached(self, uri):
        """Return a cached version of the package designated by "uri" """
        return self.__pkg_cache.get(uri, None)

    def getOwnerPackage(self):
        """Return this package. Used for breaking recursivity in the parenthood tree."""
        return self

    def getAccessPath(self):
        if self.__importer:
            r = list(self.__importer.getAccessPath())
            r.append(self)
            return tuple(r)
        else:
            return (self, )

    def getRootPackage(self):
        if self.__importer:
            return self.__importer.getRootPackage()
        else:
            return self

    def getUri(self, absolute=True, context=None):
        """
        Return the URI of the package.

        Parameter if _absolute_ is _True_, the URI will be forced absolute.
        If not, and if _context_ is _None_, the URI will be resolved with
        respect to the root package URI, whatever its stored form (absolute or
        relative).
        If context is given and is a (direct or indirect) importer package,
        the URI will be resolved with respect to the context URI, whatever its
        stored form (absolute or relative).

        You would probably rather use the uri read-only property, unless you
        want to set the parameter _absolute_.
        """
        uri = self.__uri

        if not absolute and context is self:
            return ''

        importer = self.__importer
        if importer is not None:
            uri = util.uri.urljoin(importer.getUri(absolute, context), uri)

        if absolute:
            base_uri = 'file:%s/' % urllib.pathname2url(os.getcwd())
            uri = util.uri.urljoin(base_uri, uri)

        return uri

    def getImports(self):
        """Return a collection of this package's imports"""
        if self.__imports is None:
            e = self._getChild((adveneNS, "imports"))
            self.__imports = InverseDictBundle(self, e, Import,
                                               Import.getAlias)
        return self.__imports

    def getAnnotations(self):
        """Return a collection of this package's annotations"""
        if self.__annotations is None:
            e = self._getChild((adveneNS, "annotations"))
            self.__annotations = StandardXmlBundle(self, e,
                                                   annotation.Annotation)
        return self.__annotations

    def getRelations(self):
        """Return a collection of this package's relations"""
        if self.__relations is None:
            e = self._getChild((adveneNS, "annotations"))
            # yes, "annotations"!
            #relations are under the same element as annotations
            # FIXME: is this always the case ?
            self.__relations = StandardXmlBundle(self, e, annotation.Relation)
        return self.__relations

    def getSchemas(self):
        """Return a collection of this package's schemas"""
        if self.__schemas is None:
            e = self._getChild((adveneNS, "schemas"))
            self.__schemas = ImportBundle(self, e, schema.Schema)
        return self.__schemas

    def getViews(self):
        """Return a collection of this package's view"""
        if self.__views is None:
            e = self._getChild((adveneNS, "views"))
            self.__views = ImportBundle(self, e, view.View)
        return self.__views

    def getQueries(self):
        """Return a collection of this package's queries"""
        if self.__queries is None:
            e = self._getChild((adveneNS, "queries"))
            self.__queries = ImportBundle(self, e, query.Query)
        return self.__queries

    def getAnnotationTypes(self):
        """Return a collection of this package's annotation types"""
        r = SumBundle()
        for s in self.getSchemas():
            r += s.getAnnotationTypes()
        return r

    def getRelationTypes(self):
        """Return a collection of this package's relation types"""
        r = SumBundle()
        for s in self.getSchemas():
            r += s.getRelationTypes()
        return r

    def getResources(self):
        if self.__zip is None:
            return None
        else:
            return self.__zip.getResources(package=self)

    def get_element_by_id(self, i):
        if not i:
            return None
        uri = self.uri
        for m in (self.getSchemas, self.getViews, self.getAnnotationTypes,
                  self.getRelationTypes, self.getAnnotations, self.getQueries,
                  self.getRelations):
            el = m().get('#'.join((uri, i)), None)
            if el is not None:
                return el
        return None

    def generate_statistics(self):
        """Generate the statistics.xml file.
        """
        out = u"""<?xml version="1.0" encoding="UTF-8"?>
    <statistics:statistics xmlns:statistics="urn:advene:names:tc:opendocument:xmlns:manifest:1.0">
    """
        # Note: we do not use urllib.quote, since it chokes on non-ASCII characters (unicode)
        out += u"""<statistics:title value="%s" />""" % (
            (self.title or '').replace('"', '%22') or "")
        out += u"""<statistics:description value="%s" />""" % (
            (self.getMetaData(config.data.namespace_prefix['dc'],
                              'description') or "").replace('"', '%22') or "")
        for n, l in (('schema', len(self.schemas)), ('annotation',
                                                     len(self.annotations)),
                     ('annotation_type', len(self.annotationTypes)),
                     ('relation', len(self.relations)),
                     ('relation_type', len(self.relationTypes)),
                     ('query', len(self.queries)), ('view', len(self.views))):
            out += u"""<statistics:item name="%s" value="%d" />""" % (n, l)
        out += u"""</statistics:statistics>"""
        return out

    def serialize(self, stream=sys.stdout):
        """Serialize the Package on the specified stream"""
        stream.write(self._getModel().toxml(encoding='utf8'))

    def save(self, name=None):
        """Save the Package in the specified file"""
        if name is None:
            name = self.__uri
        if name.startswith('file:///'):
            name = name[7:]

        if re.match('|/', name):
            # Windows drive: notation. Convert it from
            # a more URI-compatible syntax
            name = urllib.url2pathname(name)

        # handle .azp files.
        if name.lower().endswith('.azp') or name.endswith('/'):
            # AZP format
            if self.__zip is None:
                # Conversion necessary: we are saving to the new format.
                z = ZipPackage()
                z.new()
                self.__zip = z

            # Save the content.xml
            stream = open(self.__zip.getContentsFile(), "w")
            self.serialize(stream)
            stream.close()

            # Generate the statistics
            self.__zip.update_statistics(self)

            # Save the whole .azp
            self.__zip.save(name)
        else:
            # Assuming plain XML format
            stream = open(name, "w")
            self.serialize(stream)
            stream.close()

    def _recursive_save(self):
        """Save recursively this packages with all its imported packages"""
        self.save()
        for i in self.getImports():
            i.getPackage()._recursive_save()

    def getQnamePrefix(self, item):
        if self == item:
            return None
        if isinstance(item, Package):
            try:
                i = self.getImports()[item.uri]
                return i.getAlias()
            except KeyError:
                raise AdveneException("item %s has no QName prefix in %s" %
                                      (item, self))
        else:
            return self.getQnamePrefix(item._getParent())