Correction crash
[auf_roundup.git] / build / lib / roundup / cgi / TAL / DummyEngine.py
1 ##############################################################################
2 #
3 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the Zope Public License,
7 # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 # FOR A PARTICULAR PURPOSE.
12 #
13 ##############################################################################
14 # Modifications for Roundup:
15 # 1. commented out ITALES references
16 # 2. implemented ustr as str
17 """
18 Dummy TALES engine so that I can test out the TAL implementation.
19 """
20
21 import re
22 import sys
23
24 from TALDefs import NAME_RE, TALESError, ErrorInfo
25 #from ITALES import ITALESCompiler, ITALESEngine
26 #from DocumentTemplate.DT_Util import ustr
27 ustr = str
28
29 IDomain = None
30 if sys.modules.has_key('Zope'):
31 try:
32 from Zope.I18n.ITranslationService import ITranslationService
33 from Zope.I18n.IDomain import IDomain
34 except ImportError:
35 pass
36 if IDomain is None:
37 # Before 2.7, or not in Zope
38 class ITranslationService: pass
39 class IDomain: pass
40
41 class _Default:
42 pass
43 Default = _Default()
44
45 name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match
46
47 class CompilerError(Exception):
48 pass
49
50 class DummyEngine:
51
52 position = None
53 source_file = None
54
55 #__implements__ = ITALESCompiler, ITALESEngine
56
57 def __init__(self, macros=None):
58 if macros is None:
59 macros = {}
60 self.macros = macros
61 dict = {'nothing': None, 'default': Default}
62 self.locals = self.globals = dict
63 self.stack = [dict]
64 self.translationService = DummyTranslationService()
65
66 def getCompilerError(self):
67 return CompilerError
68
69 def getCompiler(self):
70 return self
71
72 def setSourceFile(self, source_file):
73 self.source_file = source_file
74
75 def setPosition(self, position):
76 self.position = position
77
78 def compile(self, expr):
79 return "$%s$" % expr
80
81 def uncompile(self, expression):
82 assert (expression.startswith("$") and expression.endswith("$"),
83 expression)
84 return expression[1:-1]
85
86 def beginScope(self):
87 self.stack.append(self.locals)
88
89 def endScope(self):
90 assert len(self.stack) > 1, "more endScope() than beginScope() calls"
91 self.locals = self.stack.pop()
92
93 def setLocal(self, name, value):
94 if self.locals is self.stack[-1]:
95 # Unmerge this scope's locals from previous scope of first set
96 self.locals = self.locals.copy()
97 self.locals[name] = value
98
99 def setGlobal(self, name, value):
100 self.globals[name] = value
101
102 def evaluate(self, expression):
103 assert (expression.startswith("$") and expression.endswith("$"),
104 expression)
105 expression = expression[1:-1]
106 m = name_match(expression)
107 if m:
108 type, expr = m.group(1, 2)
109 else:
110 type = "path"
111 expr = expression
112 if type in ("string", "str"):
113 return expr
114 if type in ("path", "var", "global", "local"):
115 return self.evaluatePathOrVar(expr)
116 if type == "not":
117 return not self.evaluate(expr)
118 if type == "exists":
119 return self.locals.has_key(expr) or self.globals.has_key(expr)
120 if type == "python":
121 try:
122 return eval(expr, self.globals, self.locals)
123 except:
124 raise TALESError("evaluation error in %s" % `expr`)
125 if type == "position":
126 # Insert the current source file name, line number,
127 # and column offset.
128 if self.position:
129 lineno, offset = self.position
130 else:
131 lineno, offset = None, None
132 return '%s (%s,%s)' % (self.source_file, lineno, offset)
133 raise TALESError("unrecognized expression: " + `expression`)
134
135 def evaluatePathOrVar(self, expr):
136 expr = expr.strip()
137 if self.locals.has_key(expr):
138 return self.locals[expr]
139 elif self.globals.has_key(expr):
140 return self.globals[expr]
141 else:
142 raise TALESError("unknown variable: %s" % `expr`)
143
144 def evaluateValue(self, expr):
145 return self.evaluate(expr)
146
147 def evaluateBoolean(self, expr):
148 return self.evaluate(expr)
149
150 def evaluateText(self, expr):
151 text = self.evaluate(expr)
152 if text is not None and text is not Default:
153 text = ustr(text)
154 return text
155
156 def evaluateStructure(self, expr):
157 # XXX Should return None or a DOM tree
158 return self.evaluate(expr)
159
160 def evaluateSequence(self, expr):
161 # XXX Should return a sequence
162 return self.evaluate(expr)
163
164 def evaluateMacro(self, macroName):
165 assert (macroName.startswith("$") and macroName.endswith("$"),
166 macroName)
167 macroName = macroName[1:-1]
168 file, localName = self.findMacroFile(macroName)
169 if not file:
170 # Local macro
171 macro = self.macros[localName]
172 else:
173 # External macro
174 import driver
175 program, macros = driver.compilefile(file)
176 macro = macros.get(localName)
177 if not macro:
178 raise TALESError("macro %s not found in file %s" %
179 (localName, file))
180 return macro
181
182 def findMacroDocument(self, macroName):
183 file, localName = self.findMacroFile(macroName)
184 if not file:
185 return file, localName
186 import driver
187 doc = driver.parsefile(file)
188 return doc, localName
189
190 def findMacroFile(self, macroName):
191 if not macroName:
192 raise TALESError("empty macro name")
193 i = macroName.rfind('/')
194 if i < 0:
195 # No slash -- must be a locally defined macro
196 return None, macroName
197 else:
198 # Up to last slash is the filename
199 fileName = macroName[:i]
200 localName = macroName[i+1:]
201 return fileName, localName
202
203 def setRepeat(self, name, expr):
204 seq = self.evaluateSequence(expr)
205 return Iterator(name, seq, self)
206
207 def createErrorInfo(self, err, position):
208 return ErrorInfo(err, position)
209
210 def getDefault(self):
211 return Default
212
213 def translate(self, domain, msgid, mapping, default=None):
214 return self.translationService.translate(domain, msgid, mapping,
215 default=default)
216
217
218 class Iterator:
219
220 # This is not an implementation of a Python iterator. The next()
221 # method returns true or false to indicate whether another item is
222 # available; if there is another item, the iterator instance calls
223 # setLocal() on the evaluation engine passed to the constructor.
224
225 def __init__(self, name, seq, engine):
226 self.name = name
227 self.seq = seq
228 self.engine = engine
229 self.nextIndex = 0
230
231 def next(self):
232 i = self.nextIndex
233 try:
234 item = self.seq[i]
235 except IndexError:
236 return 0
237 self.nextIndex = i+1
238 self.engine.setLocal(self.name, item)
239 return 1
240
241 class DummyDomain:
242 __implements__ = IDomain
243
244 def translate(self, msgid, mapping=None, context=None,
245 target_language=None, default=None):
246 # This is a fake translation service which simply uppercases non
247 # ${name} placeholder text in the message id.
248 #
249 # First, transform a string with ${name} placeholders into a list of
250 # substrings. Then upcase everything but the placeholders, then glue
251 # things back together.
252
253 # simulate an unknown msgid by returning None
254 if msgid == "don't translate me":
255 text = default
256 else:
257 text = msgid.upper()
258
259 def repl(m, mapping=mapping):
260 return ustr(mapping[m.group(m.lastindex).lower()])
261 cre = re.compile(r'\$(?:(%s)|\{(%s)\})' % (NAME_RE, NAME_RE))
262 return cre.sub(repl, text)
263
264 class DummyTranslationService:
265 __implements__ = ITranslationService
266
267 def translate(self, domain, msgid, mapping=None, context=None,
268 target_language=None, default=None):
269 return self.getDomain(domain).translate(msgid, mapping, context,
270 target_language,
271 default=default)
272
273 def getDomain(self, domain):
274 return DummyDomain()