"""$Id: extension.py 1018 2008-05-31 16:00:22Z sa3ruby $"""

__author__ = "Sam Ruby <http://intertwingly.net>, Mark Pilgrim <http://diveintomark.org/> and Phil Ringnalda <http://philringnalda.com>"
__version__ = "$Revision: 1018 $"
__copyright__ = "Copyright (c) 2002 Sam Ruby, Mark Pilgrim and Phil Ringnalda"
            
from validators import *
from logging import *

########################################################################
#                 Extensions that are valid everywhere                 #
########################################################################

class extension_everywhere:
  def do_dc_title(self):
    return text(), noduplicates()

  def do_dc_description(self):
    return text(), noduplicates()

  def do_dc_publisher(self):
    if "webMaster" in self.children:
      self.log(DuplicateSemantics({"core":"webMaster", "ext":"dc:publisher"}))
    return text() # duplicates allowed

  def do_dc_contributor(self):
    return text() # duplicates allowed

  def do_dc_type(self):
    return text(), noduplicates()
  
  def do_dc_format(self):
    return text(), noduplicates()

  def do_dc_identifier(self):
    return text()

  def do_dc_source(self):
    if "source" in self.children:
      self.log(DuplicateItemSemantics({"core":"source", "ext":"dc:source"}))
    return text(), noduplicates()

  def do_dc_language(self):
    if "language" in self.children:
      self.log(DuplicateSemantics({"core":"language", "ext":"dc:language"}))
    return iso639(), noduplicates()

  def do_dc_relation(self):
    return text(), # duplicates allowed

  def do_dc_coverage(self):
    return text(), # duplicates allowed

  def do_dc_rights(self):
    if "copyright" in self.children:
      self.log(DuplicateSemantics({"core":"copyright", "ext":"dc:rights"}))
    return nonhtml(), noduplicates()

  def do_dcterms_alternative(self):
    return text() #duplicates allowed

  def do_dcterms_abstract(self):
    return text(), noduplicates()

  def do_dcterms_tableOfContents(self):
    return rdfResourceURI(), noduplicates()

  def do_dcterms_created(self):
    return w3cdtf(), noduplicates()
  
  def do_dcterms_valid(self):
    return eater()
  
  def do_dcterms_available(self):
    return eater()

  def do_dcterms_issued(self):
    return w3cdtf(), noduplicates()

  def do_dcterms_modified(self):
    if "lastBuildDate" in self.children:
      self.log(DuplicateSemantics({"core":"lastBuildDate", "ext":"dcterms:modified"}))
    return w3cdtf(), noduplicates()

  def do_dcterms_dateAccepted(self):
    return text(), noduplicates()

  def do_dcterms_dateCopyrighted(self):
    return text(), noduplicates()

  def do_dcterms_dateSubmitted(self):
    return text(), noduplicates()

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

#  def do_dcterms_medium(self):
#    spec defines it as something that should never be used
#    undefined element'll do for now 

  def do_dcterms_isVersionOf(self):
    return rdfResourceURI() # duplicates allowed
  
  def do_dcterms_hasVersion(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_isReplacedBy(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_replaces(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_isRequiredBy(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_requires(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_isPartOf(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_hasPart(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_isReferencedBy(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_references(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_isFormatOf(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_hasFormat(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_conformsTo(self):
    return rdfResourceURI() # duplicates allowed

  def do_dcterms_spatial(self):
    return eater()  

  def do_dcterms_temporal(self):
    return eater()

  def do_dcterms_audience(self):
    return text()

  def do_dcterms_mediator(self):
    return text(), noduplicates()

  # added to DMCI, but no XML mapping has been defined
  def do_dcterms_accessRights(self):
    return eater()

  def do_dcterms_accrualMethod(self):
    return eater()

  def do_dcterms_accrualPeriodicity(self):
    return eater()

  def do_dcterms_accrualPolicy(self):
    return eater()

  def do_dcterms_bibliographicCitation(self):
    return eater()

  def do_dcterms_educationLevel(self):
    return eater()

  def do_dcterms_instructionalMethod(self):
    return eater()

  def do_dcterms_license(self):
    return eater()

  def do_dcterms_provenance(self):
    return eater()

  def do_dcterms_rightsHolder(self):
    return eater()

  def do_rdf_RDF(self):
    return eater()

  def do_rdf_type(self):
    return eater()

  def do_rdf_Description(self):
    return eater()

  def do_rdfs_seeAlso(self):
    return rdfResourceURI() # duplicates allowed

  def do_geo_Point(self):
    return geo_point()

  def do_geo_lat(self):
    return latitude()

  def do_geo_long(self):
    return longitude()

  def do_geo_alt(self):
    return decimal()

  def do_geourl_latitude(self):
    return latitude()

  def do_geourl_longitude(self):
    return longitude()

  def do_georss_where(self):
    return georss_where()

  def do_georss_point(self):
    return gml_pos()

  def do_georss_line(self):
    return gml_posList()

  def do_georss_polygon(self):
    return gml_posList()

  def do_georss_featuretypetag(self):
    return text()

  def do_georss_relationshiptag(self):
    return text()

  def do_georss_featurename(self):
    return text()

  def do_georss_elev(self):
    return decimal()

  def do_georss_floor(self):
    return Integer()

  def do_georss_radius(self):
    return Float()

  def do_icbm_latitude(self):
    return latitude()

  def do_icbm_longitude(self):
    return longitude()

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

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

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

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

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


########################################################################
#    Extensions that are valid at either the channel or item levels    #
########################################################################

from media import media_elements, media_content, media_group
class extension_channel_item(extension_everywhere, media_elements):
  def do_taxo_topics(self):
    return eater()

  def do_l_link(self):
    return l_link()


########################################################################
#         Extensions that are valid at only at the item level          #
########################################################################

class extension_item(extension_channel_item):
  def do_annotate_reference(self):
    return rdfResourceURI(), noduplicates()
    
  def do_ag_source(self):
    return text(), noduplicates()

  def do_ag_sourceURL(self):
    return rfc2396_full(), noduplicates()

  def do_ag_timestamp(self):
    return iso8601(), noduplicates()

  def do_ev_startdate(self):
    return iso8601(), noduplicates()

  def do_ev_enddate(self):
    return iso8601(), noduplicates()

  def do_ev_location(self):
    return eater()

  def do_ev_organizer(self):
    return eater()

  def do_ev_type(self):
    return text(), noduplicates()

  def do_feedburner_awareness(self):
    return rfc2396_full(), noduplicates()

  def do_feedburner_origEnclosureLink(self):
    return rfc2396_full(), noduplicates()

  def do_feedburner_origLink(self):
    return rfc2396_full(), noduplicates()

  def do_foaf_maker(self):
    return eater()

  def do_foaf_primaryTopic(self):
    return eater()

  def do_slash_comments(self):
    return nonNegativeInteger(), noduplicates()

  def do_slash_section(self):
    return text()

  def do_slash_department(self):
    return text()

  def do_slash_hit_parade(self):
    return commaSeparatedIntegers(), noduplicates()

  def do_thr_children(self):
    if self.getFeedType() != TYPE_RSS1:
      self.log(UndefinedElement({'parent':self.name,"element":"thr:children"}))
    return eater()

  def do_thr_total(self):
    return nonNegativeInteger(), noduplicates()

  def do_thr_in_reply_to(self):
    return in_reply_to()

  def do_wfw_comment(self):
    return rfc2396_full(), noduplicates()

  def do_wfw_commentRss(self):
    return rfc2396_full(), noduplicates()

  def do_wfw_commentRSS(self):
    self.log(CommentRSS({"parent":self.parent.name, "element":self.name}))
    return rfc2396_full(), noduplicates()

  def do_wiki_diff(self):
     return text()

  def do_wiki_history(self):
     return text()

  def do_wiki_importance(self):
     return text()

  def do_wiki_status(self):
     return text()

  def do_wiki_version(self):
     return text()

  def do_g_actor(self):
    return nonhtml(), noduplicates()

  def do_g_age(self):
    return nonNegativeInteger(), noduplicates()

  def do_g_agent(self):
    return nonhtml(), noduplicates()

  def do_g_area(self):
    return nonhtml(), noduplicates() # intUnit

  def do_g_apparel_type(self):
    return nonhtml(), noduplicates()

  def do_g_artist(self):
    return nonhtml(), noduplicates()

  def do_g_author(self):
    return nonhtml(), noduplicates()

  def do_g_bathrooms(self):
    return nonNegativeInteger(), noduplicates()

  def do_g_bedrooms(self):
    return nonNegativeInteger(), noduplicates()

  def do_g_brand(self):
    return nonhtml(), noduplicates()

  def do_g_calories(self):
    return g_float(), noduplicates()

  def do_g_cholesterol(self):
    return g_float(), noduplicates()

  def do_g_color(self):
    return nonhtml(), noduplicates()

  def do_g_cooking_time(self):
    return g_float(), noduplicates()

  def do_g_condition(self):
    return nonhtml(), noduplicates()

  def do_g_course(self):
    return nonhtml(), noduplicates()

  def do_g_course_date_range(self):
    return g_dateTimeRange(), noduplicates()

  def do_g_course_number(self):
    return nonhtml(), noduplicates()

  def do_g_course_times(self):
    return nonhtml(), noduplicates()

  def do_g_cuisine(self):
    return nonhtml(), noduplicates()

  def do_g_currency(self):
    return iso4217(), noduplicates()

  def do_g_delivery_notes(self):
    return nonhtml(), noduplicates()

  def do_g_delivery_radius(self):
    return floatUnit(), noduplicates()

  def do_g_education(self):
    return nonhtml(), noduplicates()

  def do_g_employer(self):
    return nonhtml(), noduplicates()

  def do_g_ethnicity(self):
    return nonhtml(), noduplicates()

  def do_g_event_date_range(self):
    return g_dateTimeRange(), noduplicates()

  def do_g_expiration_date(self):
    return iso8601_date(), noduplicates()

  def do_g_expiration_date_time(self):
    return iso8601(), noduplicates()

  def do_g_fiber(self):
    return g_float(), noduplicates()

  def do_g_from_location(self):
    return g_locationType(), noduplicates()

  def do_g_gender(self):
    return g_genderEnumeration(), noduplicates()

  def do_g_hoa_dues(self):
    return g_float(), noduplicates()

  def do_g_format(self):
    return nonhtml(), noduplicates()

  def do_g_id(self):
    return nonhtml(), noduplicates()

  def do_g_image_link(self):
    return rfc2396_full(), maxten()

  def do_g_immigration_status(self):
    return nonhtml(), noduplicates()

  def do_g_interested_in(self):
    return nonhtml(), noduplicates()

  def do_g_isbn(self):
    return nonhtml(), noduplicates()

  def do_g_job_function(self):
    return nonhtml(), noduplicates()

  def do_g_job_industry(self):
    return nonhtml(), noduplicates()

  def do_g_job_type(self):
    return nonhtml(), noduplicates()

  def do_g_label(self):
    return g_labelType(), maxten()

  def do_g_listing_type(self):
    return truefalse(), noduplicates()

  def do_g_location(self):
    return g_full_locationType(), noduplicates()

  def do_g_main_ingredient(self):
    return nonhtml(), noduplicates()

  def do_g_make(self):
    return nonhtml(), noduplicates()

  def do_g_manufacturer(self):
    return nonhtml(), noduplicates()

  def do_g_manufacturer_id(self):
    return nonhtml(), noduplicates()

  def do_g_marital_status(self):
    return g_maritalStatusEnumeration(), noduplicates()

  def do_g_meal_type(self):
    return nonhtml(), noduplicates()

  def do_g_megapixels(self):
    return floatUnit(), noduplicates()

  def do_g_memory(self):
    return floatUnit(), noduplicates()

  def do_g_mileage(self):
    return g_intUnit(), noduplicates()

  def do_g_model(self):
    return nonhtml(), noduplicates()

  def do_g_model_number(self):
    return nonhtml(), noduplicates()

  def do_g_name_of_item_being_reviewed(self):
    return nonhtml(), noduplicates()

  def do_g_news_source(self):
    return nonhtml(), noduplicates()

  def do_g_occupation(self):
    return nonhtml(), noduplicates()

  def do_g_payment_notes(self):
    return nonhtml(), noduplicates()

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

  def do_g_payment_accepted(self):
    return g_paymentMethodEnumeration()

  def do_g_pickup(self):
    return truefalse(), noduplicates()

  def do_g_preparation_time(self):
    return floatUnit(), noduplicates()

  def do_g_price(self):
    return floatUnit(), noduplicates()

  def do_g_price_type(self):
    return g_priceTypeEnumeration(), noduplicates()

  def do_g_processor_speed(self):
    return floatUnit(), noduplicates()

  def do_g_product_type(self):
    return nonhtml(), noduplicates()

  def do_g_property_type(self):
    return nonhtml(), noduplicates()

  def do_g_protein(self):
    return floatUnit(), noduplicates()

  def do_g_publication_name(self):
    return nonhtml(), noduplicates()

  def do_g_publication_volume(self):
    return nonhtml(), noduplicates()

  def do_g_publish_date(self):
    return iso8601_date(), noduplicates()

  def do_g_quantity(self):
    return nonNegativeInteger(), nonblank(), noduplicates()

  def do_g_rating(self):
    return g_ratingTypeEnumeration(), noduplicates()

  def do_g_review_type(self):
    return nonhtml(), noduplicates()

  def do_g_reviewer_type(self):
    return g_reviewerTypeEnumeration(), noduplicates()

  def do_g_salary(self):
    return g_float(), noduplicates()

  def do_g_salary_type(self):
    return g_salaryTypeEnumeration(), noduplicates()

  def do_g_saturated_fat(self):
    return g_float(), noduplicates()

  def do_g_school_district(self):
    return nonhtml(), noduplicates()

  def do_g_service_type(self):
    return nonhtml(), noduplicates()

  def do_g_servings(self):
    return g_float(), noduplicates()

  def do_g_sexual_orientation(self):
    return nonhtml(), noduplicates()

  def do_g_size(self):
    return nonhtml(), noduplicates() # TODO: expressed in either two or three dimensions.

  def do_g_shipping(self):
    return g_shipping(), noduplicates()

  def do_g_sodium(self):
    return g_float(), noduplicates()

  def do_g_subject(self):
    return nonhtml(), noduplicates()

  def do_g_subject_area(self):
    return nonhtml(), noduplicates()

  def do_g_tax_percent(self):
    return percentType(), noduplicates()

  def do_g_tax_region(self):
    return nonhtml(), noduplicates()

  def do_g_to_location(self):
    return g_locationType(), noduplicates()

  def do_g_total_carbs(self):
    return g_float(), noduplicates()

  def do_g_total_fat(self):
    return g_float(), noduplicates()

  def do_g_travel_date_range(self):
    return g_dateTimeRange(), noduplicates()

  def do_g_university(self):
    return nonhtml(), noduplicates()

  def do_g_upc(self):
    return nonhtml(), noduplicates()

  def do_g_url_of_item_being_reviewed(self):
    return rfc2396_full(), noduplicates()

  def do_g_vehicle_type(self):
    return nonhtml(), noduplicates()

  def do_g_vin(self):
    return nonhtml(), noduplicates()

  def do_g_weight(self):
    return floatUnit(), noduplicates()

  def do_g_year(self):
    return g_year(), noduplicates()

  def do_media_group(self):
    return media_group()

  def do_media_content(self):
    return media_content()

  def do_sx_sync(self):
    import sse
    return sse.Sync()

  def do_conversationsNetwork_introMilliseconds(self):
    return nonNegativeInteger(), noduplicates()

class heisen_uri(rfc3987, rfc2396_full):
  def validate(self):
    if self.getFeedType() == TYPE_ATOM:
      rfc3987.validate(self)
    elif not rfc2396_full.rfc2396_re.match(self.value):
      self.log(ContainsRelRef({'parent':self.parent.name}))

class feedFlare(nonhtml):
  def getExpectedAttrNames(self):
    return [(None,u'href'),(None,u'src')]
  def prevalidate(self):
    self.validate_required_attribute((None,'href'),  heisen_uri)
    self.validate_required_attribute((None,'src'),  heisen_uri)
    return text.prevalidate(self)

class xmlView(validatorBase):
  def getExpectedAttrNames(self):
    return [(None,u'href')]
  def prevalidate(self):
    self.validate_required_attribute((None,'href'),  rfc2396_full)

class georss_where(validatorBase):
  def do_gml_Point(self):
    return gml_point()
  def do_gml_LineString(self):
    return gml_line()
  def do_gml_Polygon(self):
    return gml_polygon()
  def do_gml_Envelope(self):
    return gml_envelope()

class geo_srsName(validatorBase):
  def getExpectedAttrNames(self):
    return [(None, u'srsName')]

class gml_point(geo_srsName):
  def do_gml_pos(self):
    return gml_pos()

class geo_point(validatorBase):
  def do_geo_lat(self):
    return latitude()

  def do_geo_long(self):
    return longitude()

  def validate(self):
    if "geo_lat" not in self.children:
      self.log(MissingElement({"parent":self.name.replace('_',':'), "element":"geo:lat"}))
    if "geo_long" not in self.children:
      self.log(MissingElement({"parent":self.name.replace('_',':'), "element":"geo:long"}))


class gml_pos(text):
  def validate(self):
    if not re.match('^[-+]?\d+\.?\d*[ ,][-+]?\d+\.?\d*$', self.value):
      return self.log(InvalidCoord({'value':self.value}))
    if self.value.find(',')>=0:
      self.log(CoordComma({'value':self.value}))

class gml_line(geo_srsName):
  def do_gml_posList(self):
    return gml_posList()

class gml_posList(text):
  def validate(self):
    if self.value.find(',')>=0:
      # ensure that commas are only used to separate lat and long 
      if not re.match('^[-+.0-9]+[, ][-+.0-9]( [-+.0-9]+[, ][-+.0-9])+$',
        value.strip()):
        return self.log(InvalidCoordList({'value':self.value}))
      self.log(CoordComma({'value':self.value}))
      self.value=self.value.replace(',',' ')
    values = self.value.strip().split()
    if len(values)<3 or len(values)%2 == 1:
      return self.log(InvalidCoordList({'value':self.value}))
    for value in values:
      if not re.match('^[-+]?\d+\.?\d*$', value):
        return self.log(InvalidCoordList({'value':value}))

class gml_polygon(geo_srsName):
  def do_gml_exterior(self):
    return gml_exterior()

class gml_exterior(validatorBase):
  def do_gml_LinearRing(self):
    return gml_linearRing()

class gml_linearRing(geo_srsName):
  def do_gml_posList(self):
    return gml_posList()

class gml_envelope(geo_srsName):
  def do_gml_lowerCorner(self):
    return gml_pos()
  def do_gml_upperCorner(self):
    return gml_pos()

class access_restriction(enumeration):
  error = InvalidAccessRestrictionRel
  valuelist =  ["allow", "deny"]

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

  def prevalidate(self):
    self.children.append(True) # force warnings about "mixed" content

    if not self.attrs.has_key((None,"relationship")):
      self.log(MissingAttribute({"parent":self.parent.name, "element":self.name, "attr":"relationship"}))
    else:
      self.value=self.attrs.getValue((None,"relationship"))

########################################################################
#     Extensions that are valid at only at the RSS 2.0 item level      #
########################################################################

class extension_rss20_item(extension_item):
  def do_trackback_ping(self):
    return rfc2396_full(), noduplicates()

  def do_trackback_about(self):
    return rfc2396_full()

  def do_dcterms_accessRights(self):
    return eater()

  def do_dcterms_accrualMethod(self):
    return eater()

  def do_dcterms_accrualPeriodicity(self):
    return eater()

  def do_dcterms_accrualPolicy(self):
    return eater()

  def do_dcterms_bibliographicCitation(self):
    return eater()

  def do_dcterms_educationLevel(self):
    return eater()

  def do_dcterms_instructionalMethod(self):
    return eater()

  def do_dcterms_license(self):
    return eater()

  def do_dcterms_provenance(self):
    return eater()

  def do_dcterms_rightsHolder(self):
    return eater()

########################################################################
#     Extensions that are valid at only at the RSS 1.0 item level      #
########################################################################

class extension_rss10_item(extension_item):
  def do_trackback_ping(self):
    return rdfResourceURI(), noduplicates()

  def do_trackback_about(self):
    return rdfResourceURI()

  def do_l_permalink(self):
    return l_permalink()

class l_permalink(rdfResourceURI, MimeType):
  lNS = u'http://purl.org/rss/1.0/modules/link/'
  def getExpectedAttrNames(self):
    return rdfResourceURI.getExpectedAttrNames(self) + [(self.lNS, u'type')]
  def validate(self):
    if (self.lNS, 'type') in self.attrs.getNames():
      self.value=self.attrs.getValue((self.lNS, 'type'))
      MimeType.validate(self)
    return rdfResourceURI.validate(self) 

class l_link(rdfResourceURI, MimeType):
  lNS = u'http://purl.org/rss/1.0/modules/link/'
  def getExpectedAttrNames(self):
    return rdfResourceURI.getExpectedAttrNames(self) + [
      (self.lNS, u'lang'), (self.lNS, u'rel'),
      (self.lNS, u'type'), (self.lNS, u'title')
    ]
  def prevalidate(self):
    self.validate_optional_attribute((self.lNS,'lang'), iso639)
    self.validate_required_attribute((self.lNS,'rel'), rfc2396_full)
    self.validate_optional_attribute((self.lNS,'title'), nonhtml)

    if self.attrs.has_key((self.lNS, "type")):
      if self.attrs.getValue((self.lNS, "type")).find(':') < 0:
        self.validate_optional_attribute((self.lNS,'type'), MimeType)
      else:
        self.validate_optional_attribute((self.lNS,'type'), rfc2396_full)



########################################################################
#      Extensions that are valid at only at the Atom entry level       #
########################################################################

class extension_entry(extension_item):
  def do_dc_creator(self): # atom:creator
    return text() # duplicates allowed
  def do_dc_subject(self): # atom:category
    return text() # duplicates allowed
  def do_dc_date(self): # atom:published
    return w3cdtf(), noduplicates()
  def do_creativeCommons_license(self):
    return rfc2396_full()

  def do_trackback_ping(self):
    return rfc2396_full(), noduplicates()

  # XXX This should have duplicate semantics with link[@rel='related']
  def do_trackback_about(self):
    return rfc2396_full()

########################################################################
#        Extensions that are valid at only at the channel level        #
########################################################################

class extension_channel(extension_channel_item):
  def do_admin_generatorAgent(self):
    if "generator" in self.children:
      self.log(DuplicateSemantics({"core":"generator", "ext":"admin:generatorAgent"}))
    return admin_generatorAgent(), noduplicates()

  def do_admin_errorReportsTo(self):
    return admin_errorReportsTo(), noduplicates()

  def do_blogChannel_blogRoll(self):
    return rfc2396_full(), noduplicates()

  def do_blogChannel_mySubscriptions(self):
    return rfc2396_full(), noduplicates()

  def do_blogChannel_blink(self):
    return rfc2396_full(), noduplicates()

  def do_blogChannel_changes(self):
    return rfc2396_full(), noduplicates()

  def do_sy_updatePeriod(self):
    return sy_updatePeriod(), noduplicates()

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

  def do_sy_updateBase(self):
    return w3cdtf(), noduplicates()

  def do_foaf_maker(self):
    return eater()

  def do_cp_server(self):
    return rdfResourceURI()

  def do_wiki_interwiki(self):
     return text()

  def do_thr_in_reply_to(self):
    return in_reply_to()

  def do_cf_listinfo(self):
    from cf import listinfo
    return listinfo()

  def do_cf_treatAs(self):
    from cf import treatAs
    return treatAs()

  def do_feedburner_awareness(self):
    return rfc2396_full(), noduplicates()

  def do_feedburner_browserFriendly(self):
    return nonhtml(), noduplicates()

  def do_feedburner_emailServiceId(self):
    return positiveInteger(), noduplicates()

  def do_feedburner_feedFlare(self):
    return feedFlare()

  def do_feedburner_feedburnerHostname(self):
    return rfc2396_full(), noduplicates()

  def do_opensearch_totalResults(self):
    return nonNegativeInteger(), noduplicates()
  do_opensearch10_totalResults = do_opensearch_totalResults

  def do_opensearch_startIndex(self):
    return Integer(), noduplicates()
  do_opensearch10_startIndex = do_opensearch_startIndex

  def do_opensearch_itemsPerPage(self):
    return nonNegativeInteger(), noduplicates()
  do_opensearch10_itemsPerPage = do_opensearch_itemsPerPage

  def do_opensearch_Query(self):
    from opensearch import Query
    return Query()

  def do_xhtml_div(self):
    return eater()

  def do_xhtml_meta(self):
    return xhtml_meta()

  def do_sx_sharing(self):
    import sse
    return sse.Sharing()

  def do_fh_archive(self):
    return validatorBase()

  def do_fh_complete(self):
    return validatorBase()

class xhtml_meta(validatorBase):
  def getExpectedAttrNames(self):
    return [ (None, u'name'), (None, u'content') ]
  def prevalidate(self):
    self.validate_required_attribute((None,'name'), xhtmlMetaEnumeration)
    self.validate_required_attribute((None,'content'), robotsEnumeration)

class xhtmlMetaEnumeration(caseinsensitive_enumeration):
  error = InvalidMetaName
  valuelist =  ["robots"]

class robotsEnumeration(caseinsensitive_enumeration):
  error = InvalidMetaContent
  valuelist =  [
    "all", "none",
    "index", "index,follow", "index,nofollow",
    "noindex", "noindex,follow", "noindex,nofollow",
    "follow", "follow,index", "follow,noindex",
    "nofollow", "nofollow,index", "nofollow,noindex"]

########################################################################
#       Extensions that are valid at only at the Atom feed level       #
########################################################################

class extension_feed(extension_channel):
  def do_dc_creator(self): # atom:creator
    return text() # duplicates allowed
  def do_dc_subject(self): # atom:category
    return text() # duplicates allowed
  def do_dc_date(self): # atom:updated
    return w3cdtf(), noduplicates()
  def do_creativeCommons_license(self):
    return rfc2396_full()
  def do_access_restriction(self):
    return access_restriction()

########################################################################
#                              Validators                              #
########################################################################

class admin_generatorAgent(rdfResourceURI): pass
class admin_errorReportsTo(rdfResourceURI): pass

class sy_updatePeriod(text):
  def validate(self):
    if self.value not in ('hourly', 'daily', 'weekly', 'monthly', 'yearly'):
      self.log(InvalidUpdatePeriod({"parent":self.parent.name, "element":self.name, "value":self.value}))
    else:
      self.log(ValidUpdatePeriod({"parent":self.parent.name, "element":self.name, "value":self.value}))

class g_complex_type(validatorBase):
  def getExpectedAttrNames(self):
    if self.getFeedType() == TYPE_RSS1:
      return [(u'http://www.w3.org/1999/02/22-rdf-syntax-ns#', u'parseType')]
    else:
      return []

class g_shipping(g_complex_type):
  def do_g_service(self):
    return g_serviceTypeEnumeration(), noduplicates()
  def do_g_country(self):
    return iso3166(), noduplicates()
  def do_g_price(self):
    return floatUnit(), noduplicates()

class g_dateTimeRange(g_complex_type):
  def do_g_start(self):
    return iso8601(), noduplicates()
  def do_g_end(self):
    return iso8601(), noduplicates()

class g_labelType(text):
  def validate(self):
    if self.value.find(',')>=0:
      self.log(InvalidLabel({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class g_locationType(text):
  def validate(self):
    if len(self.value.split(',')) not in [2,3]: 
      self.log(InvalidLocation({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class g_full_locationType(text):
  def validate(self):
    fields = self.value.split(',')
    if len(fields) != 5 or 0 in [len(f.strip()) for f in fields]: 
      self.log(InvalidFullLocation({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class g_genderEnumeration(enumeration):
  error = InvalidGender
  valuelist =  ["Male", "M", "Female", "F"]

class g_maritalStatusEnumeration(enumeration):
  error = InvalidMaritalStatus
  valuelist =  ["single", "divorced", "separated", "widowed", "married", "in relationship"]

class g_paymentMethodEnumeration(enumeration):
  error = InvalidPaymentMethod
  valuelist =  ["Cash", "Check", "Visa", "MasterCard",
   "AmericanExpress", "Discover", "WireTransfer"]

class g_priceTypeEnumeration(enumeration):
  error = InvalidPriceType
  valuelist =  ["negotiable", "starting"]

class g_ratingTypeEnumeration(enumeration):
  error = InvalidRatingType
  valuelist =  ["1", "2", "3", "4", "5"]

class g_reviewerTypeEnumeration(enumeration):
  error = InvalidReviewerType
  valuelist =  ["editorial", "user"]

class g_salaryTypeEnumeration(enumeration):
  error = InvalidSalaryType
  valuelist =  ["starting", "negotiable"]

class g_serviceTypeEnumeration(enumeration):
  error = InvalidServiceType
  valuelist =  ['FedEx', 'UPS', 'DHL', 'Mail', 'Other', 'Overnight', 'Standard']

class g_float(text):
  def validate(self):
    import re
    if not re.match('\d+\.?\d*\s*\w*', self.value):
      self.log(InvalidFloat({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class floatUnit(text):
  def validate(self):
    import re
    if not re.match('\d+\.?\d*\s*\w*$', self.value):
      self.log(InvalidFloatUnit({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class decimal(text):
  def validate(self):
    import re
    if not re.match('[-+]?\d+\.?\d*\s*$', self.value):
      self.log(InvalidFloatUnit({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class g_year(text):
  def validate(self):
    import time
    try:
      year = int(self.value)
      if year < 1900 or year > time.localtime()[0]+4: raise InvalidYear
    except:
      self.log(InvalidYear({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class g_intUnit(text):
  def validate(self):
    try:
      if int(self.value.split(' ')[0].replace(',','')) < 0: raise InvalidIntUnit
    except:
      self.log(InvalidIntUnit({"parent":self.parent.name, "element":self.name,
        "attr": ':'.join(self.name.split('_',1)), "value":self.value}))

class maxten(validatorBase):
  def textOK(self):
    pass

  def prevalidate(self):
    if 10 == len([1 for child in self.parent.children if self.name==child]):
      self.log(TooMany({"parent":self.parent.name, "element":self.name}))

class in_reply_to(canonicaluri, xmlbase):
  def getExpectedAttrNames(self):
    return [(None, u'href'), (None, u'ref'), (None, u'source'), (None, u'type')]

  def validate(self):
    if self.attrs.has_key((None, "href")):
      self.value = self.attrs.getValue((None, "href"))
      self.name = "href"
      xmlbase.validate(self)

    if self.attrs.has_key((None, "ref")):
      self.value = self.attrs.getValue((None, "ref"))
      self.name = "ref"
      canonicaluri.validate(self)

    if self.attrs.has_key((None, "source")):
      self.value = self.attrs.getValue((None, "source"))
      self.name = "source"
      xmlbase.validate(self)

    if self.attrs.has_key((None, "type")):
      self.value = self.attrs.getValue((None, "type"))
      if not mime_re.match(self.value):
        self.log(InvalidMIMEType({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.value}))
      else:
        self.log(ValidMIMEAttribute({"parent":self.parent.name, "element":self.name, "attr":"type", "value":self.value}))

########################################################################
#               Extensions that you just gotta question                #
########################################################################

class Questionable(extension_everywhere):
  children = []
 
  def do_atom_author(self):
    from author import author
    return author()

  def do_atom_category(self):
    from category import category
    return category()

  def do_atom_content(self):
    from content import content
    return content()

  def do_atom_contributor(self):
    from author import author
    return author()

  def do_atom_generator(self):
    from generator import generator
    return generator()

  def do_atom_icon(self):
    return rfc2396(), noduplicates()

  def do_atom_id(self):
    return canonicaluri(), noduplicates()

  def do_atom_link(self):
    from link import link
    return link()

  def do_atom_logo(self):
    return rfc2396(), noduplicates()

  def do_atom_published(self):
    return rfc3339(), noduplicates()

  def do_atom_rights(self):
    from content import textConstruct
    return textConstruct(), noduplicates()

  def do_atom_subtitle(self):
    from content import textConstruct
    return textConstruct(), noduplicates()

  def do_atom_summary(self):
    from content import textConstruct
    return textConstruct(), noduplicates()

  def do_atom_title(self):
    from content import textConstruct
    return textConstruct(), noduplicates()

  def do_atom_updated(self):
    return rfc3339(), noduplicates()

  def do_app_workspace(self):
    from service import workspace
    return workspace()

  def do_app_collection(self):
    from service import collection
    return collection()

  def do_app_categories(self):
    from categories import categories
    return categories()
