001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * ReportGenerator.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.modules.parser.base;
030:
031: import java.io.File;
032: import java.io.IOException;
033: import java.io.InputStream;
034: import java.io.OutputStreamWriter;
035: import java.io.Reader;
036: import java.net.URL;
037: import java.util.HashMap;
038: import java.util.Iterator;
039:
040: import org.jfree.io.IOUtils;
041: import org.jfree.report.JFreeReport;
042: import org.jfree.report.util.MemoryByteArrayOutputStream;
043: import org.jfree.resourceloader.FactoryParameterKey;
044: import org.jfree.resourceloader.Resource;
045: import org.jfree.resourceloader.ResourceCreationException;
046: import org.jfree.resourceloader.ResourceException;
047: import org.jfree.resourceloader.ResourceKey;
048: import org.jfree.resourceloader.ResourceKeyCreationException;
049: import org.jfree.resourceloader.ResourceLoadingException;
050: import org.jfree.resourceloader.ResourceManager;
051: import org.xml.sax.InputSource;
052:
053: /**
054: * The reportgenerator initializes the parser and provides an interface the the
055: * default parser.
056: * <p/>
057: * To create a report from an URL, use <code> ReportGenerator.getInstance().parseReport
058: * (URL myURl, URL contentBase); </code>
059: *
060: * @author Thomas Morgner
061: */
062: public class ReportGenerator {
063: /**
064: * Enable DTD validation of the parsed XML.
065: */
066: public static final String PARSER_VALIDATE_KEY = "org.jfree.report.modules.parser.base.Validate";
067:
068: /**
069: * disable DTD validation by default.
070: */
071: public static final boolean PARSER_VALIDATE_DEFAULT = true;
072:
073: /**
074: * The report generator.
075: */
076: private static ReportGenerator generator;
077:
078: private HashMap helperObjects;
079: private boolean validateDTD;
080:
081: /**
082: * Creates a new report generator. The generator uses the singleton pattern by
083: * default, so use generator.getInstance() to get the generator.
084: */
085: protected ReportGenerator() {
086: helperObjects = new HashMap();
087: }
088:
089: /**
090: * Set to false, to globaly disable the xml-validation.
091: *
092: * @param validate true, if the parser should validate the xml files.
093: */
094: public void setValidateDTD(final boolean validate) {
095: this .validateDTD = validate;
096: }
097:
098: /**
099: * returns true, if the parser should validate the xml files against the DTD
100: * supplied with JFreeReport.
101: *
102: * @return true, if the parser should validate, false otherwise.
103: */
104: public boolean isValidateDTD() {
105: return validateDTD;
106: }
107:
108: /**
109: * Parses a report using the given parameter as filename and the directory
110: * containing the file as content base.
111: *
112: * @param file the file name.
113: * @return the report.
114: * @throws java.io.IOException if an I/O error occurs.
115: */
116: public JFreeReport parseReport(final String file)
117: throws IOException, ResourceException {
118: if (file == null) {
119: throw new NullPointerException("File may not be null");
120: }
121:
122: return parseReport(new File(file));
123: }
124:
125: /**
126: * Parses an XML file which is loaded using the given URL. All needed relative
127: * file- and resourcespecification are loaded using the URL <code>file</code>
128: * as base.
129: *
130: * @param file the URL for the report template file.
131: * @return the report.
132: * @throws java.io.IOException if an I/O error occurs.
133: */
134: public JFreeReport parseReport(final URL file) throws IOException,
135: ResourceException {
136: return parseReport(file, file);
137: }
138:
139: /**
140: * Parses an XML file which is loaded using the given URL. All needed relative
141: * file- and resourcespecification are loaded using the URL
142: * <code>contentBase</code> as base.
143: * <p/>
144: * After the report is generated, the ReportDefinition-source and the
145: * contentbase are stored as string in the reportproperties.
146: *
147: * @param file the URL for the report template file.
148: * @param contentBase the URL for the report template content base.
149: * @return the parsed report.
150: */
151: public JFreeReport parseReport(final URL file, final URL contentBase)
152: throws ResourceException {
153: return parse(file, contentBase);
154: }
155:
156: /**
157: * Parses the report from a given URL.
158: *
159: * @param file the report definition location.
160: * @param contentBase the report's context (used to load content that has been
161: * referenced with relative URLs).
162: * @return the parsed report.
163: * @throws ResourceException if parsing or loading failed for some reason.
164: */
165: private JFreeReport parse(final URL file, final URL contentBase)
166: throws ResourceException {
167: final ResourceManager resourceManager = new ResourceManager();
168: resourceManager.registerDefaults();
169:
170: final ResourceKey contextKey = resourceManager
171: .createKey(contentBase);
172:
173: // Build the main key. That key also contains all context/parse-time
174: // parameters as they will influence the resulting report. It is not
175: // wise to keep caching independent from that.
176: final HashMap map = new HashMap();
177: final Iterator it = this .helperObjects.keySet().iterator();
178: while (it.hasNext()) {
179: final String name = (String) it.next();
180: map.put(new FactoryParameterKey(name), helperObjects
181: .get(name));
182: }
183:
184: final ResourceKey key = resourceManager.createKey(file, map);
185: final Resource resource = resourceManager.create(key,
186: contextKey, JFreeReport.class);
187: return (JFreeReport) resource.getResource();
188: }
189:
190: /** @noinspection IOResourceOpenedButNotSafelyClosed*/
191: private byte[] extractData(final InputSource input)
192: throws IOException {
193: final InputStream byteStream = input.getByteStream();
194: if (byteStream != null) {
195: try {
196: final MemoryByteArrayOutputStream bout = new MemoryByteArrayOutputStream();
197: IOUtils.getInstance().copyStreams(byteStream, bout);
198: return bout.toByteArray();
199: } finally {
200: byteStream.close();
201: }
202: }
203:
204: final Reader characterStream = input.getCharacterStream();
205: if (characterStream == null) {
206: throw new IOException(
207: "InputSource has neither an Byte nor a CharacterStream");
208: }
209:
210: try {
211: final MemoryByteArrayOutputStream bout = new MemoryByteArrayOutputStream();
212: final OutputStreamWriter owriter = new OutputStreamWriter(
213: bout);
214: IOUtils.getInstance().copyWriter(characterStream, owriter);
215: owriter.close();
216: return bout.toByteArray();
217: } finally {
218: characterStream.close();
219: }
220: }
221:
222: /**
223: * Parses an XML file which is loaded using the given file. All needed
224: * relative file- and resourcespecification are loaded using the parent
225: * directory of the file <code>file</code> as base.
226: *
227: * @param file the report template file.
228: * @return the parsed report.
229: * @throws java.io.IOException if an I/O error occurs.
230: */
231: public JFreeReport parseReport(final File file) throws IOException,
232: ResourceException {
233: if (file == null) {
234: throw new NullPointerException();
235: }
236: if (file.isDirectory()) {
237: throw new IOException("File is not a directory.");
238: }
239: final File contentBase = file.getCanonicalFile()
240: .getParentFile();
241: final ResourceManager resourceManager = new ResourceManager();
242: resourceManager.registerDefaults();
243:
244: final ResourceKey contextKey = resourceManager
245: .createKey(contentBase);
246:
247: // Build the main key. That key also contains all context/parse-time
248: // parameters as they will influence the resulting report. It is not
249: // wise to keep caching independent from that.
250: final HashMap map = new HashMap();
251: final Iterator it = this .helperObjects.keySet().iterator();
252: while (it.hasNext()) {
253: final String name = (String) it.next();
254: map.put(new FactoryParameterKey(name), helperObjects
255: .get(name));
256: }
257:
258: final ResourceKey key = resourceManager.createKey(file, map);
259: final Resource resource = resourceManager.create(key,
260: contextKey, JFreeReport.class);
261: return (JFreeReport) resource.getResource();
262: }
263:
264: /**
265: * Parses the report from a given SAX-InputSource.
266: *
267: * @param input the report definition location.
268: * @param contentBase the report's context (used to load content that has been
269: * referenced with relative URLs).
270: * @return the parsed report.
271: * @throws ResourceException if parsing or loading failed for some reason.
272: * @throws IOException if an IO-related error occurs.
273: */
274: public JFreeReport parseReport(final InputSource input,
275: final URL contentBase) throws IOException,
276: ResourceException {
277: if (input.getCharacterStream() != null) {
278: // Sourceforge Bug #1712734. We cannot safely route the character-stream through libloader.
279: // Therefore we skip libloader and parse the report directly. This is for backward compatibility,
280: // all other xml-based objects will still rely on LibLoader.
281:
282: return parseReportDirectly(input, contentBase);
283: }
284:
285: final byte[] bytes = extractData(input);
286: final ResourceManager resourceManager = new ResourceManager();
287: resourceManager.registerDefaults();
288:
289: final ResourceKey contextKey;
290: if (contentBase != null) {
291: contextKey = resourceManager.createKey(contentBase);
292: } else {
293: contextKey = null;
294: }
295: final HashMap map = new HashMap();
296:
297: final Iterator it = this .helperObjects.keySet().iterator();
298: while (it.hasNext()) {
299: final String name = (String) it.next();
300: map.put(new FactoryParameterKey(name), helperObjects
301: .get(name));
302: }
303:
304: final ResourceKey key = resourceManager.createKey(bytes, map);
305: final Resource resource = resourceManager.create(key,
306: contextKey, JFreeReport.class);
307: return (JFreeReport) resource.getResource();
308: }
309:
310: private JFreeReport parseReportDirectly(final InputSource input,
311: final URL contentBase) throws ResourceKeyCreationException,
312: ResourceCreationException, ResourceLoadingException {
313: final ResourceManager manager = new ResourceManager();
314: manager.registerDefaults();
315:
316: final HashMap map = new HashMap();
317:
318: final Iterator it = this .helperObjects.keySet().iterator();
319: while (it.hasNext()) {
320: final String name = (String) it.next();
321: map.put(new FactoryParameterKey(name), helperObjects
322: .get(name));
323: }
324:
325: final JFreeReportXmlResourceFactory resourceFactory = new JFreeReportXmlResourceFactory();
326: resourceFactory.initializeDefaults();
327: if (contentBase != null) {
328: return (JFreeReport) resourceFactory.parseDirectly(manager,
329: input, manager.createKey(contentBase), map);
330: } else {
331: return (JFreeReport) resourceFactory.parseDirectly(manager,
332: input, null, map);
333: }
334: }
335:
336: /**
337: * Parses the report.
338: *
339: * @param manager the resource manager (can be null).
340: * @param input the resource key pointing to the report definition.
341: * @param contextKey the report's context (used to load content that has been
342: * referenced with relative URLs).
343: * @return the parsed report.
344: * @throws ResourceException if parsing or loading failed for some reason.
345: */
346: public JFreeReport parseReport(ResourceManager manager,
347: final ResourceKey input, final ResourceKey contextKey)
348: throws ResourceException {
349: if (manager == null) {
350: manager = new ResourceManager();
351: manager.registerDefaults();
352: }
353:
354: final HashMap map = new HashMap(input.getFactoryParameters());
355: final Iterator it = this .helperObjects.keySet().iterator();
356: while (it.hasNext()) {
357: final String name = (String) it.next();
358: map.put(new FactoryParameterKey(name), helperObjects
359: .get(name));
360: }
361:
362: final ResourceKey key = new ResourceKey(input.getParent(),
363: input.getSchema(), input.getIdentifier(), input
364: .getFactoryParameters());
365: final Resource resource = manager.create(key, contextKey,
366: JFreeReport.class);
367: return (JFreeReport) resource.getResource();
368: }
369:
370: /**
371: * Returns a single shared instance of the <code>ReportGenerator</code>. This
372: * instance cannot add helper objects to configure the report parser.
373: *
374: * @return The shared report generator.
375: */
376: public static synchronized ReportGenerator getInstance() {
377: if (generator == null) {
378: generator = new ReportGenerator();
379: }
380: return generator;
381: }
382:
383: /**
384: * Returns a private (non-shared) instance of the <code>ReportGenerator</code>.
385: * Use this instance when defining helper objects.
386: *
387: * @return The shared report generator.
388: */
389: public static ReportGenerator createInstance() {
390: return new ReportGenerator();
391: }
392:
393: /**
394: * Assigns a parse-context object.
395: *
396: * @param key the parse-context key used to lookup the object later.
397: * @param value the value.
398: */
399: public void setObject(final String key, final Object value) {
400: if (key == null) {
401: throw new NullPointerException();
402: }
403: if (value == null) {
404: helperObjects.remove(key);
405: } else {
406: helperObjects.put(key, value);
407: }
408: }
409:
410: /**
411: * Returns the parse context object for the given key.
412: *
413: * @param key the key.
414: * @return the value or null if there is no such value.
415: */
416: public Object getObject(final String key) {
417: return helperObjects.get(key);
418: }
419: }
|