# Name:		XMLPyDemo.py
# Author:	Dave Renner		dmrenne@comcast.net
# Date:		09/05/11	1500
# Rev:		09/25/12	1615
# Note:		this is a called module of 'pyDemo.py'
#
# Note:		'XMLPyDemo.py'

from xml.parsers import expat

# ############################ BOILERPLATE ##########################
import pickle

myConfigFile = 'pyDemoConfig.dat'

# version has been stored in our calling module 'pyDemo.py'
PythonVersFile = 'version.obj'
versfile = open(PythonVersFile, 'rb')
PythonVers = pickle.load(versfile)
versfile.close()

if PythonVers == '30':
    import configparser
    config = configparser.ConfigParser()
    input_cmd = 'input'
else:
    import ConfigParser
    config = ConfigParser.ConfigParser()
    input_cmd = 'raw_input'

config.read(myConfigFile)
interactive = config.get('Mode', 'interactive')
continue_cmd = config.get('Strings', 'continue_cmd')

# easier to just include this boilerplate function into every module 
# than to import it
def printf(arg):
    cmdText = "print "
    arg_nonSlash = arg.strip('\\')
    if PythonVers == '30':
        cmdFull = cmdText + '("' + arg_nonSlash + '")'
    else:
        cmdFull = cmdText + '"' + arg_nonSlash + '"'
    exec( cmdFull )

# ####################################################################
class Parser:
	def __init__(self):
		self._parser = expat.ParserCreate()
		# the next three set callback handlers for the parser
		self._parser.StartElementHandler = self.start
		self._parser.CharacterDataHandler = self.data
		self._parser.EndElementHandler = self.end

	def start( self, tag, attr ):
		if PythonVers == '30':
			print( 'START', repr(tag), repr(attr) )
		else:
			print ('START'),
			print (tag),
			print ( repr(attr) )
	
	def end( self, tag ):
		if PythonVers == '30':
			print( 'END', repr(tag) )
		else:
			print ('END'),
			print (tag),
			print ( repr(tag) )

	def data( self, tag ):
		if PythonVers == '30':
			print( 'data:', repr(tag) )
		else:
			print ('data:'),
			print (tag),
			print ( repr(tag) )
		self.doSomethingElse(tag)

	def doSomethingElse(self, tag):
		#print("I have done something!")
		pass
		
	def feed( self, data ):
		self._parser.Parse(data, 0)

	def close( self ):
		# end of data. 1 = final
		self._parser.Parse("", 1)
		# get rid of circular reference
		del self._parser
	
# ######################################################################

printf("We are now in 'XMLPyDemo.py'.\
\\nThis will demo how Python can process XML files.\
\\nXML handling is beyond the scope of this demo.\
\\nSuffice to say:\
\\n\
\\nXML is a standard for writing declarative-language code.\
\\nA 'declarative' language is one that basically definies stuff --\
\\nobjects, attributes, values, functions, etc.\
\\nThe 'code' is itself interpreted or 'parsed' by a --\
\\nyou guessed it -- XML parser.\
\\n\
\\nXML, then, is certainly not a procedural language like C\
\\nor Python.\
\\nIt's used primarily in routine data processing of\
\\ndata arriving from a remote site on the Internet.\
\\nIt is popular because the XML standard is loose enougb\
\\nto be implemented with very few configuration limitations.\
\\n")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)
	printf("")

printf("By the way, XML stands for 'eXtended Markup Language.'\
\\nIt was originally developed as an extended HTML, I believe.\
\\n\
\\nNow, back to our demo:\
\\n\
\\nWe will use Python's specialized class 'xml.parsers.expat'.\
\\n'expat' implements a popular open-source sax-like XML parser.\
\\nSAX stands for Simple API for XML.")

printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	

printf("")
printf("'expat' has a lot of functionality, but only four methods\
\\nare commonly used. They are:\
\\n	_parser = expat.ParserCreate()\
\\n	 _parser.StartElementHandler = self.mystart\
\\n	 _parser.CharacterDataHandler = self.mydata\
\\n	 _parser.EndElementHandler = self.myend\
\\n\
\\nThe imported class 'xml.parsers.expat' include an object\
\\n'_parser'. The underscore is a common syntax for object naming\
\\nin Python. It emphasized that this is a class that\
\\nshouldn't usually be modified.\
\\n\
\\nRight, then. The 'ParserCreate()' method, obviously\
\\nThe 'StartElementHandler()' method describes any preliminary processing\
\\nto be done, usually printing out a header or something.\
\\nThe method is a like a required placeholder,\
\\nto be implemented by the programmer.")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
printf("'EndElementHandler()' provides similar end-of-input processing.\
\\nIt's not typicallyb required.\
\\n'CharacterDataHandler()' is the most important method. \
\\nIt describes what to do with each XML tag encountered.\
\\nThis will vary with each tag definition, of course, and\
\\n(of course) is programmer-supplied.")
printf("")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
printf("OK.")
printf("We will first create a parser.")
printf("Then we will call the required method 'feed()'\
\\npassing it the string of data to be parsed.\
\\nWe haven't talked about 'feed'; it's important, obviously,\
\\nbut very simple and self-explanatory.\
\\n\
\\nHere's the Python code for the program itself:\
\\n	 myP = Parser()\
\\n	 myP.feed(' \
\\n	 <?xml version = '1.0' encoding = 'iso-8859-1'?> \
\\n	 <author> \
\\n	 <name>John Cleese</name> \
\\n	 <city>London</city> \
\\n	 </author> ')\
\\n	 myP.close()")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	

	printf("")

printf("Well, we DID do the nose...\
\\nThat is, we changed a few of XML chars, just to make this\
\\neasily printable. Right.")
printf("")
printf("Now, here's our little 'feed' method:\
\\n	def myFeed( self ):\
\\n		self._parser.Parse(data, 0)\
\\n\
\\nAnd here's our CharacterDataHandler:\
\\n	def myCharDataHandler( self, tag )\
\\n		printf( 'data: ', repr(tag) )\
\\n		self.doSomething(tag)")

printf("And now, we'll run the code.")
printf("")
if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	

printf("")
printf("Here's the output:")
printf("")
parser = Parser()

parser.feed("\
<?xml version='1.0' encoding='iso-8859-1'?>\
<author><name>John Cleese</name>\
<city>London</city></author>")\

parser.close()

printf("")
printf("")

if interactive == '1':
	printf("")
	full_cmd = input_cmd + '("' + continue_cmd + '")'
	eval(full_cmd)	
	printf("")
	
printf("Note that the data is returned in Unicode strings --\
\\na 'u', single quote, data text, single quote.\
\\nThis is the XML standard.")
printf("")
printf("Note also that in actual practice, instead of 'feeding'\
\\nan internal XML file, the programmer will probably \
\\nfeed a filename string.\
\\nThe 'feed()' function will accept that string as its argument,\
\\nopen the file, and parse it by:\
\\n\
\\n	myFile = open(myXML.xml, r)\
\\n	self._parser.ParseFile(myFile)\
\\nThe close() function will not parse the final line;\
\\nthis is only required for a regular file, not an XML feed\
\\nsuch as this.")

printf("You should probably look at my code for details.")
printf("OK, this may take a few seconds to execute, so be patient...")
