/*
 * 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: RendererFactory.java 1780541 2017-01-27 11:27:04Z ssteiner $ */

package org.apache.fop.render;

import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.util.Service;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.render.intermediate.AbstractIFDocumentHandlerMaker;
import org.apache.fop.render.intermediate.EventProducingFilter;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandler;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.render.intermediate.IFRenderer;

Factory for FOEventHandlers and Renderers.
/** * Factory for FOEventHandlers and Renderers. */
public class RendererFactory {
the logger
/** the logger */
private static Log log = LogFactory.getLog(RendererFactory.class); private Map rendererMakerMapping = new java.util.HashMap(); private Map eventHandlerMakerMapping = new java.util.HashMap(); private Map documentHandlerMakerMapping = new java.util.HashMap(); private final boolean rendererPreferred;
Main constructor.
Params:
/** * Main constructor. * @param rendererPreferred Controls whether a {@link Renderer} is preferred over a * {@link IFDocumentHandler} if both are available for the same MIME type. True to prefer the * {@link Renderer}, false to prefer the {@link IFDocumentHandler}. */
public RendererFactory(boolean rendererPreferred) { discoverRenderers(); discoverFOEventHandlers(); discoverDocumentHandlers(); this.rendererPreferred = rendererPreferred; }
Indicates whether a Renderer is preferred over a IFDocumentHandler if both are available for the same MIME type.
Returns:true if the Renderer is preferred, false if the IFDocumentHandler is preferred.
/** * Indicates whether a {@link Renderer} is preferred over a {@link IFDocumentHandler} if * both are available for the same MIME type. * @return true if the {@link Renderer} is preferred, * false if the {@link IFDocumentHandler} is preferred. */
public boolean isRendererPreferred() { return this.rendererPreferred; }
Add a new RendererMaker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • maker – the RendererMaker
/** * Add a new RendererMaker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param maker the RendererMaker */
public void addRendererMaker(AbstractRendererMaker maker) { String[] mimes = maker.getSupportedMimeTypes(); for (String mime : mimes) { //This overrides any renderer previously set for a MIME type if (rendererMakerMapping.get(mime) != null) { log.trace("Overriding renderer for " + mime + " with " + maker.getClass().getName()); } rendererMakerMapping.put(mime, maker); } }
Add a new FOEventHandlerMaker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • maker – the FOEventHandlerMaker
/** * Add a new FOEventHandlerMaker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param maker the FOEventHandlerMaker */
public void addFOEventHandlerMaker(AbstractFOEventHandlerMaker maker) { String[] mimes = maker.getSupportedMimeTypes(); for (String mime : mimes) { //This overrides any event handler previously set for a MIME type if (eventHandlerMakerMapping.get(mime) != null) { log.trace("Overriding FOEventHandler for " + mime + " with " + maker.getClass().getName()); } eventHandlerMakerMapping.put(mime, maker); } }
Add a new document handler maker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • maker – the intermediate format document handler maker
/** * Add a new document handler maker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param maker the intermediate format document handler maker */
public void addDocumentHandlerMaker(AbstractIFDocumentHandlerMaker maker) { String[] mimes = maker.getSupportedMimeTypes(); for (String mime : mimes) { //This overrides any renderer previously set for a MIME type if (documentHandlerMakerMapping.get(mime) != null) { log.trace("Overriding document handler for " + mime + " with " + maker.getClass().getName()); } documentHandlerMakerMapping.put(mime, maker); } }
Add a new RendererMaker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • className – the fully qualified class name of the RendererMaker
/** * Add a new RendererMaker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param className the fully qualified class name of the RendererMaker */
public void addRendererMaker(String className) { try { AbstractRendererMaker makerInstance = (AbstractRendererMaker)Class.forName(className).getDeclaredConstructor().newInstance(); addRendererMaker(makerInstance); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Could not find " + className); } catch (InstantiationException e) { throw new IllegalArgumentException("Could not instantiate " + className); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Could not access " + className); } catch (ClassCastException e) { throw new IllegalArgumentException(className + " is not an " + AbstractRendererMaker.class.getName()); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); } }
Add a new FOEventHandlerMaker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • className – the fully qualified class name of the FOEventHandlerMaker
/** * Add a new FOEventHandlerMaker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param className the fully qualified class name of the FOEventHandlerMaker */
public void addFOEventHandlerMaker(String className) { try { AbstractFOEventHandlerMaker makerInstance = (AbstractFOEventHandlerMaker)Class.forName(className).getDeclaredConstructor().newInstance(); addFOEventHandlerMaker(makerInstance); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Could not find " + className); } catch (InstantiationException e) { throw new IllegalArgumentException("Could not instantiate " + className); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Could not access " + className); } catch (ClassCastException e) { throw new IllegalArgumentException(className + " is not an " + AbstractFOEventHandlerMaker.class.getName()); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); } }
Add a new document handler maker. If another maker has already been registered for a particular MIME type, this call overwrites the existing one.
Params:
  • className – the fully qualified class name of the document handler maker
/** * Add a new document handler maker. If another maker has already been registered for a * particular MIME type, this call overwrites the existing one. * @param className the fully qualified class name of the document handler maker */
public void addDocumentHandlerMaker(String className) { try { AbstractIFDocumentHandlerMaker makerInstance = (AbstractIFDocumentHandlerMaker)Class.forName(className).getDeclaredConstructor().newInstance(); addDocumentHandlerMaker(makerInstance); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Could not find " + className); } catch (InstantiationException e) { throw new IllegalArgumentException("Could not instantiate " + className); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Could not access " + className); } catch (ClassCastException e) { throw new IllegalArgumentException(className + " is not an " + AbstractIFDocumentHandlerMaker.class.getName()); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e); } }
Returns a RendererMaker which handles the given MIME type.
Params:
  • mime – the requested output format
Returns:the requested RendererMaker or null if none is available
/** * Returns a RendererMaker which handles the given MIME type. * @param mime the requested output format * @return the requested RendererMaker or null if none is available */
public AbstractRendererMaker getRendererMaker(String mime) { AbstractRendererMaker maker = (AbstractRendererMaker)rendererMakerMapping.get(mime); return maker; }
Returns a FOEventHandlerMaker which handles the given MIME type.
Params:
  • mime – the requested output format
Returns:the requested FOEventHandlerMaker or null if none is available
/** * Returns a FOEventHandlerMaker which handles the given MIME type. * @param mime the requested output format * @return the requested FOEventHandlerMaker or null if none is available */
public AbstractFOEventHandlerMaker getFOEventHandlerMaker(String mime) { AbstractFOEventHandlerMaker maker = (AbstractFOEventHandlerMaker)eventHandlerMakerMapping.get(mime); return maker; }
Returns a RendererMaker which handles the given MIME type.
Params:
  • mime – the requested output format
Returns:the requested RendererMaker or null if none is available
/** * Returns a RendererMaker which handles the given MIME type. * @param mime the requested output format * @return the requested RendererMaker or null if none is available */
private AbstractIFDocumentHandlerMaker getDocumentHandlerMaker(String mime) { AbstractIFDocumentHandlerMaker maker = (AbstractIFDocumentHandlerMaker)documentHandlerMakerMapping.get(mime); return maker; }
Creates a Renderer object based on render-type desired
Params:
  • userAgent – the user agent for access to configuration
  • outputFormat – the MIME type of the output format to use (ex. "application/pdf").
Throws:
  • FOPException – if the renderer cannot be properly constructed
Returns:the new Renderer instance
/** * Creates a Renderer object based on render-type desired * @param userAgent the user agent for access to configuration * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @return the new Renderer instance * @throws FOPException if the renderer cannot be properly constructed */
public Renderer createRenderer(FOUserAgent userAgent, String outputFormat) throws FOPException { if (userAgent.getDocumentHandlerOverride() != null) { return createRendererForDocumentHandler(userAgent.getDocumentHandlerOverride()); } else if (userAgent.getRendererOverride() != null) { return userAgent.getRendererOverride(); } else { Renderer renderer; if (isRendererPreferred()) { //Try renderer first renderer = tryRendererMaker(userAgent, outputFormat); if (renderer == null) { renderer = tryIFDocumentHandlerMaker(userAgent, outputFormat); } } else { //Try document handler first renderer = tryIFDocumentHandlerMaker(userAgent, outputFormat); if (renderer == null) { renderer = tryRendererMaker(userAgent, outputFormat); } } if (renderer == null) { throw new UnsupportedOperationException( "No renderer for the requested format available: " + outputFormat); } return renderer; } } private Renderer tryIFDocumentHandlerMaker(FOUserAgent userAgent, String outputFormat) throws FOPException { AbstractIFDocumentHandlerMaker documentHandlerMaker = getDocumentHandlerMaker(outputFormat); if (documentHandlerMaker != null) { IFDocumentHandler documentHandler = createDocumentHandler( userAgent, outputFormat); return createRendererForDocumentHandler(documentHandler); } else { return null; } } private Renderer tryRendererMaker(FOUserAgent userAgent, String outputFormat) throws FOPException { AbstractRendererMaker maker = getRendererMaker(outputFormat); if (maker != null) { Renderer rend = maker.makeRenderer(userAgent); maker.configureRenderer(userAgent, rend); return rend; } else { return null; } } private Renderer createRendererForDocumentHandler(IFDocumentHandler documentHandler) { IFRenderer rend = new IFRenderer(documentHandler.getContext().getUserAgent()); rend.setDocumentHandler(documentHandler); return rend; }
Creates FOEventHandler instances based on the desired output.
Params:
  • userAgent – the user agent for access to configuration
  • outputFormat – the MIME type of the output format to use (ex. "application/pdf").
  • out – the OutputStream where the output is written to (if applicable)
Throws:
  • FOPException – if the FOEventHandler cannot be properly constructed
Returns:the newly constructed FOEventHandler
/** * Creates FOEventHandler instances based on the desired output. * @param userAgent the user agent for access to configuration * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @param out the OutputStream where the output is written to (if applicable) * @return the newly constructed FOEventHandler * @throws FOPException if the FOEventHandler cannot be properly constructed */
public FOEventHandler createFOEventHandler(FOUserAgent userAgent, String outputFormat, OutputStream out) throws FOPException { if (userAgent.getFOEventHandlerOverride() != null) { return userAgent.getFOEventHandlerOverride(); } else { AbstractFOEventHandlerMaker maker = getFOEventHandlerMaker(outputFormat); if (maker != null) { return maker.makeFOEventHandler(userAgent, out); } else { AbstractRendererMaker rendMaker = getRendererMaker(outputFormat); AbstractIFDocumentHandlerMaker documentHandlerMaker = null; boolean outputStreamMissing = (userAgent.getRendererOverride() == null) && (userAgent.getDocumentHandlerOverride() == null); if (rendMaker == null) { documentHandlerMaker = getDocumentHandlerMaker(outputFormat); if (documentHandlerMaker != null) { outputStreamMissing &= (out == null) && (documentHandlerMaker.needsOutputStream()); } } else { outputStreamMissing &= (out == null) && (rendMaker.needsOutputStream()); } if (userAgent.getRendererOverride() != null || rendMaker != null || userAgent.getDocumentHandlerOverride() != null || documentHandlerMaker != null) { if (outputStreamMissing) { throw new FOPException( "OutputStream has not been set"); } //Found a Renderer so we need to construct an AreaTreeHandler. return new AreaTreeHandler(userAgent, outputFormat, out); } else { throw new UnsupportedOperationException( "Don't know how to handle \"" + outputFormat + "\" as an output format." + " Neither an FOEventHandler, nor a Renderer could be found" + " for this output format."); } } } }
Creates a IFDocumentHandler object based on the desired output format.
Params:
  • userAgent – the user agent for access to configuration
  • outputFormat – the MIME type of the output format to use (ex. "application/pdf").
Throws:
  • FOPException – if the document handler cannot be properly constructed
Returns:the new IFDocumentHandler instance
/** * Creates a {@link IFDocumentHandler} object based on the desired output format. * @param userAgent the user agent for access to configuration * @param outputFormat the MIME type of the output format to use (ex. "application/pdf"). * @return the new {@link IFDocumentHandler} instance * @throws FOPException if the document handler cannot be properly constructed */
public IFDocumentHandler createDocumentHandler(FOUserAgent userAgent, String outputFormat) throws FOPException { if (userAgent.getDocumentHandlerOverride() != null) { return userAgent.getDocumentHandlerOverride(); } AbstractIFDocumentHandlerMaker maker = getDocumentHandlerMaker(outputFormat); if (maker == null) { throw new UnsupportedOperationException( "No IF document handler for the requested format available: " + outputFormat); } IFDocumentHandler documentHandler = maker.makeIFDocumentHandler(new IFContext(userAgent)); // TODO: do all the configuration in the makeIfDocumentHandler method, that would beam when // you ask for a document handler, a configured one is returned to you. Getting it and // configuring it in two steps doesn't make sense. IFDocumentHandlerConfigurator configurator = documentHandler.getConfigurator(); if (configurator != null) { configurator.configure(documentHandler); } return new EventProducingFilter(documentHandler, userAgent); }
Returns:an array of all supported MIME types
/** * @return an array of all supported MIME types */
public String[] listSupportedMimeTypes() { List lst = new java.util.ArrayList(); Iterator iter = this.rendererMakerMapping.keySet().iterator(); while (iter.hasNext()) { lst.add(iter.next()); } iter = this.eventHandlerMakerMapping.keySet().iterator(); while (iter.hasNext()) { lst.add(iter.next()); } iter = this.documentHandlerMakerMapping.keySet().iterator(); while (iter.hasNext()) { lst.add(iter.next()); } Collections.sort(lst); return (String[])lst.toArray(new String[lst.size()]); }
Discovers Renderer implementations through the classpath and dynamically registers them.
/** * Discovers Renderer implementations through the classpath and dynamically * registers them. */
private void discoverRenderers() { // add mappings from available services Iterator providers = Service.providers(Renderer.class); if (providers != null) { while (providers.hasNext()) { AbstractRendererMaker maker = (AbstractRendererMaker)providers.next(); try { if (log.isDebugEnabled()) { log.debug("Dynamically adding maker for Renderer: " + maker.getClass().getName()); } addRendererMaker(maker); } catch (IllegalArgumentException e) { log.error("Error while adding maker for Renderer", e); } } } }
Discovers FOEventHandler implementations through the classpath and dynamically registers them.
/** * Discovers FOEventHandler implementations through the classpath and dynamically * registers them. */
private void discoverFOEventHandlers() { // add mappings from available services Iterator providers = Service.providers(FOEventHandler.class); if (providers != null) { while (providers.hasNext()) { AbstractFOEventHandlerMaker maker = (AbstractFOEventHandlerMaker)providers.next(); try { if (log.isDebugEnabled()) { log.debug("Dynamically adding maker for FOEventHandler: " + maker.getClass().getName()); } addFOEventHandlerMaker(maker); } catch (IllegalArgumentException e) { log.error("Error while adding maker for FOEventHandler", e); } } } }
Discovers IFDocumentHandler implementations through the classpath and dynamically registers them.
/** * Discovers {@link IFDocumentHandler} implementations through the classpath and dynamically * registers them. */
private void discoverDocumentHandlers() { // add mappings from available services Iterator providers = Service.providers(IFDocumentHandler.class); if (providers != null) { while (providers.hasNext()) { AbstractIFDocumentHandlerMaker maker = (AbstractIFDocumentHandlerMaker)providers.next(); try { if (log.isDebugEnabled()) { log.debug("Dynamically adding maker for IFDocumentHandler: " + maker.getClass().getName()); } addDocumentHandlerMaker(maker); } catch (IllegalArgumentException e) { log.error("Error while adding maker for IFDocumentHandler", e); } } } } }