--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/cone/public/_etree_wrapper.py Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,245 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+#
+
+from xml.parsers import expat
+
+# Import ElementTree (should always be available)
+try:
+ from elementtree import ElementTree
+except ImportError:
+ from xml.etree import ElementTree
+
+import exceptions
+
+
+class ElementTreeBackendWrapperBase(object):
+ def get_module(self):
+ raise NotImplementedError()
+
+ def get_lineno(self, element):
+ raise NotImplementedError()
+
+class ElementTreeBackendWrapper(ElementTreeBackendWrapperBase):
+
+ class CustomTreeBuilder(ElementTree.TreeBuilder):
+ """
+ Custom TreeBuilder for ElementTree that records line numbers
+ of the elements.
+ """
+ def start(self, tag, attrs):
+ elem = ElementTree.TreeBuilder.start(self, tag, attrs)
+ lineno = self._xmltreebuilder._parser.CurrentLineNumber
+ #print "Tag: %s, line: %r" % (tag, lineno)
+ elem.sourceline = lineno
+ return elem
+
+ def get_module(self):
+ return ElementTree
+
+ def fromstring(self, text):
+ try:
+ treebuilder = self.CustomTreeBuilder()
+ parser = ElementTree.XMLTreeBuilder(target=treebuilder)
+ treebuilder._xmltreebuilder = parser
+ parser.feed(text)
+ return parser.close()
+ except expat.ExpatError, e:
+ raise exceptions.XmlParseError(
+ "XML parse error on line %d: %s" % (e.lineno, e),
+ e.lineno, str(e))
+
+ def tostring(self, etree, encoding=None):
+ return ElementTree.tostring(etree, encoding)
+
+ def get_lineno(self, element):
+ return element.sourceline
+
+
+class CElementTreeBackendWrapper(ElementTreeBackendWrapperBase):
+ def __init__(self):
+ try:
+ from cElementTree import cElementTree
+ except ImportError:
+ from xml.etree import cElementTree
+
+ self.cElementTree = cElementTree
+
+ def get_module(self):
+ return self.cElementTree
+
+ def fromstring(self, text):
+ try:
+ return self.cElementTree.fromstring(text)
+ except SyntaxError, e:
+ # cElementTree raises a SyntaxError, but does not set
+ # its lineno attribute, so look for the line number
+ # in the exception text
+ import re
+ match = re.search(r'line (\d+)\, column \d+$', str(e))
+ if match: lineno = int(match.group(1))
+ else: lineno = None
+
+ raise exceptions.XmlParseError(
+ "XML parse error on line %s: %s" % (lineno, e),
+ lineno, str(e))
+
+ def tostring(self, etree, encoding=None):
+ return self.cElementTree.tostring(etree, encoding)
+
+ def get_lineno(self, element):
+ # cElementTree does not support line numbers
+ return None
+
+
+class LxmlBackendWrapper(ElementTreeBackendWrapperBase):
+
+ def __init__(self):
+ import lxml.etree
+ self.lxml = lxml
+
+ def get_module(self):
+ return self.lxml.etree
+
+ def fromstring(self, text):
+ try:
+ elem = self.lxml.etree.fromstring(text)
+
+ # lxml parses also comments, but ConE does not expect those,
+ # so remove them to prevent any errors on that account
+ def remove_comments(elem):
+ # Find the comments under this element
+ comments = []
+ for x in elem:
+ if isinstance(x, self.lxml.etree._Comment):
+ comments.append(x)
+
+ # Remove them
+ for c in comments:
+ elem.remove(c)
+
+ # Recurse to sub-elements
+ for subelem in elem:
+ remove_comments(subelem)
+
+ remove_comments(elem)
+
+ return elem
+ except self.lxml.etree.XMLSyntaxError, e:
+ raise exceptions.XmlParseError(
+ "XML parse error on line %d: %s" % (e.position[0], e),
+ e.position[0], str(e))
+
+ def tostring(self, etree, encoding=None):
+ return self.lxml.etree.tostring(etree, encoding=encoding)
+
+ def get_lineno(self, element):
+ return element.sourceline
+
+# ============================================================================
+#
+# ============================================================================
+
+class ElementTreeWrapper(object):
+ """
+ ElementTree wrapper class for providing a unified interface to different
+ ElementTree implementations.
+
+ Currently supported are the pure Python ElementTree implementation,
+ cElementTree and lxml.etree
+ """
+ BACKEND_ELEMENT_TREE = 'ElementTree'
+ BACKEND_C_ELEMENT_TREE = 'cElementTree'
+ BACKEND_LXML = 'lxml'
+
+ # Import order for the default back-end. The list is traversed
+ # top-down and the first back-end whose importing is successful is
+ # used as the default back-end
+ DEFAULT_BACKEND_IMPORT_ORDER = [BACKEND_C_ELEMENT_TREE,
+ BACKEND_ELEMENT_TREE]
+
+ _backend_mapping = {BACKEND_ELEMENT_TREE: ElementTreeBackendWrapper,
+ BACKEND_C_ELEMENT_TREE: CElementTreeBackendWrapper,
+ BACKEND_LXML: LxmlBackendWrapper}
+
+ _backend_id = None
+ _backend_wrapper = None
+
+ def get_backend_id(self):
+ """
+ Return the ID of the currently used ElementTree back-end.
+ """
+ # Make sure that the default back-end is set, so _backend_id
+ # will not be None
+ self._get_backend()
+ assert self._backend_id is not None
+ return self._backend_id
+
+ def set_backend_id(self, backend_id):
+ """
+ Set the used ElementTree back-end by back-end ID.
+ """
+ if backend_id not in self._backend_mapping:
+ raise ValueError("Invalid ElementTree back-end ID: %r" % backend_id)
+
+ if backend_id == self._backend_id:
+ return
+
+ backend_wrapper_class = self._backend_mapping[backend_id]
+ self._backend_wrapper = backend_wrapper_class()
+ self._backend_id = backend_id
+
+ def _get_backend(self):
+ """
+ Return the currently set ElementTree back-end wrapper object.
+ """
+ if self._backend_wrapper is None:
+ # Back-end not set, so set the default back-end.
+ # The default is the C version of ElementTree, but if that
+ # is not available, the pure Python version is used
+ for backend_id in self.DEFAULT_BACKEND_IMPORT_ORDER:
+ try:
+ self.set_backend_id(backend_id)
+ except ImportError:
+ pass
+
+ if self._backend_wrapper is None:
+ raise RuntimeError("Failed to set any ElementTree backend! Tried these: %r" % self.DEFAULT_BACKEND_IMPORT_ORDER)
+
+ return self._backend_wrapper
+
+ def get_lineno(self, element):
+ """
+ Return the source line number of the given XML element.
+
+ Note that for the cElementTree parser this will always return
+ None, since that parser does not support line numbers.
+ """
+ return self._get_backend().get_lineno(element)
+
+ def __getattribute__(self, attrname):
+ try:
+ # Try to get the attribute from this object (the top-level wrapper)
+ return object.__getattribute__(self, attrname)
+ except AttributeError:
+ # If not overridden here, try to get it from the back-end wrapper
+ backend = self._get_backend()
+ try:
+ return getattr(backend, attrname)
+ except AttributeError:
+ # Last resort: try to get it from the module
+ # the back-end wrapper wraps
+ backend_module = backend.get_module()
+ return getattr(backend_module, attrname)