Package org.apache.fop.fo

Source Code of org.apache.fop.fo.FOTreeBuilder

/*
* 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.
*/

/* $Id: FOTreeBuilder.java 429168 2006-08-06 18:23:59Z adelmelle $ */

package org.apache.fop.fo;

import java.io.OutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FormattingResults;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.ElementMapping.Maker;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.image.ImageFactory;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactory.ObjectSource;
import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
* SAX Handler that passes parsed data to the various
* FO objects, where they can be used either to build
* an FO Tree, or used by Structure Renderers to build
* other data structures.
*/
public class FOTreeBuilder extends DefaultHandler {

    /** logging instance */
    protected Log log = LogFactory.getLog(FOTreeBuilder.class);

    /** The registry for ElementMapping instances */
    protected ElementMappingRegistry elementMappingRegistry;

    /**
     * The root of the formatting object tree
     */
    protected Root rootFObj = null;

    /** Main DefaultHandler that handles the FO namespace. */
    protected MainFOHandler mainFOHandler;
   
    /** Current delegate ContentHandler to receive the SAX events */
    protected ContentHandler delegate;
   
    /**
     * The class that handles formatting and rendering to a stream
     * (mark-fop@inomial.com)
     */
    private FOEventHandler foEventHandler;

    /** The SAX locator object managing the line and column counters */
    private Locator locator;
   
    /** The user agent for this processing run. */
    private FOUserAgent userAgent;
   
    private boolean used = false;
   
    private int depth;
   
    /**
     * FOTreeBuilder constructor
     * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
     * @param foUserAgent in effect for this process
     * @param stream OutputStream to direct results
     * @throws FOPException if the FOTreeBuilder cannot be properly created
     */
    public FOTreeBuilder(String outputFormat, FOUserAgent foUserAgent,
        OutputStream stream) throws FOPException {

        this.userAgent = foUserAgent;
        this.elementMappingRegistry = userAgent.getFactory().getElementMappingRegistry();       
        //This creates either an AreaTreeHandler and ultimately a Renderer, or
        //one of the RTF-, MIF- etc. Handlers.
        foEventHandler = foUserAgent.getRendererFactory().createFOEventHandler(
                foUserAgent, outputFormat, stream);
        foEventHandler.setPropertyListMaker(new PropertyListMaker() {
            public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
                return new StaticPropertyList(fobj, parentPropertyList);
            }
        });
    }

    /**
     * This method enables to reduce memory consumption of the FO tree slightly. When it returns
     * true no Locator is passed to the FO tree nodes which would copy the information into
     * a SAX LocatorImpl instance.
     * @return true if no context information should be stored on each node in the FO tree.
     */
    protected boolean isLocatorDisabled() {
        //TODO make this configurable through the FOUserAgent so people can optimize memory
        //consumption.
        return false;
    }
   
    /**
     * SAX Handler for locator
     * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
     */
    public void setDocumentLocator(Locator locator) {
        this.locator = locator;
    }
   
    /** @return a Locator instance if it is available and not disabled */
    protected Locator getEffectiveLocator() {
        return (isLocatorDisabled() ? null : this.locator);
    }
   
    /**
     * SAX Handler for characters
     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
     */
    public void characters(char[] data, int start, int length)
                throws SAXException {
        delegate.characters(data, start, length);
    }

    /**
     * SAX Handler for the start of the document
     * @see org.xml.sax.ContentHandler#startDocument()
     */
    public void startDocument() throws SAXException {
        if (used) {
            throw new IllegalStateException("FOTreeBuilder (and the Fop class) cannot be reused."
                    + " Please instantiate a new instance.");
        }
        used = true;
        rootFObj = null;    // allows FOTreeBuilder to be reused
        if (log.isDebugEnabled()) {
            log.debug("Building formatting object tree");
        }
        foEventHandler.startDocument();
        this.mainFOHandler = new MainFOHandler();
        this.mainFOHandler.startDocument();
        this.delegate = this.mainFOHandler;
    }

    /**
     * SAX Handler for the end of the document
     * @see org.xml.sax.ContentHandler#endDocument()
     */
    public void endDocument() throws SAXException {
        this.delegate.endDocument();
        rootFObj = null;
        if (log.isDebugEnabled()) {
            log.debug("Parsing of document complete");
        }
        foEventHandler.endDocument();
       
        //Notify the image factory that this user agent has expired.
        ImageFactory imageFactory = userAgent.getFactory().getImageFactory();
        imageFactory.removeContext(this.userAgent);
    }

    /**
     * SAX Handler for the start of an element
     * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
     */
    public void startElement(String namespaceURI, String localName, String rawName,
                             Attributes attlist) throws SAXException {
        this.depth++;
        delegate.startElement(namespaceURI, localName, rawName, attlist);
    }

    /**
     * SAX Handler for the end of an element
     * @see org.xml.sax.ContentHandler#endElement(String, String, String)
     */
    public void endElement(String uri, String localName, String rawName)
                throws SAXException {
        this.delegate.endElement(uri, localName, rawName);
        this.depth--;
        if (depth == 0) {
            if (delegate != mainFOHandler) {
                //Return from sub-handler back to main handler
                delegate.endDocument();
                delegate = mainFOHandler;
                delegate.endElement(uri, localName, rawName);
            }
        }
    }

    /**
     * Finds the Maker used to create node objects of a particular type
     * @param namespaceURI URI for the namespace of the element
     * @param localName name of the Element
     * @return the ElementMapping.Maker that can create an FO object for this element
     * @throws FOPException if a Maker could not be found for a bound namespace.
     */
    private Maker findFOMaker(String namespaceURI, String localName) throws FOPException {
        return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator);
    }

    /** @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */
    public void warning(SAXParseException e) {
        log.warn(e.toString());
    }

    /** @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */
    public void error(SAXParseException e) {
        log.error(e.toString());
    }

    /** @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */
    public void fatalError(SAXParseException e) throws SAXException {
        log.error(e.toString());
        throw e;
    }

    /**
     * Provides access to the underlying FOEventHandler object.
     * @return the FOEventHandler object
     */
    public FOEventHandler getEventHandler() {
        return foEventHandler;
    }

    /**
     * Returns the results of the rendering process. Information includes
     * the total number of pages generated and the number of pages per
     * page-sequence.
     * @return the results of the rendering process.
     */
    public FormattingResults getResults() {
        if (getEventHandler() instanceof AreaTreeHandler) {
            return ((AreaTreeHandler) getEventHandler()).getResults();
        } else {
            //No formatting results available for output formats no
            //involving the layout engine.
            return null;  
        }
    }
   
    /**
     * Main DefaultHandler implementation which builds the FO tree.
     */
    private class MainFOHandler extends DefaultHandler {
       
        /**
         * Current formatting object being handled
         */
        protected FONode currentFObj = null;

        /**
         * Current propertyList for the node being handled.
         */
        protected PropertyList currentPropertyList;
       
        /**
         * Current marker nesting-depth
         */
        private int nestedMarkerDepth = 0;

        /**
         * SAX Handler for the start of an element
         * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
         */
        public void startElement(String namespaceURI, String localName, String rawName,
                                 Attributes attlist) throws SAXException {

            /* the node found in the FO document */
            FONode foNode;
            PropertyList propertyList = null;

            // Check to ensure first node encountered is an fo:root
            if (rootFObj == null) {
                if (!namespaceURI.equals(FOElementMapping.URI)
                    || !localName.equals("root")) {
                    throw new ValidationException(
                        "Error: First element must be the fo:root formatting object. "
                        + "Found " + FONode.getNodeString(namespaceURI, localName)
                        + " instead."
                        + " Please make sure you're producing a valid XSL-FO document.");
                }
            } else { // check that incoming node is valid for currentFObj
                if (namespaceURI.equals(FOElementMapping.URI)) {
                    // currently no fox: elements to validate
                    // || namespaceURI.equals(ExtensionElementMapping.URI) */) {
                    try {
                        currentFObj.validateChildNode(locator, namespaceURI, localName);
                    } catch (ValidationException e) {
                        throw e;
                    }
                }
            }
           
            ElementMapping.Maker fobjMaker =
                findFOMaker(namespaceURI, localName);

            try {
                foNode = fobjMaker.make(currentFObj);
                if (rootFObj == null) {
                    rootFObj = (Root) foNode;
                    rootFObj.setFOEventHandler(foEventHandler);
                }
                propertyList = foNode.createPropertyList(
                                    currentPropertyList, foEventHandler);
                foNode.processNode(localName, getEffectiveLocator(),
                                    attlist, propertyList);
                if (foNode.getNameId() == Constants.FO_MARKER) {
                    if (foEventHandler.inMarker()) {
                        nestedMarkerDepth++;
                    } else {
                        foEventHandler.switchMarkerContext(true);
                    }
                }
                foNode.startOfNode();
            } catch (IllegalArgumentException e) {
                throw new SAXException(e);
            }

            ContentHandlerFactory chFactory = foNode.getContentHandlerFactory();
            if (chFactory != null) {
                ContentHandler subHandler = chFactory.createContentHandler();
                if (subHandler instanceof ObjectSource
                        && foNode instanceof ObjectBuiltListener) {
                    ((ObjectSource) subHandler).setObjectBuiltListener(
                            (ObjectBuiltListener) foNode);
                }
               
                subHandler.startDocument();
                subHandler.startElement(namespaceURI, localName,
                        rawName, attlist);
                depth = 1;
                delegate = subHandler;
            }
           
            if (currentFObj != null) {
                currentFObj.addChildNode(foNode);
            }

            currentFObj = foNode;
            if (propertyList != null && !foEventHandler.inMarker()) {
                currentPropertyList = propertyList;
            }
        }

        /**
         * SAX Handler for the end of an element
         * @see org.xml.sax.ContentHandler#endElement(String, String, String)
         */
        public void endElement(String uri, String localName, String rawName)
                    throws SAXException {
            if (currentFObj == null) {
                throw new IllegalStateException(
                        "endElement() called for " + rawName
                            + " where there is no current element.");
            } else if (!currentFObj.getLocalName().equals(localName)
                    || !currentFObj.getNamespaceURI().equals(uri)) {
                log.warn("Mismatch: " + currentFObj.getLocalName()
                        + " (" + currentFObj.getNamespaceURI()
                        + ") vs. " + localName + " (" + uri + ")");
            }
           
            currentFObj.endOfNode();
           
            if (currentPropertyList != null
                    && currentPropertyList.getFObj() == currentFObj
                    && !foEventHandler.inMarker()) {
                currentPropertyList =
                    currentPropertyList.getParentPropertyList();
            }
           
            if (currentFObj.getNameId() == Constants.FO_MARKER) {
                if (nestedMarkerDepth == 0) {
                    foEventHandler.switchMarkerContext(false);
                } else {
                    nestedMarkerDepth--;
                }
            }
           
            if (currentFObj.getParent() == null) {
                log.debug("endElement for top-level " + currentFObj.getName());
            }
            currentFObj = currentFObj.getParent();
        }

        /**
         * SAX Handler for characters
         * @see org.xml.sax.ContentHandler#characters(char[], int, int)
         */
        public void characters(char[] data, int start, int length)
            throws FOPException {
            if (currentFObj != null) {
                currentFObj.addCharacters(data, start, start + length,
                        currentPropertyList, getEffectiveLocator());
            }
        }

        public void endDocument() throws SAXException {
            currentFObj = null;
        }       
    }
}
TOP

Related Classes of org.apache.fop.fo.FOTreeBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.