"""$Id: opml.py 988 2008-03-12 18:22:48Z sa3ruby $"""

__author__ = "Sam Ruby <http://intertwingly.net/> and Mark Pilgrim <http://diveintomark.org/>"
__version__ = "$Revision: 988 $"
__copyright__ = "Copyright (c) 2002 Sam Ruby and Mark Pilgrim"

from base import validatorBase
from validators import *
from logging import *
from extension import extension_everywhere
import re

#
# Outline Processor Markup Language element.
#
class opml(validatorBase, extension_everywhere):
  versionList = ['1.0', '1.1', '2.0']

  def validate(self):
    self.setFeedType(TYPE_OPML)

    if (None,'version') in self.attrs.getNames():
      if self.attrs[(None,'version')] not in opml.versionList:
        self.log(InvalidOPMLVersion({"parent":self.parent.name, "element":self.name, "value":self.attrs[(None,'version')]}))
    elif self.name != 'outlineDocument':
      self.log(MissingAttribute({"parent":self.parent.name, "element":self.name, "attr":"version"}))
    
    if 'head' not in self.children:
      self.log(MissingElement({"parent":self.name, "element":"head"}))

    if 'body' not in self.children:
      self.log(MissingElement({"parent":self.name, "element":"body"}))

  def getExpectedAttrNames(self):
    return [(None, u'version')]

  def do_head(self):
    return opmlHead()

  def do_body(self):
    return opmlBody()

class opmlHead(validatorBase, extension_everywhere):
  def do_title(self):
    return safeHtml(), noduplicates()

  def do_dateCreated(self):
    return rfc822(), noduplicates()

  def do_dateModified(self):
    return rfc822(), noduplicates()

  def do_ownerName(self):
    return safeHtml(), noduplicates()

  def do_ownerEmail(self):
    return email(), noduplicates()

  def do_ownerId(self):
    return httpURL(), noduplicates()

  def do_expansionState(self):
    return commaSeparatedLines(), noduplicates()

  def do_vertScrollState(self):
    return positiveInteger(), nonblank(), noduplicates()

  def do_windowTop(self):
    return positiveInteger(), nonblank(), noduplicates()

  def do_windowLeft(self):
    return positiveInteger(), nonblank(), noduplicates()

  def do_windowBottom(self):
    return positiveInteger(), nonblank(), noduplicates()

  def do_windowRight(self):
    return positiveInteger(), nonblank(), noduplicates()

class commaSeparatedLines(text):
  linenumbers_re=re.compile('^(\d+(,\s*\d+)*)?$')
  def validate(self):
    if not self.linenumbers_re.match(self.value):
      self.log(InvalidExpansionState({"parent":self.parent.name, "element":self.name, "value":self.value}))

class opmlBody(validatorBase, extension_everywhere):

  def validate(self):
    if 'outline' not in self.children:
      self.log(MissingElement({"parent":self.name, "element":"outline"}))

  def do_outline(self):
    return opmlOutline()

class opmlOutline(validatorBase, extension_everywhere):
  versionList = ['RSS', 'RSS1', 'RSS2', 'scriptingNews']

  def getExpectedAttrNames(self):
    return [
      (None, u'category'),
      (None, u'created'),
      (None, u'description'),
      (None, u'htmlUrl'),
      (None, u'isBreakpoint'),
      (None, u'isComment'),
      (None, u'language'),
      (None, u'text'), 
      (None, u'title'),
      (None, u'type'), 
      (None, u'url'),
      (None, u'version'),
      (None, u'xmlUrl'),
    ]

  def validate(self):

    if not (None,'text') in self.attrs.getNames():
      self.log(MissingAttribute({"parent":self.parent.name, "element":self.name, "attr":"text"}))

    if (None,'type') in self.attrs.getNames():
      if self.attrs[(None,'type')].lower() == 'rss':

        if not (None,'xmlUrl') in self.attrs.getNames():
          self.log(MissingXmlURL({"parent":self.parent.name, "element":self.name}))
        if not (None,'title') in self.attrs.getNames():
          self.log(MissingTitleAttr({"parent":self.parent.name, "element":self.name}))

      elif self.attrs[(None,'type')].lower() == 'link':

        if not (None,'url') in self.attrs.getNames():
          self.log(MissingUrlAttr({"parent":self.parent.name, "element":self.name}))

      else:

        self.log(InvalidOutlineType({"parent":self.parent.name, "element":self.name, "value":self.attrs[(None,'type')]}))

    if (None,'version') in self.attrs.getNames():
      if self.attrs[(None,'version')] not in opmlOutline.versionList:
        self.log(InvalidOutlineVersion({"parent":self.parent.name, "element":self.name, "value":self.attrs[(None,'version')]}))
 
    if len(self.attrs)>1 and not (None,u'type') in self.attrs.getNames():
      for name in u'description htmlUrl language title version xmlUrl'.split():
        if (None, name) in self.attrs.getNames():
          self.log(MissingOutlineType({"parent":self.parent.name, "element":self.name}))
          break

    self.validate_optional_attribute((None,'created'), rfc822)
    self.validate_optional_attribute((None,'description'), safeHtml)
    self.validate_optional_attribute((None,'htmlUrl'), rfc2396_full)
    self.validate_optional_attribute((None,'isBreakpoint'), truefalse)
    self.validate_optional_attribute((None,'isComment'), truefalse)
    self.validate_optional_attribute((None,'language'), iso639)
    self.validate_optional_attribute((None,'title'), safeHtml)
    self.validate_optional_attribute((None,'text'), safeHtml)
    self.validate_optional_attribute((None,'url'), rfc2396_full)

  def characters(self, string):
    if not self.value:
      if string.strip():
        self.log(UnexpectedText({"element":self.name,"parent":self.parent.name}))
        self.value = string
    
  def do_outline(self):
    return opmlOutline()
