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 final Document doc = builder.getDocument(); 832 final DocumentFragment fragment = doc.createDocumentFragment(); 833 final Node root = doc.getDocumentElement(); 834 835 root.normalize(); 837 838 boolean space = true; 840 while (root.hasChildNodes()) { 841 Node child = root.getFirstChild(); 842 root.removeChild(child); 843 844 if (space && child.getNodeType() == Node.TEXT_NODE 847 && child.getNodeValue().trim().length() == 0) { 848 continue; 849 } 850 space = false; 851 852 fragment.appendChild(child); 853 } 854 855 if (getLogger().isDebugEnabled()) { 856 Object serializedXML = null; 857 try { 858 serializedXML = fragment == null? "null": XMLUtils.serializeNode(fragment, XMLUtils.createPropertiesForXML(false)); 859 } catch (ProcessingException ignore) { 860 serializedXML = fragment; 861 } 862 getLogger().debug("End recording. Fragment=" + serializedXML); 863 } 864 865 return fragment; 866 } 867 868 872 878 public void setupTransforming() 879 throws IOException , ProcessingException, SAXException { 880 if (getLogger().isDebugEnabled()) { 881 getLogger().debug("setupTransforming"); 882 } 883 this.stack.clear(); 884 this.recorderStack.clear(); 885 this.ignoreWhitespaces = true; 886 this.ignoreEmptyCharacters = false; 887 } 888 889 897 public void startTransformingElement(String uri, 898 String name, 899 String raw, 900 Attributes attr) 901 throws ProcessingException, IOException , SAXException { 902 if (this.ignoreEventsCount == 0) { 903 super.startElement(uri, name, raw, attr); 904 } 905 } 906 907 914 public void endTransformingElement(String uri, 915 String name, 916 String raw) 917 throws ProcessingException, IOException , SAXException { 918 if (this.ignoreEventsCount == 0) { 919 super.endElement(uri, name, raw); 920 } 921 } 922 923 929 public void sendTextEvent(String text) 930 throws SAXException { 931 characters(text.toCharArray(), 0, text.length()); 932 } 933 934 941 public void sendStartElementEvent(String localname) 942 throws SAXException { 943 startElement("", localname, localname, EMPTY_ATTRIBUTES); 944 } 945 946 954 public void sendStartElementEventNS(String localname) 955 throws SAXException { 956 startElement(this.namespaceURI, 957 localname, this.ourPrefix + ':' + localname, EMPTY_ATTRIBUTES); 958 } 959 960 968 public void sendStartElementEvent(String localname, Attributes attr) 969 throws SAXException { 970 startElement("", localname, localname, attr); 971 } 972 973 981 public void sendStartElementEventNS(String localname, Attributes attr) 982 throws SAXException { 983 startElement(this.namespaceURI, 984 localname, this.ourPrefix + ':' + localname, attr); 985 } 986 987 994 public void sendEndElementEvent(String localname) 995 throws SAXException { 996 endElement("", localname, localname); 997 } 998 999 1006 public void sendEndElementEventNS(String localname) 1007 throws SAXException { 1008 endElement(this.namespaceURI, 1009 localname, this.ourPrefix + ':' + localname); 1010 } 1011 1012 1018 public void sendEvents(Node node) 1019 throws SAXException { 1020 IncludeXMLConsumer.includeNode(node, this, this); 1021 } 1022 1023 1029 public void sendParametersEvents(SourceParameters pars) 1030 throws SAXException { 1031 1032 if (pars != null) { 1033 Iterator names = pars.getParameterNames(); 1034 while (names.hasNext()) { 1035 final String currentName = (String )names.next(); 1036 Iterator values = pars.getParameterValues(currentName); 1037 while (values.hasNext()) { 1038 final String currentValue = (String )values.next(); 1039 sendStartElementEvent(currentName); 1040 sendTextEvent(currentValue); 1041 sendEndElementEvent(currentName); 1042 } 1043 } 1044 } 1045 } 1046 1047 1050 protected void sendStartPrefixMapping() 1051 throws SAXException { 1052 final int l = this.namespaces.size(); 1053 for (int i = 0; i < l; i++) { 1054 String [] prefixAndUri = (String []) this.namespaces.get(i); 1055 super.contentHandler.startPrefixMapping(prefixAndUri[0], prefixAndUri[1]); 1056 } 1057 } 1058 1059 1062 protected void sendEndPrefixMapping() 1063 throws SAXException { 1064 final int l = this.namespaces.size(); 1065 for (int i = 0; i < l; i++) { 1066 String [] prefixAndUri = (String []) this.namespaces.get(i); 1067 super.contentHandler.endPrefixMapping(prefixAndUri[0]); 1068 } 1069 } 1070 1071 1075 protected String findPrefixMapping(String uri) { 1076 final int l = this.namespaces.size(); 1077 for (int i = 0; i < l; i++) { 1078 String [] prefixAndUri = (String []) this.namespaces.get(i); 1079 if (prefixAndUri[1].equals(uri)) { 1080 return prefixAndUri[0]; 1081 } 1082 } 1083 1084 return null; 1085 } 1086 1087 1090 protected AttributesImpl getMutableAttributes(Attributes a) { 1091 if ( a instanceof AttributesImpl && !(a instanceof ImmutableAttributesImpl)) { 1092 return (AttributesImpl)a; 1093 } 1094 return new AttributesImpl(a); 1095 } 1096} 1097 | Popular Tags |