| /* |
| * 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$ */ |
| |
| 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. |
| */ |
| public class RendererFactory { |
| |
| /** 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. |
| * @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 {@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. |
| * @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. |
| * @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. |
| * @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. |
| * @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. |
| * @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. |
| * @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. |
| * @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. |
| * @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. |
| * @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 |
| * @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. |
| * @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 {@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); |
| } |
| |
| /** |
| * @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. |
| */ |
| 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. |
| */ |
| 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 {@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); |
| } |
| |
| } |
| } |
| } |
| |
| } |