Upload Tizen:Base source
[toolchains/python-lxml.git] / src / lxml / nsclasses.pxi
1 # module-level API for namespace implementations
2
3 class LxmlRegistryError(LxmlError):
4     u"""Base class of lxml registry errors.
5     """
6     pass
7
8 class NamespaceRegistryError(LxmlRegistryError):
9     u"""Error registering a namespace extension.
10     """
11     pass
12
13
14 cdef class _NamespaceRegistry:
15     u"Dictionary-like namespace registry"
16     cdef object _ns_uri
17     cdef object _ns_uri_utf
18     cdef dict _entries
19     cdef char* _c_ns_uri_utf
20     def __init__(self, ns_uri):
21         self._ns_uri = ns_uri
22         if ns_uri is None:
23             self._ns_uri_utf = None
24             self._c_ns_uri_utf = NULL
25         else:
26             self._ns_uri_utf = _utf8(ns_uri)
27             self._c_ns_uri_utf = _cstr(self._ns_uri_utf)
28         self._entries = {}
29
30     def update(self, class_dict_iterable):
31         u"""update(self, class_dict_iterable)
32
33         Forgivingly update the registry.
34
35         If registered values do not match the required type for this
36         registry, or if their name starts with '_', they will be
37         silently discarded. This allows registrations at the module or
38         class level using vars(), globals() etc."""
39         if hasattr(class_dict_iterable, u'items'):
40             class_dict_iterable = class_dict_iterable.items()
41         for name, item in class_dict_iterable:
42             if (name is None or name[:1] != u'_') and callable(item):
43                 self[name] = item
44
45     def __getitem__(self, name):
46         if name is not None:
47             name = _utf8(name)
48         return self._get(name)
49
50     def __delitem__(self, name):
51         if name is not None:
52             name = _utf8(name)
53         del self._entries[name]
54
55     cdef object _get(self, object name):
56         cdef python.PyObject* dict_result
57         dict_result = python.PyDict_GetItem(self._entries, name)
58         if dict_result is NULL:
59             raise KeyError, u"Name not registered."
60         return <object>dict_result
61
62     cdef object _getForString(self, char* name):
63         cdef python.PyObject* dict_result
64         dict_result = python.PyDict_GetItem(self._entries, name)
65         if dict_result is NULL:
66             raise KeyError, u"Name not registered."
67         return <object>dict_result
68
69     def __iter__(self):
70         return iter(self._entries)
71
72     def items(self):
73         return list(self._entries.items())
74
75     def iteritems(self):
76         return iter(self._entries.items())
77
78     def clear(self):
79         python.PyDict_Clear(self._entries)
80
81 cdef class _ClassNamespaceRegistry(_NamespaceRegistry):
82     u"Dictionary-like registry for namespace implementation classes"
83     def __setitem__(self, name, item):
84         if not python.PyType_Check(item) or not issubclass(item, ElementBase):
85             raise NamespaceRegistryError, \
86                 u"Registered element classes must be subtypes of ElementBase"
87         if name is not None:
88             name = _utf8(name)
89         self._entries[name] = item
90
91     def __repr__(self):
92         return u"Namespace(%r)" % self._ns_uri
93
94
95 cdef class ElementNamespaceClassLookup(FallbackElementClassLookup):
96     u"""ElementNamespaceClassLookup(self, fallback=None)
97
98     Element class lookup scheme that searches the Element class in the
99     Namespace registry.
100     """
101     cdef object _namespace_registries
102     def __init__(self, ElementClassLookup fallback=None):
103         self._namespace_registries = {}
104         FallbackElementClassLookup.__init__(self, fallback)
105         self._lookup_function = _find_nselement_class
106
107     def get_namespace(self, ns_uri):
108         u"""get_namespace(self, ns_uri)
109
110         Retrieve the namespace object associated with the given URI.
111
112         Creates a new one if it does not yet exist."""
113         if ns_uri:
114             ns_utf = _utf8(ns_uri)
115         else:
116             ns_utf = None
117         try:
118             return self._namespace_registries[ns_utf]
119         except KeyError:
120             registry = self._namespace_registries[ns_utf] = \
121                        _ClassNamespaceRegistry(ns_uri)
122             return registry
123
124 cdef object _find_nselement_class(state, _Document doc, xmlNode* c_node):
125     cdef python.PyObject* dict_result
126     cdef ElementNamespaceClassLookup lookup
127     cdef _NamespaceRegistry registry
128     cdef char* c_namespace_utf
129     if state is None:
130         return _lookupDefaultElementClass(None, doc, c_node)
131
132     lookup = <ElementNamespaceClassLookup>state
133     if c_node.type != tree.XML_ELEMENT_NODE:
134         return _callLookupFallback(lookup, doc, c_node)
135
136     c_namespace_utf = _getNs(c_node)
137     if c_namespace_utf is not NULL:
138         dict_result = python.PyDict_GetItem(
139             lookup._namespace_registries, c_namespace_utf)
140     else:
141         dict_result = python.PyDict_GetItem(
142             lookup._namespace_registries, None)
143     if dict_result is not NULL:
144         registry = <_NamespaceRegistry>dict_result
145         classes = registry._entries
146
147         if c_node.name is not NULL:
148             dict_result = python.PyDict_GetItem(
149                 classes, c_node.name)
150         else:
151             dict_result = NULL
152
153         if dict_result is NULL:
154             dict_result = python.PyDict_GetItem(classes, None)
155
156         if dict_result is not NULL:
157             return <object>dict_result
158     return _callLookupFallback(lookup, doc, c_node)
159
160
161 ################################################################################
162 # XPath extension functions
163
164 cdef dict __FUNCTION_NAMESPACE_REGISTRIES
165 __FUNCTION_NAMESPACE_REGISTRIES = {}
166
167 def FunctionNamespace(ns_uri):
168     u"""FunctionNamespace(ns_uri)
169
170     Retrieve the function namespace object associated with the given
171     URI.
172
173     Creates a new one if it does not yet exist. A function namespace
174     can only be used to register extension functions."""
175     if ns_uri:
176         ns_utf = _utf8(ns_uri)
177     else:
178         ns_utf = None
179     try:
180         return __FUNCTION_NAMESPACE_REGISTRIES[ns_utf]
181     except KeyError:
182         registry = __FUNCTION_NAMESPACE_REGISTRIES[ns_utf] = \
183                    _XPathFunctionNamespaceRegistry(ns_uri)
184         return registry
185
186 cdef class _FunctionNamespaceRegistry(_NamespaceRegistry):
187     def __setitem__(self, name, item):
188         if not callable(item):
189             raise NamespaceRegistryError, \
190                 u"Registered functions must be callable."
191         if not name:
192             raise ValueError, \
193                 u"extensions must have non empty names"
194         self._entries[_utf8(name)] = item
195
196     def __repr__(self):
197         return u"FunctionNamespace(%r)" % self._ns_uri
198
199 cdef class _XPathFunctionNamespaceRegistry(_FunctionNamespaceRegistry):
200     cdef object _prefix
201     cdef object _prefix_utf
202
203     property prefix:
204         u"Namespace prefix for extension functions."
205         def __del__(self):
206             self._prefix = None # no prefix configured
207             self._prefix_utf = None
208         def __get__(self):
209             if self._prefix is None:
210                 return ''
211             else:
212                 return self._prefix
213         def __set__(self, prefix):
214             if prefix == '':
215                 prefix = None # empty prefix
216             if prefix is None:
217                 self._prefix_utf = None
218             else:
219                 self._prefix_utf = _utf8(prefix)
220             self._prefix = prefix
221
222 cdef list _find_all_extension_prefixes():
223     u"Internal lookup function to find all function prefixes for XSLT/XPath."
224     cdef _XPathFunctionNamespaceRegistry registry
225     cdef list ns_prefixes = []
226     for registry in __FUNCTION_NAMESPACE_REGISTRIES.itervalues():
227         if registry._prefix_utf is not None:
228             if registry._ns_uri_utf is not None:
229                 ns_prefixes.append(
230                     (registry._prefix_utf, registry._ns_uri_utf))
231     return ns_prefixes
232
233 cdef object _find_extension(ns_uri_utf, name_utf):
234     cdef python.PyObject* dict_result
235     dict_result = python.PyDict_GetItem(
236         __FUNCTION_NAMESPACE_REGISTRIES, ns_uri_utf)
237     if dict_result is NULL:
238         return None
239     extensions = (<_NamespaceRegistry>dict_result)._entries
240     dict_result = python.PyDict_GetItem(extensions, name_utf)
241     if dict_result is NULL:
242         return None
243     else:
244         return <object>dict_result