#####################################################################################
#
#  Copyright (c) Harry Pierson. All rights reserved.
#
# This source code is subject to terms and conditions of the Microsoft Public License. 
# A  copy of the license can be found at http://opensource.org/licenses/ms-pl.html
# By using this source code in any fashion, you are agreeing to be bound 
# by the terms of the Microsoft Public License.
#
# You must not remove this notice, or any other, from this software.
#
#####################################################################################

import ipypulldom
from System.Xml import XmlNodeType
  
class _type_factory(object):
   class _type_node(object):
      def __init__(self, node):
         ty = type(node)
         self.name = ty.__name__
         self.namespace = ty.xmlns
    
   def __init__(self):
      self.types = {}

   def find_type(self, node, parent):
      def create_type(node, parent): 
         return type(node.name, (parent,), {'xmlns':node.namespace})

      if parent not in self.types:
         self.types[parent] = {}
    
      tp = self.types[parent]
      if node.name not in tp:
         tp[node.name] = [create_type(node, parent)]
    
      tpn = tp[node.name]
    
      for t in tpn:
         if t.xmlns == node.namespace: 
            return t
    
      #if there's no matching namespace type, create one and add it to the list
      new_type = create_type(node, parent)
      tpn.append(new_type)
      return new_type
  
   def __call__(self, node, parent=object):
      if isinstance(node, ipypulldom.XmlNode):
         return self.find_type(node, parent) 
      return self.find_type(self._type_node(node), parent)


xtype = _type_factory()

  
def xml2py(nodelist):
  
   def children(nodelist):
      while True:
         child = xml2py(nodelist)
         if child is None:
            break
         yield child
  
   def set_attribute(parent, child):
      name = type(child).__name__
      if not hasattr(parent, name):
         setattr(parent, name, child)
      else:
         val = getattr(parent, name)
         if isinstance(val, list):
            val.append(child)
         else:
            setattr(parent, name, [val, child])
      
   node = nodelist.next()
   if node.nodeType == XmlNodeType.EndElement:
      return None
    
   elif node.nodeType == XmlNodeType.Text or node.nodeType == XmlNodeType.CDATA:
      return node.value
    
   elif node.nodeType == XmlNodeType.Element:

      #create a new object type named for the element name
      cur = xtype(node)()
      cur._nodetype = XmlNodeType.Element
  
      #collect all the attributes and children in lists
      attributes = [xtype(attr, str)(attr.value) for attr in node.attributes]
      children = [child for child in children(nodelist)]
    
      if len(children) == 1 and isinstance(children[0], str):
         #fold up elements with a single text node
         cur = xtype(cur, str)(children[0])
         cur._nodetype = XmlNodeType.Element
      else:
         #otherwise, add child elements as properties on the current node
         for child in children:
            set_attribute(cur, child)

      for attr in attributes:
         attr._nodetype = XmlNodeType.Attribute
         set_attribute(cur, attr)
             
      return cur


def parse(xml):
   return xml2py(ipypulldom.parse(xml))
  
def parseString(xml):
   return xml2py(ipypulldom.parseString(xml))
  

if __name__ == '__main__':  
   rss = parse('http://feeds.feedburner.com/Devhawk')
   for item in rss.channel.item:
      print item.title
