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