* support for multiple xml implementations

* inform other modules that autoxml has been factored out
This commit is contained in:
Eray Özkural
2005-11-09 17:40:54 +00:00
parent ba1c4c3b19
commit c4fe20dc32
11 changed files with 590 additions and 93 deletions
+2 -1
View File
@@ -41,7 +41,8 @@ _ = __trans.ugettext
# PiSi
import pisi
# TODO: find the appropriate xmlext implementation and import from there
from pisi.xmlextmdom import *
from pisi.xmlext import *
from pisi.xmlfile import XmlFile
import pisi.context as ctx
import pisi.util as util
import pisi.oo as oo
+7 -7
View File
@@ -21,18 +21,18 @@ _ = __trans.ugettext
import pisi.context as ctx
import pisi.packagedb as packagedb
from pisi.version import Version
import pisi.xmlfile as xmlfile
import pisi.autoxml as autoxml
from pisi.util import Checks
class Dependency:
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
s_Package = [xmlfile.String, xmlfile.mandatory]
a_versionFrom = [xmlfile.String, xmlfile.optional]
a_versionTo = [xmlfile.String, xmlfile.optional]
a_releaseFrom = [xmlfile.String, xmlfile.optional]
a_releaseTo = [xmlfile.String, xmlfile.optional]
s_Package = [autoxml.String, autoxml.mandatory]
a_versionFrom = [autoxml.String, autoxml.optional]
a_versionTo = [autoxml.String, autoxml.optional]
a_releaseFrom = [autoxml.String, autoxml.optional]
a_releaseTo = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.package
+9 -9
View File
@@ -15,32 +15,32 @@
# Authors: Eray Ozkural <eray@uludag.org.tr>
import pisi.xmlfile as xmlfile
import pisi.autoxml as autoxml
from pisi.util import Checks
import pisi.lockeddbshelve as shelve
class File:
"""File holds the information for a File node/tag in files.xml"""
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
t_Path = [ xmlfile.String, xmlfile.mandatory ]
t_Type = [ xmlfile.String, xmlfile.mandatory ]
t_Size = [ xmlfile.Long, xmlfile.optional ]
t_Hash = [ xmlfile.String, xmlfile.optional, "SHA1Sum" ]
t_Path = [ autoxml.String, autoxml.mandatory ]
t_Type = [ autoxml.String, autoxml.mandatory ]
t_Size = [ autoxml.Long, autoxml.optional ]
t_Hash = [ autoxml.String, autoxml.optional, "SHA1Sum" ]
def __str__(self):
s = "%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type,
self.size, self.hash)
return s
class Files(xmlfile.XmlFile):
class Files(autoxml.XmlFile):
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
tag = "Files"
t_List = [ [File], xmlfile.optional, "File"]
t_List = [ [File], autoxml.optional, "File"]
def append(self, fileinfo):
self.list.append(fileinfo)
+4 -4
View File
@@ -29,19 +29,19 @@ import pisi.packagedb as packagedb
import pisi.util as util
from pisi.package import Package
from pisi.xmlfile import XmlFile
import pisi.xmlfile as xmlfile
import pisi.autoxml as autoxml
from pisi.uri import URI
class Error(pisi.Error):
pass
class Index(XmlFile):
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Sources = [ [specfile.Source], xmlfile.optional, "Source"]
t_Packages = [ [metadata.Package], xmlfile.optional, "Package"]
t_Sources = [ [specfile.Source], autoxml.optional, "Source"]
t_Packages = [ [metadata.Package], autoxml.optional, "Package"]
def read_uri(self, filename, repo = None):
"""Read PSPEC file"""
+17 -16
View File
@@ -26,24 +26,25 @@ _ = __trans.ugettext
import pisi.context as ctx
import pisi.specfile as specfile
import pisi.xmlfile as xmlfile
import pisi.autoxml as autoxml
import pisi.util as util
class Source:
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
t_Name = [xmlfile.String, xmlfile.mandatory]
t_Homepage = [xmlfile.String, xmlfile.optional]
t_Packager = [specfile.Packager, xmlfile.mandatory]
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [specfile.Packager, autoxml.mandatory]
class Package(specfile.Package):
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
t_Build = [ xmlfile.Integer, xmlfile.optional]
t_Distribution = [ xmlfile.String, xmlfile.mandatory]
t_DistributionRelease = [ xmlfile.String, xmlfile.mandatory]
t_Architecture = [ xmlfile.String, xmlfile.mandatory]
t_InstalledSize = [ xmlfile.Long, xmlfile.mandatory]
t_PackageURI = [ xmlfile.String, xmlfile.optional]
t_Build = [ autoxml.Integer, autoxml.optional]
t_Distribution = [ autoxml.String, autoxml.mandatory]
t_DistributionRelease = [ autoxml.String, autoxml.mandatory]
t_Architecture = [ autoxml.String, autoxml.mandatory]
t_InstalledSize = [ autoxml.Long, autoxml.mandatory]
t_PackageURI = [ autoxml.String, autoxml.optional]
def decode_hook(self, node, errs, where):
self.version = self.history[0].version
@@ -57,17 +58,17 @@ class Package(specfile.Package):
(self.architecture, self.installedSize)
return s
class MetaData(xmlfile.XmlFile):
class MetaData(autoxml.XmlFile):
"""Package metadata. Metadata is composed of Specfile and various
other information. A metadata has two parts, Source and Package."""
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
tag = "PISI"
t_Source = [ Source, xmlfile.mandatory]
t_Package = [ Package, xmlfile.mandatory]
#t_History = [ [Update], xmlfile.mandatory]
t_Source = [ Source, autoxml.mandatory]
t_Package = [ Package, autoxml.mandatory]
#t_History = [ [Update], autoxml.mandatory]
def from_spec(self, src, pkg):
self.source.name = src.name
+55 -55
View File
@@ -26,8 +26,8 @@ _ = __trans.ugettext
from os.path import basename
# pisi modules
import pisi.xmlfile as xmlfile
from pisi.xmlfile import XmlFile
import pisi.autoxml as autoxml
import pisi.context as ctx
from pisi.dependency import Dependency
import pisi.dependency
@@ -37,12 +37,12 @@ import pisi.util as util
class Error(pisi.Error):
pass
__metaclass__ = xmlfile.autoxml
__metaclass__ = autoxml.autoxml
class Packager:
t_Name = [xmlfile.Text, xmlfile.mandatory]
t_Email = [xmlfile.String, xmlfile.mandatory]
t_Name = [autoxml.Text, autoxml.mandatory]
t_Email = [autoxml.String, autoxml.mandatory]
def __str__(self):
s = "%s <%s>" % (self.name, self.email)
@@ -51,9 +51,9 @@ class Packager:
class AdditionalFile:
s_Filename = [xmlfile.String, xmlfile.mandatory]
a_target = [xmlfile.String, xmlfile.mandatory]
a_permission = [xmlfile.String, xmlfile.optional]
s_Filename = [autoxml.String, autoxml.mandatory]
a_target = [autoxml.String, autoxml.mandatory]
a_permission = [autoxml.String, autoxml.optional]
def __str__(self):
s = "%s -> %s " % (self.filename, self.target)
@@ -64,10 +64,10 @@ class AdditionalFile:
class Patch:
s_Filename = [xmlfile.String, xmlfile.mandatory]
a_compressionType = [xmlfile.String, xmlfile.optional]
a_level = [xmlfile.Integer, xmlfile.optional]
a_target = [xmlfile.String, xmlfile.optional]
s_Filename = [autoxml.String, autoxml.mandatory]
a_compressionType = [autoxml.String, autoxml.optional]
a_level = [autoxml.Integer, autoxml.optional]
a_target = [autoxml.String, autoxml.optional]
#FIXME: what's the cleanest way to give a default value for reading level?
#def decode_hook(self, node, errs, where):
@@ -87,10 +87,10 @@ class Patch:
class Update:
a_release = [xmlfile.String, xmlfile.mandatory]
t_Date = [xmlfile.String, xmlfile.mandatory]
t_Version = [xmlfile.String, xmlfile.mandatory]
t_Type = [xmlfile.String, xmlfile.optional]
a_release = [autoxml.String, autoxml.mandatory]
t_Date = [autoxml.String, autoxml.mandatory]
t_Version = [autoxml.String, autoxml.mandatory]
t_Type = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.date
@@ -103,8 +103,8 @@ class Update:
class Path:
s_Path = [xmlfile.String, xmlfile.mandatory]
a_fileType = [xmlfile.String, xmlfile.optional]
s_Path = [autoxml.String, autoxml.mandatory]
a_fileType = [autoxml.String, autoxml.optional]
def __str__(self):
s = self.path
@@ -114,8 +114,8 @@ class Path:
class ComarProvide:
s_om = [xmlfile.String, xmlfile.mandatory]
a_script = [xmlfile.String, xmlfile.mandatory]
s_om = [autoxml.String, autoxml.mandatory]
a_script = [autoxml.String, autoxml.mandatory]
def __str__(self):
# FIXME: descriptive enough?
@@ -126,9 +126,9 @@ class ComarProvide:
class Archive:
s_uri = [ xmlfile.String, xmlfile.mandatory ]
a_type =[ xmlfile.String, xmlfile.mandatory ]
a_sha1sum =[ xmlfile.String, xmlfile.mandatory ]
s_uri = [ autoxml.String, autoxml.mandatory ]
a_type =[ autoxml.String, autoxml.mandatory ]
a_sha1sum =[ autoxml.String, autoxml.mandatory ]
def decode_hook(self, node, errs, where):
self.name = basename(self.uri)
@@ -140,38 +140,38 @@ class Archive:
class Source:
t_Name = [xmlfile.String, xmlfile.mandatory]
t_Homepage = [xmlfile.String, xmlfile.optional]
t_Packager = [Packager, xmlfile.mandatory]
t_Summary = [xmlfile.LocalText, xmlfile.mandatory]
t_Description = [xmlfile.LocalText, xmlfile.mandatory]
t_IsA = [ [xmlfile.String], xmlfile.mandatory]
t_PartOf = [xmlfile.String, xmlfile.mandatory]
t_Icon = [ xmlfile.String, xmlfile.optional]
t_License = [ [xmlfile.String], xmlfile.mandatory]
t_Archive = [Archive, xmlfile.mandatory ]
t_Patches = [ [Patch], xmlfile.optional]
t_BuildDependencies = [ [Dependency], xmlfile.optional]
t_Version = [ xmlfile.String, xmlfile.optional]
t_Release = [ xmlfile.String, xmlfile.optional]
t_Name = [autoxml.String, autoxml.mandatory]
t_Homepage = [autoxml.String, autoxml.optional]
t_Packager = [Packager, autoxml.mandatory]
t_Summary = [autoxml.LocalText, autoxml.mandatory]
t_Description = [autoxml.LocalText, autoxml.mandatory]
t_IsA = [ [autoxml.String], autoxml.mandatory]
t_PartOf = [autoxml.String, autoxml.mandatory]
t_Icon = [ autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.mandatory]
t_Archive = [Archive, autoxml.mandatory ]
t_Patches = [ [Patch], autoxml.optional]
t_BuildDependencies = [ [Dependency], autoxml.optional]
t_Version = [ autoxml.String, autoxml.optional]
t_Release = [ autoxml.String, autoxml.optional]
class Package:
t_Name = [ xmlfile.String, xmlfile.mandatory ]
t_Summary = [ xmlfile.LocalText, xmlfile.optional ]
t_Description = [ xmlfile.LocalText, xmlfile.optional ]
t_IsA = [ [xmlfile.String], xmlfile.optional]
t_PartOf = [xmlfile.String, xmlfile.optional]
t_License = [ [xmlfile.String], xmlfile.optional]
t_Icon = [ xmlfile.String, xmlfile.optional]
t_RuntimeDependencies = [ [Dependency], xmlfile.optional]
t_Files = [ [Path], xmlfile.optional]
t_Conflicts = [ [xmlfile.String], xmlfile.optional, "Conflicts/Package"]
t_ProvidesComar = [ [ComarProvide], xmlfile.optional, "Provides/COMAR"]
#t_RequiresComar = [ [xmlfile.String], xmlfile.mandatory, "Requires/COMAR"]
t_AdditionalFiles = [ [AdditionalFile], xmlfile.optional]
t_History = [ [Update], xmlfile.optional]
t_Name = [ autoxml.String, autoxml.mandatory ]
t_Summary = [ autoxml.LocalText, autoxml.optional ]
t_Description = [ autoxml.LocalText, autoxml.optional ]
t_IsA = [ [autoxml.String], autoxml.optional]
t_PartOf = [autoxml.String, autoxml.optional]
t_License = [ [autoxml.String], autoxml.optional]
t_Icon = [ autoxml.String, autoxml.optional]
t_RuntimeDependencies = [ [Dependency], autoxml.optional]
t_Files = [ [Path], autoxml.optional]
t_Conflicts = [ [autoxml.String], autoxml.optional, "Conflicts/Package"]
t_ProvidesComar = [ [ComarProvide], autoxml.optional, "Provides/COMAR"]
#t_RequiresComar = [ [autoxml.String], autoxml.mandatory, "Requires/COMAR"]
t_AdditionalFiles = [ [AdditionalFile], autoxml.optional]
t_History = [ [Update], autoxml.optional]
def pkg_dir(self):
packageDir = self.name + '-' \
@@ -198,18 +198,18 @@ class Package:
class SpecFile(XmlFile):
__metaclass__ = xmlfile.autoxml #needed when we specify a superclass
__metaclass__ = autoxml.autoxml #needed when we specify a superclass
tag = "PISI"
t_Source = [ Source, xmlfile.mandatory]
t_Packages = [ [Package], xmlfile.mandatory, "Package"]
t_History = [ [Update], xmlfile.mandatory]
t_Source = [ Source, autoxml.mandatory]
t_Packages = [ [Package], autoxml.mandatory, "Package"]
t_History = [ [Update], autoxml.mandatory]
#we're not doing this with the init hook right now
#def init(self, tag = "PISI"):
#ignore tag
#XmlFile.__init__(self, tag)
#autoxml.__init__(self, tag)
def read_hook(self, errs):
"""Read PSPEC file"""
+14
View File
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@uludag.org.tr>
from xmlextmdom import *
+8 -1
View File
@@ -14,9 +14,16 @@
# Gurer Ozen <gurer@uludag.org.tr>
"""
some helper functions implemented with minidom
xmlext is a helper module for accessing XML files using
xml.dom.minidom.
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
note: this is a particularly inefficient implementation of xmlext.
"""
+170
View File
@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.org.tr>
"""
some helper functions for high-level access to XML structures
this implementation uses piksemel, a fast C-based XML library
function names are mixedCase for compatibility with minidom,
an old library
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
import piksemel as iks
class XmlError(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
def getNodeAttribute(node, attrname):
"""get named attribute from DOM node"""
if not node.hasAttribute(attrname):
return None
return node.getAttribute(attrname)
def getChildElts(node):
"""get only child elements"""
return filter(lambda x:x.nodeType == x.ELEMENT_NODE, node.childNodes)
def getTagByName(parent, childName):
return [x for x in parent.childNodes
if x.nodeType == x.ELEMENT_NODE and x.tagName == childName]
def getNodeText(node, tagpath = ""):
"""get the first child and expect it to be text!"""
if tagpath!="":
node = getNode(node, tagpath)
try:
child = node.childNodes[0]
except IndexError:
return None
except AttributeError: # no node by that name
return None
if child.nodeType == child.TEXT_NODE:
# in any case, strip whitespaces...
return child.data.strip()
else:
raise XmlError(_("getNodeText: Expected text node, got something else!"))
def getChildText(node_s, tagpath):
"""get the text of a child at the end of a tag path"""
node = getNode(node_s, tagpath)
if not node:
return None
return getNodeText(node)
def getNode(node, tagpath):
"""returns the *first* matching node for given tag path."""
assert type(tagpath)==str
tags = tagpath.split('/')
assert len(tags)>0
# iterative code to search for the path
for tag in tags:
for child in node.childNodes:
if child.nodeType == node.ELEMENT_NODE and child.tagName == tag:
return child
return None
def getAllNodes(node, tagPath):
"""retrieve all nodes that match a given tag path."""
#FIXME: better tag split
tags = tagPath.split('/')
if len(tags) == 0:
return []
nodeList = [node] # basis case
for tag in tags:
results = map(lambda x: getTagByName(x, tag), nodeList)
nodeList = []
for x in results:
nodeList.extend(x)
pass # emacs indentation error, keep it here
if len(nodeList) == 0:
return []
return nodeList
def createTagPath(node, tags):
"""create new child at the end of a tag chain starting from node
no matter what"""
if len(tags)==0:
return node
dom = node.ownerDocument
for tag in tags:
node = node.appendChild(dom.createElement(tag))
return node
def addTagPath(node, tags, newnode=None):
"""add newnode at the end of a tag chain, smart one"""
node = createTagPath(node, tags)
if newnode: # node to add specified
node.appendChild(newnode)
return node
def addNode(node, tagpath, newnode = None, branch=True):
"""add a new node at the end of the tree and returns it
if newnode is given adds that node, too."""
assert type(tagpath)==str
tags = []
if tagpath != "":
tags = tagpath.split('/') # tag chain
else:
addTagPath(node, [], newnode)
return node #FIXME: is this correct!?!?
assert len(tags)>0 # we want a chain
# iterative code to search for the path
if branch:
rem = 1
else:
rem = 0
while len(tags) > rem:
tag = tags.pop(0)
nodeList = getTagByName(node, tag)
if len(nodeList) == 0: # couldn't find
tags.insert(0, tag) # put it back in
return addTagPath(node, tags, newnode)
else:
node = nodeList[len(nodeList)-1] # discard other matches
else:
# had only one tag..
return addTagPath(node, tags, newnode)
return node
def newNode(node, tag):
return node.ownerDocument.createElement(tag)
def newTextNode(node, text):
return node.ownerDocument.createTextNode(text)
def addText(node, tagPath, text, branch = True):
newnode = newTextNode(node, text)
addNode(node, tagPath, newnode, branch)
+154
View File
@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
# Gurer Ozen <gurer@uludag.org.tr>
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module (and sorely lacking
in xml.dom :( )
function names are mixedCase for compatibility with minidom,
an 'old library'
note: this is a particularly inefficient implementation of xmlfile
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
self.newDOM()
def newDOM(self):
"""clear DOM"""
impl = mdom.getDOMImplementation()
self.dom = impl.createDocument(None, self.rootTag, None)
def unlink(self):
"""deallocate DOM structure"""
self.dom.unlink()
def rootNode(self):
"""returns root document element"""
return self.dom.documentElement
def readxml(self, fileName):
try:
self.dom = mdom.parse(fileName)
except ExpatError, inst:
raise XmlError(_("File '%s' has invalid XML: %s\n") % (fileName,
str(inst)))
def writexml(self, fileName):
f = codecs.open(fileName,'w', "utf-8")
f.write(self.dom.toprettyxml())
f.close()
def verifyRootTag(self):
actual_roottag = self.rootNode().tagName
if actual_roottag != self.rootTag:
raise Error(_("Root tagname %s not identical to %s as expected") %
(actual_roottag, self.rootTag) )
# construction helpers
def newNode(self, tag):
return self.dom.createElement(tag)
def newTextNode(self, text):
return self.dom.createTextNode(text)
def newAttribute(self, attr):
return self.dom.createAttribute(attr)
# read helpers
def getNode(self, tagPath = ""):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.dom.documentElement, tagPath)
def getNodeText(self, tagPath):
"""returns the text of *first* matching node for given tag path."""
node = self.getNode(tagPath)
if not node:
return None
return getNodeText(node)
def getAllNodes(self, tagPath):
"""returns all nodes matching a given tag path."""
self.verifyRootTag()
return getAllNodes(self.dom.documentElement, tagPath)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return node.childNodes
# get only elements of a given type
#FIXME: this doesn't work
def getChildrenWithType(self, tagpath, type):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
return filter(lambda x:x.nodeType == type, node.childNodes)
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
node.childNodes)
except AttributeError:
return None
# write helpers
def addNode(self, tagPath, newnode = None):
"this adds the newnode under given tag path"
self.verifyRootTag()
return addNode(self.dom.documentElement, tagPath, newnode)
def addNodeUnder(self, node, tagPath, newnode = None):
"this adds the new stuff under node and then following tag path"
self.verifyRootTag()
return addNode(node, tagPath, newnode)
def addChild(self, newnode):
"add a new child node right under root element document"
self.dom.documentElement.appendChild(newnode)
def addText(self, node, text):
"add text to node"
node.appendChild(self.newTextNode(text))
def addTextNode(self, tagPath, text):
"add a text node with given tag path"
node = self.addNode(tagPath, self.newTextNode(text))
return node
def addTextNodeUnder(self, node, tagPath, text):
"add a text node under given node with tag path (phew)"
return self.addNodeUnder(node, tagPath, self.newTextNode(text))
+150
View File
@@ -0,0 +1,150 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2005, TUBITAK/UEKAE
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# Please read the COPYING file.
#
# Authors: Eray Ozkural <eray@uludag.org.tr>
# Gurer Ozen <gurer@uludag.org.tr>
# Baris Metin <baris@uludag.org.tr
"""
XmlFile class further abstracts a dom object using the
high-level dom functions provided in xmlext module
this implementation uses piksemel, a fast C-based XML library
"""
import gettext
__trans = gettext.translation('pisi', fallback=True)
_ = __trans.ugettext
import pisi
class Error(pisi.Error):
"named this way because the class if mostly used with an import *"
pass
class XmlFile(object):
"""A class to help reading and writing an XML file"""
def __init__(self, tag):
self.rootTag = tag
self.newDOM()
def newDOM(self):
"""clear DOM"""
impl = mdom.getDOMImplementation()
self.dom = impl.createDocument(None, self.rootTag, None)
def unlink(self):
"""deallocate DOM structure"""
self.dom.unlink()
def rootNode(self):
"""returns root document element"""
return self.dom.documentElement
def readxml(self, fileName):
try:
self.dom = mdom.parse(fileName)
except ExpatError, inst:
raise XmlError(_("File '%s' has invalid XML: %s\n") % (fileName,
str(inst)))
def writexml(self, fileName):
f = codecs.open(fileName,'w', "utf-8")
f.write(self.dom.toprettyxml())
f.close()
def verifyRootTag(self):
actual_roottag = self.rootNode().tagName
if actual_roottag != self.rootTag:
raise Error(_("Root tagname %s not identical to %s as expected") %
(actual_roottag, self.rootTag) )
# construction helpers
def newNode(self, tag):
return self.dom.createElement(tag)
def newTextNode(self, text):
return self.dom.createTextNode(text)
def newAttribute(self, attr):
return self.dom.createAttribute(attr)
# read helpers
def getNode(self, tagPath = ""):
"""returns the *first* matching node for given tag path."""
self.verifyRootTag()
return getNode(self.dom.documentElement, tagPath)
def getNodeText(self, tagPath):
"""returns the text of *first* matching node for given tag path."""
node = self.getNode(tagPath)
if not node:
return None
return getNodeText(node)
def getAllNodes(self, tagPath):
"""returns all nodes matching a given tag path."""
self.verifyRootTag()
return getAllNodes(self.dom.documentElement, tagPath)
def getChildren(self, tagpath):
""" returns the children of the given path"""
node = self.getNode(tagpath)
return node.childNodes
# get only elements of a given type
#FIXME: this doesn't work
def getChildrenWithType(self, tagpath, type):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
return filter(lambda x:x.nodeType == type, node.childNodes)
# get only child elements
def getChildElts(self, tagpath):
""" returns the children of the given path, only with given type """
node = self.getNode(tagpath)
try:
return filter(lambda x:x.nodeType == x.ELEMENT_NODE,
node.childNodes)
except AttributeError:
return None
# write helpers
def addNode(self, tagPath, newnode = None):
"this adds the newnode under given tag path"
self.verifyRootTag()
return addNode(self.dom.documentElement, tagPath, newnode)
def addNodeUnder(self, node, tagPath, newnode = None):
"this adds the new stuff under node and then following tag path"
self.verifyRootTag()
return addNode(node, tagPath, newnode)
def addChild(self, newnode):
"add a new child node right under root element document"
self.dom.documentElement.appendChild(newnode)
def addText(self, node, text):
"add text to node"
node.appendChild(self.newTextNode(text))
def addTextNode(self, tagPath, text):
"add a text node with given tag path"
node = self.addNode(tagPath, self.newTextNode(text))
return node
def addTextNodeUnder(self, node, tagPath, text):
"add a text node under given node with tag path (phew)"
return self.addNodeUnder(node, tagPath, self.newTextNode(text))