| 1 16 package org.apache.cocoon.transformation; 17 18 import org.apache.avalon.excalibur.pool.Recyclable; 19 import org.apache.avalon.framework.activity.Disposable; 20 import org.apache.avalon.framework.configuration.Configurable; 21 import org.apache.avalon.framework.configuration.Configuration; 22 import org.apache.avalon.framework.configuration.ConfigurationException; 23 import org.apache.avalon.framework.parameters.Parameters; 24 import org.apache.avalon.framework.service.ServiceException; 25 import org.apache.avalon.framework.service.ServiceManager; 26 import org.apache.avalon.framework.service.Serviceable; 27 28 import org.apache.cocoon.ProcessingException; 29 import org.apache.cocoon.environment.Context; 30 import org.apache.cocoon.environment.ObjectModelHelper; 31 import org.apache.cocoon.environment.Request; 32 import org.apache.cocoon.environment.Response; 33 import org.apache.cocoon.environment.SourceResolver; 34 import org.apache.cocoon.transformation.helpers.ParametersRecorder; 35 import org.apache.cocoon.transformation.helpers.TextRecorder; 36 import org.apache.cocoon.util.ClassUtils; 37 import org.apache.cocoon.util.TraxErrorHandler; 38 import org.apache.cocoon.xml.AttributesImpl; 39 import org.apache.cocoon.xml.ImmutableAttributesImpl; 40 import org.apache.cocoon.xml.IncludeXMLConsumer; 41 import org.apache.cocoon.xml.SaxBuffer; 42 import org.apache.cocoon.xml.XMLConsumer; 43 import org.apache.cocoon.xml.XMLUtils; 44 import org.apache.cocoon.xml.dom.DOMBuilder; 45 46 import org.apache.excalibur.source.SourceParameters; 47 import org.apache.excalibur.xml.sax.XMLizable; 48 import org.w3c.dom.Document ; 49 import org.w3c.dom.DocumentFragment ; 50 import org.w3c.dom.Node ; 51 import org.xml.sax.Attributes ; 52 import org.xml.sax.ContentHandler ; 53 import org.xml.sax.Locator ; 54 import org.xml.sax.SAXException ; 55 import org.xml.sax.ext.LexicalHandler ; 56 57 import javax.xml.transform.TransformerFactory ; 58 import javax.xml.transform.sax.SAXTransformerFactory ; 59 import java.io.IOException ; 60 import java.util.ArrayList ; 61 import java.util.Iterator ; 62 import java.util.List ; 63 import java.util.Map ; 64 import java.util.Properties ; 65 import java.util.Stack ; 66 67 109 public abstract class AbstractSAXTransformer extends AbstractTransformer 110 implements Serviceable, Configurable, Recyclable, Disposable { 111 112 116 protected static final Attributes EMPTY_ATTRIBUTES = XMLUtils.EMPTY_ATTRIBUTES; 117 118 121 private SAXTransformerFactory tfactory; 122 123 127 protected boolean ignoreWhitespaces; 128 129 134 protected boolean ignoreEmptyCharacters; 135 136 141 protected int ignoreEventsCount; 142 143 149 protected int ignoreHooksCount; 150 151 157 protected String namespaceURI; 158 159 164 protected String defaultNamespaceURI; 165 166 171 protected final Stack stack = new Stack (); 172 173 176 protected final Stack recorderStack = new Stack (); 177 178 181 protected Request request; 182 183 186 protected Response response; 187 188 191 protected Context context; 192 193 196 protected Map objectModel; 197 198 201 protected Parameters parameters; 202 203 206 protected String source; 207 208 211 protected ServiceManager manager; 212 213 216 protected SourceResolver resolver; 217 218 221 private boolean isInitialized; 222 223 229 protected Attributes emptyAttributes = EMPTY_ATTRIBUTES; 230 231 234 private final List namespaces = new ArrayList (5); 235 236 239 private String ourPrefix; 240 241 245 248 public void service(ServiceManager manager) throws ServiceException { 249 this.manager = manager; 250 } 251 252 255 public void configure(Configuration configuration) throws ConfigurationException { 256 String tFactoryClass = configuration.getChild("transformer-factory").getValue(null); 257 if (tFactoryClass != null) { 258 try { 259 this.tfactory = (SAXTransformerFactory ) ClassUtils.newInstance(tFactoryClass); 260 if (getLogger().isDebugEnabled()) { 261 getLogger().debug("Using transformer factory " + tFactoryClass); 262 } 263 } catch (Exception e) { 264 throw new ConfigurationException("Cannot load transformer factory " + tFactoryClass, e); 265 } 266 } else { 267 this.tfactory = (SAXTransformerFactory ) TransformerFactory.newInstance(); 269 } 270 tfactory.setErrorListener(new TraxErrorHandler(getLogger())); 271 } 272 273 276 public void setup(SourceResolver resolver, 277 Map objectModel, 278 String src, 279 Parameters params) 280 throws ProcessingException, SAXException , IOException { 281 282 if (getLogger().isDebugEnabled()) { 283 getLogger().debug("Setup resolver=" + resolver + 284 ", objectModel=" + objectModel + 285 ", SRC=" + src + 286 ", parameters=" + params); 287 } 288 289 if (this.defaultNamespaceURI == null) { 291 this.defaultNamespaceURI = ""; 292 } 293 this.objectModel = objectModel; 294 295 this.request = ObjectModelHelper.getRequest(objectModel); 296 this.response = ObjectModelHelper.getResponse(objectModel); 297 this.context = ObjectModelHelper.getContext(objectModel); 298 this.resolver = resolver; 299 this.parameters = params; 300 this.source = src; 301 this.isInitialized = false; 302 303 this.namespaceURI = params.getParameter("namespaceURI", 305 this.defaultNamespaceURI); 306 307 this.ignoreHooksCount = 0; 308 this.ignoreEventsCount = 0; 309 this.ignoreWhitespaces = true; 310 this.ignoreEmptyCharacters = false; 311 } 312 313 316 public void recycle() { 317 this.namespaceURI = null; 318 this.objectModel = null; 319 this.request = null; 320 this.response = null; 321 this.context = null; 322 this.resolver = null; 323 this.stack.clear(); 324 this.recorderStack.clear(); 325 this.parameters = null; 326 this.source = null; 327 this.namespaces.clear(); 328 this.ourPrefix = null; 329 330 super.recycle(); 331 } 332 333 public void dispose() { 334 this.manager = null; 335 } 336 337 341 345 public void setDocumentLocator(Locator locator) { 346 if (this.ignoreEventsCount == 0) { 347 super.setDocumentLocator(locator); 348 } 349 } 350 351 356 public void startDocument() 357 throws SAXException { 358 if (!this.isInitialized) { 359 try { 360 setupTransforming(); 361 } catch (ProcessingException e) { 362 throw new SAXException ("ProcessingException: " + e, e); 363 } catch (IOException e) { 364 throw new SAXException ("IOException: " + e, e); 365 } 366 this.isInitialized = true; 367 } 368 369 if (this.ignoreEventsCount == 0) { 370 super.startDocument(); 371 } 372 } 373 374 378 public void endDocument() 379 throws SAXException { 380 if (this.ignoreEventsCount == 0) { 381 super.endDocument(); 382 } 383 } 384 385 389 public void startPrefixMapping(String prefix, String uri) 390 throws SAXException { 391 if (prefix != null) { 392 this.namespaces.add(new String [] {prefix, uri}); 393 } 394 if (namespaceURI.equals(uri)) { 395 this.ourPrefix = prefix; 396 } 397 if (this.ignoreEventsCount == 0) { 398 super.startPrefixMapping(prefix, uri); 399 } 400 } 401 402 406 public void endPrefixMapping(String prefix) 407 throws SAXException { 408 409 if (prefix != null) { 410 boolean found = false; 412 for (int i = this.namespaces.size() - 1; i >= 0; i--) { 413 final String [] prefixAndUri = (String []) this.namespaces.get(i); 414 if (prefixAndUri[0].equals(prefix)) { 415 this.namespaces.remove(i); 416 found = true; 417 break; 418 } 419 } 420 if (!found) { 421 throw new SAXException ("Namespace for prefix '" + prefix + "' not found."); 422 } 423 424 if (prefix.equals(this.ourPrefix)) { 425 this.ourPrefix = null; 427 428 for (int i = this.namespaces.size() - 1; i >= 0; i--) { 430 final String [] prefixAndUri = (String []) this.namespaces.get(i); 431 if (namespaceURI.equals(prefixAndUri[1])) { 432 this.ourPrefix = prefixAndUri[0]; 433 break; 434 } 435 } 436 } 437 } 438 439 if (this.ignoreEventsCount == 0) { 440 super.endPrefixMapping(prefix); 441 } 442 } 443 444 450 public void startElement(String uri, 451 String name, 452 String raw, 453 Attributes attr) 454 throws SAXException { 455 if (namespaceURI.equals(uri) && ignoreHooksCount == 0) { 456 try { 458 startTransformingElement(uri, name, raw, attr); 459 } catch (ProcessingException e) { 460 throw new SAXException ("ProcessingException: " + e, e); 461 } catch (IOException e) { 462 throw new SAXException ("IOException occured during processing: " + e, e); 463 } 464 } else { 465 if (ignoreEventsCount == 0) { 466 super.startElement(uri, name, raw, attr); 467 } 468 } 469 } 470 471 477 public void endElement(String uri, String name, String raw) 478 throws SAXException { 479 if (namespaceURI.equals(uri) && this.ignoreHooksCount == 0) { 480 try { 482 endTransformingElement(uri, name, raw); 483 } catch (ProcessingException e) { 484 throw new SAXException ("ProcessingException: " + e, e); 485 } catch (IOException e) { 486 throw new SAXException ("IOException occured during processing: " + e, e); 487 } 488 } else { 489 if (ignoreEventsCount == 0) { 490 super.endElement(uri, name, raw); 491 } 492 } 493 } 494 495 499 public void characters(char[] p0, int p1, int p2) 500 throws SAXException { 501 if (this.ignoreEventsCount == 0) { 502 if (this.ignoreEmptyCharacters) { 503 String value = new String (p0, p1, p2); 504 if (value.trim().length() > 0) { 505 super.characters(p0, p1, p2); 506 } 507 } else { 508 super.characters(p0, p1, p2); 509 } 510 } 511 } 512 513 517 public void ignorableWhitespace(char[] p0, int p1, int p2) 518 throws SAXException { 519 if (ignoreWhitespaces == false && ignoreEventsCount == 0) { 520 super.ignorableWhitespace(p0, p1, p2); 521 } 522 } 523 524 528 public void processingInstruction(String target, String data) 529 throws SAXException { 530 if (this.ignoreEventsCount == 0) { 531 super.processingInstruction(target, data); 532 } 533 } 534 535 539 public void skippedEntity(String name) 540 throws SAXException { 541 if (this.ignoreEventsCount == 0) { 542 super.skippedEntity(name); 543 } 544 } 545 546 550 553 public void startDTD(String name, String public_id, String system_id) 554 throws SAXException { 555 if (this.ignoreEventsCount == 0) { 556 super.startDTD(name, public_id, system_id); 557 } 558 } 559 560 563 public void endDTD() throws SAXException { 564 if (this.ignoreEventsCount == 0) { 565 super.endDTD(); 566 } 567 } 568 569 572 public void startEntity (String name) 573 throws SAXException { 574 if (this.ignoreEventsCount == 0) { 575 super.startEntity(name); 576 } 577 } 578 579 582 public void endEntity (String name) 583 throws SAXException { 584 if (this.ignoreEventsCount == 0) { 585 super.endEntity(name); 586 } 587 } 588 589 592 public void startCDATA() throws SAXException { 593 if (this.ignoreEventsCount == 0) { 594 super.startCDATA(); 595 } 596 } 597 598 601 public void endCDATA() throws SAXException { 602 if (this.ignoreEventsCount == 0) { 603 super.endCDATA(); 604 } 605 } 606 607 610 public void comment(char ary[], int start, int length) 611 throws SAXException { 612 if (this.ignoreEventsCount == 0) { 613 super.comment(ary, start, length); 614 } 615 } 616 617 618 623 624 private LexicalHandler originalLexicalHandler; 625 private ContentHandler originalContentHandler; 626 627 631 protected void addRecorder(XMLConsumer recorder) { 632 if (this.recorderStack.empty()) { 633 this.originalLexicalHandler = this.lexicalHandler; 635 this.originalContentHandler = this.contentHandler; 636 } 637 setContentHandler(recorder); 638 setLexicalHandler(recorder); 639 this.recorderStack.push(recorder); 640 } 641 642 646 protected Object removeRecorder() { 647 Object recorder = this.recorderStack.pop(); 648 if (this.recorderStack.empty() == true) { 649 setContentHandler(originalContentHandler); 651 setLexicalHandler(originalLexicalHandler); 652 this.originalLexicalHandler = null; 653 this.originalContentHandler = null; 654 } else { 655 XMLConsumer next = (XMLConsumer) recorderStack.peek(); 656 setContentHandler(next); 657 setLexicalHandler(next); 658 } 659 660 return recorder; 661 } 662 663 669 public void startSAXRecording() 670 throws SAXException { 671 addRecorder(new SaxBuffer()); 672 sendStartPrefixMapping(); 673 } 674 675 680 public XMLizable endSAXRecording() 681 throws SAXException { 682 sendEndPrefixMapping(); 683 return (XMLizable) removeRecorder(); 684 } 685 686 691 public void startTextRecording() 692 throws SAXException { 693 if (getLogger().isDebugEnabled()) { 694 getLogger().debug("Start text recording"); 695 } 696 addRecorder(new TextRecorder()); 697 sendStartPrefixMapping(); 698 } 699 700 704 public String endTextRecording() 705 throws SAXException { 706 sendEndPrefixMapping(); 707 708 TextRecorder recorder = (TextRecorder) removeRecorder(); 709 String text = recorder.getText(); 710 if (getLogger().isDebugEnabled()) { 711 getLogger().debug("End text recording. Text=" + text); 712 } 713 return text; 714 } 715 716 723 public void startSerializedXMLRecording(Properties format) 724 throws SAXException { 725 if (getLogger().isDebugEnabled()) { 726 getLogger().debug("Start serialized XML recording. Format=" + format); 727 } 728 this.stack.push(format == null? XMLUtils.createPropertiesForXML(false): format); 729 startSAXRecording(); 730 } 731 732 737 public String endSerializedXMLRecording() 738 throws SAXException , ProcessingException { 739 XMLizable xml = endSAXRecording(); 740 String text = XMLUtils.serialize(xml, (Properties ) this.stack.pop()); 741 if (getLogger().isDebugEnabled()) { 742 getLogger().debug("End serialized XML recording. XML=" + text); 743 } 744 return text; 745 } 746 747 754 public void startParametersRecording() 755 throws SAXException { 756 if (getLogger().isDebugEnabled()) { 757 getLogger().debug("Start parameters recording"); 758 } 759 addRecorder(new ParametersRecorder()); 760 sendStartPrefixMapping(); 761 } 762 763 770 public SourceParameters endParametersRecording(Parameters source) 771 throws SAXException { 772 sendEndPrefixMapping(); 773 774 ParametersRecorder recorder = (ParametersRecorder) this.removeRecorder(); 775 SourceParameters parameters = recorder.getParameters(source); 776 if (getLogger().isDebugEnabled()) { 777 getLogger().debug("End parameters recording. Parameters=" + parameters); 778 } 779 return parameters; 780 } 781 782 789 public SourceParameters endParametersRecording(SourceParameters source) 790 throws SAXException { 791 sendEndPrefixMapping(); 792 793 ParametersRecorder recorder = (ParametersRecorder) removeRecorder(); 794 SourceParameters parameters = recorder.getParameters(source); 795 if (getLogger().isDebugEnabled()) { 796 getLogger().debug("End parameters recording. Parameters=" + parameters); 797 } 798 return parameters; 799 } 800 801 806 public void startRecording() 807 throws SAXException { 808 if (getLogger().isDebugEnabled()) { 809 getLogger().debug("Start recording"); 810 } 811 DOMBuilder builder = new DOMBuilder(this.tfactory); 812 addRecorder(builder); 813 builder.startDocument(); 814 builder.startElement("", "cocoon", "cocoon", EMPTY_ATTRIBUTES); 815 sendStartPrefixMapping(); 816 } 817 818 822 public DocumentFragment endRecording() 823 throws SAXException { 824 sendEndPrefixMapping(); 825 826 DOMBuilder builder = (DOMBuilder) removeRecorder(); 827 builder.endElement("", "cocoon", "cocoon"); 828 builder.endDocument(); 829 830 |