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