2 The xml_simple_reader.py script is an xml parser that can parse a line separated xml text.
4 This xml parser will read a line seperated xml text and produce a tree of the xml with a document element. Each element can have an attribute table, childNodes, a class name, parentNode, text and a link to the document element.
6 This example gets an xml tree for the xml file boolean.xml. This example is run in a terminal in the folder which contains boolean.xml and xml_simple_reader.py.
10 Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
11 [GCC 4.2.1 (SUSE Linux)] on linux2
12 Type "help", "copyright", "credits" or "license" for more information.
13 >>> fileName = 'boolean.xml'
14 >>> file = open(fileName, 'r')
15 >>> xmlText = file.read()
17 >>> from xml_simple_reader import DocumentNode
18 >>> xmlParser = DocumentNode(fileName, xmlText)
20 ?xml, {'version': '1.0'}
21 ArtOfIllusion, {'xmlns:bf': '//babelfiche/codec', 'version': '2.0', 'fileversion': '3'}
22 Scene, {'bf:id': 'theScene'}
23 materials, {'bf:elem-type': 'java.lang.Object', 'bf:list': 'collection', 'bf:id': '1', 'bf:type': 'java.util.Vector'}
25 many more lines of the xml tree
31 from __future__ import absolute_import
33 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
34 from fabmetheus_utilities.geometry.geometry_utilities import matrix
35 from fabmetheus_utilities import archive
36 from fabmetheus_utilities import euclidean
37 from fabmetheus_utilities import xml_simple_writer
41 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
42 __credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
43 __date__ = '$Date: 2008/21/04 $'
44 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
47 globalGetAccessibleAttributeSet = set('getPaths getPreviousVertex getPreviousElementNode getVertexes parentNode'.split())
50 def createAppendByText(parentNode, xmlText):
51 'Create and append the child nodes from the xmlText.'
52 monad = OpenMonad(parentNode)
53 for character in xmlText:
54 monad = monad.getNextMonad(character)
56 def createAppendByTextb(parentNode, xmlText):
57 'Create and append the child nodes from the xmlText.'
58 monad = OpenMonad(parentNode)
59 for character in xmlText:
60 monad = monad.getNextMonad(character)
62 def getChildElementsByLocalName(childNodes, localName):
63 'Get the childNodes which have the given local name.'
64 childElementsByLocalName = []
65 for childNode in childNodes:
66 if localName.lower() == childNode.getNodeName():
67 childElementsByLocalName.append(childNode)
68 return childElementsByLocalName
70 def getDocumentNode(fileName):
71 'Get the document from the file name.'
72 xmlText = getFileText('test.xml')
73 return DocumentNode(fileName, xmlText)
75 def getElementsByLocalName(childNodes, localName):
76 'Get the descendents which have the given local name.'
77 elementsByLocalName = getChildElementsByLocalName(childNodes, localName)
78 for childNode in childNodes:
79 if childNode.getNodeType() == 1:
80 elementsByLocalName += childNode.getElementsByLocalName(localName)
81 return elementsByLocalName
83 def getFileText(fileName, printWarning=True, readMode='r'):
84 'Get the entire text of a file.'
86 file = open(fileName, readMode)
87 fileText = file.read()
92 print('The file ' + fileName + ' does not exist.')
96 class CDATASectionMonad(object):
97 'A monad to handle a CDATASection node.'
98 def __init__(self, input, parentNode):
101 self.parentNode = parentNode
103 def getNextMonad(self, character):
104 'Get the next monad.'
105 self.input.write(character)
107 inputString = self.input.getvalue()
108 if inputString.endswith(']]>'):
109 textContent = '<%s\n' % inputString
110 self.parentNode.childNodes.append(CDATASectionNode(self.parentNode, textContent))
111 return OpenMonad(self.parentNode)
115 class CDATASectionNode(object):
116 'A CDATASection node.'
117 def __init__(self, parentNode, textContent=''):
119 self.parentNode = parentNode
120 self.textContent = textContent
123 'Get the string representation of this CDATASection node.'
124 return self.textContent
126 def addToIdentifierDictionaries(self):
127 'Add the element to the owner document identifier dictionaries.'
130 def addXML(self, depth, output):
131 'Add xml for this CDATASection node.'
132 output.write(self.textContent)
134 def appendSelfToParent(self):
135 'Append self to the parentNode.'
136 self.parentNode.appendChild(self)
138 def copyXMLChildNodes(self, idSuffix, parentNode):
139 'Copy the xml childNodes.'
142 def getAttributes(self):
143 'Get the attributes.'
146 def getChildNodes(self):
150 def getCopy(self, idSuffix, parentNode):
151 'Copy the xml element, set its dictionary and add it to the parentNode.'
152 copy = self.getCopyShallow()
153 copy.parentNode = parentNode
154 copy.appendSelfToParent()
157 def getCopyShallow(self, attributes=None):
158 'Copy the node and set its parentNode.'
159 return CDATASectionNode(self.parentNode, self.textContent)
161 def getNodeName(self):
163 return '#cdata-section'
165 def getNodeType(self):
169 def getOwnerDocument(self):
170 'Get the owner document.'
171 return self.parentNode.getOwnerDocument()
173 def getTextContent(self):
174 'Get the text content.'
175 return self.textContent
177 def removeChildNodesFromIDNameParent(self):
178 'Remove the childNodes from the id and name dictionaries and the childNodes.'
181 def removeFromIDNameParent(self):
182 'Remove this from the id and name dictionaries and the childNodes of the parentNode.'
183 if self.parentNode != None:
184 self.parentNode.childNodes.remove(self)
186 def setParentAddToChildNodes(self, parentNode):
187 'Set the parentNode and add this to its childNodes.'
188 self.parentNode = parentNode
189 if self.parentNode != None:
190 self.parentNode.childNodes.append(self)
192 attributes = property(getAttributes)
193 childNodes = property(getChildNodes)
194 nodeName = property(getNodeName)
195 nodeType = property(getNodeType)
196 ownerDocument = property(getOwnerDocument)
199 class CommentMonad(CDATASectionMonad):
200 'A monad to handle a comment node.'
201 def getNextMonad(self, character):
202 'Get the next monad.'
203 self.input.write(character)
205 inputString = self.input.getvalue()
206 if inputString.endswith('-->'):
207 textContent = '<%s\n' % inputString
208 self.parentNode.childNodes.append(CommentNode(self.parentNode, textContent))
209 return OpenMonad(self.parentNode)
213 class CommentNode(CDATASectionNode):
215 def getCopyShallow(self, attributes=None):
216 'Copy the node and set its parentNode.'
217 return CommentNode(self.parentNode, self.textContent)
219 def getNodeName(self):
223 def getNodeType(self):
227 nodeName = property(getNodeName)
228 nodeType = property(getNodeType)
231 class DocumentNode(object):
232 'A class to parse an xml text and store the elements.'
233 def __init__(self, fileName, xmlText):
236 self.fileName = fileName
237 self.idDictionary = {}
238 self.nameDictionary = {}
239 self.parentNode = None
240 self.tagDictionary = {}
241 self.xmlText = xmlText
242 createAppendByText(self, xmlText)
245 'Get the string representation of this xml document.'
246 output = cStringIO.StringIO()
247 for childNode in self.childNodes:
248 childNode.addXML(0, output)
249 return output.getvalue()
251 def appendChild(self, elementNode):
252 'Append child elementNode to the child nodes.'
253 self.childNodes.append(elementNode)
254 elementNode.addToIdentifierDictionaries()
257 def getAttributes(self):
258 'Get the attributes.'
261 def getCascadeBoolean(self, defaultBoolean, key):
262 'Get the cascade boolean.'
263 return defaultBoolean
265 def getCascadeFloat(self, defaultFloat, key):
266 'Get the cascade float.'
269 def getDocumentElement(self):
270 'Get the document element.'
271 if len(self.childNodes) == 0:
273 return self.childNodes[-1]
275 def getElementsByLocalName(self, localName):
276 'Get the descendents which have the given local name.'
277 return getElementsByLocalName(self.childNodes, localName)
279 def getImportNameChain(self, suffix=''):
280 'Get the import name chain with the suffix at the end.'
283 def getNodeName(self):
287 def getNodeType(self):
291 def getOriginalRoot(self):
292 'Get the original reparsed document element.'
293 if evaluate.getEvaluatedBoolean(True, self.documentElement, 'getOriginalRoot'):
294 return DocumentNode(self.fileName, self.xmlText).documentElement
297 def getOwnerDocument(self):
298 'Get the owner document.'
301 attributes = property(getAttributes)
302 documentElement = property(getDocumentElement)
303 nodeName = property(getNodeName)
304 nodeType = property(getNodeType)
305 ownerDocument = property(getOwnerDocument)
308 class DocumentTypeMonad(CDATASectionMonad):
309 'A monad to handle a document type node.'
310 def getNextMonad(self, character):
311 'Get the next monad.'
312 self.input.write(character)
314 inputString = self.input.getvalue()
315 if inputString.endswith('?>'):
316 textContent = '%s\n' % inputString
317 self.parentNode.childNodes.append(DocumentTypeNode(self.parentNode, textContent))
318 return OpenMonad(self.parentNode)
322 class DocumentTypeNode(CDATASectionNode):
323 'A document type node.'
324 def getCopyShallow(self, attributes=None):
325 'Copy the node and set its parentNode.'
326 return DocumentTypeNode(self.parentNode, self.textContent)
328 def getNodeName(self):
330 return '#forNowDocumentType'
332 def getNodeType(self):
336 nodeName = property(getNodeName)
337 nodeType = property(getNodeType)
340 class ElementEndMonad(object):
341 'A monad to look for the end of an ElementNode tag.'
342 def __init__(self, parentNode):
344 self.parentNode = parentNode
346 def getNextMonad(self, character):
347 'Get the next monad.'
349 return TextMonad(self.parentNode)
353 class ElementLocalNameMonad(object):
354 'A monad to set the local name of an ElementNode.'
355 def __init__(self, character, parentNode):
357 self.input = cStringIO.StringIO()
358 self.input.write(character)
359 self.parentNode = parentNode
361 def getNextMonad(self, character):
362 'Get the next monad.'
364 if (self.input.getvalue() + character).startswith('![CDATA['):
365 self.input.write(character)
366 return CDATASectionMonad(self.input, self.parentNode)
368 if (self.input.getvalue() + character).startswith('!--'):
369 self.input.write(character)
370 return CommentMonad(self.input, self.parentNode)
371 if character.isspace():
373 return ElementReadMonad(self.elementNode)
376 self.elementNode.appendSelfToParent()
377 return ElementEndMonad(self.elementNode.parentNode)
380 self.elementNode.appendSelfToParent()
381 return TextMonad(self.elementNode)
382 self.input.write(character)
385 def setLocalName(self):
386 'Set the class name.'
387 self.elementNode = ElementNode(self.parentNode)
388 self.elementNode.localName = self.input.getvalue().lower().strip()
391 class ElementNode(object):
393 def __init__(self, parentNode=None):
398 self.parentNode = parentNode
399 self.xmlObject = None
402 'Get the string representation of this xml document.'
403 return '%s\n%s\n%s' % (self.localName, self.attributes, self.getTextContent())
405 def _getAccessibleAttribute(self, attributeName):
406 'Get the accessible attribute.'
407 global globalGetAccessibleAttributeSet
408 if attributeName in globalGetAccessibleAttributeSet:
409 return getattr(self, attributeName, None)
412 def addSuffixToID(self, idSuffix):
413 'Add the suffix to the id.'
414 if 'id' in self.attributes:
415 self.attributes['id'] += idSuffix
417 def addToIdentifierDictionaries(self):
418 'Add the element to the owner document identifier dictionaries.'
419 ownerDocument = self.getOwnerDocument()
420 importNameChain = self.getImportNameChain()
421 idKey = self.getStrippedAttributesValue('id')
423 ownerDocument.idDictionary[importNameChain + idKey] = self
424 nameKey = self.getStrippedAttributesValue('name')
426 euclidean.addElementToListDictionaryIfNotThere(self, importNameChain + nameKey, ownerDocument.nameDictionary)
427 for tagKey in self.getTagKeys():
428 euclidean.addElementToListDictionaryIfNotThere(self, tagKey, ownerDocument.tagDictionary)
430 def addXML(self, depth, output):
431 'Add xml for this elementNode.'
432 innerOutput = cStringIO.StringIO()
433 xml_simple_writer.addXMLFromObjects(depth + 1, self.childNodes, innerOutput)
434 innerText = innerOutput.getvalue()
435 xml_simple_writer.addBeginEndInnerXMLTag(self.attributes, depth, innerText, self.localName, output, self.getTextContent())
437 def appendChild(self, elementNode):
438 'Append child elementNode to the child nodes.'
439 self.childNodes.append(elementNode)
440 elementNode.addToIdentifierDictionaries()
443 def appendSelfToParent(self):
444 'Append self to the parentNode.'
445 self.parentNode.appendChild(self)
447 def copyXMLChildNodes(self, idSuffix, parentNode):
448 'Copy the xml childNodes.'
449 for childNode in self.childNodes:
450 childNode.getCopy(idSuffix, parentNode)
452 def getCascadeBoolean(self, defaultBoolean, key):
453 'Get the cascade boolean.'
454 if key in self.attributes:
455 value = evaluate.getEvaluatedBoolean(None, self, key)
458 return self.parentNode.getCascadeBoolean(defaultBoolean, key)
460 def getCascadeFloat(self, defaultFloat, key):
461 'Get the cascade float.'
462 if key in self.attributes:
463 value = evaluate.getEvaluatedFloat(None, self, key)
466 return self.parentNode.getCascadeFloat(defaultFloat, key)
468 def getChildElementsByLocalName(self, localName):
469 'Get the childNodes which have the given local name.'
470 return getChildElementsByLocalName(self.childNodes, localName)
472 def getCopy(self, idSuffix, parentNode):
473 'Copy the xml element, set its dictionary and add it to the parentNode.'
474 matrix4X4 = matrix.getBranchMatrixSetElementNode(self)
475 attributesCopy = self.attributes.copy()
476 attributesCopy.update(matrix4X4.getAttributes('matrix.'))
477 copy = self.getCopyShallow(attributesCopy)
478 copy.setParentAddToChildNodes(parentNode)
479 copy.addSuffixToID(idSuffix)
480 copy.addToIdentifierDictionaries()
481 self.copyXMLChildNodes(idSuffix, copy)
484 def getCopyShallow(self, attributes=None):
485 'Copy the xml element and set its dictionary and parentNode.'
486 if attributes == None: # to evade default initialization bug where a dictionary is initialized to the last dictionary
488 copyShallow = ElementNode(self.parentNode)
489 copyShallow.attributes = attributes
490 copyShallow.localName = self.localName
493 def getDocumentElement(self):
494 'Get the document element.'
495 return self.getOwnerDocument().getDocumentElement()
497 def getElementNodeByID(self, idKey):
498 'Get the xml element by id.'
499 idDictionary = self.getOwnerDocument().idDictionary
500 idKey = self.getImportNameChain() + idKey
501 if idKey in idDictionary:
502 return idDictionary[idKey]
505 def getElementNodesByName(self, nameKey):
506 'Get the xml elements by name.'
507 nameDictionary = self.getOwnerDocument().nameDictionary
508 nameKey = self.getImportNameChain() + nameKey
509 if nameKey in nameDictionary:
510 return nameDictionary[nameKey]
513 def getElementNodesByTag(self, tagKey):
514 'Get the xml elements by tag.'
515 tagDictionary = self.getOwnerDocument().tagDictionary
516 if tagKey in tagDictionary:
517 return tagDictionary[tagKey]
520 def getElementsByLocalName(self, localName):
521 'Get the descendents which have the given local name.'
522 return getElementsByLocalName(self.childNodes, localName)
524 def getFirstChildByLocalName(self, localName):
525 'Get the first childNode which has the given class name.'
526 for childNode in self.childNodes:
527 if localName.lower() == childNode.getNodeName():
531 def getIDSuffix(self, elementIndex=None):
532 'Get the id suffix from the dictionary.'
533 suffix = self.localName
534 if 'id' in self.attributes:
535 suffix = self.attributes['id']
536 if elementIndex == None:
537 return '_%s' % suffix
538 return '_%s_%s' % (suffix, elementIndex)
540 def getImportNameChain(self, suffix=''):
541 'Get the import name chain with the suffix at the end.'
542 importName = self.getStrippedAttributesValue('_importName')
543 if importName != None:
544 suffix = '%s.%s' % (importName, suffix)
545 return self.parentNode.getImportNameChain(suffix)
547 def getNodeName(self):
549 return self.localName
551 def getNodeType(self):
555 def getOwnerDocument(self):
556 'Get the owner document.'
557 return self.parentNode.getOwnerDocument()
561 return self.getOwnerDocument()
565 if self.xmlObject == None:
567 return self.xmlObject.getPaths()
569 def getPreviousElementNode(self):
570 'Get previous ElementNode if it exists.'
571 if self.parentNode == None:
573 previousElementNodeIndex = self.parentNode.childNodes.index(self) - 1
574 if previousElementNodeIndex < 0:
576 return self.parentNode.childNodes[previousElementNodeIndex]
578 def getPreviousVertex(self, defaultVector3=None):
579 'Get previous vertex if it exists.'
580 if self.parentNode == None:
581 return defaultVector3
582 if self.parentNode.xmlObject == None:
583 return defaultVector3
584 if len(self.parentNode.xmlObject.vertexes) < 1:
585 return defaultVector3
586 return self.parentNode.xmlObject.vertexes[-1]
588 def getStrippedAttributesValue(self, keyString):
589 'Get the stripped attribute value if the length is at least one, otherwise return None.'
590 if keyString in self.attributes:
591 strippedAttributesValue = self.attributes[keyString].strip()
592 if len(strippedAttributesValue) > 0:
593 return strippedAttributesValue
596 def getSubChildWithID( self, idReference ):
597 'Get the childNode which has the idReference.'
598 for childNode in self.childNodes:
599 if 'bf:id' in childNode.attributes:
600 if childNode.attributes['bf:id'] == idReference:
602 subChildWithID = childNode.getSubChildWithID( idReference )
603 if subChildWithID != None:
604 return subChildWithID
607 def getTagKeys(self):
608 'Get stripped tag keys.'
609 if 'tags' not in self.attributes:
612 tagString = self.attributes['tags']
613 if tagString.startswith('='):
614 tagString = tagString[1 :]
615 if tagString.startswith('['):
616 tagString = tagString[1 :]
617 if tagString.endswith(']'):
618 tagString = tagString[: -1]
619 for tagWord in tagString.split(','):
620 tagKey = tagWord.strip()
622 tagKeys.append(tagKey)
625 def getTextContent(self):
626 'Get the text from the child nodes.'
627 if len(self.childNodes) == 0:
629 firstNode = self.childNodes[0]
630 if firstNode.nodeType == 3:
631 return firstNode.textContent
634 def getValueByKey( self, key ):
635 'Get value by the key.'
636 if key in evaluate.globalElementValueDictionary:
637 return evaluate.globalElementValueDictionary[key](self)
638 if key in self.attributes:
639 return evaluate.getEvaluatedLinkValue(self, self.attributes[key])
642 def getVertexes(self):
644 if self.xmlObject == None:
646 return self.xmlObject.getVertexes()
648 def getXMLProcessor(self):
649 'Get the xmlProcessor.'
650 return self.getDocumentElement().xmlProcessor
652 def linkObject(self, xmlObject):
653 'Link self to xmlObject and add xmlObject to archivableObjects.'
654 self.xmlObject = xmlObject
655 self.xmlObject.elementNode = self
656 self.parentNode.xmlObject.archivableObjects.append(self.xmlObject)
658 def printAllVariables(self):
659 'Print all variables.'
661 print(self.attributes)
663 print(self.childNodes)
665 print(self.localName)
667 print(self.parentNode.getNodeName())
669 print(self.getTextContent())
671 print(self.xmlObject)
674 def printAllVariablesRoot(self):
675 'Print all variables and the document element variables.'
676 self.printAllVariables()
677 documentElement = self.getDocumentElement()
678 if documentElement != None:
680 print('Root variables:')
681 documentElement.printAllVariables()
683 def removeChildNodesFromIDNameParent(self):
684 'Remove the childNodes from the id and name dictionaries and the childNodes.'
685 childNodesCopy = self.childNodes[:]
686 for childNode in childNodesCopy:
687 childNode.removeFromIDNameParent()
689 def removeFromIDNameParent(self):
690 'Remove this from the id and name dictionaries and the childNodes of the parentNode.'
691 self.removeChildNodesFromIDNameParent()
692 idKey = self.getStrippedAttributesValue('id')
694 idDictionary = self.getOwnerDocument().idDictionary
695 idKey = self.getImportNameChain() + idKey
696 if idKey in idDictionary:
697 del idDictionary[idKey]
698 nameKey = self.getStrippedAttributesValue('name')
700 euclidean.removeElementFromListTable(self, self.getImportNameChain() + nameKey, self.getOwnerDocument().nameDictionary)
701 for tagKey in self.getTagKeys():
702 euclidean.removeElementFromListTable(self, tagKey, self.getOwnerDocument().tagDictionary)
703 if self.parentNode != None:
704 self.parentNode.childNodes.remove(self)
706 def setParentAddToChildNodes(self, parentNode):
707 'Set the parentNode and add this to its childNodes.'
708 self.parentNode = parentNode
709 if self.parentNode != None:
710 self.parentNode.childNodes.append(self)
712 def setTextContent(self, textContent=''):
713 'Get the text from the child nodes.'
714 if len(self.childNodes) == 0:
715 self.childNodes.append(TextNode(self, textContent))
717 firstNode = self.childNodes[0]
718 if firstNode.nodeType == 3:
719 firstNode.textContent = textContent
720 self.childNodes.append(TextNode(self, textContent))
722 nodeName = property(getNodeName)
723 nodeType = property(getNodeType)
724 ownerDocument = property(getOwnerDocument)
725 textContent = property(getTextContent)
728 class ElementReadMonad(object):
729 'A monad to read the attributes of the ElementNode tag.'
730 def __init__(self, elementNode):
732 self.elementNode = elementNode
734 def getNextMonad(self, character):
735 'Get the next monad.'
736 if character.isspace():
739 self.elementNode.appendSelfToParent()
740 return ElementEndMonad(self.elementNode.parentNode)
742 self.elementNode.appendSelfToParent()
743 return TextMonad(self.elementNode)
744 return KeyMonad(character, self.elementNode)
747 class KeyMonad(object):
748 'A monad to set the key of an attribute of an ElementNode.'
749 def __init__(self, character, elementNode):
751 self.input = cStringIO.StringIO()
752 self.input.write(character)
753 self.elementNode = elementNode
755 def getNextMonad(self, character):
756 'Get the next monad.'
758 return ValueMonad(self.elementNode, self.input.getvalue().strip())
759 self.input.write(character)
763 class OpenChooseMonad(ElementEndMonad):
764 'A monad to choose the next monad.'
765 def getNextMonad(self, character):
766 'Get the next monad.'
767 if character.isspace():
770 input = cStringIO.StringIO()
772 return DocumentTypeMonad(input, self.parentNode)
774 return ElementEndMonad(self.parentNode.parentNode)
775 return ElementLocalNameMonad(character, self.parentNode)
778 class OpenMonad(ElementEndMonad):
779 'A monad to handle the open tag character.'
780 def getNextMonad(self, character):
781 'Get the next monad.'
783 return OpenChooseMonad(self.parentNode)
787 class TextMonad(object):
788 'A monad to handle the open tag character and set the text.'
789 def __init__(self, parentNode):
791 self.input = cStringIO.StringIO()
792 self.parentNode = parentNode
794 def getNextMonad(self, character):
795 'Get the next monad.'
797 inputString = self.input.getvalue().strip()
798 if len(inputString) > 0:
799 self.parentNode.childNodes.append(TextNode(self.parentNode, inputString))
800 return OpenChooseMonad(self.parentNode)
801 self.input.write(character)
805 class TextNode(CDATASectionNode):
807 def addXML(self, depth, output):
808 'Add xml for this text node.'
811 def getCopyShallow(self, attributes=None):
812 'Copy the node and set its parentNode.'
813 return TextNode(self.parentNode, self.textContent)
815 def getNodeName(self):
819 def getNodeType(self):
823 nodeName = property(getNodeName)
824 nodeType = property(getNodeType)
827 class ValueMonad(object):
828 'A monad to set the value of an attribute of an ElementNode.'
829 def __init__(self, elementNode, key):
831 self.elementNode = elementNode
832 self.input = cStringIO.StringIO()
834 self.quoteCharacter = None
836 def getNextMonad(self, character):
837 'Get the next monad.'
838 if self.quoteCharacter == None:
839 if character == '"' or character == "'":
840 self.quoteCharacter = character
842 if self.quoteCharacter == character:
843 self.elementNode.attributes[self.key] = self.input.getvalue()
844 return ElementReadMonad(self.elementNode)
845 self.input.write(character)