Code

9c5641695793874ddb27a7006d0e578c31d6e6de
[roundup.git] / 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 """
21 import re
22 import sys
24 from TALDefs import NAME_RE, TALESError, ErrorInfo
25 #from ITALES import ITALESCompiler, ITALESEngine
26 #from DocumentTemplate.DT_Util import ustr
27 ustr = str
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
41 class _Default:
42     pass
43 Default = _Default()
45 name_match = re.compile(r"(?s)(%s):(.*)\Z" % NAME_RE).match
47 class CompilerError(Exception):
48     pass
50 class DummyEngine:
52     position = None
53     source_file = None
55     #__implements__ = ITALESCompiler, ITALESEngine
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()
66     def getCompilerError(self):
67         return CompilerError
69     def getCompiler(self):
70         return self
72     def setSourceFile(self, source_file):
73         self.source_file = source_file
75     def setPosition(self, position):
76         self.position = position
78     def compile(self, expr):
79         return "$%s$" % expr
81     def uncompile(self, expression):
82         assert (expression.startswith("$") and expression.endswith("$"),
83             expression)
84         return expression[1:-1]
86     def beginScope(self):
87         self.stack.append(self.locals)
89     def endScope(self):
90         assert len(self.stack) > 1, "more endScope() than beginScope() calls"
91         self.locals = self.stack.pop()
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
99     def setGlobal(self, name, value):
100         self.globals[name] = value
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`)
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`)
144     def evaluateValue(self, expr):
145         return self.evaluate(expr)
147     def evaluateBoolean(self, expr):
148         return self.evaluate(expr)
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
156     def evaluateStructure(self, expr):
157         # XXX Should return None or a DOM tree
158         return self.evaluate(expr)
160     def evaluateSequence(self, expr):
161         # XXX Should return a sequence
162         return self.evaluate(expr)
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
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
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
203     def setRepeat(self, name, expr):
204         seq = self.evaluateSequence(expr)
205         return Iterator(name, seq, self)
207     def createErrorInfo(self, err, position):
208         return ErrorInfo(err, position)
210     def getDefault(self):
211         return Default
213     def translate(self, domain, msgid, mapping, default=None):
214         return self.translationService.translate(domain, msgid, mapping,
215                                                  default=default)
218 class Iterator:
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.
225     def __init__(self, name, seq, engine):
226         self.name = name
227         self.seq = seq
228         self.engine = engine
229         self.nextIndex = 0
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
241 class DummyDomain:
242     __implements__ = IDomain
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.
253         # simulate an unknown msgid by returning None
254         if msgid == "don't translate me":
255             text = default
256         else:
257             text = msgid.upper()
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)
264 class DummyTranslationService:
265     __implements__ = ITranslationService
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)
273     def getDomain(self, domain):
274         return DummyDomain()