text: """
  Copyright (c) 2009, Horst Gutmann
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:
  
  * Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
  
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  
  * Neither the name of the project nor the names of its contributors may
    be used to endorse or promote products derived from this software without
    specific prior written permission.
  
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  -----------------------------------------------------------------------------
  
  This module holds all the structures produced by the parser. The main
  structures are the clases ``Bibliography`` and ``Entry``. Both are
  slightly enhanced subclasses of ``dict`` and offer some additional
  field validation.
  
  Entry also has a handful of subclasses; one for each common entry-type
  in BibTeX.
  """
  
  from ..bibtex import exceptions
  
  
  class TypeRegistry(object):
      """
      Global registry for entry types.
      """
      _registry = {}
  
      @classmethod
      def register(cls, name, type_):
          """
          Register a new type for an entry-type. The 2nd argument has to be a
          subclass of structures.Entry.
          """
  
          if not issubclass(type_, Entry):
              raise exceptions.InvalidEntryType("%s is not a subclass of Entry" % str(type_))
          cls._registry[name.lower()] = type_
  
      @classmethod
      def get_type(cls, name):
          """
          Retrieve a type from the registry using its name as used in a bibtex
          file.
          """
          return cls._registry.get(name.lower())
  
  class Bibliography(dict):
      """
      A counter for all entries of a BibTeX file. It also contains the
      cross-reference validator.
      """
  
      def __init__(self):
          self.crossrefs = []
          super(Bibliography, self).__init__()
  
      def validate(self, **kwargs):
          """
          Validates each entry (passing the provided arguments down to them and
          also tries to resolve all cross-references between the entries.
          """
          self.check_crossrefs()
          for value in self.values():
              value.validate(**kwargs)
  
      def check_crossrefs(self):
          """
          Checks all crossreferences found in the bibliography. If one can not
          be resolved from *this* Bibliography instance, a BrokenCrossReferences
          exception is raised.
          """
          broken = []
          for value in self.values():
              crossref = value.get('crossref')
              if crossref is not None:
                  if crossref not in self:
                      broken.append(value)
          if len(broken):
              raise exceptions.BrokenCrossReferences('One or more cross reference could not'
                      ' be resolved', broken)
  
      def add(self, entry):
          """
          Add an entry based on its ``name``-attribute to the Bibliography.
          """
          self[entry.name] = entry
  
  class Entry(dict):
      """
      A slightly enhanced dict structure that acts as representation of an entry
      within a bibliography. It also comes with a generic validator for required
      and potentially unsupported fields and acts as base-class for the actual
      entry-types commonly used in BibTeX.
      """
  
      required_fields = ('title',)
      optional_fields = ('key', )
  
      def __init__(self, name=None, **kwargs):
          super(Entry, self).__init__(**kwargs)
          self.name = name
  
      def validate(self, raise_unsupported=False):
          """
          Checks if the Entry instance includes all the required fields of its
          type. If ``raise_unsupported`` is set to ``True`` it will also check
          for potentially unsupported types.
  
          If a problem is found, an InvalidStructure exception is raised.
          """
          fields = set(self.keys())
          flattened_required_fields = set()
          required_errors = []
          for field in self.required_fields:
              found = False
              if isinstance(field, (list, tuple)):
                  # Check all alternatives
                  for real_f in field:
                      if real_f in fields:
                          flattened_required_fields.add(real_f)
                          found = True
              else:
                  flattened_required_fields.add(field)
                  if field in fields:
                      found = True
              if not found:
                  required_errors.append(field)
          unsupported_fields = fields - flattened_required_fields \
                  - set(self.optional_fields)
          if len(required_errors) or (raise_unsupported
                  and len(unsupported_fields)):
              raise exceptions.InvalidStructure("Missing or unsupported fields found",
                      required_fields=required_errors,
                      unsupported_fields=unsupported_fields)
  
  # The following required_fields/optiona_fields attributes are based on
  # http://en.wikipedia.org/wiki/Bibtex
  
  class Article(Entry):
      """Article in a journal, magazine etc."""
  
      required_fields = ('author', 'title', 'journal', 'year')
      optional_fields = ('volume', 'number', 'pages', 'month', 'note', 'key')
  
  TypeRegistry.register('article', Article)
  
  
  class Book(Entry):
      """A book that has already been published or at least has a publisher."""
  
      required_fields = (('author', 'editor'), 'title', 'publisher', 'year')
      optional_fields = ('address', 'pages', 'volume', 'series', 'edition',
              'month', 'note', 'key')
  
  TypeRegistry.register('book', Book)
  
  class Booklet(Entry):
      """
      Similar to a book in the sense that it is bound but without a "real"
      publisher.
      """
  
      required_fields = Entry.required_fields
      optional_fields = ('author', 'howpublished', 'address', 'month', 'year',
              'note', 'key')
  
  TypeRegistry.register('booklet', Booklet)
  
  
  class Incollection(Entry):
      """Part of a book but with its own title."""
  
      required_fields = ('author', 'title', 'year', 'booktitle'),
      optional_fields = ('editor', 'pages', 'organization', 'publisher',
              'address', 'month', 'note', 'key')
  
  TypeRegistry.register('incollection', Incollection)
  
  
  class Inproceedings(Incollection):
      """Article that is part of a conference proceedings."""
  
      pass
  
  TypeRegistry.register('inproceedings', Inproceedings)
  
  
  class Conference(Inproceedings):
      """Similar to ``Inproceedings``."""
  
      required_fields = ('author', 'title', 'booktitle', 'year')
      optional_fields = ('editor', 'pages', 'organization', 'publisher',
              'address', 'month', 'note', 'key')
  
  TypeRegistry.register('conference', Conference)
  
  
  class Inbook(Entry):
      """Part of a book."""
  
      required_fields = (('author', 'editor'), 'title', 'publisher', 'year',
              ('chapter', 'pages'))
      optional_fields = ('volume', 'series', 'address', 'edition', 'month',
              'note', 'key')
  
  TypeRegistry.register('inbook', Inbook)
  
  
  class Manual(Entry):
      """A technical manual."""
  
      required_fields = ('title',)
      optional_fields = ('author', 'organization', 'address', 'edition', 'year',
              'month', 'note', 'key')
  
  TypeRegistry.register('manual', Manual)
  
  
  class Mastersthesis(Entry):
      """A Master's thesis"""
  
      required_fields = ('author', 'title', 'school', 'year')
      optional_fields = ('address', 'month', 'note', 'key')
  
  TypeRegistry.register('mastersthesis', Mastersthesis)
  
  
  class Misc(Entry):
      """Type of document that doesn't fit into any of the other categories."""
  
      required_fields = []
      optional_fields = ('author', 'title', 'howpublished', 'month', 'year',
              'note', 'key')
  
  TypeRegistry.register('misc', Misc)
  
  
  class Phdthesis(Mastersthesis):
      """A Ph.D. thesis."""
      pass
  
  TypeRegistry.register('phdthesis', Phdthesis)
  
  
  class Proceedings(Entry):
      """Conference proceedings."""
  
      required_fields = ('title', 'year')
      optional_fields = ('editor', 'publisher', 'organization', 'address',
              'month', 'note', 'key')
  
  TypeRegistry.register('proceedings', Proceedings)
  
  
  class Techreport(Entry):
      """A technical report published by an institution."""
  
      required_fields = ('author', 'title', 'institution', 'year')
      optional_fields = ('type', 'number', 'address', 'month', 'note', 'key')
  
  TypeRegistry.register('techreport', Techreport)
  
  
  class Unpublished(Entry):
      """A not yet published document that already has an author and a title."""
      required_fields = ('author', 'title', 'note',)
      optional_fields = ('month', 'year', 'key',)
  
  TypeRegistry.register('unpublished', Unpublished)
  