Various fixes + Upgrades to the FileEdit module:
[mir.git] / source / mir / misc / HTMLTemplateProcessor.java
1 /*\r
2  * Copyright (C) 2001, 2002  The Mir-coders group\r
3  *\r
4  * This file is part of Mir.\r
5  *\r
6  * Mir is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Mir is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with Mir; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * In addition, as a special exception, The Mir-coders gives permission to link\r
21  * the code of this program with the com.oreilly.servlet library, any library\r
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
24  * the above that use the same license as the above), and distribute linked\r
25  * combinations including the two.  You must obey the GNU General Public\r
26  * License in all respects for all of the code used other than the above\r
27  * mentioned libraries.  If you modify this file, you may extend this exception\r
28  * to your version of the file, but you are not obligated to do so.  If you do\r
29  * not wish to do so, delete this exception statement from your version.\r
30  */\r
31 \r
32 package mir.misc;\r
33 \r
34 import java.io.PrintWriter;\r
35 import java.net.URLEncoder;\r
36 import java.util.GregorianCalendar;\r
37 import java.util.Iterator;\r
38 import java.util.Locale;\r
39 import java.util.Map;\r
40 import java.util.Set;\r
41 \r
42 import javax.servlet.http.HttpServletResponse;\r
43 \r
44 import mir.config.MirPropertiesConfiguration;\r
45 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;\r
46 import mir.entity.Entity;\r
47 import mir.entity.EntityList;\r
48 import mir.generator.FreemarkerGenerator;\r
49 import mir.log.LoggerWrapper;\r
50 import mir.storage.StorageObjectFailure;\r
51 import mir.util.GeneratorHTMLFunctions;\r
52 import mir.util.GeneratorIntegerFunctions;\r
53 \r
54 import org.apache.struts.util.MessageResources;\r
55 \r
56 import freemarker.template.FileTemplateCache;\r
57 import freemarker.template.SimpleHash;\r
58 import freemarker.template.SimpleList;\r
59 import freemarker.template.SimpleScalar;\r
60 import freemarker.template.Template;\r
61 import freemarker.template.TemplateModelRoot;\r
62 \r
63 /**\r
64  * Hilfsklasse zum Mergen von Template und Daten\r
65  */\r
66 public final class HTMLTemplateProcessor {\r
67 \r
68   public static String templateDir;\r
69   private static MirPropertiesConfiguration configuration;\r
70   private static FileTemplateCache templateCache;\r
71   private static String docRoot;\r
72   private static String actionRoot;\r
73   private static LoggerWrapper logger;\r
74 \r
75   static {\r
76     // ML: configuration is not thread safe: it's risky to use it like this\r
77     //     actually I don't see why HTMLTemplateProcessor needs to be a\r
78     //     class with static methods. This causes more problems than it solves.\r
79     try {\r
80       configuration = MirPropertiesConfiguration.instance();\r
81     }\r
82     catch (PropertiesConfigExc e) {\r
83       e.printStackTrace();\r
84     }\r
85 \r
86     logger = new LoggerWrapper("TemplateEngine");\r
87 \r
88     templateDir =\r
89         configuration.getStringWithHome("Mir.TemplateDir");\r
90     templateCache = new FileTemplateCache(templateDir);\r
91     templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
92     // gone in freemarker 1.7.1: templateCache.startAutoUpdate();\r
93 \r
94 \r
95     docRoot = configuration.getString("RootUri");\r
96     try {\r
97       actionRoot = docRoot +configuration.getString("Producer.ActionServlet");\r
98     }\r
99     catch (ConfigException ce) {\r
100       // if  Producer.ActionServlet is not set in the conf file\r
101       actionRoot = docRoot + "/Mir";\r
102     }\r
103   }\r
104 \r
105   /**\r
106    * empty private constructor, to avoid instantiation\r
107    */\r
108   private HTMLTemplateProcessor() {\r
109   }\r
110 \r
111   // process-methods to merge different datastructures\r
112   // with freemarker templates\r
113 \r
114   /**\r
115    * Wandelt <code>anEntity</code> in freemarker-Struktur um, mischt die Daten mit\r
116    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
117    * <code>out</code>\r
118    *\r
119    * @param templateFilename\r
120    * @param anEntity\r
121    * @param out\r
122    * @exception HTMLParseException\r
123    */\r
124 \r
125   public static void process(String templateFilename, Entity anEntity,\r
126                              PrintWriter out) throws HTMLParseException {\r
127     if (anEntity == null)\r
128       throw new HTMLParseException("entity is empty!");\r
129     else\r
130       process(templateFilename, anEntity, out);\r
131   }\r
132 \r
133   /**\r
134    * Wandelt Liste mit Entities <code>entList</code> in freemarker-Struktur um, mischt die Daten mit\r
135    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
136    * <code>out</code>\r
137    *\r
138    * @param templateFilename\r
139    * @param entList\r
140    * @param out\r
141    * @exception HTMLParseException\r
142    */\r
143   public static void process(HttpServletResponse res, String templateFilename,\r
144    EntityList entList, PrintWriter out, Locale locale) throws HTMLParseException {\r
145     process(res, templateFilename, entList, (String)null, (TemplateModelRoot)null,\r
146             out, locale);\r
147   }\r
148 \r
149   /**\r
150    * Wandelt Entitylist in freemarker-Struktur um, f?gt <code>additionalModel</code>\r
151        * unter dem Namen <code>additionalModelName</code> ein und mischt die Daten mit\r
152    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
153    * <code>out</code>\r
154    *\r
155    * @param templateFilename\r
156    * @param entList\r
157    * @param additionalModelName\r
158    * @param additionalModel\r
159    * @param out\r
160    * @exception HTMLParseException\r
161    */\r
162 \r
163   public static void process(HttpServletResponse res, String templateFilename,\r
164                              EntityList entList, String additionalModelName,\r
165                              TemplateModelRoot additionalModel, PrintWriter out,\r
166                              Locale locale) throws HTMLParseException {\r
167 \r
168     SimpleHash modelRoot = new SimpleHash();\r
169 \r
170     if (entList == null) {\r
171       process(null, templateFilename, modelRoot, out, locale);\r
172     }\r
173     else {\r
174       try {\r
175         modelRoot = makeSimpleHashWithEntitylistInfos(entList);\r
176 \r
177         // Quickhack um mal ein Popup mit reinzunhemen ..\r
178         if (additionalModelName != null && additionalModel != null)\r
179           modelRoot.put(additionalModelName, additionalModel);\r
180 \r
181         process(res, templateFilename, modelRoot, out, locale);\r
182       }\r
183       catch (StorageObjectFailure e) {\r
184         throw new HTMLParseException(e.toString());\r
185       }\r
186     }\r
187   }\r
188 \r
189 \r
190   /**\r
191    * Gibt Template <code>templateFilename</code> an den PrintWriter\r
192    * <code>out</code>\r
193    *\r
194    * @param templateFilename\r
195    * @param mergeData\r
196    * @param out\r
197    * @exception HTMLParseException\r
198    */\r
199   public static void process(String templateFilename, PrintWriter out,\r
200                              Locale locale) throws HTMLParseException {\r
201     process(null, templateFilename, (TemplateModelRoot)null, out, locale);\r
202   }\r
203 \r
204   /**\r
205    * Mischt die freemarker-Struktur <code>tmr</code> mit\r
206    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
207    * <code>out</code>\r
208    *\r
209    * @param templateFilename\r
210    * @param mergeData\r
211    * @param out\r
212    * @exception HTMLParseException\r
213    */\r
214   public static void process(HttpServletResponse res, String templateFilename,\r
215                              TemplateModelRoot tmr, PrintWriter out,\r
216                              Locale locale) throws HTMLParseException {\r
217     process(res, templateFilename, tmr, null, out, locale);\r
218   }\r
219 \r
220   public static void process(HttpServletResponse res, String templateFilename,\r
221                              TemplateModelRoot tmr, TemplateModelRoot extra,\r
222                              PrintWriter out, Locale locale) throws HTMLParseException {\r
223     process(res, templateFilename, tmr, extra, out, locale, "bundles.adminlocal", "bundles.admin");\r
224   }\r
225 \r
226   public static void process(HttpServletResponse res, String templateFilename,\r
227        TemplateModelRoot tmr, TemplateModelRoot extra, PrintWriter out,\r
228        Locale locale, String bundles) throws HTMLParseException {\r
229     process(res, templateFilename, tmr, extra, out, locale, bundles, null);\r
230   }\r
231 \r
232   /**\r
233    * Mischt die freemarker-Struktur <code>tmr</code> mit\r
234    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
235    * <code>out</code>\r
236    *\r
237    * @param templateFilename\r
238    * @param mergeData\r
239    * @param out\r
240    * @exception HTMLParseException\r
241    */\r
242   public static void process(HttpServletResponse res, String templateFilename,\r
243                              TemplateModelRoot tmr, TemplateModelRoot extra,\r
244                              PrintWriter out, Locale locale, String bundles,\r
245                              String bundles2) throws HTMLParseException {\r
246     if (out == null)\r
247       throw new HTMLParseException("no outputstream");\r
248     Template tmpl = getTemplateFor(templateFilename);\r
249     if (tmpl == null)\r
250       throw new HTMLParseException("no template: " + templateFilename);\r
251     if (tmr == null)\r
252       tmr = new SimpleHash();\r
253 \r
254       /** @todo  what is this for? (rk) */\r
255     String session = "";\r
256     if (res != null) {\r
257       session = res.encodeURL("");\r
258     }\r
259 \r
260     SimpleHash configHash = new SimpleHash();\r
261 \r
262     // pass the whole config hash to the templates\r
263     Iterator it = configuration.getKeys();\r
264     String key;\r
265     while (it.hasNext()) {\r
266       key = (String) it.next();\r
267       configHash.put(key, new SimpleScalar(\r
268         configuration.getString(key))\r
269       );\r
270     }\r
271 \r
272     // this does not come directly from the config file\r
273     configHash.put("docRoot", new SimpleScalar(docRoot));\r
274     configHash.put("actionRoot", new SimpleScalar(actionRoot + session));\r
275     configHash.put("now",\r
276                    new SimpleScalar(StringUtil.date2readableDateTime(new GregorianCalendar())));\r
277 \r
278     // this conform to updated freemarker syntax\r
279     configHash.put("compressWhitespace",\r
280                    new freemarker.template.utility.CompressWhitespace());\r
281 \r
282     SimpleHash utilityHash = new SimpleHash();\r
283     try {\r
284       utilityHash.put("compressWhitespace",\r
285                       new freemarker.template.utility.CompressWhitespace());\r
286       utilityHash.put("encodeURI",\r
287                       FreemarkerGenerator.makeAdapter(new GeneratorHTMLFunctions.\r
288           encodeURIGeneratorFunction()));\r
289       utilityHash.put("encodeHTML",\r
290                       FreemarkerGenerator.makeAdapter(new GeneratorHTMLFunctions.\r
291           encodeHTMLGeneratorFunction()));\r
292       utilityHash.put("isOdd",\r
293                       FreemarkerGenerator.makeAdapter(new GeneratorIntegerFunctions.\r
294           isOddFunction()));\r
295       utilityHash.put("increment",\r
296                       FreemarkerGenerator.makeAdapter(new GeneratorIntegerFunctions.\r
297           incrementFunction()));\r
298     }\r
299     catch (Throwable t) {\r
300       throw new HTMLParseException(t.getMessage());\r
301     }\r
302 \r
303     SimpleHash outPutHash = new SimpleHash();\r
304 \r
305     if (extra != null) {\r
306       outPutHash.put("extra", extra);\r
307     }\r
308     outPutHash.put("data", tmr);\r
309     outPutHash.put("config", configHash);\r
310     outPutHash.put("utility", utilityHash);\r
311 \r
312     MessageResources messages = MessageResources.getMessageResources(bundles);\r
313     if (bundles2!=null) {\r
314       outPutHash.put("lang", new MessageMethodModel(locale, MessageResources.getMessageResources(bundles), MessageResources.getMessageResources(bundles2)));\r
315     }\r
316     else {\r
317       outPutHash.put("lang", new MessageMethodModel(locale, MessageResources.getMessageResources(bundles)));\r
318     }\r
319 \r
320     tmpl.process(outPutHash, out);\r
321   }\r
322 \r
323   /**\r
324    *   Converts Entity-List to SimpleList of SimpleHashes.\r
325    *   @param aList ist eine Liste von Entity\r
326    *   @return eine freemarker.template.SimpleList von SimpleHashes.\r
327    *\r
328    *    @deprecated EntityLists comply with TemplateListModel now.\r
329    */\r
330   public static SimpleList makeSimpleList(EntityList aList) throws StorageObjectFailure {\r
331     logger.warn("using deprecated makeSimpleList(entityList) - a waste of resources");\r
332     SimpleList simpleList = new SimpleList();\r
333     if (aList != null) {\r
334       for (int i = 0; i < aList.size(); i++) {\r
335         simpleList.add(aList.elementAt(i));\r
336       }\r
337     }\r
338     return simpleList;\r
339   }\r
340 \r
341   /**\r
342    *  Konvertiert ein EntityList in ein freemarker.template.SimpleHash-Modell. Im Hash\r
343    *  sind die einzelnen Entities ueber ihre id zu erreichen.\r
344    *  @param aList ist die EntityList\r
345    *  @return SimpleHash mit den entsprechenden freemarker Daten\r
346    *\r
347    */\r
348   public static SimpleHash makeSimpleHash(EntityList aList) throws\r
349       StorageObjectFailure {\r
350     SimpleHash simpleHash = new SimpleHash();\r
351     Entity currentEntity;\r
352 \r
353     if (aList != null) {\r
354       for (int i = 0; i < aList.size(); i++) {\r
355         currentEntity = (Entity) aList.elementAt(i);\r
356         simpleHash.put(currentEntity.getId(), currentEntity);\r
357       }\r
358     }\r
359     return simpleHash;\r
360   }\r
361 \r
362   /**\r
363    *  Konvertiert ein Hashtable mit den keys und values als String\r
364    *  in ein freemarker.template.SimpleHash-Modell\r
365    *  @param mergeData der Map mit den String / String Daten\r
366    *  @return SimpleHash mit den entsprechenden freemarker Daten\r
367    *\r
368    */\r
369   public static SimpleHash makeSimpleHash(Map mergeData) {\r
370     SimpleHash modelRoot = new SimpleHash();\r
371     String aField;\r
372     if (mergeData != null) {\r
373       Set set = mergeData.keySet();\r
374       Iterator it = set.iterator();\r
375       for (int i = 0; i < set.size(); i++) {\r
376         aField = (String) it.next();\r
377         modelRoot.put(aField, (String) mergeData.get(aField));\r
378       }\r
379     }\r
380     return modelRoot;\r
381   }\r
382 \r
383   /**\r
384    * Converts EntityList in SimpleHash and adds additional information\r
385    * to the returned SimpleHash\r
386    *\r
387    * @param entList\r
388    * @return SimpleHash returns SimpleHash with the converted EntityList plus\r
389    *        additional Data about the list.\r
390    * @exception StorageObjectException\r
391    */\r
392 \r
393   public static SimpleHash makeSimpleHashWithEntitylistInfos(EntityList entList) throws\r
394       StorageObjectFailure {\r
395     SimpleHash modelRoot = new SimpleHash();\r
396     if (entList != null) {\r
397       modelRoot.put("contentlist", entList);\r
398       modelRoot.put("count",\r
399                     new SimpleScalar( (new Integer(entList.getCount())).toString()));\r
400       if (entList.getWhere() != null) {\r
401         modelRoot.put("where", new SimpleScalar(entList.getWhere()));\r
402         modelRoot.put("where_encoded",\r
403                       new SimpleScalar(URLEncoder.encode(entList.getWhere())));\r
404       }\r
405       if (entList.getOrder() != null) {\r
406         modelRoot.put("order", new SimpleScalar(entList.getOrder()));\r
407         modelRoot.put("order_encoded",\r
408                       new SimpleScalar(URLEncoder.encode(entList.getOrder())));\r
409       }\r
410       modelRoot.put("from",\r
411                     new SimpleScalar( (new Integer(entList.getFrom())).toString()));\r
412       modelRoot.put("to",\r
413                     new SimpleScalar( (new Integer(entList.getTo())).toString()));\r
414 \r
415       if (entList.hasNextBatch())\r
416         modelRoot.put("next",\r
417                       new SimpleScalar( (new Integer(entList.getNextBatch())).\r
418                                        toString()));\r
419       if (entList.hasPrevBatch())\r
420         modelRoot.put("prev",\r
421                       new SimpleScalar( (new Integer(entList.getPrevBatch())).\r
422                                        toString()));\r
423     }\r
424     return modelRoot;\r
425   }\r
426 \r
427   /**\r
428    * Private methods to get template from a templateFilename\r
429    * @param templateFilename\r
430    * @return Template\r
431    * @exception HTMLParseException\r
432    */\r
433   private static Template getTemplateFor(String templateFilename) throws\r
434       HTMLParseException {\r
435     Template returnTemplate = null;\r
436     if (templateFilename != null)\r
437       returnTemplate = (Template) templateCache.getItem(templateFilename,\r
438           "template");\r
439 \r
440     if (returnTemplate == null) {\r
441       logger.error("CACHE (ERR): Unknown template: " + templateFilename);\r
442       throw new HTMLParseException("Templatefile: " + templateFilename + " not found.");\r
443     }\r
444 \r
445     return returnTemplate;\r
446   }\r
447 \r
448   public static void stopAutoUpdate() {\r
449     templateCache.stopAutoUpdate();\r
450     templateCache = null;\r
451   }\r
452 \r
453 }