1 | /** |
2 | * BSD-style license; for more info see http://xradar.sourceforge.net/license.html |
3 | */ |
4 | package org.sourceforge.xradar; |
5 | |
6 | import java.io.IOException; |
7 | import java.io.InputStream; |
8 | import java.util.logging.Level; |
9 | import java.util.logging.Logger; |
10 | |
11 | import javax.xml.parsers.DocumentBuilder; |
12 | import javax.xml.parsers.DocumentBuilderFactory; |
13 | import javax.xml.parsers.ParserConfigurationException; |
14 | import javax.xml.transform.Result; |
15 | import javax.xml.transform.Transformer; |
16 | import javax.xml.transform.TransformerConfigurationException; |
17 | import javax.xml.transform.TransformerException; |
18 | import javax.xml.transform.TransformerFactory; |
19 | import javax.xml.transform.URIResolver; |
20 | import javax.xml.transform.dom.DOMSource; |
21 | import javax.xml.transform.stream.StreamSource; |
22 | |
23 | import org.sourceforge.xradar.statics.Param; |
24 | import org.sourceforge.xradar.statics.Preambule; |
25 | import org.w3c.dom.Document; |
26 | import org.xml.sax.EntityResolver; |
27 | import org.xml.sax.InputSource; |
28 | import org.xml.sax.SAXException; |
29 | |
30 | /** |
31 | * @author Romain PELISSE, belaran@gmail.com |
32 | * |
33 | */ |
34 | public class XSLTMerger { |
35 | |
36 | private final static Logger logger = Logger.getLogger(XSLTMerger.class.getSimpleName());; |
37 | |
38 | private static EntityResolver entityResolver; |
39 | private static URIResolver uriResolver; |
40 | |
41 | private Transformer transformer; |
42 | private InputStream currentXslt; |
43 | private InputSource currentXmlFile; |
44 | private Preambule preambule; |
45 | private Result xmlResult; |
46 | |
47 | /** |
48 | * Default constructor: |
49 | * Warning, this constructor register the xradar protocol handler ! |
50 | */ |
51 | public XSLTMerger() |
52 | { |
53 | AbstractProcess.registerResolvers(); |
54 | } |
55 | |
56 | /* |
57 | * Prepare factory and get new transformer |
58 | */ |
59 | private void prepareFactory() throws TransformerConfigurationException |
60 | { |
61 | TransformerFactory factory = TransformerFactory.newInstance(); |
62 | StreamSource src = new StreamSource(this.currentXslt); |
63 | this.transformer = factory.newTransformer(src); |
64 | } |
65 | |
66 | /* |
67 | * Load the xml file and return it. |
68 | * Uses XML Catalog to resolve entity if provided. |
69 | */ |
70 | private DOMSource loadXMLFile() throws ParserConfigurationException, SAXException, IOException |
71 | { |
72 | // Loading XML file |
73 | DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); |
74 | // XRadar come with it own xml catalog in order to NOT download third party dtds |
75 | if ( XSLTMerger.entityResolver != null ) |
76 | parser.setEntityResolver(XSLTMerger.entityResolver); |
77 | Document document = parser.parse(this.currentXmlFile); |
78 | return new DOMSource(document); |
79 | } |
80 | |
81 | |
82 | private final static String JAVA_VERSION = System.getProperty("java.version"); |
83 | private final static String ANT_HOME = System.getProperty("ant.home") ; |
84 | /** |
85 | * <p>FIXME: For mysterious reasons, XML Catalog has very bad side effect when |
86 | * run inside Ant and the Java5, therefore this ugly hack fix the issue.</p> |
87 | * @return |
88 | */ |
89 | private boolean canUseXmlCatalog() { |
90 | if ( ANT_HOME != null && JAVA_VERSION.startsWith("1.5") ) |
91 | return false; |
92 | return true; |
93 | } |
94 | |
95 | /* |
96 | * Execute the merge, with the provided XML Catalog (if there is one). |
97 | * |
98 | */ |
99 | private Result merge() throws ParserConfigurationException, SAXException, IOException, TransformerException |
100 | { |
101 | // Preparation work |
102 | this.prepareFactory(); |
103 | // Loading xml file |
104 | logger.log(Level.FINEST,"Processing XML file:" + this.currentXmlFile); |
105 | // FIXME: using DOMSource here may (again) lead to performance issue on large project |
106 | DOMSource xml = this.loadXMLFile(); |
107 | // Preparing transformation |
108 | this.preparePreambule(); |
109 | // Passing Catalog to transformer to avoid any internet lookup for dtd |
110 | if ( XSLTMerger.uriResolver != null && canUseXmlCatalog() ) |
111 | this.transformer.setURIResolver(XSLTMerger.uriResolver); |
112 | // Execute the transformation |
113 | this.transformer.transform(xml, xmlResult); |
114 | // Return the result |
115 | return xmlResult; |
116 | } |
117 | |
118 | /* |
119 | * Deals with any operation to do before executing the transformation |
120 | * (most likely the passing of parameters to the xslt file) |
121 | */ |
122 | private void preparePreambule() |
123 | { |
124 | // do nothing if no preambule is defined or if it is empty |
125 | if ( this.preambule != null && |
126 | this.preambule.getParams() != null && |
127 | ! this.preambule.getParams().isEmpty() ) |
128 | { |
129 | // First we clean the object of any previous parameters |
130 | this.transformer.clearParameters(); |
131 | // We add each param described in the preambule |
132 | for ( Param param : this.preambule.getParams().values() ) |
133 | { |
134 | if ( param.getName() != null && ! "".equals(param.getName()) && |
135 | param.getExpression() != null && ! "".equals(param.getExpression())) |
136 | this.transformer.setParameter(param.getName(),param.getExpression()); |
137 | } |
138 | } |
139 | } |
140 | |
141 | /** |
142 | * Transform the currentXmlFile according to the currentXslt file. |
143 | * |
144 | * @param currentXmlFile |
145 | * @param currentXslt |
146 | * @return |
147 | */ |
148 | |
149 | public Result merge(InputSource currentXmlFile, InputStream currentXslt) { |
150 | this.currentXslt = currentXslt; |
151 | this.currentXmlFile = currentXmlFile; |
152 | Result result = null; |
153 | try { |
154 | result = this.merge(); |
155 | } catch (ParserConfigurationException e) { |
156 | logger.log(Level.SEVERE, e.getLocalizedMessage()); |
157 | } catch (SAXException e) { |
158 | logger.log(Level.SEVERE, e.getLocalizedMessage()); |
159 | } catch (IOException e) { |
160 | logger.log(Level.SEVERE, e.getLocalizedMessage()); |
161 | } catch (TransformerException e) { |
162 | logger.log(Level.SEVERE, e.getLocalizedMessage()); |
163 | } |
164 | // We reset the preambule ( but we do not use clear to allow reuse of |
165 | // the previous preambule by the caller ) |
166 | if ( this.getPreambule() != null && this.getPreambule().getParams() != null ) |
167 | this.setPreambule(new Preambule()); |
168 | return result; |
169 | } |
170 | |
171 | /** |
172 | * @return the xmlResult |
173 | */ |
174 | public Result getXmlResult() { |
175 | return xmlResult; |
176 | } |
177 | |
178 | /** |
179 | * @param xmlResult the xmlResult to set |
180 | */ |
181 | public void setXmlResult(Result xmlResult) { |
182 | this.xmlResult = xmlResult; |
183 | } |
184 | |
185 | /** |
186 | * @return the preambule |
187 | */ |
188 | public Preambule getPreambule() { |
189 | return preambule; |
190 | } |
191 | |
192 | /** |
193 | * @param preambule the preambule to set |
194 | */ |
195 | public void setPreambule(Preambule preambule) { |
196 | this.preambule = preambule; |
197 | } |
198 | |
199 | /** |
200 | * @param return entityResolver |
201 | */ |
202 | public static EntityResolver getEntityResolver() { |
203 | return entityResolver; |
204 | } |
205 | |
206 | /** |
207 | * <p>Set the entityResolver.</p> |
208 | * @param entityResolver |
209 | */ |
210 | public static void setEntityResolver(EntityResolver entityResolver) { |
211 | XSLTMerger.entityResolver = entityResolver; |
212 | } |
213 | |
214 | |
215 | /** |
216 | * @param return uriResolver |
217 | */ |
218 | public static URIResolver getUriResolver() { |
219 | return uriResolver; |
220 | } |
221 | |
222 | /** |
223 | * <p>Set the uriResolver.</p> |
224 | * @param uriResolver |
225 | */ |
226 | public static void setUriResolver(URIResolver uriResolver) { |
227 | XSLTMerger.uriResolver = uriResolver; |
228 | } |
229 | |
230 | |
231 | } |