1 17 package org.apache.excalibur.xml.xslt; 18 19 import java.io.File ; 20 import java.io.IOException ; 21 import java.io.InputStream ; 22 import java.util.ArrayList ; 23 import java.util.HashMap ; 24 import java.util.List ; 25 import java.util.Map ; 26 27 import javax.xml.transform.Result ; 28 import javax.xml.transform.Templates ; 29 import javax.xml.transform.Transformer ; 30 import javax.xml.transform.TransformerException ; 31 import javax.xml.transform.TransformerFactory ; 32 import javax.xml.transform.URIResolver ; 33 import javax.xml.transform.sax.SAXTransformerFactory ; 34 import javax.xml.transform.sax.TemplatesHandler ; 35 import javax.xml.transform.sax.TransformerHandler ; 36 import javax.xml.transform.stream.StreamSource ; 37 38 import org.apache.avalon.excalibur.pool.Recyclable; 39 import org.apache.avalon.framework.activity.Disposable; 40 import org.apache.avalon.framework.activity.Initializable; 41 import org.apache.avalon.framework.component.ComponentException; 42 import org.apache.avalon.framework.logger.AbstractLogEnabled; 43 import org.apache.avalon.framework.parameters.ParameterException; 44 import org.apache.avalon.framework.parameters.Parameterizable; 45 import org.apache.avalon.framework.parameters.Parameters; 46 import org.apache.avalon.framework.service.ServiceException; 47 import org.apache.avalon.framework.service.ServiceManager; 48 import org.apache.avalon.framework.service.Serviceable; 49 import org.apache.excalibur.source.Source; 50 import org.apache.excalibur.source.SourceException; 51 import org.apache.excalibur.source.SourceResolver; 52 import org.apache.excalibur.source.SourceValidity; 53 import org.apache.excalibur.source.impl.validity.AggregatedValidity; 54 import org.apache.excalibur.store.Store; 55 import org.apache.excalibur.xml.sax.XMLizable; 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 82 public class XSLTProcessorImpl 83 extends AbstractLogEnabled 84 implements XSLTProcessor, 85 Serviceable, 86 Initializable, 87 Disposable, 88 Parameterizable, 89 Recyclable, 90 URIResolver 91 { 92 93 protected Store m_store; 94 95 96 protected String m_transformerFactory; 97 98 protected SAXTransformerFactory m_factory; 99 100 protected SAXTransformerFactory m_defaultFactory; 101 102 103 protected boolean m_useStore; 104 105 106 protected boolean m_incrementalProcessing; 107 108 109 protected SourceResolver m_resolver; 110 111 112 protected TraxErrorHandler m_errorHandler; 113 114 115 protected boolean m_checkIncludes; 116 117 118 protected Map m_includesMap = new HashMap (); 119 120 protected XMLizer m_xmlizer; 121 122 123 protected ServiceManager m_manager; 124 125 132 public void service( final ServiceManager manager ) 133 throws ServiceException 134 { 135 m_manager = manager; 136 m_xmlizer = (XMLizer)m_manager.lookup( XMLizer.ROLE ); 137 m_resolver = (SourceResolver)m_manager.lookup( SourceResolver.ROLE ); 138 139 if( m_manager.hasService( Store.TRANSIENT_STORE ) ) 140 { 141 m_store = (Store)m_manager.lookup( Store.TRANSIENT_STORE ); 142 } 143 } 144 145 148 public void initialize() 149 throws Exception 150 { 151 m_errorHandler = new TraxErrorHandler( getLogger() ); 152 m_factory = getTransformerFactory( m_transformerFactory ); 153 m_defaultFactory = m_factory; 154 } 155 156 159 public void dispose() 160 { 161 if ( null != m_manager) 162 { 163 m_manager.release( m_store ); 164 m_manager.release( m_resolver ); 165 m_manager.release( m_xmlizer ); 166 m_manager = null; 167 } 168 m_xmlizer = null; 169 m_store = null; 170 m_resolver = null; 171 m_errorHandler = null; 172 } 173 174 177 public void parameterize( final Parameters params ) 178 throws ParameterException 179 { 180 m_useStore = params.getParameterAsBoolean( "use-store", this.m_useStore ); 181 m_incrementalProcessing = params.getParameterAsBoolean( "incremental-processing", this.m_incrementalProcessing ); 182 m_transformerFactory = params.getParameter( "transformer-factory", null ); 183 m_checkIncludes = params.getParameterAsBoolean("check-includes", true); 184 if( !m_useStore ) 185 { 186 m_manager.release( m_store ); 188 m_store = null; 189 } 190 else if( null == m_store ) 191 { 192 final String message = 193 "XSLTProcessor: use-store is set to true, " + 194 "but unable to aquire the Store."; 195 throw new ParameterException( message ); 196 } 197 } 198 199 202 public void setTransformerFactory( final String classname ) 203 { 204 m_factory = getTransformerFactory( classname ); 205 } 206 207 210 public TransformerHandler getTransformerHandler( final Source stylesheet ) 211 throws XSLTProcessorException 212 { 213 return getTransformerHandler( stylesheet, null ); 214 } 215 216 219 public TransformerHandler getTransformerHandler( final Source stylesheet, 220 final XMLFilter filter ) 221 throws XSLTProcessorException 222 { 223 final XSLTProcessor.TransformerHandlerAndValidity validity = getTransformerHandlerAndValidity( stylesheet, filter ); 224 return validity.getTransfomerHandler(); 225 } 226 227 public TransformerHandlerAndValidity getTransformerHandlerAndValidity( final Source stylesheet ) 228 throws XSLTProcessorException 229 { 230 return getTransformerHandlerAndValidity( stylesheet, null ); 231 } 232 233 public TransformerHandlerAndValidity getTransformerHandlerAndValidity( Source stylesheet, XMLFilter filter ) 234 throws XSLTProcessorException 235 { 236 try 237 { 238 final String id = stylesheet.getURI(); 239 TransformerHandlerAndValidity handlerAndValidity = getTemplates( stylesheet, id ); 240 if( null == handlerAndValidity ) 241 { 242 if( getLogger().isDebugEnabled() ) 243 { 244 getLogger().debug( "Creating new Templates for " + id ); 245 } 246 247 TemplatesHandler templatesHandler = m_factory.newTemplatesHandler(); 250 251 templatesHandler.setSystemId( id ); 255 if( filter != null ) 256 { 257 filter.setContentHandler( templatesHandler ); 258 } 259 260 if( getLogger().isDebugEnabled() ) 261 { 262 getLogger().debug( "Source = " + stylesheet 263 + ", templatesHandler = " + templatesHandler ); 264 } 265 266 SourceValidity validity = stylesheet.getValidity(); 268 if( validity != null && m_checkIncludes) 269 { 270 m_includesMap.put( id, new ArrayList () ); 271 } 272 273 try 274 { 275 sourceToSAX( stylesheet, 277 filter != null ? (ContentHandler )filter : (ContentHandler )templatesHandler ); 278 279 final Templates template = templatesHandler.getTemplates(); 282 283 if( null == template ) 284 { 285 throw new XSLTProcessorException( 286 "Unable to create templates for stylesheet: " 287 + stylesheet.getURI() ); 288 } 289 290 putTemplates( template, stylesheet, id ); 291 292 final TransformerHandler handler = m_factory.newTransformerHandler( template ); 294 handler.getTransformer().setErrorListener( m_errorHandler ); 295 handler.getTransformer().setURIResolver( this ); 296 297 AggregatedValidity aggregated = null; 299 if( validity != null && m_checkIncludes) 300 { 301 List includes = (List )m_includesMap.get( id ); 302 if( includes != null ) 303 { 304 aggregated = new AggregatedValidity(); 305 aggregated.add( validity ); 306 for( int i = includes.size() - 1; i >= 0; i-- ) 307 { 308 aggregated.add( (SourceValidity)( (Object [])includes.get( i ) )[ 1 ] ); 309 } 310 validity = aggregated; 311 } 312 } 313 314 handlerAndValidity = new TransformerHandlerAndValidity( handler, validity ); 316 } 317 finally 318 { 319 if ( m_checkIncludes ) m_includesMap.remove( id ); 320 } 321 } 322 else 323 { 324 if( getLogger().isDebugEnabled() ) 325 { 326 getLogger().debug( "Reusing Templates for " + id ); 327 } 328 } 329 330 return handlerAndValidity; 331 } 332 catch( SAXException e ) 333 { 334 throw new XSLTProcessorException( "Exception in creating Transform Handler", e ); 337 347 } 348 catch( Exception e ) 349 { 350 throw new XSLTProcessorException( "Exception in creating Transform Handler", e ); 351 } 352 } 353 354 private void sourceToSAX( Source source, ContentHandler handler ) 355 throws SAXException , IOException , ComponentException, SourceException 356 { 357 if( source instanceof XMLizable ) 358 { 359 ( (XMLizable)source ).toSAX( handler ); 360 } 361 else 362 { 363 final InputStream inputStream = source.getInputStream(); 364 final String mimeType = source.getMimeType(); 365 final String systemId = source.getURI(); 366 m_xmlizer.toSAX( inputStream, mimeType, systemId, handler ); 367 } 368 } 369 370 public void transform( final Source source, 371 final Source stylesheet, 372 final Parameters params, 373 final Result result ) 374 throws XSLTProcessorException 375 { 376 try 377 { 378 if( getLogger().isDebugEnabled() ) 379 { 380 getLogger().debug( "Transform source = " + source + 381 ", stylesheet = " + stylesheet + 382 ", parameters = " + params + 383 ", result = " + result ); 384 } 385 final TransformerHandler handler = getTransformerHandler( stylesheet ); 386 if( params != null ) 387 { 388 final Transformer transformer = handler.getTransformer(); 389 transformer.clearParameters(); 390 String [] names = params.getNames(); 391 for( int i = names.length - 1; i >= 0; i-- ) 392 { 393 transformer.setParameter( names[ i ], params.getParameter( names[ i ] ) ); 394 } 395 } 396 397 handler.setResult( result ); 398 sourceToSAX( source, handler ); 399 if( getLogger().isDebugEnabled() ) 400 { 401 getLogger().debug( "Transform done" ); 402 } 403 } 404 catch( SAXException e ) 405 { 406 final String message = "Error in running Transformation"; 409 throw new XSLTProcessorException( message, e ); 410 423 } 424 catch( Exception e ) 425 { 426 final String message = "Error in running Transformation"; 427 throw new XSLTProcessorException( message, e ); 428 } 429 } 430 431 436 private SAXTransformerFactory getTransformerFactory( String factoryName ) 437 { 438 SAXTransformerFactory _factory; 439 440 if( null == factoryName ) 441 { 442 _factory = (SAXTransformerFactory )TransformerFactory.newInstance(); 443 } 444 else 445 { 446 try 447 { 448 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 449 if( loader == null ) 450 { 451 loader = getClass().getClassLoader(); 452 } 453 _factory = (SAXTransformerFactory )loader.loadClass( factoryName ).newInstance(); 454 } 455 catch( ClassNotFoundException cnfe ) 456 { 457 getLogger().error( "Cannot find the requested TrAX factory '" + factoryName 458 + "'. Using default TrAX Transformer Factory instead." ); 459 if( m_factory != null ) 460 return m_factory; 461 _factory = (SAXTransformerFactory )TransformerFactory.newInstance(); 462 } 463 catch( ClassCastException cce ) 464 { 465 getLogger().error( "The indicated class '" + factoryName 466 + "' is not a TrAX Transformer Factory. Using default TrAX Transformer Factory instead." ); 467 if( m_factory != null ) 468 return m_factory; 469 _factory = (SAXTransformerFactory )TransformerFactory.newInstance(); 470 } 471 catch( Exception e ) 472 { 473 getLogger().error( "Error found loading the requested TrAX Transformer Factory '" 474 + factoryName + "'. Using default TrAX Transformer Factory instead." ); 475 if( m_factory != null ) 476 return m_factory; 477 _factory = (SAXTransformerFactory )TransformerFactory.newInstance(); 478 } 479 } 480 481 _factory.setErrorListener( m_errorHandler ); 482 _factory.setURIResolver( this ); 483 484 if( _factory.getClass().getName().equals( "org.apache.xalan.processor.TransformerFactoryImpl" ) ) 487 { 488 _factory.setAttribute( "http://xml.apache.org/xalan/features/incremental", 489 new Boolean ( m_incrementalProcessing ) ); 490 } 491 492 return _factory; 493 } 494 495 private TransformerHandlerAndValidity getTemplates( Source stylesheet, String id ) 496 throws IOException , SourceException, TransformerException 497 { 498 if( !m_useStore ) 499 { 500 return null; 501 } 502 503 String key = id + m_factory.getClass().getName(); 507 508 if( getLogger().isDebugEnabled() ) 509 { 510 getLogger().debug( "getTemplates: stylesheet " + id ); 511 } 512 513 SourceValidity newValidity = stylesheet.getValidity(); 514 515 if( newValidity == null ) 517 { 518 m_store.remove( key ); 520 return null; 521 } 522 523 Object [] templateAndValidityAndIncludes = (Object [])m_store.get( key ); 525 if( templateAndValidityAndIncludes == null ) 526 { 527 return null; 529 } 530 531 SourceValidity storedValidity = (SourceValidity)templateAndValidityAndIncludes[ 1 ]; 533 int valid = storedValidity.isValid(); 534 boolean isValid; 535 if( valid == 0 ) 536 { 537 valid = storedValidity.isValid( newValidity ); 538 isValid = ( valid == 1 ); 539 } 540 else 541 { 542 isValid = ( valid == 1 ); 543 } 544 if( !isValid ) 545 { 546 m_store.remove( key ); 547 return null; 548 } 549 550 if ( m_checkIncludes ) 552 { 553 AggregatedValidity aggregated = null; 554 List includes = (List )templateAndValidityAndIncludes[ 2 ]; 555 if( includes != null ) 556 { 557 aggregated = new AggregatedValidity(); 558 aggregated.add( storedValidity ); 559 560 for( int i = includes.size() - 1; i >= 0; i-- ) 561 { 562 Object [] pair = (Object [])includes.get( i ); 564 storedValidity = (SourceValidity)pair[ 1 ]; 565 aggregated.add( storedValidity ); 566 567 valid = storedValidity.isValid(); 568 isValid = false; 569 if( valid == 0 ) 570 { 571 Source includedSource = null; 572 try 573 { 574 includedSource = m_resolver.resolveURI( (String )pair[ 0 ] ); 575 SourceValidity included = includedSource.getValidity(); 576 if( included != null ) 577 { 578 valid = storedValidity.isValid( included ); 579 isValid = ( valid == 1 ); 580 } 581 } 582 finally 583 { 584 m_resolver.release(includedSource); 585 } 586 } 587 else 588 { 589 isValid = ( valid == 1 ); 590 } 591 if( !isValid ) 592 { 593 m_store.remove( key ); 594 return null; 595 } 596 } 597 storedValidity = aggregated; 598 } 599 } 600 601 TransformerHandler handler = m_factory.newTransformerHandler( 602 (Templates )templateAndValidityAndIncludes[ 0 ] ); 603 handler.getTransformer().setErrorListener( m_errorHandler ); 604 handler.getTransformer().setURIResolver( this ); 605 return new TransformerHandlerAndValidity( handler, storedValidity ); 606 } 607 608 private void putTemplates( Templates templates, Source stylesheet, String id ) 609 throws IOException 610 { 611 if( !m_useStore ) 612 return; 613 614 String key = id + m_factory.getClass().getName(); 618 619 SourceValidity validity = stylesheet.getValidity(); 621 if( null != validity ) 622 { 623 Object [] templateAndValidityAndIncludes = new Object [ 3 ]; 625 templateAndValidityAndIncludes[ 0 ] = templates; 626 templateAndValidityAndIncludes[ 1 ] = validity; 627 if ( m_checkIncludes ) 628 { 629 templateAndValidityAndIncludes[ 2 ] = m_includesMap.get( id ); 630 } 631 m_store.store( key, templateAndValidityAndIncludes ); 632 } 633 } 634 635 649 public javax.xml.transform.Source resolve( String href, String base ) 650 throws TransformerException 651 { 652 if( getLogger().isDebugEnabled() ) 653 { 654 getLogger().debug( "resolve(href = " + href + 655 ", base = " + base + "); resolver = " + m_resolver ); 656 } 657 658 Source xslSource = null; 659 try 660 { 661 if( base == null || href.indexOf( ":" ) > 1 ) 662 { 663 xslSource = m_resolver.resolveURI( href ); 665 } 666 else if( href.length() == 0 ) 667 { 668 xslSource = m_resolver.resolveURI( base ); 670 } 671 else 672 { 673 if( !base.startsWith( "file:" ) ) 675 { 676 int lastPathElementPos = base.lastIndexOf( '/' ); 677 if( lastPathElementPos == -1 ) 678 { 679 return null; } 683 else 684 { 685 xslSource = m_resolver.resolveURI( base.substring( 0, lastPathElementPos ) 686 + "/" + href ); 687 } 688 } 689 else 690 { 691 File parent = new File ( base.substring( 5 ) ); 692 File parent2 = new File ( parent.getParentFile(), href ); 693 xslSource = m_resolver.resolveURI( parent2.toURL().toExternalForm() ); 694 } 695 } 696 697 InputSource is = getInputSource( xslSource ); 698 699 if( getLogger().isDebugEnabled() ) 700 { 701 getLogger().debug( "xslSource = " + xslSource + ", system id = " + xslSource.getURI() ); 702 } 703 704 if ( m_checkIncludes ) { 705 List includes = (List )m_includesMap.get( base ); 707 if( includes != null ) 708 { 709 SourceValidity included = xslSource.getValidity(); 710 if( included != null ) 711 { 712 includes.add( new Object []{xslSource.getURI(), xslSource.getValidity()} ); 713 } 714 else 715 { 716 m_includesMap.remove( base ); 718 } 719 } 720 } 721 722 return new StreamSource ( is.getByteStream(), is.getSystemId() ); 723 } 724 catch( SourceException e ) 725 { 726 if( getLogger().isDebugEnabled() ) 727 { 728 getLogger().debug( "Failed to resolve " + href 729 + "(base = " + base + "), return null", e ); 730 } 731 732 return null; 735 } 736 catch( java.net.MalformedURLException mue ) 737 { 738 if( getLogger().isDebugEnabled() ) 739 { 740 getLogger().debug( "Failed to resolve " + href 741 + "(base = " + base + "), return null", mue ); 742 } 743 744 return null; 745 } 746 catch( IOException ioe ) 747 { 748 if( getLogger().isDebugEnabled() ) 749 { 750 getLogger().debug( "Failed to resolve " + href 751 + "(base = " + base + "), return null", ioe ); 752 } 753 754 return null; 755 } 756 finally 757 { 758 m_resolver.release( xslSource ); 759 } 760 } 761 762 769 private static InputSource getInputSource( final Source source ) 770 throws IOException , SourceException 771 { 772 final InputSource newObject = new InputSource ( source.getInputStream() ); 773 newObject.setSystemId( source.getURI() ); 774 return newObject; 775 } 776 777 780 public void recycle() 781 { 782 m_includesMap.clear(); 783 if ( m_factory != m_defaultFactory ) 785 { 786 m_factory = m_defaultFactory; 787 } 788 } 789 790 } 791 | Popular Tags |