view src/kryshen/tema/TemplateParser.java @ 1:548a93c24e55

Tema 0.1jk - Javakonkurs edition (imported from CVS).
author Mikhail Kryshen <mikhail@kryshen.net>
date Thu, 14 Dec 2006 23:22:05 +0300
parents 1d2fe61a3a62
children 6c41a0b43e58
line source
1 /*
2 * Copyright (C) 2005, 2006 Mikhail A. Kryshen
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * $Id: TemplateParser.java,v 1.11 2006/12/14 19:44:31 mikhail Exp $
19 */
21 package kryshen.tema;
23 import java.io.*;
24 import java.util.*;
26 /**
27 * Parser for TEMA templates.
28 *
29 * @author Mikhail A. Kryshen
30 */
31 public class TemplateParser {
32 // TODO: report non-critical errors and warnings.
34 static final String SUPER_PREFIX = "super.";
36 /* Brackets. */
37 static final char[] BR_LEFT = {'<', '['};
38 static final char[] BR_RIGHT = {'>', ']'};
40 static final char BR_IN = '%';
42 /* Separators. */
43 static final char[] REC_DATA_SEPARATORS = {':'};
44 static final char[] NONREC_DATA_SEPARATORS = {'\\', '`'};
45 static final char[] LIST_SEPARATORS = {' ', '\t', '\r', '\n'};
47 /**
48 * Methods of parsing function arguments and data.
49 */
50 static enum FunctionData {
51 SUBFUNCTION, RECURSIVE, NONRECURSIVE;
52 };
54 static enum Terminator {
55 EOF, BRACKET, SEPARATOR;
56 };
58 static class Result {
59 Terminator terminator;
60 int retCode;
62 /** No text had been parsed. */
63 boolean empty;
65 Result(Terminator terminator, int retCode, boolean empty) {
66 this.terminator = terminator;
67 this.retCode = retCode;
68 this.empty = empty;
69 }
71 Result() {
72 this(null, -1, true);
73 }
74 };
76 private Map<String, Object> variables = new HashMap<String, Object>();
77 private TemplateParser superParser;
79 private int termBracket = -1;
81 public TemplateParser() {
82 this(null);
83 }
85 public TemplateParser(TemplateParser superParser) {
86 this.superParser = superParser;
88 Functions.registerAllFunctions(this);
89 }
91 public void registerFunction(String name, Function f) {
92 variables.put(name, f);
93 }
95 public int parse(TemplateReader in, Writer out)
96 throws IOException, TemplateException {
98 return parse(in, out, true, null, null).retCode;
99 }
101 /**
102 * Parse template.
103 *
104 * @param in TemplateReader to read template data.
105 * @param out Writer to write processed data.
106 * @param recursive enables recursive processing.
107 * @param separators characters which terminate parsing block.
108 * @param leading characters to ignore at the beginning of the block.
109 */
110 Result parse(TemplateReader in, Writer out, boolean recursive,
111 char[] separators, char[] leading)
112 throws IOException, TemplateException {
114 Result result = new Result();
115 int lc = -1;
117 while (true) {
118 int c = in.read();
120 if (leading != null && isSeparator(c, leading))
121 continue;
122 else
123 leading = null;
125 boolean leftBracket = false;
126 int index;
128 for (index = 0; index < BR_LEFT.length; index++) {
129 if (lc == BR_LEFT[index]) {
130 leftBracket = true;
131 break;
132 }
133 }
135 if (recursive && leftBracket && c == BR_IN) {
137 if (result.retCode < 0)
138 result.retCode = 0;
140 lc = -1;
141 int tb = termBracket;
142 termBracket = BR_RIGHT[index];
143 result.retCode += parseFunction(in, out);
144 termBracket = tb;
146 } else if (lc == BR_IN && c == termBracket) {
148 lc = -1;
149 result.terminator = Terminator.BRACKET;
150 break;
152 } else {
154 if (lc >= 0)
155 out.write(lc);
157 lc = c;
158 }
160 if (c < 0) {
161 result.terminator = Terminator.EOF;
162 break;
163 } else if (separators != null && isSeparator(c, separators)) {
164 result.terminator = Terminator.SEPARATOR;
165 break;
166 }
168 result.empty = false;
169 }
171 return result;
172 }
174 boolean isSeparator(int c, char[] separators) {
175 for (char p : separators) {
176 if (c == p) return true;
177 }
179 return false;
180 }
182 int parseFunction(TemplateReader in, Writer out)
183 throws IOException, TemplateException {
185 StringBuffer sb = new StringBuffer();
187 while (true) {
188 int c = in.read();
190 if (isSeparator(c, LIST_SEPARATORS)) {
191 return invokeFunction(sb.toString(),
192 FunctionData.SUBFUNCTION,
193 in, out);
194 } else if (isSeparator(c, REC_DATA_SEPARATORS)) {
195 return invokeFunction(sb.toString(),
196 FunctionData.RECURSIVE,
197 in, out);
198 } else if (isSeparator(c, NONREC_DATA_SEPARATORS)) {
199 return invokeFunction(sb.toString(),
200 FunctionData.NONRECURSIVE,
201 in, out);
202 } else if (c < 0) {
203 throw new TemplateException("Unexpected end of file.", in);
204 } else {
205 sb.append((char)c);
206 }
207 }
208 }
210 int parseValue(Object value, Writer out) throws IOException {
211 if (value == null)
212 return 0;
214 String svalue = value.toString();
216 if (svalue == null || svalue.length() == 0)
217 return 0;
219 out.write(svalue);
220 return 1;
221 }
223 private int invokeFunction(String name, FunctionData fd,
224 TemplateReader in, Writer out)
225 throws IOException, TemplateException {
227 FunctionDataParser fdp = new FunctionDataParser(this, fd, in);
229 if ("".equals(name)) {
230 return fdp.parseData(out);
231 }
233 Object value = getValue(name);
234 int r;
236 if (value instanceof Function) {
237 r = ((Function) value).invoke(fdp, out);
238 } else {
239 r = parseValue(value, out);
240 }
242 if (fdp.hasMoreData()) {
243 /* Skip remaining function data. */
244 fdp.parseData(new Writer() {
245 public void close() {}
246 public void flush() {}
247 public void write(char[] cbuf, int off, int len) {}
248 });
249 }
251 return r;
252 }
254 public Object getValue(String name) throws TemplateException {
255 Object value = variables.get(name);
256 if (value != null) return value;
258 if (superParser != null) {
259 if (name.startsWith(SUPER_PREFIX))
260 return superParser.getValue
261 (name.substring(SUPER_PREFIX.length()));
262 else
263 return superParser.getValue(name);
264 }
266 return null;
267 }
269 public void setValue(String name, Object value) {
270 variables.put(name, value);
271 }
273 public void clearValues() {
274 variables.clear();
275 Functions.registerAllFunctions(this);
276 }
278 // TEST
279 public static void main(String[] args)
280 throws IOException, TemplateException {
282 TemplateParser p = new TemplateParser();
284 TemplateReader in =
285 new TemplateReader
286 (new LineNumberReader
287 (new FileReader("parser.in")), "parser.in");
289 FileWriter out = new FileWriter("parser.out");
291 p.parse(in, out);
293 in.close();
294 out.close();
295 }
296 }