Package org.apache.fop.fo

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

/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed 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 344111 2005-11-14 12:55:46Z jeremias $ */

package org.apache.fop.fo;

import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.util.Service;
import org.apache.fop.fo.ElementMapping.Maker;
import org.apache.fop.fo.pagination.Root;
import org.apache.fop.image.ImageFactory;
import org.xml.sax.Attributes;
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 {

    /**
     * Table mapping element names to the makers of objects
     * representing formatting objects.
     */
    protected Map fobjTable = new java.util.HashMap();

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

    /**
     * Set of mapped namespaces.
     */
    protected Set namespaces = new java.util.HashSet();

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

    /**
     * Current formatting object being handled
     */
    protected FONode currentFObj = null;

    /**
     * Current propertyList for the node being handled.
     */
    protected PropertyList currentPropertyList;

    /**
     * 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;
   
    /**
     * 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 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);
            }
        });

        // Add standard element mappings
        setupDefaultMappings();

        // add additional ElementMappings defined within FOUserAgent
        List addlEMs = foUserAgent.getAdditionalElementMappings();

        if (addlEMs != null) {
            for (int i = 0; i < addlEMs.size(); i++) {
                addElementMapping((ElementMapping) addlEMs.get(i));
            }
        }
    }

    /**
     * Sets all the element and property list mappings to their default values.
     *
     */
    private void setupDefaultMappings() {
        addElementMapping("org.apache.fop.fo.FOElementMapping");
        addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping");
        addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping");
        addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping");
        addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping");

        // add mappings from available services
        Iterator providers = Service.providers(ElementMapping.class);
        if (providers != null) {
            while (providers.hasNext()) {
                String str = (String)providers.next();
                try {
                    addElementMapping(str);
                } catch (IllegalArgumentException e) {
                    log.warn("Error while adding element mapping", e);
                }

            }
        }
    }

    /**
     * Add the element mapping with the given class name.
     * @param mappingClassName the class name representing the element mapping.
     * @throws IllegalArgumentException if there was not such element mapping.
     */
    public void addElementMapping(String mappingClassName)
                throws IllegalArgumentException {

        try {
            ElementMapping mapping
                = (ElementMapping)Class.forName(mappingClassName).newInstance();
            addElementMapping(mapping);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Could not find "
                                               + mappingClassName);
        } catch (InstantiationException e) {
            throw new IllegalArgumentException("Could not instantiate "
                                               + mappingClassName);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Could not access "
                                               + mappingClassName);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException(mappingClassName
                                               + " is not an ElementMapping");
        }
    }

    private void addElementMapping(ElementMapping mapping) {
        this.fobjTable.put(mapping.getNamespaceURI(), mapping.getTable());
        this.namespaces.add(mapping.getNamespaceURI().intern());
    }

    /**
     * SAX Handler for locator
     * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
     */
    public void setDocumentLocator(Locator locator) {
        this.locator = locator;
    }
   
    /**
     * 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, locator);
            }
    }

    /**
     * SAX Handler for the start of the document
     * @see org.xml.sax.ContentHandler#startDocument()
     */
    public void startDocument() throws SAXException {
        rootFObj = null;    // allows FOTreeBuilder to be reused
        if (log.isDebugEnabled()) {
            log.debug("Building formatting object tree");
        }
        foEventHandler.startDocument();
    }

    /**
     * SAX Handler for the end of the document
     * @see org.xml.sax.ContentHandler#endDocument()
     */
    public void endDocument() throws SAXException {
        rootFObj = null;
        currentFObj = null;
        if (log.isDebugEnabled()) {
            log.debug("Parsing of document complete");
        }
        foEventHandler.endDocument();
       
        //Notify the image factory that this user agent has expired.
        ImageFactory.getInstance().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 {

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

        // Check to ensure first node encountered is an fo:root
        if (rootFObj == null) {
            if (!namespaceURI.equals(FOElementMapping.URI)
                || !localName.equals("root")) {
                throw new SAXException(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);
//      log.debug("found a " + fobjMaker.toString());

        try {
            foNode = fobjMaker.make(currentFObj);
            propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
            foNode.processNode(localName, locator, attlist, propertyList);
            foNode.startOfNode();
        } catch (IllegalArgumentException e) {
            throw new SAXException(e);
        }

        if (rootFObj == null) {
            rootFObj = (Root) foNode;
            rootFObj.setFOEventHandler(foEventHandler);
        } else {
            currentFObj.addChildNode(foNode);
        }

        currentFObj = foNode;
        if (propertyList != null) {
            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 FOPException {
        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.getFObj() == currentFObj) {
            currentPropertyList = currentPropertyList.getParentPropertyList();
        }
        if (currentFObj.getParent() == null) {
            log.debug("endElement for top-level " + currentFObj.getName());
        }
        currentFObj = currentFObj.getParent();
    }

    /**
     * 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 {
      Map table = (Map)fobjTable.get(namespaceURI);
      Maker fobjMaker = null;
      if (table != null) {
          fobjMaker = (ElementMapping.Maker)table.get(localName);
          // try default
          if (fobjMaker == null) {
              fobjMaker = (ElementMapping.Maker)table.get(ElementMapping.DEFAULT);
          }
      }

      if (fobjMaker == null) {
          if (namespaces.contains(namespaceURI.intern())) {
                throw new FOPException(FONode.errorText(locator)
                    + "No element mapping definition found for "
                    + FONode.getNodeString(namespaceURI, localName), locator);
          } else {
              log.warn("Unknown formatting object " + namespaceURI + "^" + localName);
              fobjMaker = new UnknownXMLObj.Maker(namespaceURI);
          }
      }
      return fobjMaker;
    }

    /** @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;  
        }
    }
   
    /**
     * Resets this object for another run.
     */
    public void reset() {
        currentFObj = null;
        rootFObj = null;
        foEventHandler = 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.