1 16 package org.apache.cocoon.components.xslt; 17 18 import java.io.File ; 19 import java.io.IOException ; 20 import java.io.InputStream ; 21 import java.util.ArrayList ; 22 import java.util.HashMap ; 23 import java.util.List ; 24 import java.util.Map ; 25 26 import javax.xml.transform.Result ; 27 import javax.xml.transform.Templates ; 28 import javax.xml.transform.Transformer ; 29 import javax.xml.transform.TransformerException ; 30 import javax.xml.transform.TransformerFactory ; 31 import javax.xml.transform.URIResolver ; 32 import javax.xml.transform.sax.SAXTransformerFactory ; 33 import javax.xml.transform.sax.TemplatesHandler ; 34 import javax.xml.transform.sax.TransformerHandler ; 35 import javax.xml.transform.stream.StreamSource ; 36 37 import org.apache.avalon.excalibur.pool.Recyclable; 38 import org.apache.avalon.framework.activity.Disposable; 39 import org.apache.avalon.framework.activity.Initializable; 40 import org.apache.avalon.framework.logger.AbstractLogEnabled; 41 import org.apache.avalon.framework.parameters.ParameterException; 42 import org.apache.avalon.framework.parameters.Parameterizable; 43 import org.apache.avalon.framework.parameters.Parameters; 44 import org.apache.avalon.framework.service.ServiceException; 45 import org.apache.avalon.framework.service.ServiceManager; 46 import org.apache.avalon.framework.service.Serviceable; 47 import org.apache.excalibur.source.Source; 48 import org.apache.excalibur.source.SourceException; 49 import org.apache.excalibur.source.SourceResolver; 50 import org.apache.excalibur.source.SourceValidity; 51 import org.apache.excalibur.source.impl.validity.AggregatedValidity; 52 import org.apache.excalibur.store.Store; 53 import org.apache.excalibur.xml.sax.XMLizable; 54 import org.apache.excalibur.xml.xslt.XSLTProcessor; 55 import org.apache.excalibur.xml.xslt.XSLTProcessorException; 56 import org.apache.excalibur.xmlizer.XMLizer; 57 import org.xml.sax.ContentHandler ; 58 import org.xml.sax.InputSource ; 59 import org.xml.sax.SAXException ; 60 import org.xml.sax.XMLFilter ; 61 62 69 70 public class TraxProcessor extends AbstractLogEnabled implements XSLTProcessor, Serviceable, Initializable, Disposable, Parameterizable, 71 Recyclable, URIResolver { 72 73 protected Store m_store; 74 75 76 protected String m_transformerFactory; 77 78 79 protected SAXTransformerFactory m_factory; 80 81 82 protected SAXTransformerFactory m_defaultFactory; 83 84 85 protected boolean m_useStore; 86 87 88 protected boolean m_incrementalProcessing; 89 90 91 protected SourceResolver m_resolver; 92 93 94 protected boolean m_checkIncludes; 95 96 97 protected Map m_includesMap = new HashMap (); 98 99 protected XMLizer m_xmlizer; 100 101 102 protected ServiceManager m_manager; 103 104 111 public void service(final ServiceManager manager) throws ServiceException { 112 m_manager = manager; 113 m_xmlizer = (XMLizer) m_manager.lookup(XMLizer.ROLE); 114 m_resolver = (SourceResolver) m_manager.lookup(SourceResolver.ROLE); 115 116 if (m_manager.hasService(Store.TRANSIENT_STORE)) { 117 m_store = (Store) m_manager.lookup(Store.TRANSIENT_STORE); 118 } 119 } 120 121 124 public void initialize() throws Exception { 125 m_factory = getTransformerFactory(m_transformerFactory); 126 m_defaultFactory = m_factory; 127 } 128 129 132 public void dispose() { 133 if (null != m_manager) { 134 m_manager.release(m_store); 135 m_manager.release(m_resolver); 136 m_manager.release(m_xmlizer); 137 m_manager = null; 138 } 139 m_xmlizer = null; 140 m_store = null; 141 m_resolver = null; 142 } 143 144 147 public void parameterize(final Parameters params) throws ParameterException { 148 m_useStore = params.getParameterAsBoolean("use-store", this.m_useStore); 149 m_incrementalProcessing = params.getParameterAsBoolean("incremental-processing", this.m_incrementalProcessing); 150 m_transformerFactory = params.getParameter("transformer-factory", null); 151 m_checkIncludes = params.getParameterAsBoolean("check-includes", true); 152 if (!m_useStore) { 153 m_manager.release(m_store); 155 m_store = null; 156 } else if (null == m_store) { 157 final String message = "XSLTProcessor: use-store is set to true, " + "but unable to aquire the Store."; 158 throw new ParameterException(message); 159 } 160 } 161 162 165 public void setTransformerFactory(final String classname) { 166 m_factory = getTransformerFactory(classname); 167 } 168 169 172 public TransformerHandler getTransformerHandler(final Source stylesheet) throws XSLTProcessorException { 173 return getTransformerHandler(stylesheet, null); 174 } 175 176 180 public TransformerHandler getTransformerHandler(final Source stylesheet, final XMLFilter filter) throws XSLTProcessorException { 181 final XSLTProcessor.TransformerHandlerAndValidity validity = getTransformerHandlerAndValidity(stylesheet, filter); 182 return validity.getTransfomerHandler(); 183 } 184 185 public TransformerHandlerAndValidity getTransformerHandlerAndValidity(final Source stylesheet) throws XSLTProcessorException { 186 return getTransformerHandlerAndValidity(stylesheet, null); 187 } 188 189 public TransformerHandlerAndValidity getTransformerHandlerAndValidity(Source stylesheet, XMLFilter filter) throws XSLTProcessorException { 190 191 final String id = stylesheet.getURI(); 192 TransformerHandlerAndValidity handlerAndValidity; 193 194 try { 195 handlerAndValidity = getTemplates(stylesheet, id); 196 if (handlerAndValidity != null) { 197 if (getLogger().isDebugEnabled()) { 198 getLogger().debug("Reusing Templates for " + id); 199 } 200 return handlerAndValidity; 201 } 202 } catch(Exception e) { 203 throw new XSLTProcessorException("Error retrieving template", e); 204 } 205 206 TraxErrorListener errorListener = new TraxErrorListener(getLogger(), stylesheet.getURI()); 207 try{ 208 if (getLogger().isDebugEnabled()) { 209 getLogger().debug("Creating new Templates for " + id); 210 } 211 212 m_factory.setErrorListener(errorListener); 213 214 TemplatesHandler templatesHandler = m_factory.newTemplatesHandler(); 217 218 templatesHandler.setSystemId(id); 222 if (filter != null) { 223 filter.setContentHandler(templatesHandler); 224 } 225 226 if (getLogger().isDebugEnabled()) { 227 getLogger().debug("Source = " + stylesheet + ", templatesHandler = " + templatesHandler); 228 } 229 230 SourceValidity validity = stylesheet.getValidity(); 232 if (validity != null && m_checkIncludes) { 233 m_includesMap.put(id, new ArrayList ()); 234 } 235 236 try { 237 sourceToSAX(stylesheet, filter != null ? (ContentHandler ) filter : (ContentHandler ) templatesHandler); 239 240 final Templates template = templatesHandler.getTemplates(); 243 244 if (null == template) { 245 throw new XSLTProcessorException("Unable to create templates for stylesheet: " + stylesheet.getURI()); 246 } 247 248 putTemplates(template, stylesheet, id); 249 250 final TransformerHandler handler = m_factory.newTransformerHandler(template); 252 handler.getTransformer().setErrorListener(new TraxErrorListener(getLogger(), stylesheet.getURI())); 253 handler.getTransformer().setURIResolver(this); 254 255 AggregatedValidity aggregated = null; 257 if (validity != null && m_checkIncludes) { 258 List includes = (List ) m_includesMap.get(id); 259 if (includes != null) { 260 aggregated = new AggregatedValidity(); 261 aggregated.add(validity); 262 for (int i = includes.size() - 1; i >= 0; i--) { 263 aggregated.add((SourceValidity) ((Object []) includes.get(i))[1]); 264 } 265 validity = aggregated; 266 } 267 } 268 269 handlerAndValidity = new MyTransformerHandlerAndValidity(handler, validity); 271 } finally { 272 if (m_checkIncludes) 273 m_includesMap.remove(id); 274 } 275 276 return handlerAndValidity; 277 } catch (Exception e) { 278 Throwable realEx = errorListener.getThrowable(); 279 if (realEx == null) realEx = e; 280 281 if (realEx instanceof RuntimeException ) { 282 throw (RuntimeException )realEx; 283 } 284 285 if (realEx instanceof XSLTProcessorException) { 286 throw (XSLTProcessorException)realEx; 287 } 288 289 throw new XSLTProcessorException("Exception when creating Transformer from " + stylesheet.getURI(), realEx); 290 } 291 } 292 293 protected void sourceToSAX(Source source, ContentHandler handler) throws SAXException , IOException , SourceException { 294 if (source instanceof XMLizable) { 295 ((XMLizable) source).toSAX(handler); 296 } else { 297 final InputStream inputStream = source.getInputStream(); 298 final String mimeType = source.getMimeType(); 299 final String systemId = source.getURI(); 300 m_xmlizer.toSAX(inputStream, mimeType, systemId, handler); 301 } 302 } 303 304 public void transform(final Source source, final Source stylesheet, final Parameters params, final Result result) throws XSLTProcessorException { 305 try { 306 if (getLogger().isDebugEnabled()) { 307 getLogger().debug( 308 "Transform source = " + source + ", stylesheet = " + stylesheet + ", parameters = " + params + ", result = " + result); 309 } 310 final TransformerHandler handler = getTransformerHandler(stylesheet); 311 if (params != null) { 312 final Transformer transformer = handler.getTransformer(); 313 transformer.clearParameters(); 314 String [] names = params.getNames(); 315 for (int i = names.length - 1; i >= 0; i--) { 316 transformer.setParameter(names[i], params.getParameter(names[i])); 317 } 318 } 319 320 handler.setResult(result); 321 sourceToSAX(source, handler); 322 if (getLogger().isDebugEnabled()) { 323 getLogger().debug("Transform done"); 324 } 325 } catch (SAXException e) { 326 final String message = "Error in running Transformation"; 329 throw new XSLTProcessorException(message, e); 330 338 } catch (Exception e) { 339 final String message = "Error in running Transformation"; 340 throw new XSLTProcessorException(message, e); 341 } 342 } 343 344 349 private SAXTransformerFactory getTransformerFactory(String factoryName) { 350 SAXTransformerFactory _factory; 351 352 if (null == factoryName) { 353 _factory = (SAXTransformerFactory ) TransformerFactory.newInstance(); 354 } else { 355 try { 356 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 357 if (loader == null) { 358 loader = getClass().getClassLoader(); 359 } 360 _factory = (SAXTransformerFactory ) loader.loadClass(factoryName).newInstance(); 361 } catch (ClassNotFoundException cnfe) { 362 getLogger().error("Cannot find the requested TrAX factory '" + factoryName + "'. Using default TrAX Transformer Factory instead."); 363 if (m_factory != null) 364 return m_factory; 365 _factory = (SAXTransformerFactory ) TransformerFactory.newInstance(); 366 } catch (ClassCastException cce) { 367 getLogger().error( 368 "The indicated class '" + factoryName 369 + "' is not a TrAX Transformer Factory. Using default TrAX Transformer Factory instead."); 370 if (m_factory != null) 371 return m_factory; 372 _factory = (SAXTransformerFactory ) TransformerFactory.newInstance(); 373 } catch (Exception e) { 374 getLogger().error( 375 "Error found loading the requested TrAX Transformer Factory '" + factoryName 376 + "'. Using default TrAX Transformer Factory instead."); 377 if (m_factory != null) 378 return m_factory; 379 _factory = (SAXTransformerFactory ) TransformerFactory.newInstance(); 380 } 381 } 382 383 _factory.setErrorListener(new TraxErrorListener(getLogger(), null)); 384 _factory.setURIResolver(this); 385 386 if (_factory.getClass().getName().equals("org.apache.xalan.processor.TransformerFactoryImpl")) { 389 _factory.setAttribute("http://xml.apache.org/xalan/features/incremental", new Boolean (m_incrementalProcessing)); 390 } 391 392 return _factory; 393 } 394 395 private TransformerHandlerAndValidity getTemplates(Source stylesheet, String id) throws IOException , TransformerException { 396 if (!m_useStore) { 397 return null; 398 } 399 400 String key = "XSLTTemplate: " + id + '(' + m_factory.getClass().getName() + ')'; 404 405 if (getLogger().isDebugEnabled()) { 406 getLogger().debug("getTemplates: stylesheet " + id); 407 } 408 409 SourceValidity newValidity = stylesheet.getValidity(); 410 411 if (newValidity == null) { 413 m_store.remove(key); 415 return null; 416 } 417 418 Object [] templateAndValidityAndIncludes = (Object []) m_store.get(key); 421 if (templateAndValidityAndIncludes == null) { 422 return null; 424 } 425 426 SourceValidity storedValidity = (SourceValidity) templateAndValidityAndIncludes[1]; 428 int valid = storedValidity.isValid(); 429 boolean isValid; 430 if (valid == 0) { 431 valid = storedValidity.isValid(newValidity); 432 isValid = (valid == 1); 433 } else { 434 isValid = (valid == 1); 435 } 436 if (!isValid) { 437 m_store.remove(key); 438 return null; 439 } 440 441 if (m_checkIncludes) { 443 AggregatedValidity aggregated = null; 444 List includes = (List ) templateAndValidityAndIncludes[2]; 445 if (includes != null) { 446 aggregated = new AggregatedValidity(); 447 aggregated.add(storedValidity); 448 449 for (int i = includes.size() - 1; i >= 0; i--) { 450 Object [] pair = (Object []) includes.get(i); 452 storedValidity = (SourceValidity) pair[1]; 453 aggregated.add(storedValidity); 454 455 valid = storedValidity.isValid(); 456 isValid = false; 457 if (valid == 0) { 458 Source includedSource = null; 459 try { 460 includedSource = m_resolver.resolveURI((String ) pair[0]); 461 SourceValidity included = includedSource.getValidity(); 462 if (included != null) { 463 valid = storedValidity.isValid(included); 464 isValid = (valid == 1); 465 } 466 } finally { 467 m_resolver.release(includedSource); 468 } 469 } else { 470 isValid = (valid == 1); 471 } 472 if (!isValid) { 473 m_store.remove(key); 474 return null; 475 } 476 } 477 storedValidity = aggregated; 478 } 479 } 480 481 TransformerHandler handler = m_factory.newTransformerHandler((Templates ) templateAndValidityAndIncludes[0]); 482 handler.getTransformer().setErrorListener(new TraxErrorListener(getLogger(), stylesheet.getURI())); 483 handler.getTransformer().setURIResolver(this); 484 return new MyTransformerHandlerAndValidity(handler, storedValidity); 485 } 486 487 private void putTemplates(Templates templates, Source stylesheet, String id) throws IOException { 488 if (!m_useStore) 489 return; 490 491 String key = "XSLTTemplate: " + id + '(' + m_factory.getClass().getName() + ')'; 495 496 SourceValidity validity = stylesheet.getValidity(); 498 if (null != validity) { 499 Object [] templateAndValidityAndIncludes = new Object [3]; 501 templateAndValidityAndIncludes[0] = templates; 502 templateAndValidityAndIncludes[1] = validity; 503 if (m_checkIncludes) { 504 templateAndValidityAndIncludes[2] = m_includesMap.get(id); 505 } 506 m_store.store(key, templateAndValidityAndIncludes); 507 } 508 } 509 510 526 public javax.xml.transform.Source resolve(String href, String base) throws TransformerException { 527 if (getLogger().isDebugEnabled()) { 528 getLogger().debug("resolve(href = " + href + ", base = " + base + "); resolver = " + m_resolver); 529 } 530 531 Source xslSource = null; 532 try { 533 if (base == null || href.indexOf(":") > 1) { 534 xslSource = m_resolver.resolveURI(href); 536 } else if (href.length() == 0) { 537 xslSource = m_resolver.resolveURI(base); 539 } else { 540 if (!base.startsWith("file:")) { 542 int lastPathElementPos = base.lastIndexOf('/'); 543 if (lastPathElementPos == -1) { 544 return null; } else { 548 xslSource = m_resolver.resolveURI(base.substring(0, lastPathElementPos) + "/" + href); 549 } 550 } else { 551 File parent = new File (base.substring(5)); 552 File parent2 = new File (parent.getParentFile(), href); 553 xslSource = m_resolver.resolveURI(parent2.toURL().toExternalForm()); 554 } 555 } 556 557 InputSource is = getInputSource(xslSource); 558 559 if (getLogger().isDebugEnabled()) { 560 getLogger().debug("xslSource = " + xslSource + ", system id = " + xslSource.getURI()); 561 } 562 563 if (m_checkIncludes) { 564 List includes = (List ) m_includesMap.get(base); 566 if (includes != null) { 567 SourceValidity included = xslSource.getValidity(); 568 if (included != null) { 569 includes.add(new Object [] { xslSource.getURI(), xslSource.getValidity() }); 570 } else { 571 m_includesMap.remove(base); 573 } 574 } 575 } 576 577 return new StreamSource (is.getByteStream(), is.getSystemId()); 578 } catch (SourceException e) { 579 if (getLogger().isDebugEnabled()) { 580 getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", e); 581 } 582 583 return null; 586 } catch (java.net.MalformedURLException mue) { 587 if (getLogger().isDebugEnabled()) { 588 getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", mue); 589 } 590 591 return null; 592 } catch (IOException ioe) { 593 if (getLogger().isDebugEnabled()) { 594 getLogger().debug("Failed to resolve " + href + "(base = " + base + "), return null", ioe); 595 } 596 597 return null; 598 } finally { 599 m_resolver.release(xslSource); 600 } 601 } 602 603 611 protected InputSource getInputSource(final Source source) throws IOException , SourceException { 612 final InputSource newObject = new InputSource (source.getInputStream()); 613 newObject.setSystemId(source.getURI()); 614 return newObject; 615 } 616 617 620 public void recycle() { 621 m_includesMap.clear(); 622 if (m_factory != m_defaultFactory) { 624 m_factory = m_defaultFactory; 625 } 626 } 627 628 632 public static class MyTransformerHandlerAndValidity extends TransformerHandlerAndValidity { 633 634 protected MyTransformerHandlerAndValidity(TransformerHandler handler, SourceValidity validity) { 635 super(handler, validity); 636 } 637 } 638 } 639 | Popular Tags |