KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > bridge > jsp > taglib > FormatterTag


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.bridge.jsp.taglib;
11
12 import org.mmbase.bridge.jsp.taglib.util.Attribute;
13 import org.mmbase.bridge.jsp.taglib.util.Referids;
14
15 import javax.servlet.jsp.*;
16
17 import javax.servlet.jsp.tagext.TagSupport JavaDoc;
18
19 import org.w3c.dom.Document JavaDoc;
20
21 import javax.xml.transform.Source JavaDoc;
22 import javax.xml.transform.Templates JavaDoc;
23 import javax.xml.transform.TransformerFactory JavaDoc;
24 import javax.xml.parsers.DocumentBuilder JavaDoc;
25
26 import org.mmbase.bridge.util.xml.Generator;
27 import org.mmbase.bridge.Cloud;
28
29 import java.net.URL JavaDoc;
30 import java.util.*;
31 import javax.servlet.jsp.PageContext JavaDoc;
32
33 import org.mmbase.util.Encode;
34 import org.mmbase.util.Entry;
35 import org.mmbase.util.logging.Logger;
36 import org.mmbase.util.logging.Logging;
37
38 import org.mmbase.cache.xslt.*;
39
40
41 /**
42  * The formatter can reformat its body. It usually uses XSL for this.
43  *
44  * @since MMBase-1.6
45  * @author Michiel Meeuwissen
46  * @version $Id: FormatterTag.java,v 1.68.2.1 2006/09/18 12:54:53 michiel Exp $
47  */

48 public class FormatterTag extends CloudReferrerTag implements ParamHandler {
49
50     private static final Logger log = Logging.getLoggerInstance(FormatterTag.class);
51
52
53     protected Attribute xslt = Attribute.NULL;
54     protected Attribute format = Attribute.NULL;
55     protected Attribute options = Attribute.NULL;
56     protected Attribute wants = Attribute.NULL;
57
58     protected Attribute namespaceAware = Attribute.NULL;
59     protected Attribute referids = Attribute.NULL;
60
61     protected List extraParameters = new ArrayList();
62
63     protected Source JavaDoc xsltSource = null;
64
65     private DocumentBuilder JavaDoc documentBuilder;
66     private DocumentBuilder JavaDoc documentBuilderNS;
67     private URL JavaDoc cwd;
68
69     private static final class Counter {
70         private int i = 0;
71         public int inc() { return ++i;}
72         public String JavaDoc toString() {
73             return "" + i;
74         }
75     }
76     private Counter counter; // times that formatter was called in this page, can be useful for some transformations to know.
77

78     private Cloud cloud;
79
80     // formats that needs XML input, when setting these 'wantXML' will be true, and a DOM Document will be created.
81

82     // XSLT transformations:
83
private static final int FORMAT_XHTML = 1;
84     private static final int FORMAT_PRESENTXML = 2;
85     private static final int FORMAT_CODE = 3;
86     private static final int FORMAT_TEXTONLY = 4;
87     private static final int FORMAT_RICH = 5;
88
89     // wants XML, but uses Encode to output (no XSLT is used).
90
private static final int FORMAT_ESCAPEXMLPRETTY = 500;
91
92
93     private static final int FORMAT_LIMIT_WANTXML = 1000; // if smaller then this, then need XML.
94

95     // These formats take the body as a string. So the body doesn't need to be valid XML.
96
private static final int FORMAT_ESCAPEXML = 1001;
97     private static final int FORMAT_DATE = 1002;
98     private static final int FORMAT_LOWERCASE = 1003;
99     private static final int FORMAT_UPPERCASE = 1004;
100     private static final int FORMAT_NONE = 2000; // can be useful for only the escape= functionality.
101

102
103     private static final int FORMAT_UNSET = -1;
104
105
106     private static final int WANTS_DEFAULT = -1;
107     private static final int WANTS_DOM = 1;
108     private static final int WANTS_STRING = 2;
109
110     private static final String JavaDoc PAGECONTEXT_COUNTER = "formatter__counter";
111     
112     /* transformer properties */
113     Properties props = new Properties();
114
115     {
116         log.debug("Init of FormatterTag.");
117
118         try {
119             javax.xml.parsers.DocumentBuilderFactory JavaDoc dfactory = javax.xml.parsers.DocumentBuilderFactory.newInstance();
120             dfactory.setNamespaceAware(false);
121             documentBuilder = dfactory.newDocumentBuilder();
122             dfactory.setNamespaceAware(true);
123             documentBuilderNS = dfactory.newDocumentBuilder();
124             org.xml.sax.ErrorHandler JavaDoc handler = new org.mmbase.util.XMLErrorHandler();
125             org.xml.sax.EntityResolver JavaDoc resolver = new org.mmbase.util.XMLEntityResolver();
126             documentBuilder.setErrorHandler(handler);
127             documentBuilder.setEntityResolver(resolver);
128             documentBuilderNS.setErrorHandler(handler);
129             documentBuilderNS.setEntityResolver(resolver);
130         } catch (Exception JavaDoc e) {
131             log.error(e.toString());
132         }
133     }
134
135     /**
136      * Must store the XML somewhere. This will contain the central DOM Document.
137      *
138      */

139     private Generator xmlGenerator = null;
140
141     /**
142      * A handle necessary when using the Timer Tag;
143      */

144     protected int timerHandle;
145
146     /**
147      * You can give the path the the XSLT-file by this attribute.
148      */

149     public void setXslt(String JavaDoc x) throws JspTagException {
150         xslt = getAttribute(x);
151     }
152
153     /**
154      * Predefined formattings.
155      */

156     public void setFormat(String JavaDoc f) throws JspTagException {
157         format = getAttribute(f);
158     }
159
160     protected int getFormat() throws JspTagException {
161         String JavaDoc fs = format.getString(this).toLowerCase();
162         if ("".equals(fs)) {
163             return FORMAT_UNSET;
164         } else if ("none".equals(fs)) {
165             return FORMAT_NONE;
166         } else if ("xhtml".equals(fs)) {
167             return FORMAT_XHTML;
168         } else if ("presentxml".equals(fs)) {
169             return FORMAT_PRESENTXML;
170         } else if ("code".equals(fs)) {
171             return FORMAT_CODE;
172         } else if ("escapexml".equals(fs)) {
173             return FORMAT_ESCAPEXML;
174         } else if ("escapexmlpretty".equals(fs)) {
175             return FORMAT_ESCAPEXMLPRETTY;
176         } else if ("date".equals(fs)) {
177             return FORMAT_DATE;
178         } else if ("textonly".equals(fs)) {
179             return FORMAT_TEXTONLY;
180         } else if ("rich".equals(fs)) {
181             return FORMAT_RICH;
182         } else {
183             throw new JspTagException("Unknown format " + fs);
184         }
185     }
186
187     /**
188      * The 'options' attribute can be used to provide option to the transformation to be done
189      */

190     public void setOptions(String JavaDoc o) throws JspTagException {
191         options = getAttribute(o);
192     }
193
194     public void setWants(String JavaDoc w) throws JspTagException {
195         wants = getAttribute(w);
196     }
197     protected int getWants() throws JspTagException {
198         if (wants == Attribute.NULL) return WANTS_DEFAULT;
199         String JavaDoc ww = wants.getString(this).toLowerCase();
200         if ("default".equals(ww)) {
201             return WANTS_DEFAULT;
202         } else if ("dom".equals(ww)) {
203             return WANTS_DOM;
204         } else if ( "string".equals(ww)) {
205             return WANTS_STRING;
206         } else {
207             throw new JspTagException("Unknown value '" + ww + "' for wants attribute.");
208         }
209     }
210
211     public void setNamespaceaware(String JavaDoc n) throws JspTagException {
212         namespaceAware = getAttribute(n);
213     }
214
215     public void setReferids(String JavaDoc r) throws JspTagException {
216         referids = getAttribute(r);
217     }
218
219     public void addParameter(String JavaDoc key, Object JavaDoc value) throws JspTagException {
220         if (log.isDebugEnabled()) {
221             log.debug("adding parameter " + key + "/" + value);
222         }
223         extraParameters.add(new Entry(key, value));
224     }
225     /**
226      * The Xslt tag will call this, to inform this tag about the XSLT which must be done.
227      */

228     public void setXsltSource(Source JavaDoc xs) {
229         xsltSource = xs;
230     }
231
232
233     /**
234      * Subtags can write themselves as XML to the DOM document of this
235      * tag. This functions returns this document.
236      */

237     public Generator getGenerator() {
238         return xmlGenerator;
239     }
240
241     /**
242      * Subtags need to know how they must communicate there content to
243      * this tag. If wantXML evaluates false, they must simply write to
244      * the page, and formatter will pick it up.
245      */

246     public final boolean wantXML() {
247         return xmlGenerator != null;
248     }
249
250     /**
251      * return false if the cloud was set already (nothing happened);
252      * @since MMBase-1.8.1
253      */

254     public boolean setCloud(Cloud c) {
255         boolean result = cloud == null;
256         if (result) {
257             cloud = c;
258         }
259         return result;
260     }
261
262
263     public void setPageContext(PageContext JavaDoc pageContext) {
264         super.setPageContext(pageContext);
265         javax.servlet.http.HttpServletRequest JavaDoc request = (javax.servlet.http.HttpServletRequest JavaDoc)pageContext.getRequest();
266         try {
267             String JavaDoc includingServlet = (String JavaDoc) request.getAttribute(org.mmbase.bridge.jsp.taglib.pageflow.IncludeTag.INCLUDE_PATH_KEY);
268             if (includingServlet == null) {
269                 includingServlet = request.getServletPath();
270             }
271             cwd = pageContext.getServletContext().getResource(org.mmbase.util.ResourceLoader.getDirectory(includingServlet) + "/");
272         } catch (Exception JavaDoc e) {
273         }
274     }
275
276
277     public int doStartTag() throws JspTagException {
278         extraParameters.clear();
279         cloud = null;
280         counter = (Counter) pageContext.getAttribute(PAGECONTEXT_COUNTER);
281         if (counter == null) {
282             log.debug("counter not found");
283             counter = new Counter();
284             pageContext.setAttribute(PAGECONTEXT_COUNTER, counter);
285         }
286         counter.inc();
287
288         if (log.isDebugEnabled()) log.debug("startag of formatter tag " + counter);
289
290         // serve parent timer tag:
291
TagSupport JavaDoc t = findParentTag(org.mmbase.bridge.jsp.taglib.debug.TimerTag.class, null, false);
292         if (t != null) {
293             timerHandle = ((org.mmbase.bridge.jsp.taglib.debug.TimerTag)t).startTimer(getId(), getClass().getName());
294         } else {
295             timerHandle = -1;
296         }
297
298         xsltSource = null;
299
300         if (format != Attribute.NULL && xslt != Attribute.NULL) {
301             throw new JspTagException ("One of the attributes xslt and format must be specified, or none (then you have to use an mm:xslt subtag.");
302         }
303
304         int w = getWants();
305         if ((w == WANTS_DEFAULT && getFormat() < FORMAT_LIMIT_WANTXML) || w == WANTS_DOM) { // also if format is unset, that means: use xslt
306
if(namespaceAware.getBoolean(this, true)) {
307                 xmlGenerator = new Generator(documentBuilderNS);
308                 xmlGenerator.setNamespaceAware(true);
309             } else {
310                 xmlGenerator = new Generator(documentBuilder);
311                 xmlGenerator.setNamespaceAware(false);
312             }
313         } else {
314             xmlGenerator = null; // my childen will know, that this formatter doesn't want them.
315
}
316
317         return EVAL_BODY_BUFFERED;
318     }
319
320     public int doAfterBody() throws JspException {
321         return helper.doAfterBody();
322     }
323
324
325     public int doEndTag() throws JspTagException {
326         if (helper.getJspvar() == null) {
327             helper.overrideWrite(true);
328         }
329
330         Document JavaDoc doc;
331
332         String JavaDoc body = bodyContent.getString().trim();
333         bodyContent.clearBody(); // should not be shown itself.
334

335         if(getFormat() < FORMAT_LIMIT_WANTXML) { // determin the Document
336
if (xmlGenerator != null && xmlGenerator.getDocument().getDocumentElement().getFirstChild() != null) {
337                 if (body.length() > 0) {
338                     throw new JspTagException ("It is not possible to have tags which produce DOM-XML and text in the body. Perhaps you want to use the attribute wants='string'?");
339                 } else {
340                     doc = xmlGenerator.getDocument();
341                 }
342             } else {
343                 if (body == null || body.equals("")) body = "<mmxf />"; // something random that will at least parse.
344
if (log.isDebugEnabled()) log.debug("Using bodycontent as input:>" + body + "<");
345                 try {
346                     String JavaDoc encoding = org.mmbase.util.GenericResponseWrapper.getXMLEncoding(body);
347                     if (encoding == null) encoding = "UTF-8"; // it _must_ be XML.
348
javax.servlet.http.HttpServletRequest JavaDoc request = (javax.servlet.http.HttpServletRequest JavaDoc)pageContext.getRequest();
349                     DocumentBuilder JavaDoc db = namespaceAware.getBoolean(this, true) ? documentBuilderNS : documentBuilder;
350                     doc = db.parse(new java.io.ByteArrayInputStream JavaDoc(body.getBytes(encoding)),
351                                                 pageContext.getServletContext().getResource(request.getServletPath()).toString()
352                                                 );
353                 } catch (Exception JavaDoc e) {
354                     throw new TaglibException(e.getMessage() + "when parsing '" + body + "'", e);
355                 }
356                 if (log.isDebugEnabled()) {
357                     log.debug("created an element: " + doc.getDocumentElement().getTagName());
358                 }
359             }
360
361         } else {
362             doc = null;
363         }
364
365
366         if (log.isDebugEnabled()) {
367             if (doc != null) {
368                 log.trace("XSL converting document: " + prettyXML(doc));
369             } else {
370                 log.trace("Converting: " + body);
371             }
372         }
373
374
375         // Determin which XSL to use.
376
// useXsl is the pathless name.
377
// First it is searced for in <current directory>/xslt
378
// if it cannot be found there, then the default in mmbase.config/xslt will be used.
379
// File useXslt;
380

381         if (format != Attribute.NULL) {
382             if (xslt != Attribute.NULL) {
383                 throw new JspTagException("Cannot specify both the 'xslt' attribute and the 'format' attribute");
384             }
385             if (xsltSource != null) {
386                 throw new JspTagException("Cannot use 'xslt' subtag and the 'format' attribute");
387             }
388             switch(getFormat()) {
389             case FORMAT_NONE:
390                 helper.useEscaper(true);
391                 helper.setValue(body);
392                 break;
393             case FORMAT_XHTML:
394                 helper.useEscaper(false); // fieldinfo typicaly produces xhtml
395
helper.setValue(xslTransform(doc, "xslt/2xhtml.xslt"));
396                 break;
397             case FORMAT_PRESENTXML:
398                 helper.useEscaper(false); // fieldinfo typicaly produces xhtml
399
helper.setValue(xslTransform(doc, "xslt/2xml.xslt"));
400                 break;
401             case FORMAT_CODE:
402                 helper.setValue(xslTransform(doc, "xslt/code2xml.xslt"));
403                 break;
404             case FORMAT_TEXTONLY:
405                 helper.useEscaper(true);
406                 helper.setValue(xslTransform(doc, "xslt/2ascii.xslt"));
407                 break;
408             case FORMAT_RICH:
409                 helper.useEscaper(false); // fieldinfo typicaly produces xhtml
410
helper.setValue(xslTransform(doc, "xslt/mmxf2rich.xslt"));
411                 break;
412             case FORMAT_ESCAPEXMLPRETTY:
413                 helper.useEscaper(false); // fieldinfo typicaly produces xhtml
414
helper.setValue(Encode.encode("ESCAPE_XML", prettyXML(doc)));
415                 // helper.setValue(prettyXML(doc));
416
break;
417             // -- FORMAT_LIMIT_XML
418
case FORMAT_ESCAPEXML:
419                 helper.useEscaper(false); // fieldinfo typicaly produces xhtml
420
helper.setValue(Encode.encode("ESCAPE_XML", body));
421                 break;
422             case FORMAT_DATE:
423                 java.text.SimpleDateFormat JavaDoc dateFormat = (java.text.SimpleDateFormat JavaDoc) java.text.SimpleDateFormat.getDateInstance();
424                 String JavaDoc pattern;
425                 if (options == Attribute.NULL) {
426                     pattern = "yyyy-MM-dd HH:mm:ss";
427                 } else {
428                     pattern = options.getString(this);
429                 }
430                 // iso 8601 for date/time
431
dateFormat.applyPattern(pattern);
432                 Date datum = new Date((new Long JavaDoc(body)).longValue() * 1000);
433                 helper.setValue(dateFormat.format(datum));
434                 break;
435             default:
436                 log.debug("Unknown format "+getFormat());
437                 break;
438             }
439         } else {
440
441             if (xslt != Attribute.NULL) {
442                 if (xsltSource != null) {
443                     throw new JspTagException("Cannot use 'xslt' subtag and the 'xslt' attribute");
444                 }
445                 if (log.isDebugEnabled()) log.debug("Transforming with " + xslt.getString(this));
446
447                 helper.setValue(xslTransform(doc, xslt.getString(this)));
448             } else {
449                 if (xsltSource == null) {
450                     throw new JspTagException("No 'format' attribute, no 'xslt' attribute and no 'xslt' subtag. Don't know what to do.");
451                 }
452                 helper.setValue(xslTransform(doc, xsltSource));
453             }
454         }
455
456         if (log.isDebugEnabled()) {
457             log.trace("found result " + helper.getValue());
458
459         }
460
461
462         if (getId() != null) {
463             getContextProvider().getContextContainer().register(getId(), helper.getValue());
464         }
465
466         if (timerHandle != -1) {
467             ((org.mmbase.bridge.jsp.taglib.debug.TimerTag)findParentTag(org.mmbase.bridge.jsp.taglib.debug.TimerTag.class, null, false)).haltTimer(timerHandle);
468         }
469         helper.doEndTag();
470         return super.doEndTag();
471     } // doEndTag
472

473     public void doFinally() {
474         xsltSource = null;
475         props = null;
476         if (helper != null) {
477             helper.doFinally();
478         }
479         if (extraParameters != null) {
480             extraParameters.clear();
481         }
482         xmlGenerator = null;
483         cloud = null;
484         super.doFinally();
485     }
486
487     /**
488      * @return the Factory which must be used for XSL transformations in this directory.
489      */

490
491     private TransformerFactory JavaDoc getFactory() {
492         return FactoryCache.getCache().getFactory(cwd);
493     }
494
495     /**
496      * Base function for XSL conversions, which this Tag does.
497      *
498      * It returns a String, even if it goes wrong, in which case the string contains the error
499      * message.
500      * @param A Source (representing the XSLT).
501      * @return The result ot the transformation.
502      */

503     private String JavaDoc xslTransform(Document JavaDoc doc, Source JavaDoc xsl) throws JspTagException {
504         if (log.isDebugEnabled()) {
505             log.debug("transforming in " + cwd + " with " + xsl.getSystemId());
506         }
507
508         TemplateCache cache= TemplateCache.getCache();
509         Templates JavaDoc cachedXslt = cache.getTemplates(xsl);
510         if (cachedXslt == null) {
511             try {
512                 if (log.isDebugEnabled()) {
513                     log.debug("getting for " + cwd);
514                 }
515                 cachedXslt = getFactory().newTemplates(xsl);
516                 assert cachedXslt != null; // according to javadoc may not happen
517
cache.put(xsl, cachedXslt);
518             } catch (javax.xml.transform.TransformerConfigurationException JavaDoc e) {
519                 return e.toString() + ": " + Logging.stackTrace(e);
520             }
521         } else {
522             if (log.isDebugEnabled()) log.debug("Used xslt from cache with " + xsl.getSystemId());
523         }
524
525         if (cachedXslt == null) {
526             // according to javadoc of TransformerFactory, this cannot happen, but
527
// I say it occurs any way!
528
throw new JspTagException("Could not create Templates object from " + xsl.getSystemId() + " " + xsl);
529         }
530
531         // set some parameters to the XSLT style sheet.
532
Map params = new HashMap();
533         String JavaDoc context = ((javax.servlet.http.HttpServletRequest JavaDoc)pageContext.getRequest()).getContextPath();
534         params.put("formatter_requestcontext", context);
535         //params.put("formatter_imgdb", org.mmbase.module.builders.AbstractImages.getImageServletPath(context));
536
// use node function
537
LocaleTag localeTag = (LocaleTag) findParentTag(org.mmbase.bridge.jsp.taglib.LocaleTag.class, null, false);
538         Locale locale;
539         if (localeTag != null) {
540             locale = localeTag.getLocale();
541             if (locale == null) {
542                 locale = Locale.getDefault(); // should perhaps somehow find the MMBase default language setting.
543
}
544         } else {
545             locale = Locale.getDefault(); // should perhaps somehow find the MMBase default language setting.
546
}
547         params.put("formatter_language", locale.getLanguage());
548         params.put("formatter_counter", counter.toString());
549         if (cloud != null) {
550             params.put("cloud", cloud);
551         } else {
552             CloudProvider cp = findCloudProvider(false);
553             if (cp != null) {
554                 params.put("cloud", cp.getCloudVar());
555             }
556         }
557         params.put("request", pageContext.getRequest());
558
559         //other options
560
// a=b,c=d,e=f
561
if (options != Attribute.NULL) {
562             Iterator i = options.getList(this).iterator();
563             while (i.hasNext()) {
564                 String JavaDoc option = (String JavaDoc) i.next();
565                 // List o = StringSplitter.split(option, "=");
566
List o = Arrays.asList( option.trim().split("\\s*=\\s*") );
567                 if (o.size() != 2) {
568                     throw new JspTagException("Option '" + option + "' is not in the format key=value (required for XSL transformations)");
569
570                 } else {
571                     if (log.isDebugEnabled()) log.debug("Setting XSLT option " + option);
572                     params.put(o.get(0), o.get(1));
573                 }
574             }
575         }
576         params.putAll(Referids.getReferids(referids, this));
577         Iterator i = extraParameters.iterator();
578         while (i.hasNext()) {
579             Map.Entry entry = (Map.Entry) i.next();
580             params.put(entry.getKey(), entry.getValue());
581         }
582
583         return ResultCache.getCache().get(cachedXslt, xsl, params, props, doc);
584
585     }
586     public void setOutputProperties(Properties properties) {
587         this.props = properties;
588     }
589     /**
590      * @see xslTransform
591      * @param A name of an XSLT file.
592      */

593     private String JavaDoc xslTransform(Document JavaDoc doc, String JavaDoc xsl) throws JspTagException {
594         try {
595             Source JavaDoc source = getFactory().getURIResolver().resolve(xsl, null);
596             if (source == null) {
597                 throw new TaglibException("Could not find XSL for " + xsl + " with uri-resolver " + getFactory().getURIResolver());
598             }
599
600             return xslTransform(doc, source);
601          } catch (javax.xml.transform.TransformerException JavaDoc e) {
602              throw new TaglibException(e); // probably the file could not be found.
603
}
604     }
605
606     private String JavaDoc prettyXML(Document JavaDoc doc) throws JspTagException {
607         if ( log.isTraceEnabled() ) {
608             log.trace("pretty XML " + doc);
609         }
610         return xslTransform(doc, "xslt/indentxml.xslt");
611     }
612
613 }
614
Popular Tags