root/trunk/proj/w3c/xmlClassBuilder.py

Revision 475, 12.9 kB (checked in by brian, 4 years ago)

Fixed treelist to work with change in inheritance hierarchy

Line 
1 #!/usr/bin/env python
2 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 ##~ Copyright (C) 2002-2004  TechGame Networks, LLC.
4 ##~
5 ##~ This library is free software; you can redistribute it and/or
6 ##~ modify it under the terms of the BSD style License as found in the
7 ##~ LICENSE file included with this distribution.
8 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9
10 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 #~ Imports
12 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13
14 import sys
15 import keyword
16 import xmlBuilder
17
18 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 #~ Definitions
20 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
22 ElementFactoryError = xmlBuilder.ElementFactoryError
23
24 class XMLFactory(object):
25     def xmlnsFromName(moduleName):
26         moduleName = moduleName.replace('.__init__', '')
27         return moduleName
28     xmlnsFromName = staticmethod(xmlnsFromName)
29
30     class Collection(dict):
31         next = None
32
33         def __repr__(self):
34             return '<%s.%s \'%s\'>' % (XMLFactory.__name__, self.__class__.__name__, self.getName())
35
36         def getElementFactory(self, owner, parent, node, attributes, namespacemap):
37             try:
38                 for idx in self._getElementFactoryIndices(node):
39                     FactoryFactory = self.get(idx)
40                     if FactoryFactory is not None:
41                         result = FactoryFactory(owner, parent, node, attributes, namespacemap)
42                         if result:
43                             return result
44             except XMLFactory.InheritFromNextFactory, (args, kw):
45                 if self.next is not None:
46                     return self.next.getElementFactory(*args, **kw)
47             raise ElementFactoryError("Could not find a class to build for node %r" % (node,))
48
49         def addFactory(self, key, xmlfactory):
50             if isinstance(key, basestring):
51                 key = (key,)
52             elif isinstance(key, tuple) and not (0 < len(key) <= 2):
53                 # trim out common usecase of __name__ when packaged for zipimport
54                 raise ValueError('Invalid factory key: %r' % (key,))
55
56             self[key] = xmlfactory
57
58         def copy(self):
59             result = self.__class__(dict.copy(self))
60             result.__dict__ = self.__dict__.copy()
61             return result
62
63         def setName(self, name):
64             self.name = str(name)
65             return self
66         def getName(self):
67             return getattr(self, 'name', '???')
68
69         def getNext(self):
70             return self.next
71         def setNext(self, factoryCollection):
72             if factoryCollection is not self:
73                 self.next = factoryCollection
74             return self
75
76         def pushNext(self, factory):
77             if self.getNext() is None: result = self
78             else: result = self.copy()
79             return result.setNext(factory)
80
81         def popNext(self):
82             result = self.getNext()
83             self.setNext(None)
84             return result
85
86         def _getElementFactoryIndices(self, node):
87             if node:
88                 return tuple(node), tuple(node[:-1]), (None, node[-1]), None
89             else: return None
90
91     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92     #~ Specific Factories
93     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
94
95     class FactoryBase(object):
96         def __call__(self, *args, **kw):
97             return self.getFactory(*args, **kw)
98
99     class InheritFromNextFactory(Exception):
100         def __call__(self, *args, **kw):
101             raise self.__class__, (args, kw)
102     InheritFromNext = InheritFromNextFactory
103
104     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105
106     class FromClassmethod(FactoryBase):
107         def __init__(self, nextFactory, name='fromXMLBuilder'):
108             self.nextFactory = nextFactory
109             self.name = name
110         def getFactory(self, *args, **kw):
111             result = self.nextFactory.getFactory(*args, **kw)
112             result = getattr(result, self.methodName)
113             return result
114
115     class Static(FactoryBase):
116         def __init__(self, result):
117             self.result = result
118         def getFactory(self, *args, **kw):
119             return self.result
120
121     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122
123     class Raise(FactoryBase):
124         def __init__(self, exception=ElementFactoryError, message=''):
125             self.exception = exception
126             self.message = message
127
128         def getFactory(self, *args, **kw):
129             raise self.exception, self.message
130
131     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132
133     class BaseImport(FactoryBase):
134         retryimport = True
135
136         def __init__(self, pyPathRoot=''):
137             self.pyPathRoot = pyPathRoot
138             self._CachedElementFactories = {}
139
140         def isEnabled(self):
141             try:
142                 return self._enabled
143             except AttributeError:
144                 try:
145                     __import__(self.pyPathRoot, {}, {})
146                 except ImportError:
147                     self._enabled = False
148                 else:
149                     self._enabled = True
150                 return self._enabled
151
152         def _import(self, pyPath, name):
153             if not self.isEnabled():
154                 raise ImportError, 'Import element finder %r is disabled because root %r cannot be imported' % (self, self.pyPathRoot)
155             name = name.replace('-', '_')
156             pyPath = pyPath.replace('-', '_')
157
158             if self.pyPathRoot and pyPath:
159                 impPath = '.'.join((self.pyPathRoot, pyPath))
160             elif not pyPath:
161                 impPath = self.pyPathRoot
162             module = __import__(impPath, {}, {}, name)
163             return self._getNameFromModule(module, name)
164
165         def _getNameFromModule(self, module, name):
166             try:
167                 return getattr(module, name)
168             except AttributeError:
169                 if self.retryimport:
170                     module = reload(module)
171                     try: return getattr(module, name)
172                     except AttributeError: pass
173                 raise ElementFactoryError('Could not find "%s" in module %r' % (name, module))
174
175         def getFactory(self, owner, parent, node, attributes, namespacemap):
176             result = self._CachedElementFactories.get(node, None)
177             if not result:
178                 ns, name = node
179                 if keyword.iskeyword(name): name = name + '_'
180                 result = self._import('', name)
181
182                 ## The following works for another scheme
183                 ##result = self.DoImport(*node)
184
185                 self._CachedElementFactories[node] = result
186             return result
187
188     class HierarchyBaseImport(BaseImport):
189         def __init__(self, pyPathRoot='', subpackages=('')):
190             super(XMLFactory.HierarchyBaseImport, self).__init__(pyPathRoot)
191             self.subpackages = subpackages
192
193         def _import(self, pyPath, name):
194             if not self.isEnabled():
195                 raise ImportError('Import element finder %r is disabled because root %r cannot be imported' % (self, self.pyPathRoot))
196             name = name.replace('-', '_')
197             pyPath = pyPath.replace('-', '_')
198
199             for subPath in self.subpackages:
200                 if subPath:
201                     impPath = '.'.join((self.pyPathRoot, subPath, pyPath))
202                 else:
203                     impPath = '.'.join((self.pyPathRoot, pyPath))
204
205                 try:
206                     module = __import__(impPath, {}, {}, name)
207                 except ImportError, e:
208                     notFoundError = sys.exc_info()[2].tb_next is not None
209                     if notFoundError:
210                         raise
211                 else:
212                     break
213             else:
214                 raise ImportError("Could not find skin module for '%s' in '%s' or sub-packages %r" % (pyPath, self.pyPathRoot, self.subpackages))
215
216             return self._getNameFromModule(module, name)
217
218     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
219
220     class StaticImport(BaseImport):
221         def __init__(self, pyPath, name, *args, **kw):
222             XMLFactory.BaseImport.__init__(self, pyPath)
223             self.name = name
224             self.result = None
225
226         def getFactory(self, *args, **kw):
227             if not self.result:
228                 self.result = self._import('', self.name)
229             return self.result
230            
231     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
232
233     class NodeImport(BaseImport):
234         def getFactory(self, owner, parent, node, attributes, namespacemap):
235             result = self._CachedElementFactories.get(node, None)
236             if not result:
237                 ns, name = node
238                 if keyword.iskeyword(name): name = name + '_'
239                 result = self._import(name, name)
240
241                 ## The following works for another scheme
242                 ##result = self.DoImport(*node)
243
244                 self._CachedElementFactories[node] = result
245             return result
246            
247     class HierarchyNodeImport(HierarchyBaseImport, NodeImport):
248         pass
249
250     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
251
252     class NamespaceImport(BaseImport):
253         def getFactory(self, owner, parent, node, attributes, namespacemap):
254             result = self._CachedElementFactories.get(node, None)
255             if not result:
256                 ns, name = node
257                 if keyword.iskeyword(name): name = name + '_'
258                 result = self._import('%s.%s' % (ns, name), name)
259
260                 ## The following works for another scheme
261                 ##result = self.DoImport(*node)
262
263                 self._CachedElementFactories[node] = result
264             return result
265
266     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
267
268     class TryList(FactoryBase):
269         def __init__(self, trylist, ignoreExceptions=[ElementFactoryError, ImportError]):#, AttributeError, KeyError]):
270             self._trylist = trylist
271             self._ignoreExceptions = tuple(ignoreExceptions)
272
273         def getFactory(self, owner, parent, node, attributes, namespacemap):
274             for each in self._trylist:
275                 try:
276                     return each(owner, parent, node, attributes, namespacemap)
277                 except self._ignoreExceptions:
278                     pass
279
280             raise ElementFactoryError('No suitable element factory for node %r' % (node,))
281
282     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
283
284     class CachedTryList(TryList):
285         def __init__(self, *args, **kw):
286             XMLFactory.TryList.__init__(self, *args, **kw)
287             self._CachedElementFactories = {}
288
289         def getFactory(self, owner, parent, node, attributes, namespacemap):
290             result = self._CachedElementFactories.get(node, None)
291             if result:
292                 return result
293             for each in self._trylist:
294                 try:
295                     result = each(owner, parent, node, attributes, namespacemap)
296                     self._CachedElementFactories[node] = result
297                     return result
298                 except self._ignoreExceptions, e:
299                     pass
300
301             raise ElementFactoryError('No suitable element factory for node %r' % (node,))
302
303 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
304 #~ Class Builders
305 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306
307 class ClassBuilderState(xmlBuilder.BuilderState):
308     def __init__(self, xmlbuilder):
309         xmlBuilder.BuilderState.__init__(self, xmlbuilder)
310         self._xmlFactories = xmlbuilder.xmlFactories
311
312     def restore(self, xmlbuilder):
313         xmlbuilder.xmlFactories = self._xmlFactories
314         return xmlBuilder.BuilderState.restore(self, xmlbuilder)
315
316 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
317
318 class XMLClassBuilder(xmlBuilder.XMLBuilder):
319     xmlFactories = XMLFactory.Collection()
320     BuilderStateFactory = ClassBuilderState
321
322     def getElementFactory(self, *args, **kw):
323         return self.xmlFactories.getElementFactory(*args, **kw)
324
325     def pushXMLFactories(self, xmlFactories):
326         self.xmlFactories = xmlFactories.pushNext(self.xmlFactories)
327
328     def popXMLFactories(self):
329         result = self.xmlFactories
330         self.xmlFactories = self.xmlFactories.popNext()
331         if self.xmlFactories is None:
332             raise Exception("Popped last XMLFactory off stack! Last: %r" % (result,))
333         return result
334
335 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
336
337 class ClassElementBase(xmlBuilder.ElementBase):
338     def xmlChildFactory(self, elemBuilder, parentElem, node, attributes, namespaceMap):
339         """This gets called so that elements may override the factory for their direct children"""
340
341 class XMLClassObjectBuilder(XMLClassBuilder):
342     def getElementFactory(self, *args, **kw):
343         if self.elementStack:
344             # give the element a shot at providing the factory
345             result = self.elementStack.topElement().xmlChildFactory(*args, **kw)
346             if result is not None:
347                 return result
348         return XMLClassObjectBuilder.getElementFactory(*args, **kw)
349
Note: See TracBrowser for help on using the browser.