Correction crash
[auf_roundup.git] / build / lib / roundup / cgi / TAL / TALDefs.py
CommitLineData
c638d827
CR
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"""
17Common definitions used by TAL and METAL compilation an transformation.
18"""
19
20from types import ListType, TupleType
21
22#from ITALES import ITALESErrorInfo
23
24TAL_VERSION = "1.4"
25
26XML_NS = "http://www.w3.org/XML/1998/namespace" # URI for XML namespace
27XMLNS_NS = "http://www.w3.org/2000/xmlns/" # URI for XML NS declarations
28
29ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal"
30ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal"
31ZOPE_I18N_NS = "http://xml.zope.org/namespaces/i18n"
32
33# This RE must exactly match the expression of the same name in the
34# zope.i18n.simpletranslationservice module:
35NAME_RE = "[a-zA-Z_][-a-zA-Z0-9_]*"
36
37KNOWN_METAL_ATTRIBUTES = [
38 "define-macro",
39 "use-macro",
40 "define-slot",
41 "fill-slot",
42 "slot",
43 ]
44
45KNOWN_TAL_ATTRIBUTES = [
46 "define",
47 "condition",
48 "content",
49 "replace",
50 "repeat",
51 "attributes",
52 "on-error",
53 "omit-tag",
54 "tal tag",
55 ]
56
57KNOWN_I18N_ATTRIBUTES = [
58 "translate",
59 "domain",
60 "target",
61 "source",
62 "attributes",
63 "data",
64 "name",
65 ]
66
67class TALError(Exception):
68
69 def __init__(self, msg, position=(None, None)):
70 assert msg != ""
71 self.msg = msg
72 self.lineno = position[0]
73 self.offset = position[1]
74 self.filename = None
75
76 def setFile(self, filename):
77 self.filename = filename
78
79 def __str__(self):
80 result = self.msg
81 if self.lineno is not None:
82 result = result + ", at line %d" % self.lineno
83 if self.offset is not None:
84 result = result + ", column %d" % (self.offset + 1)
85 if self.filename is not None:
86 result = result + ', in file %s' % self.filename
87 return result
88
89class METALError(TALError):
90 pass
91
92class TALESError(TALError):
93 pass
94
95class I18NError(TALError):
96 pass
97
98
99class ErrorInfo:
100
101 #__implements__ = ITALESErrorInfo
102
103 def __init__(self, err, position=(None, None)):
104 if isinstance(err, Exception):
105 self.type = err.__class__
106 self.value = err
107 else:
108 self.type = err
109 self.value = None
110 self.lineno = position[0]
111 self.offset = position[1]
112
113
114
115import re
116_attr_re = re.compile(r"\s*([^\s]+)\s+([^\s].*)\Z", re.S)
117_subst_re = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S)
118del re
119
120def parseAttributeReplacements(arg, xml):
121 dict = {}
122 for part in splitParts(arg):
123 m = _attr_re.match(part)
124 if not m:
125 raise TALError("Bad syntax in attributes: " + `part`)
126 name, expr = m.group(1, 2)
127 if not xml:
128 name = name.lower()
129 if dict.has_key(name):
130 raise TALError("Duplicate attribute name in attributes: " + `part`)
131 dict[name] = expr
132 return dict
133
134def parseSubstitution(arg, position=(None, None)):
135 m = _subst_re.match(arg)
136 if not m:
137 raise TALError("Bad syntax in substitution text: " + `arg`, position)
138 key, expr = m.group(1, 2)
139 if not key:
140 key = "text"
141 return key, expr
142
143def splitParts(arg):
144 # Break in pieces at undoubled semicolons and
145 # change double semicolons to singles:
146 arg = arg.replace(";;", "\0")
147 parts = arg.split(';')
148 parts = [p.replace("\0", ";") for p in parts]
149 if len(parts) > 1 and not parts[-1].strip():
150 del parts[-1] # It ended in a semicolon
151 return parts
152
153def isCurrentVersion(program):
154 version = getProgramVersion(program)
155 return version == TAL_VERSION
156
157def getProgramMode(program):
158 version = getProgramVersion(program)
159 if (version == TAL_VERSION and isinstance(program[1], TupleType) and
160 len(program[1]) == 2):
161 opcode, mode = program[1]
162 if opcode == "mode":
163 return mode
164 return None
165
166def getProgramVersion(program):
167 if (len(program) >= 2 and
168 isinstance(program[0], TupleType) and len(program[0]) == 2):
169 opcode, version = program[0]
170 if opcode == "version":
171 return version
172 return None
173
174import re
175_ent1_re = re.compile('&(?![A-Z#])', re.I)
176_entch_re = re.compile('&([A-Z][A-Z0-9]*)(?![A-Z0-9;])', re.I)
177_entn1_re = re.compile('&#(?![0-9X])', re.I)
178_entnx_re = re.compile('&(#X[A-F0-9]*)(?![A-F0-9;])', re.I)
179_entnd_re = re.compile('&(#[0-9][0-9]*)(?![0-9;])')
180del re
181
182def attrEscape(s):
183 """Replace special characters '&<>' by character entities,
184 except when '&' already begins a syntactically valid entity."""
185 s = _ent1_re.sub('&amp;', s)
186 s = _entch_re.sub(r'&amp;\1', s)
187 s = _entn1_re.sub('&amp;#', s)
188 s = _entnx_re.sub(r'&amp;\1', s)
189 s = _entnd_re.sub(r'&amp;\1', s)
190 s = s.replace('<', '&lt;')
191 s = s.replace('>', '&gt;')
192 s = s.replace('"', '&quot;')
193 return s