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
32 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
35 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
36 from fabmetheus_utilities.geometry.geometry_utilities import matrix
37 from fabmetheus_utilities import archive
38 from fabmetheus_utilities import euclidean
39 from fabmetheus_utilities import xml_simple_writer
43 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
44 __credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
45 __date__ = '$Date: 2008/21/04 $'
46 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
49 globalGetAccessibleAttributeSet = set('getPaths getPreviousVertex getPreviousElementNode getVertexes parentNode'.split())
52 def createAppendByText(parentNode, xmlText):
53 'Create and append the child nodes from the xmlText.'
54 monad = OpenMonad(parentNode)
55 for character in xmlText:
56 monad = monad.getNextMonad(character)
58 def createAppendByTextb(parentNode, xmlText):
59 'Create and append the child nodes from the xmlText.'
60 monad = OpenMonad(parentNode)
61 for character in xmlText:
62 monad = monad.getNextMonad(character)
64 def getChildElementsByLocalName(childNodes, localName):
65 'Get the childNodes which have the given local name.'
66 childElementsByLocalName = []
67 for childNode in childNodes:
68 if localName.lower() == childNode.getNodeName():
69 childElementsByLocalName.append(childNode)
70 return childElementsByLocalName
72 def getDocumentNode(fileName):
73 'Get the document from the file name.'
74 xmlText = getFileText('test.xml')
75 return DocumentNode(fileName, xmlText)
77 def getElementsByLocalName(childNodes, localName):
78 'Get the descendents which have the given local name.'
79 elementsByLocalName = getChildElementsByLocalName(childNodes, localName)
80 for childNode in childNodes:
81 if childNode.getNodeType() == 1:
82 elementsByLocalName += childNode.getElementsByLocalName(localName)
83 return elementsByLocalName
85 def getFileText(fileName, printWarning=True, readMode='r'):
86 'Get the entire text of a file.'
88 file = open(fileName, readMode)
89 fileText = file.read()
94 print('The file ' + fileName + ' does not exist.')
98 class CDATASectionMonad:
99 'A monad to handle a CDATASection node.'
100 def __init__(self, input, parentNode):
103 self.parentNode = parentNode
105 def getNextMonad(self, character):
106 'Get the next monad.'
107 self.input.write(character)
109 inputString = self.input.getvalue()
110 if inputString.endswith(']]>'):
111 textContent = '<%s\n' % inputString
112 self.parentNode.childNodes.append(CDATASectionNode(self.parentNode, textContent))
113 return OpenMonad(self.parentNode)
117 class CDATASectionNode:
118 'A CDATASection node.'
119 def __init__(self, parentNode, textContent=''):
121 self.parentNode = parentNode
122 self.textContent = textContent
125 'Get the string representation of this CDATASection node.'
126 return self.textContent
128 def addToIdentifierDictionaries(self):
129 'Add the element to the owner document identifier dictionaries.'
132 def addXML(self, depth, output):
133 'Add xml for this CDATASection node.'
134 output.write(self.textContent)
136 def appendSelfToParent(self):
137 'Append self to the parentNode.'
138 self.parentNode.appendChild(self)
140 def copyXMLChildNodes(self, idSuffix, parentNode):
141 'Copy the xml childNodes.'
144 def getAttributes(self):
145 'Get the attributes.'
148 def getChildNodes(self):
152 def getCopy(self, idSuffix, parentNode):
153 'Copy the xml element, set its dictionary and add it to the parentNode.'
154 copy = self.getCopyShallow()
155 copy.parentNode = parentNode
156 copy.appendSelfToParent()
159 def getCopyShallow(self, attributes=None):
160 'Copy the node and set its parentNode.'
161 return CDATASectionNode(self.parentNode, self.textContent)
163 def getNodeName(self):
165 return '#cdata-section'
167 def getNodeType(self):
171 def getOwnerDocument(self):
172 'Get the owner document.'
173 return self.parentNode.getOwnerDocument()
175 def getTextContent(self):
176 'Get the text content.'
177 return self.textContent
179 def removeChildNodesFromIDNameParent(self):
180 'Remove the childNodes from the id and name dictionaries and the childNodes.'
183 def removeFromIDNameParent(self):
184 'Remove this from the id and name dictionaries and the childNodes of the parentNode.'
185 if self.parentNode != None:
186 self.parentNode.childNodes.remove(self)
188 def setParentAddToChildNodes(self, parentNode):
189 'Set the parentNode and add this to its childNodes.'
190 self.parentNode = parentNode
191 if self.parentNode != None:
192 self.parentNode.childNodes.append(self)
194 attributes = property(getAttributes)
195 childNodes = property(getChildNodes)
196 nodeName = property(getNodeName)
197 nodeType = property(getNodeType)
198 ownerDocument = property(getOwnerDocument)
201 class CommentMonad(CDATASectionMonad):
202 'A monad to handle a comment node.'
203 def getNextMonad(self, character):
204 'Get the next monad.'
205 self.input.write(character)
207 inputString = self.input.getvalue()
208 if inputString.endswith('-->'):
209 textContent = '<%s\n' % inputString
210 self.parentNode.childNodes.append(CommentNode(self.parentNode, textContent))
211 return OpenMonad(self.parentNode)
215 class CommentNode(CDATASectionNode):
217 def getCopyShallow(self, attributes=None):
218 'Copy the node and set its parentNode.'
219 return CommentNode(self.parentNode, self.textContent)
221 def getNodeName(self):
225 def getNodeType(self):
229 nodeName = property(getNodeName)
230 nodeType = property(getNodeType)
234 'A class to parse an xml text and store the elements.'
235 def __init__(self, fileName, xmlText):
238 self.fileName = fileName
239 self.idDictionary = {}
240 self.nameDictionary = {}
241 self.parentNode = None
242 self.tagDictionary = {}
243 self.xmlText = xmlText
244 createAppendByText(self, xmlText)
247 'Get the string representation of this xml document.'
248 output = cStringIO.StringIO()
249 for childNode in self.childNodes:
250 childNode.addXML(0, output)
251 return output.getvalue()
253 def appendChild(self, elementNode):
254 'Append child elementNode to the child nodes.'
255 self.childNodes.append(elementNode)
256 elementNode.addToIdentifierDictionaries()
259 def getAttributes(self):
260 'Get the attributes.'
263 def getCascadeBoolean(self, defaultBoolean, key):
264 'Get the cascade boolean.'
265 return defaultBoolean
267 def getCascadeFloat(self, defaultFloat, key):
268 'Get the cascade float.'
271 def getDocumentElement(self):
272 'Get the document element.'
273 if len(self.childNodes) == 0:
275 return self.childNodes[-1]
277 def getElementsByLocalName(self, localName):
278 'Get the descendents which have the given local name.'
279 return getElementsByLocalName(self.childNodes, localName)
281 def getImportNameChain(self, suffix=''):
282 'Get the import name chain with the suffix at the end.'
285 def getNodeName(self):
289 def getNodeType(self):
293 def getOriginalRoot(self):
294 'Get the original reparsed document element.'
295 if evaluate.getEvaluatedBoolean(True, self.documentElement, 'getOriginalRoot'):
296 return DocumentNode(self.fileName, self.xmlText).documentElement
299 def getOwnerDocument(self):
300 'Get the owner document.'
303 attributes = property(getAttributes)
304 documentElement = property(getDocumentElement)
305 nodeName = property(getNodeName)
306 nodeType = property(getNodeType)
307 ownerDocument = property(getOwnerDocument)
310 class DocumentTypeMonad(CDATASectionMonad):
311 'A monad to handle a document type node.'
312 def getNextMonad(self, character):
313 'Get the next monad.'
314 self.input.write(character)
316 inputString = self.input.getvalue()
317 if inputString.endswith('?>'):
318 textContent = '%s\n' % inputString
319 self.parentNode.childNodes.append(DocumentTypeNode(self.parentNode, textContent))
320 return OpenMonad(self.parentNode)
324 class DocumentTypeNode(CDATASectionNode):
325 'A document type node.'
326 def getCopyShallow(self, attributes=None):
327 'Copy the node and set its parentNode.'
328 return DocumentTypeNode(self.parentNode, self.textContent)
330 def getNodeName(self):
332 return '#forNowDocumentType'
334 def getNodeType(self):
338 nodeName = property(getNodeName)
339 nodeType = property(getNodeType)
342 class ElementEndMonad:
343 'A monad to look for the end of an ElementNode tag.'
344 def __init__(self, parentNode):
346 self.parentNode = parentNode
348 def getNextMonad(self, character):
349 'Get the next monad.'
351 return TextMonad(self.parentNode)
355 class ElementLocalNameMonad:
356 'A monad to set the local name of an ElementNode.'
357 def __init__(self, character, parentNode):
359 self.input = cStringIO.StringIO()
360 self.input.write(character)
361 self.parentNode = parentNode
363 def getNextMonad(self, character):
364 'Get the next monad.'
366 if (self.input.getvalue() + character).startswith('![CDATA['):
367 self.input.write(character)
368 return CDATASectionMonad(self.input, self.parentNode)
370 if (self.input.getvalue() + character).startswith('!--'):
371 self.input.write(character)
372 return CommentMonad(self.input, self.parentNode)
373 if character.isspace():
375 return ElementReadMonad(self.elementNode)
378 self.elementNode.appendSelfToParent()
379 return ElementEndMonad(self.elementNode.parentNode)
382 self.elementNode.appendSelfToParent()
383 return TextMonad(self.elementNode)
384 self.input.write(character)
387 def setLocalName(self):
388 'Set the class name.'
389 self.elementNode = ElementNode(self.parentNode)
390 self.elementNode.localName = self.input.getvalue().lower().strip()
395 def __init__(self, parentNode=None):
400 self.parentNode = parentNode
401 self.xmlObject = None
404 'Get the string representation of this xml document.'
405 return '%s\n%s\n%s' % (self.localName, self.attributes, self.getTextContent())
407 def _getAccessibleAttribute(self, attributeName):
408 'Get the accessible attribute.'
409 global globalGetAccessibleAttributeSet
410 if attributeName in globalGetAccessibleAttributeSet:
411 return getattr(self, attributeName, None)
414 def addSuffixToID(self, idSuffix):
415 'Add the suffix to the id.'
416 if 'id' in self.attributes:
417 self.attributes['id'] += idSuffix
419 def addToIdentifierDictionaries(self):
420 'Add the element to the owner document identifier dictionaries.'
421 ownerDocument = self.getOwnerDocument()
422 importNameChain = self.getImportNameChain()
423 idKey = self.getStrippedAttributesValue('id')
425 ownerDocument.idDictionary[importNameChain + idKey] = self
426 nameKey = self.getStrippedAttributesValue('name')
428 euclidean.addElementToListDictionaryIfNotThere(self, importNameChain + nameKey, ownerDocument.nameDictionary)
429 for tagKey in self.getTagKeys():
430 euclidean.addElementToListDictionaryIfNotThere(self, tagKey, ownerDocument.tagDictionary)
432 def addXML(self, depth, output):
433 'Add xml for this elementNode.'
434 innerOutput = cStringIO.StringIO()
435 xml_simple_writer.addXMLFromObjects(depth + 1, self.childNodes, innerOutput)
436 innerText = innerOutput.getvalue()
437 xml_simple_writer.addBeginEndInnerXMLTag(self.attributes, depth, innerText, self.localName, output, self.getTextContent())
439 def appendChild(self, elementNode):
440 'Append child elementNode to the child nodes.'
441 self.childNodes.append(elementNode)
442 elementNode.addToIdentifierDictionaries()
445 def appendSelfToParent(self):
446 'Append self to the parentNode.'
447 self.parentNode.appendChild(self)
449 def copyXMLChildNodes(self, idSuffix, parentNode):
450 'Copy the xml childNodes.'
451 for childNode in self.childNodes:
452 childNode.getCopy(idSuffix, parentNode)
454 def getCascadeBoolean(self, defaultBoolean, key):
455 'Get the cascade boolean.'
456 if key in self.attributes:
457 value = evaluate.getEvaluatedBoolean(None, self, key)
460 return self.parentNode.getCascadeBoolean(defaultBoolean, key)
462 def getCascadeFloat(self, defaultFloat, key):
463 'Get the cascade float.'
464 if key in self.attributes:
465 value = evaluate.getEvaluatedFloat(None, self, key)
468 return self.parentNode.getCascadeFloat(defaultFloat, key)
470 def getChildElementsByLocalName(self, localName):
471 'Get the childNodes which have the given local name.'
472 return getChildElementsByLocalName(self.childNodes, localName)
474 def getCopy(self, idSuffix, parentNode):
475 'Copy the xml element, set its dictionary and add it to the parentNode.'
476 matrix4X4 = matrix.getBranchMatrixSetElementNode(self)
477 attributesCopy = self.attributes.copy()
478 attributesCopy.update(matrix4X4.getAttributes('matrix.'))
479 copy = self.getCopyShallow(attributesCopy)
480 copy.setParentAddToChildNodes(parentNode)
481 copy.addSuffixToID(idSuffix)
482 copy.addToIdentifierDictionaries()
483 self.copyXMLChildNodes(idSuffix, copy)
486 def getCopyShallow(self, attributes=None):
487 'Copy the xml element and set its dictionary and parentNode.'
488 if attributes == None: # to evade default initialization bug where a dictionary is initialized to the last dictionary
490 copyShallow = ElementNode(self.parentNode)
491 copyShallow.attributes = attributes
492 copyShallow.localName = self.localName
495 def getDocumentElement(self):
496 'Get the document element.'
497 return self.getOwnerDocument().getDocumentElement()
499 def getElementNodeByID(self, idKey):
500 'Get the xml element by id.'
501 idDictionary = self.getOwnerDocument().idDictionary
502 idKey = self.getImportNameChain() + idKey
503 if idKey in idDictionary:
504 return idDictionary[idKey]
507 def getElementNodesByName(self, nameKey):
508 'Get the xml elements by name.'
509 nameDictionary = self.getOwnerDocument().nameDictionary
510 nameKey = self.getImportNameChain() + nameKey
511 if nameKey in nameDictionary:
512 return nameDictionary[nameKey]
515 def getElementNodesByTag(self, tagKey):
516 'Get the xml elements by tag.'
517 tagDictionary = self.getOwnerDocument().tagDictionary
518 if tagKey in tagDictionary:
519 return tagDictionary[tagKey]
522 def getElementsByLocalName(self, localName):
523 'Get the descendents which have the given local name.'
524 return getElementsByLocalName(self.childNodes, localName)
526 def getFirstChildByLocalName(self, localName):
527 'Get the first childNode which has the given class name.'
528 for childNode in self.childNodes:
529 if localName.lower() == childNode.getNodeName():
533 def getIDSuffix(self, elementIndex=None):
534 'Get the id suffix from the dictionary.'
535 suffix = self.localName
536 if 'id' in self.attributes:
537 suffix = self.attributes['id']
538 if elementIndex == None:
539 return '_%s' % suffix
540 return '_%s_%s' % (suffix, elementIndex)
542 def getImportNameChain(self, suffix=''):
543 'Get the import name chain with the suffix at the end.'
544 importName = self.getStrippedAttributesValue('_importName')
545 if importName != None:
546 suffix = '%s.%s' % (importName, suffix)
547 return self.parentNode.getImportNameChain(suffix)
549 def getNodeName(self):
551 return self.localName
553 def getNodeType(self):
557 def getOwnerDocument(self):
558 'Get the owner document.'
559 return self.parentNode.getOwnerDocument()
563 return self.getOwnerDocument()
567 if self.xmlObject == None:
569 return self.xmlObject.getPaths()
571 def getPreviousElementNode(self):
572 'Get previous ElementNode if it exists.'
573 if self.parentNode == None:
575 previousElementNodeIndex = self.parentNode.childNodes.index(self) - 1
576 if previousElementNodeIndex < 0:
578 return self.parentNode.childNodes[previousElementNodeIndex]
580 def getPreviousVertex(self, defaultVector3=None):
581 'Get previous vertex if it exists.'
582 if self.parentNode == None:
583 return defaultVector3
584 if self.parentNode.xmlObject == None:
585 return defaultVector3
586 if len(self.parentNode.xmlObject.vertexes) < 1:
587 return defaultVector3
588 return self.parentNode.xmlObject.vertexes[-1]
590 def getStrippedAttributesValue(self, keyString):
591 'Get the stripped attribute value if the length is at least one, otherwise return None.'
592 if keyString in self.attributes:
593 strippedAttributesValue = self.attributes[keyString].strip()
594 if len(strippedAttributesValue) > 0:
595 return strippedAttributesValue
598 def getSubChildWithID( self, idReference ):
599 'Get the childNode which has the idReference.'
600 for childNode in self.childNodes:
601 if 'bf:id' in childNode.attributes:
602 if childNode.attributes['bf:id'] == idReference:
604 subChildWithID = childNode.getSubChildWithID( idReference )
605 if subChildWithID != None:
606 return subChildWithID
609 def getTagKeys(self):
610 'Get stripped tag keys.'
611 if 'tags' not in self.attributes:
614 tagString = self.attributes['tags']
615 if tagString.startswith('='):
616 tagString = tagString[1 :]
617 if tagString.startswith('['):
618 tagString = tagString[1 :]
619 if tagString.endswith(']'):
620 tagString = tagString[: -1]
621 for tagWord in tagString.split(','):
622 tagKey = tagWord.strip()
624 tagKeys.append(tagKey)
627 def getTextContent(self):
628 'Get the text from the child nodes.'
629 if len(self.childNodes) == 0:
631 firstNode = self.childNodes[0]
632 if firstNode.nodeType == 3:
633 return firstNode.textContent
636 def getValueByKey( self, key ):
637 'Get value by the key.'
638 if key in evaluate.globalElementValueDictionary:
639 return evaluate.globalElementValueDictionary[key](self)
640 if key in self.attributes:
641 return evaluate.getEvaluatedLinkValue(self, self.attributes[key])
644 def getVertexes(self):
646 if self.xmlObject == None:
648 return self.xmlObject.getVertexes()
650 def getXMLProcessor(self):
651 'Get the xmlProcessor.'
652 return self.getDocumentElement().xmlProcessor
654 def linkObject(self, xmlObject):
655 'Link self to xmlObject and add xmlObject to archivableObjects.'
656 self.xmlObject = xmlObject
657 self.xmlObject.elementNode = self
658 self.parentNode.xmlObject.archivableObjects.append(self.xmlObject)
660 def printAllVariables(self):
661 'Print all variables.'
663 print(self.attributes)
665 print(self.childNodes)
667 print(self.localName)
669 print(self.parentNode.getNodeName())
671 print(self.getTextContent())
673 print(self.xmlObject)
676 def printAllVariablesRoot(self):
677 'Print all variables and the document element variables.'
678 self.printAllVariables()
679 documentElement = self.getDocumentElement()
680 if documentElement != None:
682 print('Root variables:')
683 documentElement.printAllVariables()
685 def removeChildNodesFromIDNameParent(self):
686 'Remove the childNodes from the id and name dictionaries and the childNodes.'
687 childNodesCopy = self.childNodes[:]
688 for childNode in childNodesCopy:
689 childNode.removeFromIDNameParent()
691 def removeFromIDNameParent(self):
692 'Remove this from the id and name dictionaries and the childNodes of the parentNode.'
693 self.removeChildNodesFromIDNameParent()
694 idKey = self.getStrippedAttributesValue('id')
696 idDictionary = self.getOwnerDocument().idDictionary
697 idKey = self.getImportNameChain() + idKey
698 if idKey in idDictionary:
699 del idDictionary[idKey]
700 nameKey = self.getStrippedAttributesValue('name')
702 euclidean.removeElementFromListTable(self, self.getImportNameChain() + nameKey, self.getOwnerDocument().nameDictionary)
703 for tagKey in self.getTagKeys():
704 euclidean.removeElementFromListTable(self, tagKey, self.getOwnerDocument().tagDictionary)
705 if self.parentNode != None:
706 self.parentNode.childNodes.remove(self)
708 def setParentAddToChildNodes(self, parentNode):
709 'Set the parentNode and add this to its childNodes.'
710 self.parentNode = parentNode
711 if self.parentNode != None:
712 self.parentNode.childNodes.append(self)
714 def setTextContent(self, textContent=''):
715 'Get the text from the child nodes.'
716 if len(self.childNodes) == 0:
717 self.childNodes.append(TextNode(self, textContent))
719 firstNode = self.childNodes[0]
720 if firstNode.nodeType == 3:
721 firstNode.textContent = textContent
722 self.childNodes.append(TextNode(self, textContent))
724 nodeName = property(getNodeName)
725 nodeType = property(getNodeType)
726 ownerDocument = property(getOwnerDocument)
727 textContent = property(getTextContent)
730 class ElementReadMonad:
731 'A monad to read the attributes of the ElementNode tag.'
732 def __init__(self, elementNode):
734 self.elementNode = elementNode
736 def getNextMonad(self, character):
737 'Get the next monad.'
738 if character.isspace():
741 self.elementNode.appendSelfToParent()
742 return ElementEndMonad(self.elementNode.parentNode)
744 self.elementNode.appendSelfToParent()
745 return TextMonad(self.elementNode)
746 return KeyMonad(character, self.elementNode)
750 'A monad to set the key of an attribute of an ElementNode.'
751 def __init__(self, character, elementNode):
753 self.input = cStringIO.StringIO()
754 self.input.write(character)
755 self.elementNode = elementNode
757 def getNextMonad(self, character):
758 'Get the next monad.'
760 return ValueMonad(self.elementNode, self.input.getvalue().strip())
761 self.input.write(character)
765 class OpenChooseMonad(ElementEndMonad):
766 'A monad to choose the next monad.'
767 def getNextMonad(self, character):
768 'Get the next monad.'
769 if character.isspace():
772 input = cStringIO.StringIO()
774 return DocumentTypeMonad(input, self.parentNode)
776 return ElementEndMonad(self.parentNode.parentNode)
777 return ElementLocalNameMonad(character, self.parentNode)
780 class OpenMonad(ElementEndMonad):
781 'A monad to handle the open tag character.'
782 def getNextMonad(self, character):
783 'Get the next monad.'
785 return OpenChooseMonad(self.parentNode)
790 'A monad to handle the open tag character and set the text.'
791 def __init__(self, parentNode):
793 self.input = cStringIO.StringIO()
794 self.parentNode = parentNode
796 def getNextMonad(self, character):
797 'Get the next monad.'
799 inputString = self.input.getvalue().strip()
800 if len(inputString) > 0:
801 self.parentNode.childNodes.append(TextNode(self.parentNode, inputString))
802 return OpenChooseMonad(self.parentNode)
803 self.input.write(character)
807 class TextNode(CDATASectionNode):
809 def addXML(self, depth, output):
810 'Add xml for this text node.'
813 def getCopyShallow(self, attributes=None):
814 'Copy the node and set its parentNode.'
815 return TextNode(self.parentNode, self.textContent)
817 def getNodeName(self):
821 def getNodeType(self):
825 nodeName = property(getNodeName)
826 nodeType = property(getNodeType)
830 'A monad to set the value of an attribute of an ElementNode.'
831 def __init__(self, elementNode, key):
833 self.elementNode = elementNode
834 self.input = cStringIO.StringIO()
836 self.quoteCharacter = None
838 def getNextMonad(self, character):
839 'Get the next monad.'
840 if self.quoteCharacter == None:
841 if character == '"' or character == "'":
842 self.quoteCharacter = character
844 if self.quoteCharacter == character:
845 self.elementNode.attributes[self.key] = self.input.getvalue()
846 return ElementReadMonad(self.elementNode)
847 self.input.write(character)