new admin templates! with many thanks to init...
[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 org.apache.struts.util.MessageResources;\r
45 \r
46 import freemarker.template.FileTemplateCache;\r
47 import freemarker.template.SimpleHash;\r
48 import freemarker.template.SimpleList;\r
49 import freemarker.template.SimpleScalar;\r
50 import freemarker.template.Template;\r
51 import freemarker.template.TemplateModelRoot;\r
52 \r
53 import mir.config.MirPropertiesConfiguration;\r
54 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;\r
55 import mir.entity.Entity;\r
56 import mir.entity.EntityList;\r
57 import mir.generator.FreemarkerGenerator;\r
58 import mir.log.LoggerWrapper;\r
59 import mir.storage.StorageObjectFailure;\r
60 import mir.util.GeneratorHTMLFunctions;\r
61 import mir.util.GeneratorIntegerFunctions;\r
62 import mir.util.*;\r
63 \r
64 /**\r
65  * Hilfsklasse zum Mergen von Template und Daten\r
66  */\r
67 public final class HTMLTemplateProcessor {\r
68 \r
69   public static String templateDir;\r
70   private static final String[] bundles = { "bundles.adminlocal", "bundles.admin" };\r
71   private static MirPropertiesConfiguration configuration;\r
72   private static FileTemplateCache templateCache;\r
73   private static String docRoot;\r
74   private static String actionRoot;\r
75   private static LoggerWrapper logger;\r
76 \r
77   static {\r
78     // ML: configuration is not thread safe: it's risky to use it like this\r
79     //     actually I don't see why HTMLTemplateProcessor needs to be a\r
80     //     class with static methods. This causes more problems than it solves.\r
81     try {\r
82       configuration = MirPropertiesConfiguration.instance();\r
83     }\r
84     catch (PropertiesConfigExc e) {\r
85       e.printStackTrace();\r
86     }\r
87 \r
88     logger = new LoggerWrapper("TemplateEngine");\r
89 \r
90     templateDir =\r
91         configuration.getStringWithHome("Mir.TemplateDir");\r
92     templateCache = new FileTemplateCache(templateDir);\r
93     templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
94     // gone in freemarker 1.7.1: templateCache.startAutoUpdate();\r
95 \r
96 \r
97     docRoot = configuration.getString("RootUri");\r
98     try {\r
99       actionRoot = docRoot +configuration.getString("Producer.ActionServlet");\r
100     }\r
101     catch (ConfigException ce) {\r
102       // if  Producer.ActionServlet is not set in the conf file\r
103       actionRoot = docRoot + "/Mir";\r
104     }\r
105   }\r
106 \r
107   /**\r
108    * empty private constructor, to avoid instantiation\r
109    */\r
110   private HTMLTemplateProcessor() {\r
111   }\r
112 \r
113   /**\r
114    * Wandelt Liste mit Entities <code>entList</code> in freemarker-Struktur um, mischt die Daten mit\r
115    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
116    * <code>out</code>\r
117    *\r
118    * @param templateFilename\r
119    * @param entList\r
120    * @param out\r
121    * @exception HTMLParseException\r
122    */\r
123 \r
124   public static void process(HttpServletResponse res, String templateFilename, EntityList entList, PrintWriter out, Locale locale, Locale aFallbackLocale) throws HTMLParseException {\r
125     process(res, templateFilename, entList, (String)null, (TemplateModelRoot) null, out, locale, aFallbackLocale);\r
126   }\r
127 \r
128   /**\r
129    * Wandelt Entitylist in freemarker-Struktur um, f?gt <code>additionalModel</code>\r
130        * unter dem Namen <code>additionalModelName</code> ein und mischt die Daten mit\r
131    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
132    * <code>out</code>\r
133    *\r
134    * @param templateFilename\r
135    * @param entList\r
136    * @param additionalModelName\r
137    * @param additionalModel\r
138    * @param out\r
139    * @exception HTMLParseException\r
140    */\r
141 \r
142   public static void process(HttpServletResponse res, String templateFilename,\r
143                              EntityList entList, String additionalModelName,\r
144                              TemplateModelRoot additionalModel, PrintWriter out,\r
145                              Locale locale, Locale aFallbackLocale) throws HTMLParseException {\r
146 \r
147     SimpleHash modelRoot = new SimpleHash();\r
148 \r
149     if (entList == null) {\r
150       process(null, templateFilename, modelRoot, null, out, locale, aFallbackLocale);\r
151     }\r
152     else {\r
153       try {\r
154         modelRoot = makeSimpleHashWithEntitylistInfos(entList);\r
155 \r
156         // Quickhack um mal ein Popup mit reinzunhemen ..\r
157         if (additionalModelName != null && additionalModel != null)\r
158           modelRoot.put(additionalModelName, additionalModel);\r
159 \r
160         process(res, templateFilename, modelRoot, null, out, locale, aFallbackLocale);\r
161       }\r
162       catch (StorageObjectFailure e) {\r
163         throw new HTMLParseException(e.toString());\r
164       }\r
165     }\r
166   }\r
167 \r
168 \r
169   /**\r
170    * Mischt die freemarker-Struktur <code>tmr</code> mit\r
171    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
172    * <code>out</code>\r
173    *\r
174    * @param templateFilename\r
175    * @param mergeData\r
176    * @param out\r
177    * @exception HTMLParseException\r
178    */\r
179   public static void process(HttpServletResponse res, String templateFilename,\r
180                              TemplateModelRoot tmr, PrintWriter out,\r
181                              Locale locale, Locale aFallbackLocale) throws HTMLParseException {\r
182     process(res, templateFilename, tmr, null, out, locale, aFallbackLocale);\r
183   }\r
184 \r
185   /**\r
186    * Mischt die freemarker-Struktur <code>tmr</code> mit\r
187    * Template <code>templateFilename</code> und gibt das Ergebnis an den PrintWriter\r
188    * <code>out</code>\r
189    *\r
190    * @param templateFilename\r
191    * @param mergeData\r
192    * @param out\r
193    * @exception HTMLParseException\r
194    */\r
195   public static void process(HttpServletResponse res, String templateFilename,\r
196                              TemplateModelRoot tmr, TemplateModelRoot extra,\r
197                              PrintWriter out, Locale locale, Locale aFallbackLocale) throws HTMLParseException {\r
198 \r
199     if (out == null)\r
200       throw new HTMLParseException("no outputstream");\r
201     Template tmpl = getTemplateFor(templateFilename);\r
202     if (tmpl == null)\r
203       throw new HTMLParseException("no template: " + templateFilename);\r
204     if (tmr == null)\r
205       tmr = new SimpleHash();\r
206 \r
207       /** @todo  what is this for? (rk) */\r
208     String session = "";\r
209     if (res != null) {\r
210       session = res.encodeURL("");\r
211     }\r
212 \r
213     SimpleHash configHash = new SimpleHash();\r
214 \r
215     // pass the whole config hash to the templates\r
216     Iterator it = configuration.getKeys();\r
217     String key;\r
218     while (it.hasNext()) {\r
219       key = (String) it.next();\r
220       configHash.put(key, new SimpleScalar(\r
221         configuration.getString(key))\r
222       );\r
223     }\r
224 \r
225     // this does not come directly from the config file\r
226     configHash.put("docRoot", new SimpleScalar(docRoot));\r
227     configHash.put("actionRoot", new SimpleScalar(actionRoot + session));\r
228     configHash.put("now",\r
229                    new SimpleScalar(StringUtil.date2readableDateTime(new GregorianCalendar())));\r
230 \r
231     // this conform to updated freemarker syntax\r
232     configHash.put("compressWhitespace",\r
233                    new freemarker.template.utility.CompressWhitespace());\r
234 \r
235     SimpleHash utilityHash = new SimpleHash();\r
236     try {\r
237       utilityHash.put("compressWhitespace",\r
238                       new freemarker.template.utility.CompressWhitespace());\r
239       utilityHash.put("encodeURI",\r
240                       FreemarkerGenerator.makeAdapter(new GeneratorHTMLFunctions.\r
241           encodeURIGeneratorFunction()));\r
242       utilityHash.put("encodeHTML",\r
243                       FreemarkerGenerator.makeAdapter(new GeneratorHTMLFunctions.\r
244           encodeHTMLGeneratorFunction()));\r
245       utilityHash.put("isOdd",\r
246                       FreemarkerGenerator.makeAdapter(new GeneratorIntegerFunctions.\r
247           isOddFunction()));\r
248       utilityHash.put("increment",\r
249                       FreemarkerGenerator.makeAdapter(new GeneratorIntegerFunctions.\r
250           incrementFunction()));\r
251     }\r
252     catch (Throwable t) {\r
253       throw new HTMLParseException(t.getMessage());\r
254     }\r
255 \r
256     SimpleHash outPutHash = new SimpleHash();\r
257 \r
258     if (extra != null) {\r
259       outPutHash.put("extra", extra);\r
260     }\r
261     outPutHash.put("data", tmr);\r
262     outPutHash.put("config", configHash);\r
263     outPutHash.put("utility", utilityHash);\r
264 \r
265     MessageResources messages[] = new MessageResources[bundles.length];\r
266 \r
267     for (int i=0; i<bundles.length; i++) {\r
268       messages[i] = MessageResources.getMessageResources(bundles[i]);\r
269     }\r
270 \r
271     try {\r
272       outPutHash.put("lang", FreemarkerGenerator.makeAdapter(new ResourceBundleGeneratorFunction(new Locale[] {locale, aFallbackLocale}, messages)));\r
273     }\r
274     catch (Throwable t) {\r
275       throw new HTMLParseException(t.toString());\r
276     }\r
277 \r
278     tmpl.process(outPutHash, out);\r
279   }\r
280 \r
281   /**\r
282    *   Converts Entity-List to SimpleList of SimpleHashes.\r
283    *   @param aList ist eine Liste von Entity\r
284    *   @return eine freemarker.template.SimpleList von SimpleHashes.\r
285    *\r
286    *    @deprecated EntityLists comply with TemplateListModel now.\r
287    */\r
288   public static SimpleList makeSimpleList(EntityList aList) throws StorageObjectFailure {\r
289     logger.warn("using deprecated makeSimpleList(entityList) - a waste of resources");\r
290     SimpleList simpleList = new SimpleList();\r
291     if (aList != null) {\r
292       for (int i = 0; i < aList.size(); i++) {\r
293         simpleList.add(aList.elementAt(i));\r
294       }\r
295     }\r
296     return simpleList;\r
297   }\r
298 \r
299   /**\r
300    *  Konvertiert ein EntityList in ein freemarker.template.SimpleHash-Modell. Im Hash\r
301    *  sind die einzelnen Entities ueber ihre id zu erreichen.\r
302    *  @param aList ist die EntityList\r
303    *  @return SimpleHash mit den entsprechenden freemarker Daten\r
304    *\r
305    */\r
306   public static SimpleHash makeSimpleHash(EntityList aList) throws\r
307       StorageObjectFailure {\r
308     SimpleHash simpleHash = new SimpleHash();\r
309     Entity currentEntity;\r
310 \r
311     if (aList != null) {\r
312       for (int i = 0; i < aList.size(); i++) {\r
313         currentEntity = (Entity) aList.elementAt(i);\r
314         simpleHash.put(currentEntity.getId(), currentEntity);\r
315       }\r
316     }\r
317     return simpleHash;\r
318   }\r
319 \r
320   /**\r
321    *  Konvertiert ein Hashtable mit den keys und values als String\r
322    *  in ein freemarker.template.SimpleHash-Modell\r
323    *  @param mergeData der Map mit den String / String Daten\r
324    *  @return SimpleHash mit den entsprechenden freemarker Daten\r
325    *\r
326    */\r
327   public static SimpleHash makeSimpleHash(Map mergeData) {\r
328     SimpleHash modelRoot = new SimpleHash();\r
329     String aField;\r
330     if (mergeData != null) {\r
331       Set set = mergeData.keySet();\r
332       Iterator it = set.iterator();\r
333       for (int i = 0; i < set.size(); i++) {\r
334         aField = (String) it.next();\r
335         modelRoot.put(aField, (String) mergeData.get(aField));\r
336       }\r
337     }\r
338     return modelRoot;\r
339   }\r
340 \r
341   /**\r
342    * Converts EntityList in SimpleHash and adds additional information\r
343    * to the returned SimpleHash\r
344    *\r
345    * @param entList\r
346    * @return SimpleHash returns SimpleHash with the converted EntityList plus\r
347    *        additional Data about the list.\r
348    * @exception StorageObjectException\r
349    */\r
350 \r
351   public static SimpleHash makeSimpleHashWithEntitylistInfos(EntityList entList) throws\r
352       StorageObjectFailure {\r
353     SimpleHash modelRoot = new SimpleHash();\r
354     if (entList != null) {\r
355       modelRoot.put("contentlist", entList);\r
356       modelRoot.put("count",\r
357                     new SimpleScalar( (new Integer(entList.getCount())).toString()));\r
358       if (entList.getWhere() != null) {\r
359         modelRoot.put("where", new SimpleScalar(entList.getWhere()));\r
360         modelRoot.put("where_encoded",\r
361                       new SimpleScalar(URLEncoder.encode(entList.getWhere())));\r
362       }\r
363       if (entList.getOrder() != null) {\r
364         modelRoot.put("order", new SimpleScalar(entList.getOrder()));\r
365         modelRoot.put("order_encoded",\r
366                       new SimpleScalar(URLEncoder.encode(entList.getOrder())));\r
367       }\r
368       modelRoot.put("from",\r
369                     new SimpleScalar( (new Integer(entList.getFrom())).toString()));\r
370       modelRoot.put("to",\r
371                     new SimpleScalar( (new Integer(entList.getTo())).toString()));\r
372 \r
373       if (entList.hasNextBatch())\r
374         modelRoot.put("next",\r
375                       new SimpleScalar( (new Integer(entList.getNextBatch())).\r
376                                        toString()));\r
377       if (entList.hasPrevBatch())\r
378         modelRoot.put("prev",\r
379                       new SimpleScalar( (new Integer(entList.getPrevBatch())).\r
380                                        toString()));\r
381     }\r
382     return modelRoot;\r
383   }\r
384 \r
385   /**\r
386    * Private methods to get template from a templateFilename\r
387    * @param templateFilename\r
388    * @return Template\r
389    * @exception HTMLParseException\r
390    */\r
391   private static Template getTemplateFor(String templateFilename) throws\r
392       HTMLParseException {\r
393     Template returnTemplate = null;\r
394     if (templateFilename != null)\r
395       returnTemplate = (Template) templateCache.getItem(templateFilename,\r
396           "template");\r
397 \r
398     if (returnTemplate == null) {\r
399       logger.error("CACHE (ERR): Unknown template: " + templateFilename);\r
400       throw new HTMLParseException("Templatefile: " + templateFilename + " not found.");\r
401     }\r
402 \r
403     return returnTemplate;\r
404   }\r
405 }