# TODO: properly handle enumerated nodes

import os.path
import sys
#import xml.etree.ElementTree as ET
import lxml.etree as ET

from props import PropertyNode, root

# internal xml tree parsing routine
def _parseXML(pynode, xmlnode, basepath):
    if len(xmlnode) or 'include' in xmlnode.attrib:
        # has children
        newnode = PropertyNode()
        if 'include' in xmlnode.attrib:
            filename = basepath + '/' + xmlnode.attrib['include']
            print "calling load():", filename, xmlnode.attrib
            load(filename, newnode)
        if xmlnode.tag in pynode.__dict__:
            # node exists
            print "node exists:", xmlnode.tag
            if type(pynode.__dict__[xmlnode.tag]) is list:
                # all is well
                pynode.__dict__[xmlnode.tag].append( newnode )
                print "all is well appending to list:", len(pynode.__dict__[xmlnode.tag])
            else:
                # we need to convert this to an enumerated list
                print "converting node to enumerate"
                savenode = pynode.__dict__[xmlnode.tag]
                pynode.__dict__[xmlnode.tag] = [ savenode, newnode ]
        else:
            # create new node
            pynode.__dict__[xmlnode.tag] = newnode
        for child in xmlnode:
            _parseXML(newnode, child, basepath)
    else:
        # leaf
        if 'n' in xmlnode.attrib:
            # enumerated node
            n = int(xmlnode.attrib['n'])
            if not xmlnode.tag in pynode.__dict__:
                pynode.__dict__[xmlnode.tag] = []
            tmp = pynode.__dict__[xmlnode.tag]
            pynode.extendEnumeratedNode(tmp, n)
            pynode.__dict__[xmlnode.tag][n] = xmlnode.text
            print "leaf:", xmlnode.tag, xmlnode.text, xmlnode.attrib
        elif xmlnode.tag in pynode.__dict__:
            # exists already, convert to enumerated.
            print "converting node to enumerate"
            savenode = pynode.__dict__[xmlnode.tag]
            pynode.__dict__[xmlnode.tag] = [ savenode, xmlnode.text ]
        elif type(xmlnode.tag) is str:
            pynode.__dict__[xmlnode.tag] = xmlnode.text
        else:
            print "Skipping unknown node:", xmlnode.tag, ":", xmlnode.text
                
# load xml file and create a property tree rooted at the given node
# supports <mytag include="relative_file_path.xml" />
def load(filename, pynode):
    try:
        xml = ET.parse(filename)
    except:
        print filename + ": xml parse error:\n" + str(sys.exc_info()[1])
        return

    path = os.path.dirname(filename)
    print "path:", path
    xmlroot = xml.getroot()
    for child in xmlroot:
        _parseXML(pynode, child, path)

def _buildXML(xmlnode, pynode):
    for child in pynode.__dict__:
        node = pynode.__dict__[child]
        if isinstance(node, PropertyNode):
            xmlchild = ET.Element(child)
            xmlnode.append(xmlchild)
            _buildXML(xmlchild, node)
        elif type(node) is list:
            for i, ele in enumerate(node):
                if isinstance(ele, PropertyNode):
                    xmlchild = ET.Element(child)
                    xmlnode.append(xmlchild)
                    _buildXML(xmlchild, ele)
                else:
                    xmlchild = ET.Element(child)
                    xmlchild.text = str(ele)
                    xmlnode.append(xmlchild)
   
        elif type(child) is str:
            xmlchild = ET.Element(child)
            xmlchild.text = str(node)
            xmlnode.append(xmlchild)
        else:
            print "skipping:", child, ":", str(node)
        
# save the property tree starting at pynode into an xml file.
def save(filename, pynode=root):
    xmlroot = ET.Element('PropertyList')
    xml = ET.ElementTree(xmlroot)
    _buildXML(xmlroot, pynode)
    try:
        xml.write(filename, encoding="us-ascii", xml_declaration=False,
                  pretty_print=True)
    except:
        print filename + ": xml write error:\n" + str(sys.exc_info()[1])
        return

@@ -238,16 +238,99 @@
 epath):%0A
+    merge  = 'merge' in xmlnode.attrib%0A    exists = xmlnode.tag in pynode.__dict__%0A
     if l
@@ -365,32 +365,32 @@
 xmlnode.attrib:%0A
-
         # has ch
@@ -648,65 +648,15 @@
  if 
-xmlnode.tag in pynode.__dict__:%0A            # node 
 exists
+:
 %0A   
@@ -697,16 +697,33 @@
 node.tag
+, %22merge:%22, merge
 %0A       
@@ -1543,38 +1543,14 @@
 not 
-xmlnode.tag in pynode.__dict__
+exists
 :%0A  
@@ -1837,69 +1837,155 @@
 lif 
-xmlnode.tag in pynode.__dict__:%0A            # exists already,
+exists:%0A            if not merge:%0A                # append%0A                if not type(pynode.__dict__%5Bxmlnode.tag%5D) is list:%0A                    #
  con
@@ -2004,16 +2004,24 @@
 erated.%0A
+        
         
@@ -2065,32 +2065,40 @@
 te%22%0A            
+ 
+       
 savenode = pynod
@@ -2125,32 +2125,40 @@
 ag%5D%0A            
+   
+     
 pynode.__dict__%5B
@@ -2186,24 +2186,182 @@
 node
-, xmlnode.text %5D
+ %5D%0A                pynode.__dict__%5Bxmlnode.tag%5D.append(xmlnode.text)%0A            else:%0A                # overwrite%0A                pynode.__dict__%5Bxmlnode.tag%5D = xmlnode.text
 %0A   

Support merge="true" for leaf nodes to allow overwriting previously defined (or more likely 'included') settings. This allows a top level xml file to include a generic block of config code and then overwrite specific settings afterwards.