KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xalan > lib > Redirect


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: Redirect.java,v 1.22 2004/02/18 23:51:20 minchau Exp $
18  */

19 package org.apache.xalan.lib;
20
21 import java.io.File JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.util.Hashtable JavaDoc;
25
26 import javax.xml.transform.Result JavaDoc;
27 import javax.xml.transform.TransformerException JavaDoc;
28 import javax.xml.transform.stream.StreamResult JavaDoc;
29
30 import org.apache.xalan.extensions.XSLProcessorContext;
31 import org.apache.xalan.res.XSLTErrorResources;
32 import org.apache.xalan.templates.ElemExtensionCall;
33 import org.apache.xalan.templates.OutputProperties;
34 import org.apache.xalan.transformer.TransformerImpl;
35 import org.apache.xpath.XPath;
36 import org.apache.xpath.objects.XObject;
37 import org.apache.xml.serializer.SerializationHandler;
38 import org.xml.sax.ContentHandler JavaDoc;
39
40 /**
41  * Implements three extension elements to allow an XSLT transformation to
42  * redirect its output to multiple output files.
43  *
44  * It is accessed by specifying a namespace URI as follows:
45  * <pre>
46  * xmlns:redirect="http://xml.apache.org/xalan/redirect"
47  * </pre>
48  *
49  * <p>You can either just use redirect:write, in which case the file will be
50  * opened and immediately closed after the write, or you can bracket the
51  * write calls by redirect:open and redirect:close, in which case the
52  * file will be kept open for multiple writes until the close call is
53  * encountered. Calls can be nested.
54  *
55  * <p>Calls can take a 'file' attribute
56  * and/or a 'select' attribute in order to get the filename. If a select
57  * attribute is encountered, it will evaluate that expression for a string
58  * that indicates the filename. If the string evaluates to empty, it will
59  * attempt to use the 'file' attribute as a default. Filenames can be relative
60  * or absolute. If they are relative, the base directory will be the same as
61  * the base directory for the output document. This is obtained by calling
62  * getOutputTarget() on the TransformerImpl. You can set this base directory
63  * by calling TransformerImpl.setOutputTarget() or it is automatically set
64  * when using the two argument form of transform() or transformNode().
65  *
66  * <p>Calls to redirect:write and redirect:open also take an optional
67  * attribute append="true|yes", which will attempt to simply append
68  * to an existing file instead of always opening a new file. The
69  * default behavior of always overwriting the file still happens
70  * if you do not specify append.
71  * <p><b>Note:</b> this may give unexpected results when using xml
72  * or html output methods, since this is <b>not</b> coordinated
73  * with the serializers - hence, you may get extra xml decls in
74  * the middle of your file after appending to it.
75  *
76  * <p>Example:</p>
77  * <PRE>
78  * &lt;?xml version="1.0"?>
79  * &lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
80  * version="1.0"
81  * xmlns:redirect="http://xml.apache.org/xalan/redirect"
82  * extension-element-prefixes="redirect">
83  *
84  * &lt;xsl:template match="/">
85  * &lt;out>
86  * default output.
87  * &lt;/out>
88  * &lt;redirect:open file="doc3.out"/>
89  * &lt;redirect:write file="doc3.out">
90  * &lt;out>
91  * &lt;redirect:write file="doc1.out">
92  * &lt;out>
93  * doc1 output.
94  * &lt;redirect:write file="doc3.out">
95  * Some text to doc3
96  * &lt;/redirect:write>
97  * &lt;/out>
98  * &lt;/redirect:write>
99  * &lt;redirect:write file="doc2.out">
100  * &lt;out>
101  * doc2 output.
102  * &lt;redirect:write file="doc3.out">
103  * Some more text to doc3
104  * &lt;redirect:write select="doc/foo">
105  * text for doc4
106  * &lt;/redirect:write>
107  * &lt;/redirect:write>
108  * &lt;/out>
109  * &lt;/redirect:write>
110  * &lt;/out>
111  * &lt;/redirect:write>
112  * &lt;redirect:close file="doc3.out"/>
113  * &lt;/xsl:template>
114  *
115  * &lt;/xsl:stylesheet>
116  * </PRE>
117  *
118  * @author Scott Boag
119  * @version 1.0
120  * @see <a HREF="../../../../../../extensions.html#ex-redirect" target="_top">Example with Redirect extension</a>
121  */

122 public class Redirect
123 {
124   /**
125    * List of formatter listeners indexed by filename.
126    */

127   protected Hashtable JavaDoc m_formatterListeners = new Hashtable JavaDoc ();
128
129   /**
130    * List of output streams indexed by filename.
131    */

132   protected Hashtable JavaDoc m_outputStreams = new Hashtable JavaDoc ();
133
134   /**
135    * Default append mode for bare open calls.
136    * False for backwards compatibility (I think).
137    */

138   public static final boolean DEFAULT_APPEND_OPEN = false;
139
140   /**
141    * Default append mode for bare write calls.
142    * False for backwards compatibility.
143    */

144   public static final boolean DEFAULT_APPEND_WRITE = false;
145
146   /**
147    * Open the given file and put it in the XML, HTML, or Text formatter listener's table.
148    */

149   public void open(XSLProcessorContext context, ElemExtensionCall elem)
150     throws java.net.MalformedURLException JavaDoc,
151            java.io.FileNotFoundException JavaDoc,
152            java.io.IOException JavaDoc,
153            javax.xml.transform.TransformerException JavaDoc
154   {
155     String JavaDoc fileName = getFilename(context, elem);
156     Object JavaDoc flistener = m_formatterListeners.get(fileName);
157     if(null == flistener)
158     {
159       String JavaDoc mkdirsExpr
160         = elem.getAttribute ("mkdirs", context.getContextNode(),
161                                                   context.getTransformer());
162       boolean mkdirs = (mkdirsExpr != null)
163                        ? (mkdirsExpr.equals("true") || mkdirsExpr.equals("yes")) : true;
164
165       // Whether to append to existing files or not, <jpvdm@iafrica.com>
166
String JavaDoc appendExpr = elem.getAttribute("append", context.getContextNode(), context.getTransformer());
167       boolean append = (appendExpr != null)
168                        ? (appendExpr.equals("true") || appendExpr.equals("yes")) : DEFAULT_APPEND_OPEN;
169
170       Object JavaDoc ignored = makeFormatterListener(context, elem, fileName, true, mkdirs, append);
171     }
172   }
173   
174   /**
175    * Write the evalutation of the element children to the given file. Then close the file
176    * unless it was opened with the open extension element and is in the formatter listener's table.
177    */

178   public void write(XSLProcessorContext context, ElemExtensionCall elem)
179     throws java.net.MalformedURLException JavaDoc,
180            java.io.FileNotFoundException JavaDoc,
181            java.io.IOException JavaDoc,
182            javax.xml.transform.TransformerException JavaDoc
183   {
184     String JavaDoc fileName = getFilename(context, elem);
185     Object JavaDoc flObject = m_formatterListeners.get(fileName);
186     ContentHandler JavaDoc formatter;
187     boolean inTable = false;
188     if(null == flObject)
189     {
190       String JavaDoc mkdirsExpr
191         = ((ElemExtensionCall)elem).getAttribute ("mkdirs",
192                                                   context.getContextNode(),
193                                                   context.getTransformer());
194       boolean mkdirs = (mkdirsExpr != null)
195                        ? (mkdirsExpr.equals("true") || mkdirsExpr.equals("yes")) : true;
196
197       // Whether to append to existing files or not, <jpvdm@iafrica.com>
198
String JavaDoc appendExpr = elem.getAttribute("append", context.getContextNode(), context.getTransformer());
199       boolean append = (appendExpr != null)
200                        ? (appendExpr.equals("true") || appendExpr.equals("yes")) : DEFAULT_APPEND_WRITE;
201
202       formatter = makeFormatterListener(context, elem, fileName, true, mkdirs, append);
203     }
204     else
205     {
206       inTable = true;
207       formatter = (ContentHandler JavaDoc)flObject;
208     }
209     
210     TransformerImpl transf = context.getTransformer();
211     
212     transf.executeChildTemplates(elem,
213                                  context.getContextNode(),
214                                  context.getMode(), formatter);
215     
216     if(!inTable)
217     {
218       OutputStream JavaDoc ostream = (OutputStream JavaDoc)m_outputStreams.get(fileName);
219       if(null != ostream)
220       {
221         try
222         {
223           formatter.endDocument();
224         }
225         catch(org.xml.sax.SAXException JavaDoc se)
226         {
227           throw new TransformerException JavaDoc(se);
228         }
229         ostream.close();
230         m_outputStreams.remove(fileName);
231         m_formatterListeners.remove(fileName);
232       }
233     }
234   }
235
236
237   /**
238    * Close the given file and remove it from the formatter listener's table.
239    */

240   public void close(XSLProcessorContext context, ElemExtensionCall elem)
241     throws java.net.MalformedURLException JavaDoc,
242     java.io.FileNotFoundException JavaDoc,
243     java.io.IOException JavaDoc,
244     javax.xml.transform.TransformerException JavaDoc
245   {
246     String JavaDoc fileName = getFilename(context, elem);
247     Object JavaDoc formatterObj = m_formatterListeners.get(fileName);
248     if(null != formatterObj)
249     {
250       ContentHandler JavaDoc fl = (ContentHandler JavaDoc)formatterObj;
251       try
252       {
253         fl.endDocument();
254       }
255       catch(org.xml.sax.SAXException JavaDoc se)
256       {
257         throw new TransformerException JavaDoc(se);
258       }
259       OutputStream JavaDoc ostream = (OutputStream JavaDoc)m_outputStreams.get(fileName);
260       if(null != ostream)
261       {
262         ostream.close();
263         m_outputStreams.remove(fileName);
264       }
265       m_formatterListeners.remove(fileName);
266     }
267   }
268
269   /**
270    * Get the filename from the 'select' or the 'file' attribute.
271    */

272   private String JavaDoc getFilename(XSLProcessorContext context, ElemExtensionCall elem)
273     throws java.net.MalformedURLException JavaDoc,
274     java.io.FileNotFoundException JavaDoc,
275     java.io.IOException JavaDoc,
276     javax.xml.transform.TransformerException JavaDoc
277   {
278     String JavaDoc fileName;
279     String JavaDoc fileNameExpr
280       = ((ElemExtensionCall)elem).getAttribute ("select",
281                                                 context.getContextNode(),
282                                                 context.getTransformer());
283     if(null != fileNameExpr)
284     {
285       org.apache.xpath.XPathContext xctxt
286         = context.getTransformer().getXPathContext();
287       XPath myxpath = new XPath(fileNameExpr, elem, xctxt.getNamespaceContext(), XPath.SELECT);
288       XObject xobj = myxpath.execute(xctxt, context.getContextNode(), elem);
289       fileName = xobj.str();
290       if((null == fileName) || (fileName.length() == 0))
291       {
292         fileName = elem.getAttribute ("file",
293                                       context.getContextNode(),
294                                       context.getTransformer());
295       }
296     }
297     else
298     {
299       fileName = elem.getAttribute ("file", context.getContextNode(),
300                                                                context.getTransformer());
301     }
302     if(null == fileName)
303     {
304       context.getTransformer().getMsgMgr().error(elem, elem,
305                                      context.getContextNode(),
306                                      XSLTErrorResources.ER_REDIRECT_COULDNT_GET_FILENAME);
307                               //"Redirect extension: Could not get filename - file or select attribute must return vald string.");
308
}
309     return fileName;
310   }
311   
312   // yuck.
313
// Note: this is not the best way to do this, and may not even
314
// be fully correct! Patches (with test cases) welcomed. -sc
315
private String JavaDoc urlToFileName(String JavaDoc base)
316   {
317     if(null != base)
318     {
319       if(base.startsWith("file:////"))
320       {
321         base = base.substring(7);
322       }
323       else if(base.startsWith("file:///"))
324       {
325         base = base.substring(6);
326       }
327       else if(base.startsWith("file://"))
328       {
329         base = base.substring(5); // absolute?
330
}
331       else if(base.startsWith("file:/"))
332       {
333         base = base.substring(5);
334       }
335       else if(base.startsWith("file:"))
336       {
337         base = base.substring(4);
338       }
339     }
340     return base;
341   }
342
343   /**
344    * Create a new ContentHandler, based on attributes of the current ContentHandler.
345    */

346   private ContentHandler JavaDoc makeFormatterListener(XSLProcessorContext context,
347                                                ElemExtensionCall elem,
348                                                String JavaDoc fileName,
349                                                boolean shouldPutInTable,
350                                                boolean mkdirs,
351                                                boolean append)
352     throws java.net.MalformedURLException JavaDoc,
353     java.io.FileNotFoundException JavaDoc,
354     java.io.IOException JavaDoc,
355     javax.xml.transform.TransformerException JavaDoc
356   {
357     File JavaDoc file = new File JavaDoc(fileName);
358     TransformerImpl transformer = context.getTransformer();
359     String JavaDoc base; // Base URI to use for relative paths
360

361     if(!file.isAbsolute())
362     {
363       // This code is attributed to Jon Grov <jon@linpro.no>. A relative file name
364
// is relative to the Result used to kick off the transform. If no such
365
// Result was supplied, the filename is relative to the source document.
366
// When transforming with a SAXResult or DOMResult, call
367
// TransformerImpl.setOutputTarget() to set the desired Result base.
368
// String base = urlToFileName(elem.getStylesheet().getSystemId());
369

370       Result JavaDoc outputTarget = transformer.getOutputTarget();
371       if ( (null != outputTarget) && ((base = outputTarget.getSystemId()) != null) ) {
372         base = urlToFileName(base);
373       }
374       else
375       {
376         base = urlToFileName(transformer.getBaseURLOfSource());
377       }
378
379       if(null != base)
380       {
381         File JavaDoc baseFile = new File JavaDoc(base);
382         file = new File JavaDoc(baseFile.getParent(), fileName);
383       }
384       // System.out.println("file is: "+file.toString());
385
}
386
387     if(mkdirs)
388     {
389       String JavaDoc dirStr = file.getParent();
390       if((null != dirStr) && (dirStr.length() > 0))
391       {
392         File JavaDoc dir = new File JavaDoc(dirStr);
393         dir.mkdirs();
394       }
395     }
396
397     // This should be worked on so that the output format can be
398
// defined by a first child of the redirect element.
399
OutputProperties format = transformer.getOutputFormat();
400
401     // FileOutputStream ostream = new FileOutputStream(file);
402
// Patch from above line to below by <jpvdm@iafrica.com>
403
// Note that in JDK 1.2.2 at least, FileOutputStream(File)
404
// is implemented as a call to
405
// FileOutputStream(File.getPath, append), thus this should be
406
// the equivalent instead of getAbsolutePath()
407
FileOutputStream JavaDoc ostream = new FileOutputStream JavaDoc(file.getPath(), append);
408     
409     try
410     {
411       SerializationHandler flistener =
412         transformer.createSerializationHandler(new StreamResult JavaDoc(ostream),
413                                                format);
414       try
415       {
416         flistener.startDocument();
417       }
418       catch(org.xml.sax.SAXException JavaDoc se)
419       {
420         throw new TransformerException JavaDoc(se);
421       }
422       if(shouldPutInTable)
423       {
424         m_outputStreams.put(fileName, ostream);
425         m_formatterListeners.put(fileName, flistener);
426       }
427       return flistener;
428     }
429     catch(TransformerException JavaDoc te)
430     {
431       throw new javax.xml.transform.TransformerException JavaDoc(te);
432     }
433     
434   }
435 }
436
Popular Tags