#!/usr/bin/python
#

"""%prog [OPTIONS]  [XML]

sed for XML
"""

__version__="$Revision: 33337 $"
__author__="Thomas Schraitle <thomas DOT schraitle AT suse DOT de>"

import optparse
import sys
import os

try:
  import lxml.etree as et
except ImportError, e:
  print >> sys.stderr, "ERROR: %s. I need the package python-lxml" % e
  sys.exit(100)

class FileNotFound(IOError):
  def __init__(self, filename):
    self.message = "File '%s' not found." % filename
  
  def __str__(self):
    return self.message



def main():
    """Create global options parser """
    parser = optparse.OptionParser(__doc__.strip(), \
                                   version="Revision %s" % __version__[11:-2])
    parser.add_option("-q", "--quiet",
        dest="verbose",
        action="store_false",
        help="Be quiet")
    parser.set_defaults(inplace=False)
    parser.add_option("-i", "--in-place",
        dest="inplace",
        action="store_true",
        help="edit files in place")
    parser.add_option("-e", "--expression",
        dest="expr",
        action="append",
        help="add the script to the commands to be executed")
    
    options, args = parser.parse_args()

    print >> sys.stderr, options, args

    if len(sys.argv) == 1:
      parser.print_help()
      sys.exit(0)
    
    if len(args) == 0:
      parser.error("I need a filename.")
      
    if options.expr == None:
      parser.error("I need an expression (option -e|--expression)")
    
    return parser, options, args

class SedObject(object):
  """ """
  
  def __init__(self, filename, parser=None):
    """Constructor"""
    if not os.path.exists(filename):
      raise FileNotFound(filename)
    
    self.parser=None
    self.filename = filename
    if parser==None:
      self.xml = et.parse(self.filename)
    else:
      self.parser = parser
      self.xml = et.parse(self.filename, self.parser)

    self.root = self.xml.getroot()
   
  def setXMLParser(parser):
    """ Set the XML parser and reparse"""
    self.parser = parser
    self.xml = et.parse(self.filename, parser=self.parser)
  
  def xpath(self, expr, namespaces={}):
    xp=SedResultObject(self.root, expr, namespaces=namespaces)
    return xp.execute()
    # return self.root.xpath(expr, namespaces=namespaces)
    
  def __str__(self):
    return et.tostring(self.xml)


class SedResultObject(object):
  
  def __init__(self, node, xpath, namespaces={}):
    self.node=node
    self.xpath = xpath
    self.namespaces=namespaces
    self.result=None
    
  def execute(self):
    self.result=self.node.xpath(self.xpath, namespaces=self.namespaces)
    return self
  
  def __repr__(self):
    return "<%s 0%x: %s>" % (self.__class__.__name__, id(self), self.xpath)



def process(parser, options, args):
  xmlp = et.XMLParser(
                      dtd_validation=False,    # We don't want validation
                      remove_blank_text=False, # Spaces are not relevant
                      resolve_entities=False,  # We want to preserve entities
                      remove_comments=False,   # We are not interested in comments
                      encoding="UTF-8",        # Use our default encoding
                     )
  # xpath = "//sect2/title"
  print >> sys.stderr, "Expressions:", options.expr
  for f in args:
    sedx = SedObject(f, xmlp)
    for ex in options.expr:
      if ex[1] != '#':
        raise optparse.OptionValueError("Expected a # at position 2 of the string %s" % ex)
      
      x, xpath = ex.split("#", 1)
      if x not in ("i",):
        raise optparse.OptionValueError("Only command 'i' is possible at the moment")
      
      # Split '==' from the end:
      A = xpath.rsplit("==", 1)
      if len(A) != 2:
        content = None
        # TODO: Kill exception and replace it with better algorithm
        optparse.OptionValueError("Expected the content of a XPath expression")
      else:
        xpath, content = A
        xpath = xpath.strip()
      
      p = sedx.xpath(xpath)
      # print x.execute()
      print >> sys.stderr,p.result
      print >> sys.stderr,p
      # print >> sys.stderr, type(p.result[0])
      
      for el in p.result:
        if isinstance(el, et._Element):
          # print >> sys.stderr, "Changing content of element"
          el.text = content
    #      
    if options.inplace:
      output = f
    else:
      output = sys.stdout
    
    print >> output, sedx

if __name__ == "__main__":
  try:
    p, o, a = main()
    process(p, o, a)
  except et.XPathEvalError, e:
    print >> sys.stderr, "XPath Error:", e
  except optparse.OptionValueError, e:
    print >> sys.stderr, e