Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/odf/element.py: 42%

319 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1#!/usr/bin/python 

2# -*- coding: utf-8 -*- 

3# Copyright (C) 2007-2010 Søren Roug, European Environment Agency 

4# 

5# This library is free software; you can redistribute it and/or 

6# modify it under the terms of the GNU Lesser General Public 

7# License as published by the Free Software Foundation; either 

8# version 2.1 of the License, or (at your option) any later version. 

9# 

10# This library is distributed in the hope that it will be useful, 

11# but WITHOUT ANY WARRANTY; without even the implied warranty of 

12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

13# Lesser General Public License for more details. 

14# 

15# You should have received a copy of the GNU Lesser General Public 

16# License along with this library; if not, write to the Free Software 

17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

18# 

19# Contributor(s): 

20# 

21 

22# Note: This script has copied a lot of text from xml.dom.minidom. 

23# Whatever license applies to that file also applies to this file. 

24# 

25import sys, os.path 

26sys.path.append(os.path.dirname(__file__)) 

27import re 

28import xml.dom 

29from xml.dom.minicompat import * 

30from odf.namespaces import nsdict 

31import odf.grammar as grammar 

32from odf.attrconverters import AttrConverters 

33 

34if sys.version_info[0] == 3: 34 ↛ 38line 34 didn't jump to line 38, because the condition on line 34 was never false

35 unicode=str # unicode function does not exist 

36 unichr=chr # unichr does not exist 

37 

38_xml11_illegal_ranges = ( 

39 (0x0, 0x0,), 

40 (0xd800, 0xdfff,), 

41 (0xfffe, 0xffff,), 

42) 

43 

44_xml10_illegal_ranges = _xml11_illegal_ranges + ( 

45 (0x01, 0x08,), 

46 (0x0b, 0x0c,), 

47 (0x0e, 0x1f,), 

48) 

49 

50_xml_discouraged_ranges = ( 

51 (0x7f, 0x84,), 

52 (0x86, 0x9f,), 

53) 

54 

55if sys.maxunicode >= 0x10000: 55 ↛ 77line 55 didn't jump to line 77, because the condition on line 55 was never false

56 # modern or "wide" python build 

57 _xml_discouraged_ranges = _xml_discouraged_ranges + ( 

58 (0x1fffe, 0x1ffff,), 

59 (0x2fffe, 0x2ffff,), 

60 (0x3fffe, 0x3ffff,), 

61 (0x4fffe, 0x4ffff,), 

62 (0x5fffe, 0x5ffff,), 

63 (0x6fffe, 0x6ffff,), 

64 (0x7fffe, 0x7ffff,), 

65 (0x8fffe, 0x8ffff,), 

66 (0x9fffe, 0x9ffff,), 

67 (0xafffe, 0xaffff,), 

68 (0xbfffe, 0xbffff,), 

69 (0xcfffe, 0xcffff,), 

70 (0xdfffe, 0xdffff,), 

71 (0xefffe, 0xeffff,), 

72 (0xffffe, 0xfffff,), 

73 (0x10fffe, 0x10ffff,), 

74 ) 

75# else "narrow" python build - only possible with old versions 

76 

77def _range_seq_to_re(range_seq): 

78 # range pairs are specified as closed intervals 

79 return re.compile(u"[{}]".format( 

80 u"".join( 

81 u"{}-{}".format(re.escape(unichr(lo)), re.escape(unichr(hi))) 

82 for lo, hi in range_seq 

83 ) 

84 ), flags=re.UNICODE) 

85 

86_xml_filtered_chars_re = _range_seq_to_re(_xml10_illegal_ranges + _xml_discouraged_ranges) 

87 

88def _handle_unrepresentable(data): 

89 return _xml_filtered_chars_re.sub(u"\ufffd", data) 

90 

91# The following code is pasted form xml.sax.saxutils 

92# Tt makes it possible to run the code without the xml sax package installed 

93# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts 

94def _escape(data, entities={}): 

95 """ Escape &, <, and > in a string of data. 

96 

97 You can escape other strings of data by passing a dictionary as 

98 the optional entities parameter. The keys and values must all be 

99 strings; each key will be replaced with its corresponding value. 

100 """ 

101 data = data.replace("&", "&amp;") 

102 data = data.replace("<", "&lt;") 

103 data = data.replace(">", "&gt;") 

104 for chars, entity in entities.items(): 

105 data = data.replace(chars, entity) 

106 return data 

107 

108def _sanitize(data, entities={}): 

109 return _escape(_handle_unrepresentable(data), entities=entities) 

110 

111def _quoteattr(data, entities={}): 

112 """ Escape and quote an attribute value. 

113 

114 Escape &, <, and > in a string of data, then quote it for use as 

115 an attribute value. The \" character will be escaped as well, if 

116 necessary. 

117 

118 You can escape other strings of data by passing a dictionary as 

119 the optional entities parameter. The keys and values must all be 

120 strings; each key will be replaced with its corresponding value. 

121 """ 

122 entities['\n']='&#10;' 

123 entities['\r']='&#12;' 

124 data = _sanitize(data, entities) 

125 if '"' in data: 

126 if "'" in data: 

127 data = '"%s"' % data.replace('"', "&quot;") 

128 else: 

129 data = "'%s'" % data 

130 else: 

131 data = '"%s"' % data 

132 return data 

133 

134def _nssplit(qualifiedName): 

135 """ Split a qualified name into namespace part and local part. """ 

136 fields = qualifiedName.split(':', 1) 

137 if len(fields) == 2: 

138 return fields 

139 else: 

140 return (None, fields[0]) 

141 

142def _nsassign(namespace): 

143 return nsdict.setdefault(namespace,"ns" + str(len(nsdict))) 

144 

145 

146# Exceptions 

147class IllegalChild(Exception): 

148 """ Complains if you add an element to a parent where it is not allowed """ 

149class IllegalText(Exception): 

150 """ Complains if you add text or cdata to an element where it is not allowed """ 

151 

152class Node(xml.dom.Node): 

153 """ super class for more specific nodes """ 

154 parentNode = None 

155 nextSibling = None 

156 previousSibling = None 

157 

158 def hasChildNodes(self): 

159 """ Tells whether this element has any children; text nodes, 

160 subelements, whatever. 

161 """ 

162 if self.childNodes: 

163 return True 

164 else: 

165 return False 

166 

167 def _get_childNodes(self): 

168 return self.childNodes 

169 

170 def _get_firstChild(self): 

171 if self.childNodes: 

172 return self.childNodes[0] 

173 

174 def _get_lastChild(self): 

175 if self.childNodes: 

176 return self.childNodes[-1] 

177 

178 def insertBefore(self, newChild, refChild): 

179 """ Inserts the node newChild before the existing child node refChild. 

180 If refChild is null, insert newChild at the end of the list of children. 

181 """ 

182 if newChild.nodeType not in self._child_node_types: 

183 raise IllegalChild( "%s cannot be child of %s" % (newChild.tagName, self.tagName)) 

184 if newChild.parentNode is not None: 

185 newChild.parentNode.removeChild(newChild) 

186 if refChild is None: 

187 self.appendChild(newChild) 

188 else: 

189 try: 

190 index = self.childNodes.index(refChild) 

191 except ValueError: 

192 raise xml.dom.NotFoundErr() 

193 self.childNodes.insert(index, newChild) 

194 newChild.nextSibling = refChild 

195 refChild.previousSibling = newChild 

196 if index: 

197 node = self.childNodes[index-1] 

198 node.nextSibling = newChild 

199 newChild.previousSibling = node 

200 else: 

201 newChild.previousSibling = None 

202 newChild.parentNode = self 

203 return newChild 

204 

205 def appendChild(self, newChild): 

206 """ Adds the node newChild to the end of the list of children of this node. 

207 If the newChild is already in the tree, it is first removed. 

208 """ 

209 if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: 209 ↛ 210line 209 didn't jump to line 210, because the condition on line 209 was never true

210 for c in tuple(newChild.childNodes): 

211 self.appendChild(c) 

212 ### The DOM does not clearly specify what to return in this case 

213 return newChild 

214 if newChild.nodeType not in self._child_node_types: 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true

215 raise IllegalChild( "<%s> is not allowed in %s" % ( newChild.tagName, self.tagName)) 

216 if newChild.parentNode is not None: 216 ↛ 217line 216 didn't jump to line 217, because the condition on line 216 was never true

217 newChild.parentNode.removeChild(newChild) 

218 _append_child(self, newChild) 

219 newChild.nextSibling = None 

220 return newChild 

221 

222 def removeChild(self, oldChild): 

223 """ Removes the child node indicated by oldChild from the list of children, and returns it. 

224 """ 

225 #FIXME: update ownerDocument.element_dict or find other solution 

226 try: 

227 self.childNodes.remove(oldChild) 

228 except ValueError: 

229 raise xml.dom.NotFoundErr() 

230 if oldChild.nextSibling is not None: 

231 oldChild.nextSibling.previousSibling = oldChild.previousSibling 

232 if oldChild.previousSibling is not None: 

233 oldChild.previousSibling.nextSibling = oldChild.nextSibling 

234 oldChild.nextSibling = oldChild.previousSibling = None 

235 if self.ownerDocument: 

236 self.ownerDocument.remove_from_caches(oldChild) 

237 oldChild.parentNode = None 

238 return oldChild 

239 

240 def __str__(self): 

241 val = [] 

242 for c in self.childNodes: 

243 val.append(str(c)) 

244 return ''.join(val) 

245 

246 def __unicode__(self): 

247 val = [] 

248 for c in self.childNodes: 

249 val.append(unicode(c)) 

250 return u''.join(val) 

251 

252defproperty(Node, "firstChild", doc="First child node, or None.") 

253defproperty(Node, "lastChild", doc="Last child node, or None.") 

254 

255def _append_child(self, node): 

256 # fast path with less checks; usable by DOM builders if careful 

257 childNodes = self.childNodes 

258 if childNodes: 258 ↛ 259line 258 didn't jump to line 259, because the condition on line 258 was never true

259 last = childNodes[-1] 

260 node.__dict__["previousSibling"] = last 

261 last.__dict__["nextSibling"] = node 

262 childNodes.append(node) 

263 node.__dict__["parentNode"] = self 

264 

265class Childless: 

266 """ Mixin that makes childless-ness easy to implement and avoids 

267 the complexity of the Node methods that deal with children. 

268 """ 

269 

270 attributes = None 

271 childNodes = EmptyNodeList() 

272 firstChild = None 

273 lastChild = None 

274 

275 def _get_firstChild(self): 

276 return None 

277 

278 def _get_lastChild(self): 

279 return None 

280 

281 def appendChild(self, node): 

282 """ Raises an error """ 

283 raise xml.dom.HierarchyRequestErr( 

284 self.tagName + " nodes cannot have children") 

285 

286 def hasChildNodes(self): 

287 return False 

288 

289 def insertBefore(self, newChild, refChild): 

290 """ Raises an error """ 

291 raise xml.dom.HierarchyRequestErr( 

292 self.tagName + " nodes do not have children") 

293 

294 def removeChild(self, oldChild): 

295 """ Raises an error """ 

296 raise xml.dom.NotFoundErr( 

297 self.tagName + " nodes do not have children") 

298 

299 def replaceChild(self, newChild, oldChild): 

300 """ Raises an error """ 

301 raise xml.dom.HierarchyRequestErr( 

302 self.tagName + " nodes do not have children") 

303 

304class Text(Childless, Node): 

305 nodeType = Node.TEXT_NODE 

306 tagName = "Text" 

307 

308 def __init__(self, data): 

309 self.data = data 

310 

311 def __str__(self): 

312 return self.data 

313 

314 def __unicode__(self): 

315 return self.data 

316 

317 def toXml(self,level,f): 

318 """ Write XML in UTF-8 """ 

319 if self.data: 

320 f.write(_sanitize(unicode(self.data))) 

321 

322class CDATASection(Text, Childless): 

323 nodeType = Node.CDATA_SECTION_NODE 

324 

325 def toXml(self,level,f): 

326 """ Generate XML output of the node. If the text contains "]]>", then 

327 escape it by going out of CDATA mode (]]>), then write the string 

328 and then go into CDATA mode again. (<![CDATA[) 

329 """ 

330 if self.data: 

331 f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA[')) 

332 

333class Element(Node): 

334 """ Creates a arbitrary element and is intended to be subclassed not used on its own. 

335 This element is the base of every element it defines a class which resembles 

336 a xml-element. The main advantage of this kind of implementation is that you don't 

337 have to create a toXML method for every different object. Every element 

338 consists of an attribute, optional subelements, optional text and optional cdata. 

339 """ 

340 

341 nodeType = Node.ELEMENT_NODE 

342 namespaces = {} # Due to shallow copy this is a static variable 

343 

344 _child_node_types = (Node.ELEMENT_NODE, 

345 Node.PROCESSING_INSTRUCTION_NODE, 

346 Node.COMMENT_NODE, 

347 Node.TEXT_NODE, 

348 Node.CDATA_SECTION_NODE, 

349 Node.ENTITY_REFERENCE_NODE) 

350 

351 def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args): 

352 if qname is not None: 352 ↛ 354line 352 didn't jump to line 354, because the condition on line 352 was never false

353 self.qname = qname 

354 assert(hasattr(self, 'qname')) 

355 self.ownerDocument = None 

356 self.childNodes=[] 

357 self.allowed_children = grammar.allowed_children.get(self.qname) 

358 prefix = self.get_nsprefix(self.qname[0]) 

359 self.tagName = prefix + ":" + self.qname[1] 

360 if text is not None: 360 ↛ 361line 360 didn't jump to line 361, because the condition on line 360 was never true

361 self.addText(text) 

362 if cdata is not None: 362 ↛ 363line 362 didn't jump to line 363, because the condition on line 362 was never true

363 self.addCDATA(cdata) 

364 

365 allowed_attrs = self.allowed_attributes() 

366 if allowed_attrs is not None: 366 ↛ 368line 366 didn't jump to line 368, because the condition on line 366 was never false

367 allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs] 

368 self.attributes={} 

369 # Load the attributes from the 'attributes' argument 

370 if attributes: 370 ↛ 371line 370 didn't jump to line 371, because the condition on line 370 was never true

371 for attr, value in attributes.items(): 

372 self.setAttribute(attr, value) 

373 # Load the qualified attributes 

374 if qattributes: 374 ↛ 375line 374 didn't jump to line 375, because the condition on line 374 was never true

375 for attr, value in qattributes.items(): 

376 self.setAttrNS(attr[0], attr[1], value) 

377 if allowed_attrs is not None: 377 ↛ 382line 377 didn't jump to line 382, because the condition on line 377 was never false

378 # Load the attributes from the 'args' argument 

379 for arg in args.keys(): 

380 self.setAttribute(arg, args[arg]) 

381 else: 

382 for arg in args.keys(): # If any attribute is allowed 

383 self.attributes[arg]=args[arg] 

384 if not check_grammar: 384 ↛ 385line 384 didn't jump to line 385, because the condition on line 384 was never true

385 return 

386 # Test that all mandatory attributes have been added. 

387 required = grammar.required_attributes.get(self.qname) 

388 if required: 

389 for r in required: 

390 if self.getAttrNS(r[0],r[1]) is None: 390 ↛ 391line 390 didn't jump to line 391, because the condition on line 390 was never true

391 raise AttributeError( "Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName)) 

392 

393 def get_knownns(self, prefix): 

394 """ Odfpy maintains a list of known namespaces. In some cases a prefix is used, and 

395 we need to know which namespace it resolves to. 

396 """ 

397 global nsdict 

398 for ns,p in nsdict.items(): 

399 if p == prefix: return ns 

400 return None 

401 

402 def get_nsprefix(self, namespace): 

403 """ Odfpy maintains a list of known namespaces. In some cases we have a namespace URL, 

404 and needs to look up or assign the prefix for it. 

405 """ 

406 if namespace is None: namespace = "" 

407 prefix = _nsassign(namespace) 

408 if not namespace in self.namespaces: 

409 self.namespaces[namespace] = prefix 

410 return prefix 

411 

412 def allowed_attributes(self): 

413 return grammar.allowed_attributes.get(self.qname) 

414 

415 def _setOwnerDoc(self, element): 

416 element.ownerDocument = self.ownerDocument 

417 for child in element.childNodes: 417 ↛ 418line 417 didn't jump to line 418, because the loop on line 417 never started

418 self._setOwnerDoc(child) 

419 

420 def addElement(self, element, check_grammar=True): 

421 """ adds an element to an Element 

422 

423 Element.addElement(Element) 

424 """ 

425 if check_grammar and self.allowed_children is not None: 425 ↛ 428line 425 didn't jump to line 428, because the condition on line 425 was never false

426 if element.qname not in self.allowed_children: 426 ↛ 427line 426 didn't jump to line 427, because the condition on line 426 was never true

427 raise IllegalChild( "<%s> is not allowed in <%s>" % ( element.tagName, self.tagName)) 

428 self.appendChild(element) 

429 self._setOwnerDoc(element) 

430 if self.ownerDocument: 430 ↛ 431line 430 didn't jump to line 431, because the condition on line 430 was never true

431 self.ownerDocument.rebuild_caches(element) 

432 

433 def addText(self, text, check_grammar=True): 

434 """ Adds text to an element 

435 Setting check_grammar=False turns off grammar checking 

436 """ 

437 if check_grammar and self.qname not in grammar.allows_text: 

438 raise IllegalText( "The <%s> element does not allow text" % self.tagName) 

439 else: 

440 if text != '': 

441 self.appendChild(Text(text)) 

442 

443 def addCDATA(self, cdata, check_grammar=True): 

444 """ Adds CDATA to an element 

445 Setting check_grammar=False turns off grammar checking 

446 """ 

447 if check_grammar and self.qname not in grammar.allows_text: 

448 raise IllegalText( "The <%s> element does not allow text" % self.tagName) 

449 else: 

450 self.appendChild(CDATASection(cdata)) 

451 

452 def removeAttribute(self, attr, check_grammar=True): 

453 """ Removes an attribute by name. """ 

454 allowed_attrs = self.allowed_attributes() 

455 if allowed_attrs is None: 

456 if type(attr) == type(()): 

457 prefix, localname = attr 

458 self.removeAttrNS(prefix, localname) 

459 else: 

460 raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)") 

461 else: 

462 # Construct a list of allowed arguments 

463 allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs] 

464 if check_grammar and attr not in allowed_args: 

465 raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)) 

466 i = allowed_args.index(attr) 

467 self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1]) 

468 

469 def setAttribute(self, attr, value, check_grammar=True): 

470 """ Add an attribute to the element 

471 This is sort of a convenience method. All attributes in ODF have 

472 namespaces. The library knows what attributes are legal and then allows 

473 the user to provide the attribute as a keyword argument and the 

474 library will add the correct namespace. 

475 Must overwrite, If attribute already exists. 

476 """ 

477 if attr == 'parent' and value is not None: 477 ↛ 478line 477 didn't jump to line 478, because the condition on line 477 was never true

478 value.addElement(self) 

479 else: 

480 allowed_attrs = self.allowed_attributes() 

481 if allowed_attrs is None: 481 ↛ 482line 481 didn't jump to line 482, because the condition on line 481 was never true

482 if type(attr) == type(()): 

483 prefix, localname = attr 

484 self.setAttrNS(prefix, localname, value) 

485 else: 

486 raise AttributeError( "Unable to add simple attribute - use (namespace, localpart)") 

487 else: 

488 # Construct a list of allowed arguments 

489 allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs] 

490 if check_grammar and attr not in allowed_args: 490 ↛ 491line 490 didn't jump to line 491, because the condition on line 490 was never true

491 raise AttributeError( "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)) 

492 i = allowed_args.index(attr) 

493 self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value) 

494 

495 def setAttrNS(self, namespace, localpart, value): 

496 """ Add an attribute to the element 

497 In case you need to add an attribute the library doesn't know about 

498 then you must provide the full qualified name 

499 It will not check that the attribute is legal according to the schema. 

500 Must overwrite, If attribute already exists. 

501 """ 

502 allowed_attrs = self.allowed_attributes() 

503 prefix = self.get_nsprefix(namespace) 

504# if allowed_attrs and (namespace, localpart) not in allowed_attrs: 

505# raise AttributeError( "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName)) 

506 c = AttrConverters() 

507 self.attributes[(namespace, localpart)] = c.convert((namespace, localpart), value, self) 

508 

509 def getAttrNS(self, namespace, localpart): 

510 """ 

511 gets an attribute, given a namespace and a key 

512 @param namespace a unicode string or a bytes: the namespace 

513 @param localpart a unicode string or a bytes: 

514 the key to get the attribute 

515 @return an attribute as a unicode string or a bytes: if both paramters 

516 are byte strings, it will be a bytes; if both attributes are 

517 unicode strings, it will be a unicode string 

518 """ 

519 prefix = self.get_nsprefix(namespace) 

520 result = self.attributes.get((namespace, localpart)) 

521 

522 assert( 

523 (type(namespace), type(namespace), type(namespace) == \ 

524 type(b""), type(b""), type(b"")) or 

525 (type(namespace), type(namespace), type(namespace) == \ 

526 type(u""), type(u""), type(u"")) 

527 ) 

528 

529 return result 

530 

531 def removeAttrNS(self, namespace, localpart): 

532 del self.attributes[(namespace, localpart)] 

533 

534 def getAttribute(self, attr): 

535 """ Get an attribute value. The method knows which namespace the attribute is in 

536 """ 

537 allowed_attrs = self.allowed_attributes() 

538 if allowed_attrs is None: 

539 if type(attr) == type(()): 

540 prefix, localname = attr 

541 return self.getAttrNS(prefix, localname) 

542 else: 

543 raise AttributeError( "Unable to get simple attribute - use (namespace, localpart)") 

544 else: 

545 # Construct a list of allowed arguments 

546 allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs] 

547 i = allowed_args.index(attr) 

548 return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1]) 

549 

550 def write_open_tag(self, level, f): 

551 f.write(('<'+self.tagName)) 

552 if level == 0: 

553 for namespace, prefix in self.namespaces.items(): 

554 f.write(u' xmlns:' + prefix + u'="'+ _sanitize(str(namespace))+'"') 

555 for qname in self.attributes.keys(): 

556 prefix = self.get_nsprefix(qname[0]) 

557 f.write(u' '+_sanitize(str(prefix+u':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname]))) 

558 f.write(u'>') 

559 

560 def write_close_tag(self, level, f): 

561 f.write('</'+self.tagName+'>') 

562 

563 def toXml(self, level, f): 

564 """ 

565 Generate an XML stream out of the tree structure 

566 @param level integer: level in the XML tree; zero at root of the tree 

567 @param f an open writable file able to accept unicode strings 

568 """ 

569 f.write(u'<'+self.tagName) 

570 if level == 0: 

571 for namespace, prefix in self.namespaces.items(): 

572 f.write(u' xmlns:' + prefix + u'="'+ _sanitize(str(namespace))+u'"') 

573 for qname in self.attributes.keys(): 

574 prefix = self.get_nsprefix(qname[0]) 

575 f.write(u' '+_sanitize(unicode(prefix+':'+qname[1]))+u'='+_quoteattr(unicode(self.attributes[qname]))) 

576 if self.childNodes: 

577 f.write(u'>') 

578 for element in self.childNodes: 

579 element.toXml(level+1,f) 

580 f.write(u'</'+self.tagName+'>') 

581 else: 

582 f.write(u'/>') 

583 

584 def _getElementsByObj(self, obj, accumulator): 

585 if self.qname == obj.qname: 

586 accumulator.append(self) 

587 for e in self.childNodes: 

588 if e.nodeType == Node.ELEMENT_NODE: 

589 accumulator = e._getElementsByObj(obj, accumulator) 

590 return accumulator 

591 

592 def getElementsByType(self, element): 

593 """ Gets elements based on the type, which is function from text.py, draw.py etc. """ 

594 obj = element(check_grammar=False) 

595 return self._getElementsByObj(obj,[]) 

596 

597 def isInstanceOf(self, element): 

598 """ This is a check to see if the object is an instance of a type """ 

599 obj = element(check_grammar=False) 

600 return self.qname == obj.qname