1 # -*- coding: utf-8 -*-
 
   2 from xml.parsers.expat import ExpatError
 
   4 # Import ElementTree from anywhere
 
   6     import xml.etree.ElementTree as ET # Python >= 2.5
 
   9         import elementtree.ElementTree as ET # effbot's pure Python module
 
  11         import lxml.etree as ET # ElementTree API using libxml2
 
  17 __all__ = ('parse', 'ParseError')
 
  21 class ParseError(Exception):
 
  22     def __init__(self, message):
 
  23         super(self, Exception).__init__(message)
 
  27 class XMLNamespace(object):
 
  28     '''Represents XML namespace.'''
 
  30     def __init__(self, uri):
 
  33     def __call__(self, tag):
 
  34         return '{%s}%s' % (self.uri, tag)
 
  36     def __contains__(self, tag):
 
  37         return tag.startswith(str(self))
 
  40         return 'NS(%r)' % self.uri
 
  43         return '%s' % self.uri
 
  47 class BookInfo(object):
 
  48     RDF = XMLNamespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
 
  49     DC = XMLNamespace('http://purl.org/dc/elements/1.1/')
 
  52         DC('creator')        : ('author', converters.str_to_person),
 
  53         DC('title')          : ('title', converters.str_to_unicode),
 
  54         DC('subject.period') : ('epoch', converters.str_to_unicode),
 
  55         DC('subject.type')   : ('kind', converters.str_to_unicode),
 
  56         DC('subject.genre')  : ('genre', converters.str_to_unicode),
 
  57         DC('date')           : ('created_at', converters.str_to_date),
 
  58         DC('date.pd')        : ('released_to_public_domain_at', converters.str_to_date),
 
  59         DC('contributor.translator') : ('translator', converters.str_to_person),
 
  60         DC('contributor.technical_editor') : ('technical_editor', converters.str_to_person),
 
  61         DC('publisher')      : ('publisher', converters.str_to_unicode),
 
  62         DC('source')         : ('source_name', converters.str_to_unicode),
 
  63         DC('source.URL')     : ('source_url', converters.str_to_unicode),
 
  68     def from_string(cls, xml):
 
  69         """docstring for from_string"""
 
  70         from StringIO import StringIO
 
  71         return cls.from_file(StringIO(xml))
 
  75     def from_file(cls, xml_file):
 
  79             tree = ET.parse(xml_file)
 
  83         description = tree.find('//' + book_info.RDF('Description'))
 
  84         if description is None:
 
  85             raise ParseError('no Description tag found in document')
 
  87         for element in description.findall('*'):
 
  88             book_info.parse_element(element) 
 
  93     def parse_element(self, element):
 
  95             attribute, converter = self.mapping[element.tag]
 
  96             setattr(self, attribute, converter(element.text))
 
 102         """XML representation of this object."""
 
 103         ET._namespace_map[str(self.RDF)] = 'rdf'
 
 104         ET._namespace_map[str(self.DC)] = 'dc'
 
 106         root = ET.Element(self.RDF('RDF'))
 
 107         description = ET.SubElement(root, self.RDF('Description'))
 
 109         for tag, (attribute, converter) in self.mapping.iteritems():
 
 110             if hasattr(self, attribute):
 
 112                 e.text = unicode(getattr(self, attribute))
 
 113                 description.append(e)
 
 115         return unicode(ET.tostring(root, 'utf-8'), 'utf-8')
 
 118 def parse(file_name):
 
 119     return BookInfo.from_file(file_name)