| 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 | } |