001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: RendererFactory.java 542237 2007-05-28 14:31:24Z jeremias $ */
019:
020: package org.apache.fop.render;
021:
022: import java.io.OutputStream;
023: import java.util.Collections;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: import org.apache.xmlgraphics.util.Service;
032:
033: import org.apache.fop.apps.FOPException;
034: import org.apache.fop.apps.FOUserAgent;
035: import org.apache.fop.area.AreaTreeHandler;
036: import org.apache.fop.fo.FOEventHandler;
037:
038: /**
039: * Factory for FOEventHandlers and Renderers.
040: */
041: public class RendererFactory {
042:
043: /** the logger */
044: private static Log log = LogFactory.getLog(RendererFactory.class);
045:
046: private Map rendererMakerMapping = new java.util.HashMap();
047: private Map eventHandlerMakerMapping = new java.util.HashMap();
048:
049: /**
050: * Main constructor.
051: */
052: public RendererFactory() {
053: discoverRenderers();
054: discoverFOEventHandlers();
055: }
056:
057: /**
058: * Add a new RendererMaker. If another maker has already been registered for a
059: * particular MIME type, this call overwrites the existing one.
060: * @param maker the RendererMaker
061: */
062: public void addRendererMaker(AbstractRendererMaker maker) {
063: String[] mimes = maker.getSupportedMimeTypes();
064: for (int i = 0; i < mimes.length; i++) {
065: //This overrides any renderer previously set for a MIME type
066: if (rendererMakerMapping.get(mimes[i]) != null) {
067: log.trace("Overriding renderer for " + mimes[i]
068: + " with " + maker.getClass().getName());
069: }
070: rendererMakerMapping.put(mimes[i], maker);
071: }
072: }
073:
074: /**
075: * Add a new FOEventHandlerMaker. If another maker has already been registered for a
076: * particular MIME type, this call overwrites the existing one.
077: * @param maker the FOEventHandlerMaker
078: */
079: public void addFOEventHandlerMaker(AbstractFOEventHandlerMaker maker) {
080: String[] mimes = maker.getSupportedMimeTypes();
081: for (int i = 0; i < mimes.length; i++) {
082: //This overrides any event handler previously set for a MIME type
083: if (eventHandlerMakerMapping.get(mimes[i]) != null) {
084: log.trace("Overriding FOEventHandler for " + mimes[i]
085: + " with " + maker.getClass().getName());
086: }
087: eventHandlerMakerMapping.put(mimes[i], maker);
088: }
089: }
090:
091: /**
092: * Add a new RendererMaker. If another maker has already been registered for a
093: * particular MIME type, this call overwrites the existing one.
094: * @param className the fully qualified class name of the RendererMaker
095: */
096: public void addRendererMaker(String className) {
097: try {
098: AbstractRendererMaker makerInstance = (AbstractRendererMaker) Class
099: .forName(className).newInstance();
100: addRendererMaker(makerInstance);
101: } catch (ClassNotFoundException e) {
102: throw new IllegalArgumentException("Could not find "
103: + className);
104: } catch (InstantiationException e) {
105: throw new IllegalArgumentException("Could not instantiate "
106: + className);
107: } catch (IllegalAccessException e) {
108: throw new IllegalArgumentException("Could not access "
109: + className);
110: } catch (ClassCastException e) {
111: throw new IllegalArgumentException(className
112: + " is not an "
113: + AbstractRendererMaker.class.getName());
114: }
115: }
116:
117: /**
118: * Add a new FOEventHandlerMaker. If another maker has already been registered for a
119: * particular MIME type, this call overwrites the existing one.
120: * @param className the fully qualified class name of the FOEventHandlerMaker
121: */
122: public void addFOEventHandlerMaker(String className) {
123: try {
124: AbstractFOEventHandlerMaker makerInstance = (AbstractFOEventHandlerMaker) Class
125: .forName(className).newInstance();
126: addFOEventHandlerMaker(makerInstance);
127: } catch (ClassNotFoundException e) {
128: throw new IllegalArgumentException("Could not find "
129: + className);
130: } catch (InstantiationException e) {
131: throw new IllegalArgumentException("Could not instantiate "
132: + className);
133: } catch (IllegalAccessException e) {
134: throw new IllegalArgumentException("Could not access "
135: + className);
136: } catch (ClassCastException e) {
137: throw new IllegalArgumentException(className
138: + " is not an "
139: + AbstractFOEventHandlerMaker.class.getName());
140: }
141: }
142:
143: /**
144: * Returns a RendererMaker which handles the given MIME type.
145: * @param mime the requested output format
146: * @return the requested RendererMaker or null if none is available
147: */
148: public AbstractRendererMaker getRendererMaker(String mime) {
149: AbstractRendererMaker maker = (AbstractRendererMaker) rendererMakerMapping
150: .get(mime);
151: return maker;
152: }
153:
154: /**
155: * Returns a FOEventHandlerMaker which handles the given MIME type.
156: * @param mime the requested output format
157: * @return the requested FOEventHandlerMaker or null if none is available
158: */
159: public AbstractFOEventHandlerMaker getFOEventHandlerMaker(
160: String mime) {
161: AbstractFOEventHandlerMaker maker = (AbstractFOEventHandlerMaker) eventHandlerMakerMapping
162: .get(mime);
163: return maker;
164: }
165:
166: /**
167: * Creates a Renderer object based on render-type desired
168: * @param userAgent the user agent for access to configuration
169: * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
170: * @return the new Renderer instance
171: * @throws FOPException if the renderer cannot be properly constructed
172: */
173: public Renderer createRenderer(FOUserAgent userAgent,
174: String outputFormat) throws FOPException {
175: if (userAgent.getRendererOverride() != null) {
176: return userAgent.getRendererOverride();
177: } else {
178: AbstractRendererMaker maker = getRendererMaker(outputFormat);
179: if (maker == null) {
180: throw new UnsupportedOperationException(
181: "No renderer for the requested format available: "
182: + outputFormat);
183: }
184: Renderer rend = maker.makeRenderer(userAgent);
185: rend.setUserAgent(userAgent);
186: RendererConfigurator configurator = maker
187: .getConfigurator(userAgent);
188: if (configurator != null) {
189: configurator.configure(rend);
190: }
191: return rend;
192: }
193: }
194:
195: /**
196: * Creates FOEventHandler instances based on the desired output.
197: * @param userAgent the user agent for access to configuration
198: * @param outputFormat the MIME type of the output format to use (ex. "application/pdf").
199: * @param out the OutputStream where the output is written to (if applicable)
200: * @return the newly constructed FOEventHandler
201: * @throws FOPException if the FOEventHandler cannot be properly constructed
202: */
203: public FOEventHandler createFOEventHandler(FOUserAgent userAgent,
204: String outputFormat, OutputStream out) throws FOPException {
205:
206: if (userAgent.getFOEventHandlerOverride() != null) {
207: return userAgent.getFOEventHandlerOverride();
208: } else {
209: AbstractFOEventHandlerMaker maker = getFOEventHandlerMaker(outputFormat);
210: if (maker == null) {
211: AbstractRendererMaker rendMaker = getRendererMaker(outputFormat);
212: if (rendMaker == null
213: && userAgent.getRendererOverride() == null) {
214: throw new UnsupportedOperationException(
215: "Don't know how to handle \""
216: + outputFormat
217: + "\" as an output format."
218: + " Neither an FOEventHandler, nor a Renderer could be found"
219: + " for this output format.");
220: } else {
221: if (out == null
222: && userAgent.getRendererOverride() == null
223: && rendMaker.needsOutputStream()) {
224: throw new FOPException(
225: "OutputStream has not been set");
226: }
227: //Found a Renderer so we need to construct an AreaTreeHandler.
228: return new AreaTreeHandler(userAgent, outputFormat,
229: out);
230: }
231: } else {
232: return maker.makeFOEventHandler(userAgent, out);
233: }
234: }
235: }
236:
237: /**
238: * @return an array of all supported MIME types
239: */
240: public String[] listSupportedMimeTypes() {
241: List lst = new java.util.ArrayList();
242: Iterator iter = this .rendererMakerMapping.keySet().iterator();
243: while (iter.hasNext()) {
244: lst.add(((String) iter.next()));
245: }
246: iter = this .eventHandlerMakerMapping.keySet().iterator();
247: while (iter.hasNext()) {
248: lst.add(((String) iter.next()));
249: }
250: Collections.sort(lst);
251: return (String[]) lst.toArray(new String[lst.size()]);
252: }
253:
254: /**
255: * Discovers Renderer implementations through the classpath and dynamically
256: * registers them.
257: */
258: private void discoverRenderers() {
259: // add mappings from available services
260: Iterator providers = Service.providers(Renderer.class);
261: if (providers != null) {
262: while (providers.hasNext()) {
263: AbstractRendererMaker maker = (AbstractRendererMaker) providers
264: .next();
265: try {
266: if (log.isDebugEnabled()) {
267: log
268: .debug("Dynamically adding maker for Renderer: "
269: + maker.getClass().getName());
270: }
271: addRendererMaker(maker);
272: } catch (IllegalArgumentException e) {
273: log.error("Error while adding maker for Renderer",
274: e);
275: }
276:
277: }
278: }
279: }
280:
281: /**
282: * Discovers FOEventHandler implementations through the classpath and dynamically
283: * registers them.
284: */
285: private void discoverFOEventHandlers() {
286: // add mappings from available services
287: Iterator providers = Service.providers(FOEventHandler.class);
288: if (providers != null) {
289: while (providers.hasNext()) {
290: AbstractFOEventHandlerMaker maker = (AbstractFOEventHandlerMaker) providers
291: .next();
292: try {
293: if (log.isDebugEnabled()) {
294: log
295: .debug("Dynamically adding maker for FOEventHandler: "
296: + maker.getClass().getName());
297: }
298: addFOEventHandlerMaker(maker);
299: } catch (IllegalArgumentException e) {
300: log
301: .error(
302: "Error while adding maker for FOEventHandler",
303: e);
304: }
305:
306: }
307: }
308: }
309:
310: }
|