scripts/mir-setup/README: update with link to new doc on wiki
[mir.git] / source / mir / generator / tal / template / CoreTemplateNodeLibrary.java
1 /*
2  * Copyright (C) 2005 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License.
22  * You must obey the GNU General Public License in all respects for all of the code used
23  * other than the above mentioned libraries.  If you modify this file, you may extend this
24  * exception to your version of the file, but you are not obligated to do so.
25  * If you do not wish to do so, delete this exception statement from your version.
26  */
27 package mir.generator.tal.template;
28
29 import mir.generator.tal.interfaces.TALExpressionParser;
30 import mir.generator.tal.interfaces.TALLogger;
31 import mir.util.HTMLRoutines;
32 import mir.util.StringRoutines;
33 import mir.util.xml.XMLName;
34 import mir.util.xml.XMLParserExc;
35 import mir.util.xml.XMLReaderTool;
36
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.TreeMap;
44
45 public class CoreTemplateNodeLibrary implements TemplateNodeLibrary {
46   private String prefix;
47   private String uri;
48
49   private boolean isOurTag(XMLName aName) {
50     return prefix.equals(aName.getPrefix()) || uri.equals(aName.getNamespaceURI());
51   }
52
53   public CoreTemplateNodeLibrary(String aPrefix, String aUri) {
54     prefix = aPrefix;
55     uri = aUri;
56   }
57
58   private static final String CONDITION_ATTRIBUTE = "condition";
59   private static final String REPEAT_ATTRIBUTE = "repeat";
60   private static final String CONTENT_ATTRIBUTE = "content";
61   private static final String ERROR_ATTRIBUTE = "on-error";
62   private static final String REPLACE_ATTRIBUTE = "replace";
63   private static final String DEFINITION_ATTRIBUTE = "define";
64   private static final String OMITTAG_ATTRIBUTE = "omit-tag";
65   private static final String ATTRIBUTE_ATTRIBUTE = "attributes";
66
67   public TemplateNode constructTemplateNode(TALExpressionParser aParser, XMLName aTag, Map anAttributes,
68       TemplateNode aChildTemplateNode, Map aTemplateContext) throws XMLParserExc {
69     TALBasicTemplateNode result = new TALBasicTemplateNode(XMLReaderTool.normalizeXMLName(aTag));
70     result.setBody(aChildTemplateNode);
71
72     if (isOurTag(aTag))
73       result.setOmitTag(aParser.preparseTRUE());
74
75     Iterator i = anAttributes.entrySet().iterator();
76     while (i.hasNext()) {
77       Map.Entry entry = (Map.Entry) i.next();
78       XMLName name = (XMLName) entry.getKey();
79
80       if (!isOurTag(name)) {
81         result.addFixedAttribute(XMLReaderTool.normalizeXMLName(name), (String) entry.getValue());
82       }
83       else {
84         if (name.getLocalName().equalsIgnoreCase(DEFINITION_ATTRIBUTE)) {
85           List definitions = StringRoutines.splitStringWithEscape((String) entry.getValue(), ';', '\\');
86
87           Iterator j = definitions.iterator();
88           while (j.hasNext())
89           {
90             List parts = StringRoutines.separateString((String) j.next(), " ");
91
92             if (parts.size()==2) {
93               result.addDefinition(aParser.preparseReferenceExpression((String) parts.get(0)), aParser.preparseExpression((String) parts.get(1)));
94             }
95           }
96         }
97         else if (name.getLocalName().equalsIgnoreCase(CONDITION_ATTRIBUTE)) {
98           result.setCondition(aParser.preparseBooleanExpression((String) entry.getValue()));
99         }
100         else if (name.getLocalName().equalsIgnoreCase(CONTENT_ATTRIBUTE)) {
101           result.setContent(aParser.preparseStringExpression((String) entry.getValue()));
102         }
103         else if (name.getLocalName().equalsIgnoreCase(ERROR_ATTRIBUTE)) {
104           result.setError(aParser.preparseStringExpression((String) entry.getValue()));
105         }
106         else if (name.getLocalName().equalsIgnoreCase(OMITTAG_ATTRIBUTE)) {
107           if (((String) entry.getValue()).trim().length()==0)
108             result.setOmitTag(aParser.preparseTRUE());
109           else
110             result.setOmitTag(aParser.preparseBooleanExpression((String) entry.getValue()));
111         }
112         else if (name.getLocalName().equalsIgnoreCase(REPLACE_ATTRIBUTE)) {
113           result.setOmitTag(aParser.preparseTRUE());
114           result.setContent(aParser.preparseStringExpression((String) entry.getValue()));
115         }
116         else if (name.getLocalName().equalsIgnoreCase(REPEAT_ATTRIBUTE)) {
117           List parts = StringRoutines.separateString((String) entry.getValue(), " ");
118
119           if (parts.size()==2) {
120             result.setRepeat(aParser.preparseReferenceExpression((String) parts.get(0)), aParser.preparseExpression((String) parts.get(1)));
121           }
122         }
123         else if (name.getLocalName().equalsIgnoreCase(ATTRIBUTE_ATTRIBUTE)) {
124           List attributes = StringRoutines.splitStringWithEscape((String) entry.getValue(), ';', '\\');
125
126           Iterator j = attributes.iterator();
127           while (j.hasNext()) {
128             String value = (String) j.next();
129             List parts = StringRoutines.separateString(value.trim(), " ");
130
131             if (parts.size()==2) {
132               result.addModifiedAttribute((String) parts.get(0), aParser.preparseExpression((String) parts.get(1)));
133             }
134             else {
135               throw new XMLParserExc(ATTRIBUTE_ATTRIBUTE + " tag should have exactly 2 parts ("+value+")");
136             }
137           }
138         }
139       }
140     }
141
142     return result;
143   }
144
145   public static class TALBasicTemplateNode implements TemplateNode {
146     private String tag;
147     private Map fixedAttributes;
148     private Map attributeModifiers;
149
150     private List definitions;
151     private Object condition;
152
153     private Object repeatVariable;
154     private Object repeatExpression;
155     private Object contentExpression;
156     private Object omitTagExpression;
157     private Object errorExpression;
158
159     private TemplateNode body;
160
161     public TALBasicTemplateNode(String aTag) {
162       tag = aTag;
163
164       fixedAttributes = new HashMap();
165       attributeModifiers = new HashMap();
166
167       definitions = new ArrayList();
168       condition = null;
169
170       repeatVariable = null;
171       repeatExpression = null;
172       contentExpression = null;
173       omitTagExpression = null;
174
175       body = null;
176     }
177
178     public void setBody(TemplateNode aBody) {
179       body = aBody;
180     }
181
182     public void addFixedAttribute(String aKey, String aValue) {
183       fixedAttributes.put(aKey, aValue);
184     }
185
186     public void addModifiedAttribute(String aKey, Object aValue) {
187       attributeModifiers.put(aKey, aValue);
188     }
189
190     public void addDefinition(Object aKey, Object aValue) {
191       definitions.add(new Definition(aKey, aValue));
192     }
193
194     public void setCondition(Object aCondition) {
195       condition = aCondition;
196     }
197
198     public void setRepeat(Object aRepeatVariable, Object aRepeatExpression) {
199       repeatVariable = aRepeatVariable;
200       repeatExpression = aRepeatExpression;
201     }
202
203     public void setContent(Object aContentExpression) {
204       contentExpression = aContentExpression;
205     }
206
207     public void setOmitTag(Object anOmitTagExpression) {
208       omitTagExpression = anOmitTagExpression;
209     }
210
211     public void setError(Object anErrorExpression) {
212       errorExpression = anErrorExpression;
213     }
214
215     public static class Definition {
216       private Object variable;
217       private Object expression;
218
219       public Definition(Object aVariable, Object anExpression) {
220         variable = aVariable;
221         expression = anExpression;
222       }
223
224       public Object getVariable() {
225         return variable;
226       }
227
228       public Object getExpression() {
229         return expression;
230       }
231     }
232
233     public void process(TALExpressionParser aParser, Object aContext,
234         StringBuffer aDestination, TALLogger aLogger, Map aTemplateContext,
235         TemplateLibrary aLibrary) throws TemplateProcessingException {
236       if (errorExpression != null) {
237         StringBuffer destination = new StringBuffer();
238
239         try {
240           outerProcess(aParser, aContext, destination, aLogger, aTemplateContext, aLibrary);
241         }
242         catch (Throwable t) {
243           try {
244 //            destination.delete(0, destination.length());
245             aParser.processPseudoAssignment(aContext, "exception", t);
246             destination.insert(0, aParser.evaluateStringExpression(aContext, errorExpression));
247             destination.append(" >>>ERROR POSITION<<< ");
248           }
249           catch (Throwable s) {
250             throw new TemplateProcessingException(s);
251           }
252         }
253         finally {
254           aDestination.append(destination);
255         }
256       }
257       else {
258         outerProcess(aParser, aContext, aDestination, aLogger, aTemplateContext, aLibrary);
259       }
260     }
261
262     public String getPlainText() {
263       return body.getPlainText();
264     }
265
266     public void outerProcess(TALExpressionParser aParser, Object aContext,
267         StringBuffer aDestination, TALLogger aLogger, Map aTemplateContext,
268         TemplateLibrary aLibrary) throws TemplateProcessingException {
269
270       Object oldAttributes = aParser.evaluatePseudoVariable(aContext, "tagattributes");
271       aParser.processPseudoAssignment(aContext, "tagattributes", Collections.unmodifiableMap(fixedAttributes));
272
273       Object oldContent = aParser.evaluatePseudoVariable(aContext, "tagcontent");
274       aParser.processPseudoAssignment(aContext, "tagcontent", body.getPlainText());
275
276       try {
277         Iterator i;
278
279         i = definitions.iterator();
280         while (i.hasNext()) {
281           Definition d = (Definition) i.next();
282           aParser.processAssignment(aContext, d.getVariable(), d.getExpression());
283         }
284
285         if (condition == null || aParser.evaluateBooleanExpression(aContext, condition)) {
286           if (repeatExpression != null) {
287             i = aParser.evaluateListExpression(aContext, repeatExpression);
288
289             while (i.hasNext()) {
290               aParser.processDirectAssignment(aContext, repeatVariable, i.next());
291               innerProcess(aParser, aContext, aDestination, aLogger, aTemplateContext, aLibrary);
292             }
293           }
294           else {
295             innerProcess(aParser, aContext, aDestination, aLogger, aTemplateContext, aLibrary);
296           }
297         }
298       }
299       finally {
300         try {
301           aParser.processPseudoAssignment(aContext, "tagattributes", oldAttributes);
302           aParser.processPseudoAssignment(aContext, "tagcontent", oldContent);
303         }
304         catch (Throwable t) {
305         }
306       }
307     }
308
309     private void innerProcess(TALExpressionParser aParser, Object aContext,
310         StringBuffer aDestination, TALLogger aLogger, Map aTemplateContext, TemplateLibrary aLibrary)
311         throws TemplateProcessingException {
312
313       boolean omitTag = false;
314       StringBuffer content = aDestination;
315
316       if (omitTagExpression != null)
317         omitTag = aParser.evaluateBooleanExpression(aContext, omitTagExpression);
318
319       if (!omitTag) {
320         content = new StringBuffer();
321         Map generatedAttributes = new TreeMap(fixedAttributes);
322
323         Iterator i = attributeModifiers.entrySet().iterator();
324         while (i.hasNext()) {
325           Map.Entry entry = (Map.Entry) i.next();
326
327           generatedAttributes.put(entry.getKey(), aParser.evaluateStringExpression(aContext, entry.getValue()));
328         }
329
330         aDestination.append("<");
331         aDestination.append(tag);
332
333         i = generatedAttributes.entrySet().iterator();
334         while (i.hasNext()) {
335           Map.Entry entry = (Map.Entry) i.next();
336           aDestination.append(" ");
337           aDestination.append(entry.getKey());
338           aDestination.append("=");
339           aDestination.append("\"");
340           aDestination.append(HTMLRoutines.encodeHTML( (String) entry.getValue()));
341           aDestination.append("\"");
342         }
343       }
344
345       try{
346         if (contentExpression != null) {
347           content.append(aParser.evaluateStringExpression(aContext, contentExpression));
348         }
349         else {
350           if (body != null) {
351             body.process(aParser, aContext, content, aLogger, aTemplateContext, aLibrary);
352           }
353         }
354         if (!omitTag) {
355           if (content.length()==0) {
356             aDestination.append(" />");
357           }
358           else {
359             aDestination.append(">");
360             aDestination.append(content);
361             aDestination.append("</");
362             aDestination.append(tag);
363             aDestination.append(">");
364           }
365         }
366       }
367       catch (Throwable t) {
368         if (!omitTag) {
369           aDestination.append(content);
370         }
371
372         if (t instanceof TemplateProcessingException) {
373           throw (TemplateProcessingException) t;
374         }
375         else if (t instanceof RuntimeException) {
376           throw (RuntimeException) t;
377         }
378         else throw new TemplateProcessingException(t.toString());
379       }
380     }
381   }
382 }