/* * 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.serialization; import java.io.IOException; import java.io.OutputStream; import java.util.Stack; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.cocoon.components.elementprocessor.CannotCreateElementProcessorException; import org.apache.cocoon.components.elementprocessor.ElementProcessor; import org.apache.cocoon.components.elementprocessor.ElementProcessorFactory; import org.apache.cocoon.components.elementprocessor.types.Attribute; import org.xml.sax.Attributes; import org.xml.sax.Locator; import org.xml.sax.SAXException; /** * An implementation of nearly all of the methods included in the * org.apache.poi.serialization.Serializer interface * * This is an abstract class. Concrete extensions need to implement * the following methods: * <ul> * <li>String getMimeType()</li> * <li>void endDocument()</li> * <li>ElementProcessorFactory getElementProcessorFactory()</li> * <li>void doPreInitialization(ElementProcessor processor)</li> * </ul> * * @author Marc Johnson (marc_johnson27591@hotmail.com) * @author Nicola Ken Barozzi (nicolaken@apache.org) * @version $Id$ */ public abstract class ElementProcessorSerializer extends AbstractLogEnabled implements Serializer, Serviceable { private final Stack openElements; protected ServiceManager manager; private OutputStream outputStream; private Locator locator; /** * Constructor */ public ElementProcessorSerializer() { this.openElements = new Stack(); } public void service(ServiceManager manager) { this.manager = manager; } /** * get the appropriate ElementProcessorFactory * * @return an ElementProcessorFactory suitable for the file type */ protected abstract ElementProcessorFactory getElementProcessorFactory(); /** * perform whatever pre-initialization seems good on the * ElementProcessor * * @param processor the processor to be initialized * * @exception SAXException on errors */ protected abstract void doPreInitialization(ElementProcessor processor) throws SAXException; /** * @return the output stream */ protected OutputStream getOutputStream() { return this.outputStream; } /** * Create a new SAXException * * @param message the exception message * @param e the underlying exception (may be null) * * @return new SAXException */ protected SAXException SAXExceptionFactory(final String message, final Exception e) { StringBuffer message_buffer = new StringBuffer(); message_buffer.append((message == null) ? "" : message); if (this.locator != null) { message_buffer.append("; System id: \""); message_buffer.append(this.locator.getSystemId()); message_buffer.append("\"; public id: \""); message_buffer.append(this.locator.getPublicId()); message_buffer.append("\"; line number: "); message_buffer.append(this.locator.getLineNumber()); message_buffer.append("; column number: "); message_buffer.append(this.locator.getColumnNumber()); } SAXException rval = null; if (e != null) { rval = new SAXException(message_buffer.toString(), e); } else { rval = new SAXException(message_buffer.toString()); } return rval; } /** * Create a SAXException * * @param message the exception message * * @return new SAXException */ protected SAXException SAXExceptionFactory(final String message) { return SAXExceptionFactory(message, null); } private ElementProcessor getCurrentElementProcessor() { return this.openElements.empty() ? null : (ElementProcessor)this.openElements.peek(); } private char [] cleanupArray(final char [] array, final int start, final int length) { char[] output = new char[length]; System.arraycopy(array, start, output, 0, length); return output; } /* ********** START implementation of SitemapOutputComponent ********** */ /** * Set the OutputStream where the requested resource should be * serialized. * * @param out the OutputStream to which the serialized data will * be written */ public void setOutputStream(final OutputStream out) { this.outputStream = out; } /** * Test if the component wants to set the content length. * * @return false */ public boolean shouldSetContentLength() { return false; } /* ********** END implementation of SitemapOutputComponent ********** */ /* ********** START implementation of LexicalHandler ********** */ /** * Report an XML comment anywhere in the document. We don't really * care. */ public void comment(final char [] ch, final int start, final int length) { } /** * Report the end of a CDATA section. We don't really care. */ public void endCDATA() { } /** * Report the end of DTD declarations. We don't really care. */ public void endDTD() { } /** * Report the end of an entity. We don't really care. */ public void endEntity(final String name) { } /** * Report the start of a CDATA section. We don't really care. */ public void startCDATA() { } /** * Report the start of DTD declarations, if any. We don't really * care. */ public void startDTD(final String name, final String publicId, final String systemId) { } /** * Report the beginning of some internal and external XML * entities. We don't really care. */ public void startEntity(final String name) { } /* ********** END implementation of LexicalHandler ********** */ /* ********** START implementation of ContentHandler ********** */ /** * Receive notification of character data. * * @param ch the character array * @param start the start index in ch * @param length the length of the valid part of ch * * @exception SAXException if anything goes wrong in processing * the character data */ public void characters(final char [] ch, final int start, final int length) throws SAXException { ElementProcessor currentElementProcessor = getCurrentElementProcessor(); if (currentElementProcessor != null) { currentElementProcessor.acceptCharacters(cleanupArray(ch, start, length)); } } /** * Receive notification of the end of an element. * * @exception SAXException on any errors processing the event. */ public void endElement(final String namespaceURI, final String localName, final String qName) throws SAXException { try { getCurrentElementProcessor().endProcessing(); this.openElements.pop(); } catch (IOException e) { throw SAXExceptionFactory("could not process endElement event", e); } } /** * End the scope of a prefix-URI mapping. We don't really care. */ public void endPrefixMapping(final String prefix) { } /** * Receive notification of ignorable whitespace in element * content. * * @param ch the character array * @param start the start index in ch * @param length the length of the valid part of ch * * @exception SAXException if anything goes wrong in processing * the character data */ public void ignorableWhitespace(final char [] ch, final int start, final int length) throws SAXException { ElementProcessor currentElementProcessor = getCurrentElementProcessor(); if (currentElementProcessor != null) { currentElementProcessor.acceptWhitespaceCharacters(cleanupArray(ch, start, length)); } } /** * Receive notification of a processing instruction. We don't * really care. */ public void processingInstruction(final String target, final String data) { } /** * Receive an object for locating the origin of SAX document * events. * * @param locator the Locator object */ public void setDocumentLocator(final Locator locator) { this.locator = locator; } /** * Receive notification of a skipped entity. We don't really care. */ public void skippedEntity(final String name) { } /** * Receive notification of the beginning of a document. */ public void startDocument() { // nothing to do; should be ready as soon as we were // constructed } /** * Receive notification of the beginning of an element. * * @param namespaceURI the namespace this element is in * @param localName the local name of the element * @param qName the qualified name of the element * @param atts the Attributes, if any, of the element * * @exception SAXException if we cannot create an ElementProcessor * to handle the element */ public void startElement(final String namespaceURI, final String localName, final String qName, final Attributes atts) throws SAXException { String name = ""; if (localName != null && localName.length() != 0) { name = localName; } else if (qName != null && qName.length() != 0) { name = qName; } ElementProcessor processor; try { processor = getElementProcessorFactory().createElementProcessor(name); } catch (CannotCreateElementProcessorException e) { throw SAXExceptionFactory("could not process startElement event", e); } doPreInitialization(processor); Attribute[] attributes = (atts == null) ? new Attribute[0] : new Attribute[atts.getLength()]; for (int j = 0; j < attributes.length; j++) { attributes[j] = new Attribute(atts.getQName(j), atts.getValue(j)); } try { processor.initialize(attributes, getCurrentElementProcessor()); } catch (IOException e) { throw SAXExceptionFactory("Exception processing startElement", e); } this.openElements.push(processor); } /** * Begin the scope of a prefix-URI Namespace mapping. We don't * really care. */ public void startPrefixMapping(final String prefix, final String uri) { } /* ********** END implementation of ContentHandler ********** */ } // end public abstract class ElementProcessorSerializer