1 16 package org.apache.cocoon.transformation; 17 18 import java.io.IOException ; 19 import java.io.OutputStream ; 20 import java.util.Map ; 21 22 import org.apache.avalon.framework.activity.Disposable; 23 import org.apache.avalon.framework.configuration.Configuration; 24 import org.apache.avalon.framework.configuration.ConfigurationException; 25 import org.apache.avalon.framework.parameters.Parameters; 26 import org.apache.avalon.framework.service.ServiceException; 27 import org.apache.avalon.framework.service.ServiceManager; 28 import org.apache.avalon.framework.service.ServiceSelector; 29 import org.apache.cocoon.ProcessingException; 30 import org.apache.cocoon.components.source.SourceUtil; 31 import org.apache.cocoon.environment.SourceResolver; 32 import org.apache.cocoon.serialization.Serializer; 33 import org.apache.cocoon.xml.XMLUtils; 34 import org.apache.cocoon.xml.dom.DOMStreamer; 35 import org.apache.cocoon.xml.dom.DOMUtil; 36 import org.apache.excalibur.source.ModifiableSource; 37 import org.apache.excalibur.source.Source; 38 import org.apache.excalibur.source.SourceException; 39 import org.apache.excalibur.xml.dom.DOMParser; 40 import org.apache.excalibur.xml.xpath.XPathProcessor; 41 import org.w3c.dom.DOMException ; 42 import org.w3c.dom.Document ; 43 import org.w3c.dom.DocumentFragment ; 44 import org.w3c.dom.Node ; 45 import org.w3c.dom.NodeList ; 46 import org.xml.sax.Attributes ; 47 import org.xml.sax.SAXException ; 48 49 235 public class SourceWritingTransformer extends AbstractSAXTransformer 236 implements Disposable { 237 238 public static final String SWT_URI = "http://apache.org/cocoon/source/1.0"; 239 public static final String DEFAULT_SERIALIZER = "xml"; 240 241 242 public static final String WRITE_ELEMENT = "write"; 243 public static final String INSERT_ELEMENT = "insert"; 244 public static final String PATH_ELEMENT = "path"; 245 public static final String FRAGMENT_ELEMENT = "fragment"; 246 public static final String REPLACE_ELEMENT = "replace"; 247 public static final String DELETE_ELEMENT = "delete"; 248 public static final String SOURCE_ELEMENT = "source"; 249 public static final String REINSERT_ELEMENT = "reinsert"; 250 251 public static final String RESULT_ELEMENT = "sourceResult"; 252 public static final String EXECUTION_ELEMENT = "execution"; 253 public static final String BEHAVIOUR_ELEMENT = "behaviour"; 254 public static final String ACTION_ELEMENT = "action"; 255 public static final String MESSAGE_ELEMENT = "message"; 256 public static final String SERIALIZER_ELEMENT = "serializer"; 257 258 public static final String SERIALIZER_ATTRIBUTE = "serializer"; 259 public static final String CREATE_ATTRIBUTE = "create"; 260 public static final String OVERWRITE_ATTRIBUTE = "overwrite"; 261 262 public static final String RESULT_FAILED = "failed"; 263 public static final String RESULT_SUCCESS = "success"; 264 public static final String ACTION_NONE = "none"; 265 public static final String ACTION_NEW = "new"; 266 public static final String ACTION_OVER = "overwritten"; 267 public static final String ACTION_DELETE = "deleted"; 268 269 private static final int STATE_OUTSIDE = 0; 270 private static final int STATE_INSERT = 1; 271 private static final int STATE_PATH = 3; 272 private static final int STATE_FRAGMENT = 4; 273 private static final int STATE_REPLACE = 5; 274 private static final int STATE_FILE = 6; 275 private static final int STATE_REINSERT = 7; 276 private static final int STATE_WRITE = 8; 277 private static final int STATE_DELETE = 9; 278 private int state; 279 private int parent_state; 280 281 282 protected String configuredSerializerName; 283 284 285 protected XPathProcessor xpathProcessor; 286 287 290 public SourceWritingTransformer() { 291 this.defaultNamespaceURI = SWT_URI; 292 } 293 294 298 public void configure(Configuration configuration) 299 throws ConfigurationException { 300 super.configure(configuration); 301 this.configuredSerializerName = configuration.getChild(SERIALIZER_ATTRIBUTE).getValue(DEFAULT_SERIALIZER); 302 } 303 304 308 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) 309 throws ProcessingException, SAXException , IOException { 310 super.setup(resolver, objectModel, src, par); 311 312 this.configuredSerializerName = par.getParameter(SERIALIZER_ATTRIBUTE, 313 this.configuredSerializerName); 314 this.state = STATE_OUTSIDE; 315 } 316 317 330 public void startTransformingElement(String uri, String name, String raw, Attributes attr) 331 throws SAXException , IOException , ProcessingException { 332 if (getLogger().isDebugEnabled()) { 333 getLogger().debug("Start transforming element. uri=" + uri + 334 ", name=" + name + ", raw=" + raw + ", attr=" + attr); 335 } 336 337 if (this.state == STATE_OUTSIDE 339 && (name.equals(INSERT_ELEMENT) || name.equals(WRITE_ELEMENT))) { 340 341 this.state = (name.equals(INSERT_ELEMENT) ? STATE_INSERT : STATE_WRITE); 342 this.parent_state = this.state; 343 if (attr.getValue(CREATE_ATTRIBUTE) != null 344 && attr.getValue(CREATE_ATTRIBUTE).equals("false")) { 345 this.stack.push("false"); 346 } else { 347 this.stack.push("true"); } 349 if (attr.getValue(OVERWRITE_ATTRIBUTE) != null 350 && attr.getValue(OVERWRITE_ATTRIBUTE).equals("false")) { 351 this.stack.push("false"); 352 } else { 353 this.stack.push("true"); } 355 this.stack.push(attr.getValue(SERIALIZER_ATTRIBUTE)); 356 this.stack.push("END"); 357 358 } else if (this.state == STATE_OUTSIDE && name.equals(DELETE_ELEMENT)) { 360 this.state = STATE_DELETE; 361 this.parent_state = state; 362 this.stack.push("END"); 363 } else if (name.equals(SOURCE_ELEMENT) 365 && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) { 366 this.state = STATE_FILE; 367 this.startTextRecording(); 368 369 } else if (name.equals(PATH_ELEMENT) 371 && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) { 372 this.state = STATE_PATH; 373 this.startTextRecording(); 374 375 } else if (name.equals(REPLACE_ELEMENT) 377 && this.state == STATE_INSERT) { 378 this.state = STATE_REPLACE; 379 this.startTextRecording(); 380 381 } else if (name.equals(FRAGMENT_ELEMENT) 383 && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) { 384 this.state = STATE_FRAGMENT; 385 this.startRecording(); 386 387 } else if (name.equals(REINSERT_ELEMENT) 389 && this.state == STATE_INSERT) { 390 this.state = STATE_REINSERT; 391 this.startTextRecording(); 392 393 } else { 394 super.startTransformingElement(uri, name, raw, attr); 395 } 396 } 397 398 399 410 public void endTransformingElement(String uri, String name, String raw) 411 throws SAXException , IOException , ProcessingException { 412 if (getLogger().isDebugEnabled()) { 413 getLogger().debug("End transforming element. uri=" + uri + 414 ", name=" + name + ", raw=" + raw); 415 } 416 417 if ((name.equals(INSERT_ELEMENT) && this.state == STATE_INSERT) 418 || (name.equals(WRITE_ELEMENT) && this.state == STATE_WRITE)) { 419 420 DocumentFragment fragment = null; 422 String tag; 423 String sourceName = null; 424 String path = (this.state == STATE_INSERT ? null : "/"); 425 String replacePath = null; 427 String reinsert = null; 428 do { 429 tag = (String )this.stack.pop(); 430 if (tag.equals("PATH")) { 431 path = (String )this.stack.pop(); 432 } else if (tag.equals("FILE")) { 433 sourceName = (String )this.stack.pop(); 434 } else if (tag.equals("FRAGMENT")) { 435 fragment = (DocumentFragment )this.stack.pop(); 436 } else if (tag.equals("REPLACE")) { 437 replacePath = (String )this.stack.pop(); 438 } else if (tag.equals("REINSERT")) { 439 reinsert = (String )this.stack.pop(); 440 } 441 } while ( !tag.equals("END") ); 442 443 final String localSerializer = (String )this.stack.pop(); 444 final boolean overwrite = this.stack.pop().equals("true"); 445 final boolean create = this.stack.pop().equals("true"); 446 447 this.insertFragment(sourceName, 448 path, 449 fragment, 450 replacePath, 451 create, 452 overwrite, 453 reinsert, 454 localSerializer, 455 name); 456 457 this.state = STATE_OUTSIDE; 458 459 } else if (name.equals(DELETE_ELEMENT) && this.state == STATE_DELETE) { 461 String sourceName = null; 462 String tag; 463 do { 464 tag = (String )this.stack.pop(); 465 if (tag.equals("FILE")) { 466 sourceName = (String )this.stack.pop(); 467 } else if (tag.equals("FRAGMENT")) { 468 this.stack.pop(); 470 } 471 } while ( !tag.equals("END")); 472 473 this.deleteSource(sourceName); 474 this.state = STATE_OUTSIDE; 475 } else if (name.equals(SOURCE_ELEMENT) && this.state == STATE_FILE) { 477 this.state = this.parent_state; 478 this.stack.push(this.endTextRecording()); 479 this.stack.push("FILE"); 480 481 } else if (name.equals(PATH_ELEMENT) && this.state == STATE_PATH) { 483 this.state = this.parent_state; 484 this.stack.push(this.endTextRecording()); 485 this.stack.push("PATH"); 486 487 } else if (name.equals(REPLACE_ELEMENT) && this.state == STATE_REPLACE) { 489 this.state = this.parent_state; 490 this.stack.push(this.endTextRecording()); 491 this.stack.push("REPLACE"); 492 493 } else if (name.equals(FRAGMENT_ELEMENT) && this.state == STATE_FRAGMENT) { 495 this.state = this.parent_state; 496 this.stack.push(this.endRecording()); 497 this.stack.push("FRAGMENT"); 498 499 } else if (name.equals(REINSERT_ELEMENT) && this.state == STATE_REINSERT) { 501 this.state = this.parent_state; 502 this.stack.push(this.endTextRecording()); 503 this.stack.push("REINSERT"); 504 505 } else { 507 super.endTransformingElement(uri, name, raw); 508 } 509 } 510 511 515 private void deleteSource(String systemID) throws ProcessingException, IOException , SAXException { 516 Source source = null; 517 try { 518 source = resolver.resolveURI(systemID); 519 if (!(source instanceof ModifiableSource)) { 520 throw new ProcessingException("Source '" + systemID + "' is not writeable."); 521 } 522 523 ((ModifiableSource)source).delete(); 524 reportResult("none", 525 "delete", 526 "source deleted successfully", 527 systemID, 528 RESULT_SUCCESS, 529 ACTION_DELETE); 530 } catch (SourceException se) { 531 if (getLogger().isDebugEnabled()) { 532 getLogger().debug("FAIL exception: " + se, se); 533 } 534 reportResult("none", 535 "delete", 536 "unable to delete source: " + se.getMessage(), 537 systemID, 538 RESULT_FAILED, 539 ACTION_DELETE); 540 } finally { 541 resolver.release(source); 542 } 543 } 544 545 567 protected void insertFragment(String systemID, 568 String path, 569 DocumentFragment fragment, 570 String replacePath, 571 boolean create, 572 boolean overwrite, 573 String reinsertPath, 574 String localSerializer, 575 String tagname) 576 throws SAXException , IOException , ProcessingException { 577 if (getLogger().isDebugEnabled()) { 579 getLogger().debug("Insert fragment. systemID=" + systemID + 580 ", path=" + path + 581 ", replace=" + replacePath + 582 ", create=" + create + 583 ", overwrite=" + overwrite + 584 ", reinsert=" + reinsertPath + 585 ", fragment=" + (fragment == null ? "null" : XMLUtils.serializeNode(fragment))); 586 } 587 588 if (systemID == null) { 590 throw new ProcessingException("insertFragment: systemID is required."); 591 } 592 if (path == null) { 593 throw new ProcessingException("insertFragment: path is required."); 594 } 595 if (path.startsWith("/")) { 596 path = path.substring(1); 597 } 598 if (fragment == null) { 599 throw new ProcessingException("insertFragment: fragment is required."); 600 } 601 602 Source source = null; 604 Document resource = null; 605 boolean failed = true; 606 boolean exists = false; 607 String message = ""; 608 String target = systemID; 609 try { 610 source = this.resolver.resolveURI(systemID); 611 if (! (source instanceof ModifiableSource)) { 612 throw new ProcessingException("Source '" + systemID + "' is not writeable."); 613 } 614 ModifiableSource ws = (ModifiableSource) source; 615 exists = ws.exists(); 616 target = source.getURI(); 617 618 if (exists && this.state == STATE_INSERT) { 620 message = "content inserted at: " + path; 621 resource = SourceUtil.toDOM(source); 622 Node importNode = resource.importNode(fragment, true); 624 Node parent = DOMUtil.selectSingleNode(resource, path, this.xpathProcessor); 626 627 if (replacePath != null) { 629 try { 630 Node replaceNode = DOMUtil.getSingleNode(parent, replacePath, this.xpathProcessor); 631 while (replaceNode != null && !replaceNode.getParentNode().equals(parent)) { 633 replaceNode = replaceNode.getParentNode(); 634 } 635 636 if (replaceNode != null) { 637 if (overwrite) { 638 if (parent.getNodeType() == Node.DOCUMENT_NODE) { 639 resource = newDocument(); 641 resource.appendChild(resource.importNode(importNode, true)); 642 parent = resource; 643 replaceNode = resource.importNode(replaceNode, true); 644 } else { 645 parent.replaceChild(importNode, replaceNode); 646 } 647 message += ", replacing: " + replacePath; 648 if (reinsertPath != null) { 649 Node insertAt = DOMUtil.getSingleNode(parent, reinsertPath, this.xpathProcessor); 650 if (insertAt != null) { 651 while (replaceNode.hasChildNodes()) { 652 insertAt.appendChild(replaceNode.getFirstChild()); 653 } 654 } else { message = "replace failed, could not find your reinsert path: " + reinsertPath; 656 resource = null; 657 } 658 } 659 } else { message = "replace failed, no overwrite allowed."; 661 resource = null; 662 } 663 } else { parent.appendChild(importNode); 665 } 666 } catch (javax.xml.transform.TransformerException sax) { 667 throw new ProcessingException("TransformerException: " + sax, sax); 668 } 669 } else { parent.appendChild(importNode); 671 } 672 673 } else if (create) { 675 resource = newDocument(); 677 678 Node importNode = resource.importNode(fragment, true); 680 681 if (path.equals("")) { 682 NodeList nodes = importNode.getChildNodes(); 684 for (int i = 0; i < nodes.getLength();) { 685 Node node = nodes.item(i); 686 switch (node.getNodeType()) { 687 case Node.ELEMENT_NODE: 688 resource.appendChild(node); 690 break; 691 692 case Node.DOCUMENT_TYPE_NODE: 693 case Node.PROCESSING_INSTRUCTION_NODE: 694 case Node.COMMENT_NODE: 695 resource.appendChild(node); 696 break; 697 698 default: 699 i++; 701 break; 702 } 703 } 704 message = "entire source overwritten"; 705 706 } else { 707 Node parent = DOMUtil.selectSingleNode(resource, path, this.xpathProcessor); 709 parent.appendChild(importNode); 711 message = "content appended to: " + path; 712 } 713 714 } else { 716 message = "create not allowed"; 717 resource = null; 718 } 719 720 721 if (resource != null) { 723 resource.normalize(); 724 if (localSerializer == null) { 726 localSerializer = this.configuredSerializerName; 727 } 728 729 if (localSerializer != null) { 730 ServiceSelector selector = null; 732 Serializer serializer = null; 733 OutputStream oStream = null; 734 try { 735 selector = (ServiceSelector)manager.lookup(Serializer.ROLE + "Selector"); 736 serializer = (Serializer)selector.select(localSerializer); 737 oStream = ws.getOutputStream(); 738 serializer.setOutputStream(oStream); 739 DOMStreamer streamer = new DOMStreamer(serializer); 740 streamer.stream(resource); 741 } finally { 742 if (oStream != null) { 743 oStream.flush(); 744 try { 745 oStream.close(); 746 failed = false; 747 } catch (Throwable t) { 748 if (getLogger().isDebugEnabled()) { 749 getLogger().debug("FAIL (oStream.close) exception"+t, t); 750 } 751 throw new ProcessingException("Could not process your document.", t); 752 } finally { 753 if (selector != null) { 754 selector.release(serializer); 755 this.manager.release(selector); 756 } 757 } 758 } 759 } 760 } else { 761 if (getLogger().isDebugEnabled()) { 762 getLogger().debug("ERROR: No serializer"); 763 } 764 message = "That source requires a serializer, please add the appropirate tag to your code."; 766 } 767 } 768 } catch (DOMException de) { 769 if (getLogger().isDebugEnabled()) { 770 getLogger().debug("FAIL exception: "+de, de); 771 } 772 message = "There was a problem manipulating your document: " + de; 773 } catch (ServiceException ce) { 774 if (getLogger().isDebugEnabled()) { 775 getLogger().debug("FAIL exception: "+ce, ce); 776 } 777 message = "There was a problem looking up a component: " + ce; 778 } catch (SourceException se) { 779 if (getLogger().isDebugEnabled()) { 780 getLogger().debug("FAIL exception: "+se, se); 781 } 782 message = "There was a problem resolving that source: [" + systemID + "] : " + se; 783 } finally { 784 this.resolver.release(source); 785 } 786 787 String result = (failed) ? RESULT_FAILED : RESULT_SUCCESS; 789 String action = ACTION_NONE; 790 if (!failed) { 791 action = (exists) ? ACTION_OVER : ACTION_NEW; 792 } 793 794 reportResult(localSerializer, tagname, message, target, result, action); 795 } 796 797 private void reportResult(String localSerializer, 798 String tagname, 799 String message, 800 String target, 801 String result, 802 String action) 803 throws SAXException { 804 sendStartElementEvent(RESULT_ELEMENT); 805 sendStartElementEvent(EXECUTION_ELEMENT); 806 sendTextEvent(result); 807 sendEndElementEvent(EXECUTION_ELEMENT); 808 sendStartElementEvent(MESSAGE_ELEMENT); 809 sendTextEvent(message); 810 sendEndElementEvent(MESSAGE_ELEMENT); 811 sendStartElementEvent(BEHAVIOUR_ELEMENT); 812 sendTextEvent(tagname); 813 sendEndElementEvent(BEHAVIOUR_ELEMENT); 814 sendStartElementEvent(ACTION_ELEMENT); 815 sendTextEvent(action); 816 sendEndElementEvent(ACTION_ELEMENT); 817 sendStartElementEvent(SOURCE_ELEMENT); 818 sendTextEvent(target); 819 sendEndElementEvent(SOURCE_ELEMENT); 820 if (localSerializer != null) { 821 sendStartElementEvent(SERIALIZER_ELEMENT); 822 sendTextEvent(localSerializer); 823 sendEndElementEvent(SERIALIZER_ELEMENT); 824 } 825 sendEndElementEvent(RESULT_ELEMENT); 826 } 827 828 private Document newDocument() throws SAXException , ServiceException { 829 DOMParser parser = (DOMParser) this.manager.lookup(DOMParser.ROLE); 830 try { 831 return parser.createDocument(); 832 } finally { 833 this.manager.release(parser); 834 } 835 } 836 837 840 public void service(ServiceManager manager) throws ServiceException { 841 super.service(manager); 842 this.xpathProcessor = (XPathProcessor) this.manager.lookup(XPathProcessor.ROLE); 843 } 844 845 848 public void dispose() { 849 if (this.manager != null) { 850 this.manager.release(this.xpathProcessor); 851 this.xpathProcessor = null; 852 } 853 super.dispose(); 854 } 855 } 856 | Popular Tags |