blob: ecfe57af63e814607f7d592e4f4356b703f96878 [file] [log] [blame]
/*
* 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);
}
}
}
}
}