/* * Copyright 2004-2006 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$ */ package org.apache.fop.render; import java.io.OutputStream; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.container.ContainerUtil; 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; /** * 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(); /** * Main constructor. */ public RendererFactory() { discoverRenderers(); discoverFOEventHandlers(); } /** * 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 (int i = 0; i < mimes.length; i++) { //This overrides any renderer previously set for a MIME type if (rendererMakerMapping.get(mimes[i]) != null) { log.trace("Overriding renderer for " + mimes[i] + " with " + maker.getClass().getName()); } rendererMakerMapping.put(mimes[i], 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 (int i = 0; i < mimes.length; i++) { //This overrides any event handler previously set for a MIME type if (eventHandlerMakerMapping.get(mimes[i]) != null) { log.trace("Overriding FOEventHandler for " + mimes[i] + " with " + maker.getClass().getName()); } eventHandlerMakerMapping.put(mimes[i], 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).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()); } } /** * 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).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()); } } /** * 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; } /** * 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.getRendererOverride() != null) { return userAgent.getRendererOverride(); } else { AbstractRendererMaker maker = getRendererMaker(outputFormat); if (maker == null) { throw new UnsupportedOperationException( "No renderer for the requested format available: " + outputFormat); } Renderer rend = maker.makeRenderer(userAgent); rend.setUserAgent(userAgent); String mimeType = rend.getMimeType(); //Always use main MIME type for this Configuration userRendererConfig = null; if (mimeType != null) { userRendererConfig = userAgent.getUserRendererConfig(mimeType); } if (userRendererConfig != null) { try { ContainerUtil.configure(rend, userRendererConfig); } catch (ConfigurationException e) { throw new FOPException(e); } } 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) { AbstractRendererMaker rendMaker = getRendererMaker(outputFormat); if (rendMaker == null && userAgent.getRendererOverride() == null) { 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."); } else { if (out == null && userAgent.getRendererOverride() == null && rendMaker.needsOutputStream()) { 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 { return maker.makeFOEventHandler(userAgent, out); } } } /** * @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(((String)iter.next())); } iter = this.eventHandlerMakerMapping.keySet().iterator(); while (iter.hasNext()) { lst.add(((String)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); } } } } }