/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.woody.transformation; import java.util.LinkedList; import org.apache.cocoon.woody.Constants; import org.apache.cocoon.xml.AbstractXMLPipe; import org.apache.cocoon.xml.SaxBuffer; import org.apache.cocoon.xml.XMLConsumer; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.AttributesImpl; // TODO: Reduce the Element creation and deletion churn by providing startElement // and endElement methods which do not create or use Elements on the stack. /* * Base class for XMLPipe's. Allows the structure of the source code of * the XMLPipe to match the structure of the data being transformed. * * @author Timothy Larson * @version $Id$ */ public class EffectPipe extends AbstractXMLPipe { protected static final int EVENT_SET_DOCUMENT_LOCATOR = 0; protected static final int EVENT_START_DOCUMENT = 1; protected static final int EVENT_END_DOCUMENT = 2; protected static final int EVENT_START_PREFIX_MAPPING = 3; protected static final int EVENT_END_PREFIX_MAPPING = 4; protected static final int EVENT_START_ELEMENT = 5; protected static final int EVENT_END_ELEMENT = 6; protected static final int EVENT_ELEMENT = 7; protected static final int EVENT_CHARACTERS = 8; protected static final int EVENT_IGNORABLE_WHITESPACE = 9; protected static final int EVENT_PROCESSING_INSTRUCTION =10; protected static final int EVENT_SKIPPED_ENTITY =11; protected static final int EVENT_START_DTD =12; protected static final int EVENT_END_DTD =13; protected static final int EVENT_START_ENTITY =14; protected static final int EVENT_END_ENTITY =15; protected static final int EVENT_START_CDATA =16; protected static final int EVENT_END_CDATA =17; protected static final int EVENT_COMMENT =18; protected class Element { public String prefix; public String uri; public String loc; public String raw; public Attributes attrs; public boolean mine; public Element() { prefix = null; uri = null; loc = null; raw = null; attrs = Constants.EMPTY_ATTRS; mine = true; } public Element(String prefix, String uri) { this.prefix = prefix; this.uri = uri; } public Element(String uri, String loc, String raw, Attributes attrs) { this.uri = uri; this.loc = loc; this.raw = raw; this.attrs = Constants.EMPTY_ATTRS; if (attrs == null) { this.attrs = Constants.EMPTY_ATTRS; mine = true; } else { this.attrs = attrs; mine = false; } } public void addAttributes(Attributes attrs) { if (attrs != null) { if (mine == true) { if (this.attrs == Constants.EMPTY_ATTRS) { this.attrs = attrs; mine = false; } else { ((AttributesImpl)this.attrs).setAttributes(attrs); } } else { this.attrs = new AttributesImpl(this.attrs); ((AttributesImpl)this.attrs).setAttributes(attrs); mine = true; } } } public void addAttribute(String uri, String loc, String raw, String type, String value) { if (!mine || attrs == Constants.EMPTY_ATTRS) { attrs = new AttributesImpl(attrs); mine = true; } ((AttributesImpl)attrs).addAttribute(uri, loc, raw, type, value); } public void addAttribute(String prefix, String uri, String loc, String value) { if (!mine || attrs == Constants.EMPTY_ATTRS) { attrs = new AttributesImpl(attrs); mine = true; } ((AttributesImpl)attrs).addAttribute(uri, loc, prefix + ":" +loc, "CDATA", value); } public void addAttribute(String loc, String value) { if (!mine || attrs == Constants.EMPTY_ATTRS) { attrs = new AttributesImpl(attrs); mine = true; } ((AttributesImpl)attrs).addAttribute("", loc, loc, "CDATA", value); } public void claimAttributes() { if (!mine) { attrs = new AttributesImpl(attrs); mine = true; } } } protected abstract class Handler { public abstract Handler process() throws SAXException; } protected class NullHandler extends Handler { public Handler process() throws SAXException { return this; } } protected class BufferHandler extends Handler { public Handler process() throws SAXException { switch (event) { case EVENT_ELEMENT: return this; default: out.buffer(); return this; } } } protected class Output extends AbstractXMLPipe { private LinkedList buffers = null; private SaxBuffer saxBuffer = null; private LinkedList elements = null; protected Element element = null; public Output() { elements = new LinkedList(); } public void startPrefixMapping() throws SAXException { super.startPrefixMapping(input.prefix, input.uri); } public void endPrefixMapping() throws SAXException { super.endPrefixMapping(input.prefix); } public void element(String prefix, String uri, String loc, Attributes attrs) throws SAXException { element = new Element(uri, loc, prefix + ":" + loc, attrs); } public void element(String prefix, String uri, String loc) throws SAXException { element(prefix, uri, loc, null); } public void element(String loc, Attributes attrs) throws SAXException { element = new Element("", loc, loc, attrs); } public void element(String loc) throws SAXException { element(loc, null); } public void element() throws SAXException { element = new Element(input.uri, input.loc, input.raw, input.attrs); } public void attribute(String prefix, String uri, String name, String value) throws SAXException { element.addAttribute(prefix, uri, name, value); } public void attribute(String name, String value) throws SAXException { element.addAttribute(name, value); } public void copyAttribute(String prefix, String uri, String name) throws SAXException { String value = null; if (input.attrs != null && (value = input.attrs.getValue(uri, name)) != null) { attribute(prefix, uri, name, value); } else { throw new SAXException("Attribute \"" + name + "\" cannot be copied because it does not exist."); } } public void attributes(Attributes attrs) throws SAXException { element.addAttributes(attrs); } public void attributes() throws SAXException { attributes(input.attrs); } public void startElement() throws SAXException { if (element.attrs == null) { element.attrs = Constants.EMPTY_ATTRS; } super.startElement(element.uri, element.loc, element.raw, element.attrs); elements.addFirst(element); element = null; } public void endElement() throws SAXException { element = (Element)elements.removeFirst(); super.endElement(element.uri, element.loc, element.raw); element = null; } public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { super.startElement(uri, loc, raw, attrs); } public void endElement(String uri, String loc, String raw) throws SAXException { super.endElement(uri, loc, raw); } public void copy() throws SAXException { switch(event) { case EVENT_SET_DOCUMENT_LOCATOR: this.setDocumentLocator(locator); break; case EVENT_START_DOCUMENT: this.startDocument(); break; case EVENT_END_DOCUMENT: this.endDocument(); break; case EVENT_START_PREFIX_MAPPING: this.startPrefixMapping(); break; case EVENT_END_PREFIX_MAPPING: this.endPrefixMapping(); break; case EVENT_START_ELEMENT: this.element(); attributes(); startElement(); break; case EVENT_END_ELEMENT: this.endElement(); break; case EVENT_CHARACTERS: this.characters(c, start, len); break; case EVENT_IGNORABLE_WHITESPACE: this.ignorableWhitespace(c, start, len); break; case EVENT_PROCESSING_INSTRUCTION: this.processingInstruction(target, data); break; case EVENT_SKIPPED_ENTITY: this.skippedEntity(name); break; case EVENT_START_DTD: this.startDTD(name, publicId, systemId); break; case EVENT_END_DTD: this.endDTD(); break; case EVENT_START_ENTITY: this.startEntity(name); break; case EVENT_END_ENTITY: this.endEntity(name); break; case EVENT_START_CDATA: this.startCDATA(); break; case EVENT_END_CDATA: this.endCDATA(); break; case EVENT_COMMENT: this.comment(c, start, len); break; } } protected void bufferInit() { if (saxBuffer != null) { if (buffers == null) { buffers = new LinkedList(); } buffers.addFirst(saxBuffer); } saxBuffer = new SaxBuffer(); } protected void bufferFini() { if (buffers != null && buffers.size() > 0) { saxBuffer = (SaxBuffer)buffers.removeFirst(); } else { saxBuffer = null; } } protected SaxBuffer getBuffer() { return saxBuffer; } public void buffer() throws SAXException { switch(event) { case EVENT_SET_DOCUMENT_LOCATOR: saxBuffer.setDocumentLocator(locator); break; case EVENT_START_DOCUMENT: saxBuffer.startDocument(); break; case EVENT_END_DOCUMENT: saxBuffer.endDocument(); break; case EVENT_START_PREFIX_MAPPING: saxBuffer.startPrefixMapping(prefix, uri); break; case EVENT_END_PREFIX_MAPPING: saxBuffer.endPrefixMapping(prefix); break; case EVENT_START_ELEMENT: saxBuffer.startElement(input.uri, input.loc, input.raw, input.attrs); break; case EVENT_END_ELEMENT: saxBuffer.endElement(input.uri, input.loc, input.raw); break; case EVENT_CHARACTERS: saxBuffer.characters(c, start, len); break; case EVENT_IGNORABLE_WHITESPACE: saxBuffer.ignorableWhitespace(c, start, len); break; case EVENT_PROCESSING_INSTRUCTION: saxBuffer.processingInstruction(target, data); break; case EVENT_SKIPPED_ENTITY: saxBuffer.skippedEntity(name); break; case EVENT_START_DTD: saxBuffer.startDTD(name, publicId, systemId); break; case EVENT_END_DTD: saxBuffer.endDTD(); break; case EVENT_START_ENTITY: saxBuffer.startEntity(name); break; case EVENT_END_ENTITY: saxBuffer.endEntity(name); break; case EVENT_START_CDATA: saxBuffer.startCDATA(); break; case EVENT_END_CDATA: saxBuffer.endCDATA(); break; case EVENT_COMMENT: saxBuffer.comment(c, start, len); break; } } } protected int event = 0; protected Handler nullHandler = new NullHandler(); protected Handler bufferHandler = new BufferHandler(); protected LinkedList handlers = null; protected Handler handler = null; protected LinkedList elements = null; protected Element input = null; protected Locator locator = null; protected String name = null; protected String publicId = null; protected String systemId = null; protected String target = null; protected String data = null; protected String prefix = null; protected String uri = null; protected char c[] = null; protected int start = 0; protected int len = 0; public Output out = null; public void init() { handlers = new LinkedList(); elements = new LinkedList(); out = new Output(); } //==================================== // Methods overriding AbstractXMLPipe //==================================== public void setConsumer(XMLConsumer consumer) { super.setConsumer(consumer); out.setConsumer(consumer); } public void setContentHandler(ContentHandler handler) { super.setContentHandler(handler); out.setContentHandler(handler); } public void setLexicalHandler(LexicalHandler handler) { super.setLexicalHandler(handler); out.setLexicalHandler(handler); } public void recycle() { super.recycle(); handlers = null; elements = null; out = null; } public void setDocumentLocator(Locator locator) { this.locator = locator; try { event = EVENT_SET_DOCUMENT_LOCATOR; handler = handler.process(); } catch(Exception e) { throw new RuntimeException(e.getMessage()); } } public void startDocument() throws SAXException { event = EVENT_START_DOCUMENT; handler = handler.process(); } public void endDocument() throws SAXException { event = EVENT_END_DOCUMENT; handler = handler.process(); } public void startPrefixMapping(String prefix, String uri) throws SAXException { input = new Element(prefix, uri); elements.addFirst(input); //this.prefix = prefix; this.uri = uri; event = EVENT_START_PREFIX_MAPPING; handler = handler.process(); } public void endPrefixMapping(String prefix) throws SAXException { input = (Element)elements.removeFirst(); //this.prefix = prefix; event = EVENT_END_PREFIX_MAPPING; handler = handler.process(); input = null; } public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { input = new Element(uri, loc, raw, attrs); elements.addFirst(input); handlers.addFirst(handler); event = EVENT_ELEMENT; handler = handler.process(); event = EVENT_START_ELEMENT; handler = handler.process(); } public void endElement(String uri, String loc, String raw) throws SAXException { input = (Element)elements.removeFirst(); event = EVENT_END_ELEMENT; handler.process(); handler = (Handler)handlers.removeFirst(); input = null; } public void characters(char c[], int start, int len) throws SAXException { this.c = c; this.start = start; this.len = len; event = EVENT_CHARACTERS; handler = handler.process(); } public void ignorableWhitespace(char c[], int start, int len) throws SAXException { this.c = c; this.start = start; this.len = len; event = EVENT_IGNORABLE_WHITESPACE; handler = handler.process(); } public void processingInstruction(String target, String data) throws SAXException { this.target = target; this.data = data; event = EVENT_PROCESSING_INSTRUCTION; handler = handler.process(); } public void skippedEntity(String name) throws SAXException { this.name = name; event = EVENT_SKIPPED_ENTITY; handler = handler.process(); } public void startDTD(String name, String publicId, String systemId) throws SAXException { this.name = name; this.publicId = publicId; this.systemId = systemId; event = EVENT_START_DTD; handler = handler.process(); } public void endDTD() throws SAXException { event = EVENT_END_DTD; handler = handler.process(); } public void startEntity(String name) throws SAXException { this.name = name; event = EVENT_START_ENTITY; handler = handler.process(); } public void endEntity(String name) throws SAXException { this.name = name; event = EVENT_END_ENTITY; handler = handler.process(); } public void startCDATA() throws SAXException { event = EVENT_START_CDATA; handler = handler.process(); } public void endCDATA() throws SAXException { event = EVENT_END_CDATA; handler = handler.process(); } public void comment(char c[], int start, int len) throws SAXException { this.c = c; this.start = start; this.len = len; event = EVENT_COMMENT; handler = handler.process(); } }