#!/usr/bin/python
#
# Copyright (C) 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Contains extensions to Atom objects used with Google Spreadsheets.
"""

__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)'


try:
  from xml.etree import cElementTree as ElementTree
except ImportError:
  try:
    import cElementTree as ElementTree
  except ImportError:
    try:
      from xml.etree import ElementTree
    except ImportError:
      from elementtree import ElementTree
import googleapi.atom as atom
import googleapi.gdata as gdata
import re
import string


# XML namespaces which are often used in Google Spreadsheets entities.
GSPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006'
GSPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s'

GSPREADSHEETS_EXTENDED_NAMESPACE = ('http://schemas.google.com/spreadsheets'
                                    '/2006/extended')
GSPREADSHEETS_EXTENDED_TEMPLATE = ('{http://schemas.google.com/spreadsheets'
                                   '/2006/extended}%s')


class ColCount(atom.AtomBase):
  """The Google Spreadsheets colCount element """
  
  _tag = 'colCount'
  _namespace = GSPREADSHEETS_NAMESPACE
  _children = atom.AtomBase._children.copy()
  _attributes = atom.AtomBase._attributes.copy()

  def __init__(self, text=None, extension_elements=None,
      extension_attributes=None):
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}

    
def ColCountFromString(xml_string):
  return atom.CreateClassFromXMLString(ColCount, xml_string)


class RowCount(atom.AtomBase):
  """The Google Spreadsheets rowCount element """
  
  _tag = 'rowCount'
  _namespace = GSPREADSHEETS_NAMESPACE
  _children = atom.AtomBase._children.copy()
  _attributes = atom.AtomBase._attributes.copy()

  def __init__(self, text=None, extension_elements=None,
      extension_attributes=None):
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}

def RowCountFromString(xml_string):
  return atom.CreateClassFromXMLString(RowCount, xml_string)
      

class Cell(atom.AtomBase):
  """The Google Spreadsheets cell element """
  
  _tag = 'cell'
  _namespace = GSPREADSHEETS_NAMESPACE
  _children = atom.AtomBase._children.copy()
  _attributes = atom.AtomBase._attributes.copy()
  _attributes['row'] = 'row'
  _attributes['col'] = 'col'
  _attributes['inputValue'] = 'inputValue'
  _attributes['numericValue'] = 'numericValue'
  
  def __init__(self, text=None, row=None, col=None, inputValue=None, 
      numericValue=None, extension_elements=None, extension_attributes=None):
    self.text = text
    self.row = row
    self.col = col
    self.inputValue = inputValue
    self.numericValue = numericValue
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}


def CellFromString(xml_string):
  return atom.CreateClassFromXMLString(Cell, xml_string)


class Custom(atom.AtomBase):
  """The Google Spreadsheets custom element"""
  
  _namespace = GSPREADSHEETS_EXTENDED_NAMESPACE
  _children = atom.AtomBase._children.copy()
  _attributes = atom.AtomBase._attributes.copy()

  def __init__(self, column=None, text=None, extension_elements=None,
      extension_attributes=None):
    self.column = column   # The name of the column
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}
    
  def _BecomeChildElement(self, tree):
    new_child = ElementTree.Element('')
    tree.append(new_child)
    new_child.tag = '{%s}%s' % (self.__class__._namespace, 
                                self.column)
    self._AddMembersToElementTree(new_child)
  
  def _ToElementTree(self):
    new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace,
                                               self.column))
    self._AddMembersToElementTree(new_tree)
    return new_tree
    
  def _HarvestElementTree(self, tree):
    namespace_uri, local_tag = string.split(tree.tag[1:], "}", 1)
    self.column = local_tag
    # Fill in the instance members from the contents of the XML tree.
    for child in tree:
      self._ConvertElementTreeToMember(child)
    for attribute, value in tree.attrib.iteritems():
      self._ConvertElementAttributeToMember(attribute, value)
    self.text = tree.text


def CustomFromString(xml_string):
  element_tree = ElementTree.fromstring(xml_string)
  return _CustomFromElementTree(element_tree)

  
def _CustomFromElementTree(element_tree):
  namespace_uri, local_tag = string.split(element_tree.tag[1:], "}", 1)
  if namespace_uri == GSPREADSHEETS_EXTENDED_NAMESPACE:
    new_custom = Custom()
    new_custom._HarvestElementTree(element_tree)
    new_custom.column = local_tag
    return new_custom
  return None

  


                                                  
class SpreadsheetsSpreadsheet(gdata.GDataEntry):
  """A Google Spreadsheets flavor of a Spreadsheet Atom Entry """
  
  _tag = 'entry'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataEntry._children.copy()
  _attributes = gdata.GDataEntry._attributes.copy()
  
  def __init__(self, author=None, category=None, content=None,
      contributor=None, atom_id=None, link=None, published=None, rights=None,
      source=None, summary=None, title=None, control=None, updated=None,
      text=None, extension_elements=None, extension_attributes=None):
    self.author = author or []
    self.category = category or []
    self.content = content
    self.contributor = contributor or []
    self.id = atom_id
    self.link = link or []
    self.published = published
    self.rights = rights
    self.source = source
    self.summary = summary
    self.control = control
    self.title = title
    self.updated = updated
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}
    
    
def SpreadsheetsSpreadsheetFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheet, 
                                       xml_string)


class SpreadsheetsWorksheet(gdata.GDataEntry):
  """A Google Spreadsheets flavor of a Worksheet Atom Entry """
  
  _tag = 'entry'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataEntry._children.copy()
  _attributes = gdata.GDataEntry._attributes.copy()
  _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', 
                                                         RowCount)
  _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', 
                                                         ColCount)
  
  def __init__(self, author=None, category=None, content=None,
      contributor=None, atom_id=None, link=None, published=None, rights=None,
      source=None, summary=None, title=None, control=None, updated=None, 
      row_count=None, col_count=None, text=None, extension_elements=None, 
      extension_attributes=None):
    self.author = author or []
    self.category = category or []
    self.content = content
    self.contributor = contributor or []
    self.id = atom_id
    self.link = link or []
    self.published = published
    self.rights = rights
    self.source = source
    self.summary = summary
    self.control = control
    self.title = title
    self.updated = updated
    self.row_count = row_count
    self.col_count = col_count
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}

    
def SpreadsheetsWorksheetFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsWorksheet, 
                                       xml_string)


class SpreadsheetsCell(gdata.BatchEntry):
  """A Google Spreadsheets flavor of a Cell Atom Entry """
  
  _tag = 'entry'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.BatchEntry._children.copy()
  _attributes = gdata.BatchEntry._attributes.copy()
  _children['{%s}cell' % GSPREADSHEETS_NAMESPACE] = ('cell', Cell)
  
  def __init__(self, author=None, category=None, content=None,
      contributor=None, atom_id=None, link=None, published=None, rights=None,
      source=None, summary=None, title=None, control=None, updated=None, 
      cell=None, batch_operation=None, batch_id=None, batch_status=None,
      text=None, extension_elements=None, extension_attributes=None):
    self.author = author or []
    self.category = category or []
    self.content = content
    self.contributor = contributor or []
    self.id = atom_id
    self.link = link or []
    self.published = published
    self.rights = rights
    self.source = source
    self.summary = summary
    self.control = control
    self.title = title
    self.batch_operation = batch_operation
    self.batch_id = batch_id
    self.batch_status = batch_status
    self.updated = updated
    self.cell = cell
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}


def SpreadsheetsCellFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsCell, 
                                       xml_string)

                                       
class SpreadsheetsList(gdata.GDataEntry):
  """A Google Spreadsheets flavor of a List Atom Entry """
  
  _tag = 'entry'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataEntry._children.copy()
  _attributes = gdata.GDataEntry._attributes.copy()
  
  def __init__(self, author=None, category=None, content=None,
      contributor=None, atom_id=None, link=None, published=None, rights=None,
      source=None, summary=None, title=None, control=None, updated=None, 
      custom=None, 
      text=None, extension_elements=None, extension_attributes=None):
    self.author = author or []
    self.category = category or []
    self.content = content
    self.contributor = contributor or []
    self.id = atom_id
    self.link = link or []
    self.published = published
    self.rights = rights
    self.source = source
    self.summary = summary
    self.control = control
    self.title = title
    self.updated = updated
    self.custom = custom or {}
    self.text = text
    self.extension_elements = extension_elements or []
    self.extension_attributes = extension_attributes or {}
    
  # We need to overwrite _ConvertElementTreeToMember to add special logic to
  # convert custom attributes to members
  def _ConvertElementTreeToMember(self, child_tree):
    # Find the element's tag in this class's list of child members
    if self.__class__._children.has_key(child_tree.tag):
      member_name = self.__class__._children[child_tree.tag][0]
      member_class = self.__class__._children[child_tree.tag][1]
      # If the class member is supposed to contain a list, make sure the
      # matching member is set to a list, then append the new member
      # instance to the list.
      if isinstance(member_class, list):
        if getattr(self, member_name) is None:
          setattr(self, member_name, [])
        getattr(self, member_name).append(atom._CreateClassFromElementTree(
            member_class[0], child_tree))
      else:
        setattr(self, member_name, 
                atom._CreateClassFromElementTree(member_class, child_tree))
    elif child_tree.tag.find('{%s}' % GSPREADSHEETS_EXTENDED_NAMESPACE) == 0:
      # If this is in the custom namespace, make add it to the custom dict.
      name = child_tree.tag[child_tree.tag.index('}')+1:]
      custom = _CustomFromElementTree(child_tree)
      if custom:
        self.custom[name] = custom
    else:
      atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree)
  
  # We need to overwtite _AddMembersToElementTree to add special logic to
  # convert custom members to XML nodes.
  def _AddMembersToElementTree(self, tree):
    # Convert the members of this class which are XML child nodes. 
    # This uses the class's _children dictionary to find the members which
    # should become XML child nodes.
    member_node_names = [values[0] for tag, values in 
                                       self.__class__._children.iteritems()]
    for member_name in member_node_names:
      member = getattr(self, member_name)
      if member is None:
        pass
      elif isinstance(member, list):
        for instance in member:
          instance._BecomeChildElement(tree)
      else:
        member._BecomeChildElement(tree)
    # Convert the members of this class which are XML attributes.
    for xml_attribute, member_name in self.__class__._attributes.iteritems():
      member = getattr(self, member_name)
      if member is not None:
        tree.attrib[xml_attribute] = member
    # Convert all special custom item attributes to nodes
    for name, custom in self.custom.iteritems():
      custom._BecomeChildElement(tree)
    # Lastly, call the ExtensionContainers's _AddMembersToElementTree to 
    # convert any extension attributes.
    atom.ExtensionContainer._AddMembersToElementTree(self, tree)

  
def SpreadsheetsListFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsList, 
                                       xml_string)
  element_tree = ElementTree.fromstring(xml_string)
  return _SpreadsheetsListFromElementTree(element_tree)


class SpreadsheetsSpreadsheetsFeed(gdata.GDataFeed):
  """A feed containing Google Spreadsheets Spreadsheets"""
  
  _tag = 'feed'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataFeed._children.copy()
  _attributes = gdata.GDataFeed._attributes.copy()
  _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', 
                                                  [SpreadsheetsSpreadsheet])


def SpreadsheetsSpreadsheetsFeedFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheetsFeed, 
                                       xml_string)
                                       
      
class SpreadsheetsWorksheetsFeed(gdata.GDataFeed):
  """A feed containing Google Spreadsheets Spreadsheets"""
  
  _tag = 'feed'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataFeed._children.copy()
  _attributes = gdata.GDataFeed._attributes.copy()
  _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', 
                                                  [SpreadsheetsWorksheet])


def SpreadsheetsWorksheetsFeedFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsWorksheetsFeed, 
                                       xml_string)


class SpreadsheetsCellsFeed(gdata.BatchFeed):
  """A feed containing Google Spreadsheets Cells"""
  
  _tag = 'feed'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.BatchFeed._children.copy()
  _attributes = gdata.BatchFeed._attributes.copy()
  _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', 
                                                  [SpreadsheetsCell])
  _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', 
                                                         RowCount)
  _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', 
                                                         ColCount)
                                                  
  def __init__(self, author=None, category=None, contributor=None,
               generator=None, icon=None, atom_id=None, link=None, logo=None, 
               rights=None, subtitle=None, title=None, updated=None,
               entry=None, total_results=None, start_index=None,
               items_per_page=None, extension_elements=None,
               extension_attributes=None, text=None, row_count=None,
               col_count=None, interrupted=None):
    gdata.BatchFeed.__init__(self, author=author, category=category,
                             contributor=contributor, generator=generator,
                             icon=icon,  atom_id=atom_id, link=link,
                             logo=logo, rights=rights, subtitle=subtitle,
                             title=title, updated=updated, entry=entry,
                             total_results=total_results,
                             start_index=start_index,
                             items_per_page=items_per_page,
                             extension_elements=extension_elements,
                             extension_attributes=extension_attributes,
                             text=text, interrupted=interrupted)
    self.row_count = row_count
    self.col_count = col_count

  def GetBatchLink(self):
    for link in self.link:
      if link.rel == 'http://schemas.google.com/g/2005#batch':
        return link
    return None


def SpreadsheetsCellsFeedFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsCellsFeed, 
                                       xml_string)

      
class SpreadsheetsListFeed(gdata.GDataFeed):
  """A feed containing Google Spreadsheets Spreadsheets"""
  
  _tag = 'feed'
  _namespace = atom.ATOM_NAMESPACE
  _children = gdata.GDataFeed._children.copy()
  _attributes = gdata.GDataFeed._attributes.copy()
  _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', 
                                                  [SpreadsheetsList])


def SpreadsheetsListFeedFromString(xml_string):
  return atom.CreateClassFromXMLString(SpreadsheetsListFeed, 
                                       xml_string)
