001: /*
002: Copyright (C) 2003 Know Gate S.L. All rights reserved.
003: C/Oņa, 107 1š2 28050 Madrid (Spain)
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions
007: are met:
008:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011:
012: 2. The end-user documentation included with the redistribution,
013: if any, must include the following acknowledgment:
014: "This product includes software parts from hipergate
015: (http://www.hipergate.org/)."
016: Alternately, this acknowledgment may appear in the software itself,
017: if and wherever such third-party acknowledgments normally appear.
018:
019: 3. The name hipergate must not be used to endorse or promote products
020: derived from this software without prior written permission.
021: Products derived from this software may not be called hipergate,
022: nor may hipergate appear in their name, without prior written
023: permission.
024:
025: This library is distributed in the hope that it will be useful,
026: but WITHOUT ANY WARRANTY; without even the implied warranty of
027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
028:
029: You should have received a copy of hipergate License with this code;
030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
031: */
032:
033: package com.knowgate.dataxslt;
034:
035: import java.io.File;
036: import java.io.IOException;
037: import java.io.FileNotFoundException;
038: import java.io.InputStream;
039: import java.io.OutputStream;
040: import java.io.StringBufferInputStream;
041: import java.io.ByteArrayInputStream;
042: import java.io.ByteArrayOutputStream;
043: import java.io.UnsupportedEncodingException;
044:
045: import java.util.Date;
046: import java.util.WeakHashMap;
047: import java.util.Iterator;
048: import java.util.Properties;
049: import java.util.Enumeration;
050:
051: import javax.xml.transform.TransformerFactory;
052: import javax.xml.transform.Transformer;
053: import javax.xml.transform.Templates;
054: import javax.xml.transform.TransformerException;
055: import javax.xml.transform.TransformerConfigurationException;
056: import javax.xml.transform.stream.StreamSource;
057: import javax.xml.transform.stream.StreamResult;
058:
059: import com.knowgate.debug.DebugFile;
060: import com.knowgate.misc.Gadgets;
061:
062: /**
063: * XSL File Cache
064: * This class keeps a master copy in memory of each XSL Stylesheet file.<br>
065: * When a Transformer object is requested a copy of the master Stylesheet is
066: * done. This is faster than re-loading de XSL file from disk.<br>
067: * StylesheetCache is a WeakHashMap so cached stylesheets can be automatically
068: * garbage collected is memory runs low.
069: * @author Sergio Montoro Ten
070: * @version 1.0
071: */
072: public class StylesheetCache {
073:
074: private StylesheetCache() {
075: }
076:
077: // ---------------------------------------------------------------------------
078:
079: /**
080: * Get Transformer object for XSL file.
081: * StylesheetCache automatically checks file last modification date and compares
082: * it with loading date for cached objects. If file is more recent than its cached
083: * object then the disk copy is reloaded.
084: * @param sFilePath File Path
085: * @throws IOException
086: * @throws TransformerException
087: * @throws TransformerConfigurationException
088: */
089: public static synchronized Transformer newTransformer(
090: String sFilePath) throws FileNotFoundException,
091: IOException, TransformerException,
092: TransformerConfigurationException {
093:
094: if (DebugFile.trace) {
095: DebugFile.writeln("Begin StylesheetCache.newTransformer("
096: + sFilePath + ")");
097: DebugFile.incIdent();
098: }
099:
100: File oFile = new File(sFilePath);
101:
102: if (!oFile.exists()) {
103: if (DebugFile.trace) {
104: DebugFile.writeln("File not found " + sFilePath);
105: DebugFile.decIdent();
106: }
107: throw new FileNotFoundException(sFilePath);
108: }
109: long lastMod = oFile.lastModified();
110:
111: TransformerFactory oFactory;
112: Templates oTemplates;
113: StreamSource oStreamSrc;
114: SheetEntry oSheet = (SheetEntry) oCache.get(sFilePath);
115:
116: if (null != oSheet) {
117: if (DebugFile.trace) {
118: DebugFile.writeln("Cache hit: Cached stylesheet date "
119: + new Date(oSheet.lastModified).toString()
120: + " Disk file date "
121: + new Date(lastMod).toString());
122: }
123: if (lastMod > oSheet.lastModified) {
124: oSheet = null;
125: oCache.remove(sFilePath);
126: }
127: } // fi (oSheet)
128:
129: if (null == oSheet) {
130: if (DebugFile.trace)
131: DebugFile.writeln("TransformerFactory.newInstance()");
132: oFactory = TransformerFactory.newInstance();
133: if (DebugFile.trace)
134: DebugFile
135: .writeln("new StreamSource(" + sFilePath + ")");
136: oStreamSrc = new StreamSource(sFilePath);
137: oTemplates = oFactory.newTemplates(oStreamSrc);
138: oSheet = new SheetEntry(lastMod, oTemplates);
139: oCache.put(sFilePath, oSheet);
140: }
141:
142: if (DebugFile.trace)
143: DebugFile
144: .writeln("javax.xml.transform.Templates.newTransformer()");
145: Transformer oTransformer = oSheet.templates.newTransformer();
146:
147: if (DebugFile.trace) {
148: DebugFile.decIdent();
149: DebugFile.writeln("End StylesheetCache.newTransformer()");
150: }
151:
152: return oTransformer;
153: } // newTransformer()
154:
155: // ---------------------------------------------------------------------------
156:
157: /**
158: * Set parameters for a StyleSheet taken from a properties collection.
159: * This method is primarily designed for setting environment parameters.
160: * @param oXSL Transformer object.
161: * @param oProps Properties to be set as parameters. The substring "param_"
162: * will be added as a preffix to each property name passed as parameter.
163: * So if you pass a property named "workarea" it must be retrieved from XSL
164: * as <xsl:param name="param_workarea"/>
165: * @throws NullPointerException if oXSL is <b>null</b> or oProps is <b>null</b>
166: */
167: public static void setParameters(Transformer oXSL, Properties oProps)
168: throws NullPointerException {
169:
170: if (DebugFile.trace) {
171: DebugFile
172: .writeln("Begin StylesheetCache.setParameters(Transformer, Properties)");
173: if (null == oXSL)
174: throw new NullPointerException(
175: "StylesheetCache.setParameters() Transformer may not be null");
176: if (null == oXSL)
177: throw new NullPointerException(
178: "StylesheetCache.setParameters() Properties may not be null");
179: DebugFile.incIdent();
180: }
181:
182: String sKey, sVal;
183: Iterator myIterator = oProps.keySet().iterator();
184:
185: while (myIterator.hasNext()) {
186: sKey = (String) myIterator.next();
187: sVal = oProps.getProperty(sKey);
188:
189: if (DebugFile.trace) {
190: DebugFile.writeln("set param_" + sKey + " = " + sVal);
191: }
192: oXSL.setParameter("param_" + sKey, sVal);
193: } // wend()
194:
195: if (DebugFile.trace) {
196: DebugFile.decIdent();
197: DebugFile.writeln("End StylesheetCache.setParameters()");
198: }
199: } // setParameters
200:
201: // ---------------------------------------------------------------------------
202:
203: /**
204: * Perform XSLT transformation
205: * @param sStyleSheetPath File Path to XSL style sheet file
206: * @param oXMLInputStream Input Stream for XML source data
207: * @param oOutputStream Stream where output is to be written
208: * @param oProps Parameters for Transformer. The substring "param_"
209: * will be added as a preffix to each property name passed as parameter.
210: * So if you pass a property named "workarea" it must be retrieved from XSL
211: * as <xsl:param name="param_workarea"/>
212: * @throws NullPointerException if oProps is <b>null</b>
213: * @throws FileNotFoundException if sStyleSheetPath does not exist
214: * @throws IOException
215: * @throws TransformerException
216: * @throws TransformerConfigurationException
217: */
218: public static void transform(String sStyleSheetPath,
219: InputStream oXMLInputStream, OutputStream oOutputStream,
220: Properties oProps) throws IOException,
221: FileNotFoundException, NullPointerException,
222: TransformerException, TransformerConfigurationException {
223:
224: long lElapsed = 0;
225:
226: if (DebugFile.trace) {
227: lElapsed = System.currentTimeMillis();
228:
229: DebugFile.writeln("Begin StylesheetCache.transform("
230: + sStyleSheetPath + ", InputStream, Properties)");
231: DebugFile.incIdent();
232: }
233:
234: Transformer oTransformer = StylesheetCache
235: .newTransformer(sStyleSheetPath);
236:
237: if (null != oProps)
238: setParameters(oTransformer, oProps);
239:
240: StreamSource oStreamSrcXML = new StreamSource(oXMLInputStream);
241:
242: StreamResult oStreamResult = new StreamResult(oOutputStream);
243:
244: if (DebugFile.trace)
245: DebugFile
246: .writeln("Transformer.transform(StreamSource,StreamResult)");
247:
248: oTransformer.transform(oStreamSrcXML, oStreamResult);
249:
250: if (DebugFile.trace) {
251: DebugFile.writeln("done in "
252: + String.valueOf(System.currentTimeMillis()
253: - lElapsed) + " miliseconds");
254: DebugFile.decIdent();
255: DebugFile.writeln("End StylesheetCache.transform()");
256: }
257: } // transform
258:
259: // ---------------------------------------------------------------------------
260:
261: /**
262: * Perform XSLT transformation
263: * @param sStyleSheetPath File Path to XSL style sheet file
264: * @param sXMLInput Input String with XML source data
265: * @param oProps Parameters for Transformer. The substring "param_"
266: * will be added as a preffix to each property name passed as parameter.
267: * So if you pass a property named "workarea" it must be retrieved from XSL
268: * as <xsl:param name="param_workarea"/>
269: * @return String Transformed document
270: * @throws NullPointerException if sXMLInput or oProps are <b>null</b>
271: * @throws FileNotFoundException if sStyleSheetPath does not exist
272: * @throws IOException
273: * @throws UnsupportedEncodingException
274: * @throws TransformerException
275: * @throws TransformerConfigurationException
276: * @since 3.0
277: */
278: public static String transform(String sStyleSheetPath,
279: String sXMLInput, Properties oProps) throws IOException,
280: FileNotFoundException, UnsupportedEncodingException,
281: NullPointerException, TransformerException,
282: TransformerConfigurationException {
283:
284: if (DebugFile.trace) {
285: DebugFile.writeln("Begin StylesheetCache.transform("
286: + sStyleSheetPath + ", String, Properties)");
287: DebugFile.incIdent();
288: }
289:
290: if (null == sXMLInput) {
291: if (DebugFile.trace)
292: DebugFile.decIdent();
293: throw new NullPointerException(
294: "StylesheetCache.transform() XML input String may not be null");
295: }
296:
297: // ****************************************
298: // Get character encoding of input XML data
299: String sEncoding;
300: int iEnc = Gadgets.indexOfIgnoreCase(sXMLInput, "encoding");
301: if (iEnc < 0) {
302: sEncoding = "ISO8859_1";
303: } else {
304: int iBeg = iEnc + 8;
305: int iEnd;
306: while (sXMLInput.charAt(iBeg) == ' '
307: || sXMLInput.charAt(iBeg) == '=')
308: iBeg++;
309: while (sXMLInput.charAt(iBeg) == ' ')
310: iBeg++;
311: if (sXMLInput.charAt(iBeg) == '"') {
312: iEnd = ++iBeg;
313: while (sXMLInput.charAt(iEnd) != '"')
314: iEnd++;
315: } else {
316: iEnd = iBeg;
317: while (sXMLInput.charAt(iEnd) != ' '
318: && sXMLInput.charAt(iEnd) != '?')
319: iEnd++;
320: } // fi
321: sEncoding = sXMLInput.substring(iBeg, iEnd);
322: } // fi
323: // ****************************************
324:
325: if (DebugFile.trace) {
326: DebugFile
327: .writeln("XML input file encoding is " + sEncoding);
328: }
329:
330: ByteArrayOutputStream oOutputStream = new ByteArrayOutputStream();
331: ByteArrayInputStream oXMLInputStream = new ByteArrayInputStream(
332: sXMLInput.getBytes(sEncoding));
333: Transformer oTransformer = StylesheetCache
334: .newTransformer(sStyleSheetPath);
335: if (null != oProps)
336: setParameters(oTransformer, oProps);
337: StreamSource oStreamSrcXML = new StreamSource(oXMLInputStream);
338: StreamResult oStreamResult = new StreamResult(oOutputStream);
339: if (DebugFile.trace)
340: DebugFile
341: .writeln("Transformer.transform(StreamSource,StreamResult)");
342: oTransformer.transform(oStreamSrcXML, oStreamResult);
343: oStreamSrcXML = null;
344: oXMLInputStream.close();
345: String sRetVal = oOutputStream.toString(sEncoding);
346: if (DebugFile.trace) {
347: if (null == sRetVal)
348: DebugFile
349: .writeln("Transformer.transform() returned null");
350: else
351: DebugFile.writeln("Transformer.transform() returned "
352: + String.valueOf(sRetVal.length())
353: + " characters");
354: }
355: oStreamResult = null;
356: oOutputStream.close();
357:
358: if (DebugFile.trace) {
359: DebugFile.decIdent();
360: DebugFile.writeln("End StylesheetCache.transform()");
361: }
362: return sRetVal;
363: } // transform
364:
365: // ---------------------------------------------------------------------------
366:
367: static class SheetEntry {
368: long lastModified;
369: Templates templates;
370:
371: SheetEntry(long lLastModified, Templates oTemplats) {
372: lastModified = lLastModified;
373: templates = oTemplats;
374: }
375: } // SheetEntry
376:
377: private static WeakHashMap oCache = new WeakHashMap();
378: } // StylesheetCache
|