001 /* ========================================================================
002 * JCommon : a free general purpose class library for the Java(tm) platform
003 * ========================================================================
004 *
005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jcommon/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * --------------------
028 * ReportGenerator.java
029 * --------------------
030 * (C)opyright 2002-2005, by Thomas Morgner and Contributors.
031 *
032 * Original Author: Thomas Morgner (taquera@sherito.org);
033 * Contributor(s): David Gilbert (for Object Refinery Limited);
034 *
035 * $Id: ParserFrontend.java,v 1.8 2005/11/14 10:58:19 mungady Exp $
036 *
037 * Changes
038 * -------
039 * 10-May-2002 : Initial version
040 * 12-Dec-2002 : Fixed issues reported by Checkstyle (DG);
041 * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
042 *
043 */
044
045 package org.jfree.xml;
046
047 import java.io.BufferedInputStream;
048 import java.io.IOException;
049 import java.net.URL;
050 import javax.xml.parsers.ParserConfigurationException;
051 import javax.xml.parsers.SAXParser;
052 import javax.xml.parsers.SAXParserFactory;
053
054 import org.jfree.util.Log;
055 import org.xml.sax.EntityResolver;
056 import org.xml.sax.InputSource;
057 import org.xml.sax.SAXException;
058 import org.xml.sax.XMLReader;
059
060 /**
061 * The reportgenerator initializes the parser and provides an interface
062 * the the default parser.
063 *
064 * To create a report from an URL, use
065 * <code>
066 * ReportGenerator.getInstance().parseReport (URL myURl, URL contentBase);
067 * </code>
068 *
069 * @author Thomas Morgner
070 */
071 public class ParserFrontend {
072
073 /** The report handler. */
074 private FrontendDefaultHandler defaulthandler;
075
076 /** The parser factory. */
077 private SAXParserFactory factory;
078
079 /** The DTD. */
080 private EntityResolver entityResolver;
081
082 /** A flag indicating whether to use a DTD to validate the xml input. */
083 private boolean validateDTD;
084
085 /**
086 * Creates a new report generator. The generator uses the singleton pattern by default,
087 * so use generator.getInstance() to get the generator.
088 *
089 * @param parser the parser that is used to coordinate the parsing process.
090 */
091 protected ParserFrontend(final FrontendDefaultHandler parser) {
092 if (parser == null) {
093 throw new NullPointerException();
094 }
095 this.defaulthandler = parser;
096 }
097
098 /**
099 * Returns <code>true</code> if the report definition should be validated against the
100 * DTD, and <code>false</code> otherwise.
101 *
102 * @return A boolean.
103 */
104 public boolean isValidateDTD() {
105 return this.validateDTD;
106 }
107
108 /**
109 * Sets a flag that controls whether or not the report definition is validated
110 * against the DTD.
111 *
112 * @param validateDTD the flag.
113 */
114 public void setValidateDTD(final boolean validateDTD) {
115 this.validateDTD = validateDTD;
116 }
117
118 /**
119 * Returns the entity resolver.
120 *
121 * @return The entity resolver.
122 */
123 public EntityResolver getEntityResolver() {
124 return this.entityResolver;
125 }
126
127 /**
128 * Sets the entity resolver.
129 *
130 * @param entityResolver the entity resolver.
131 */
132 public void setEntityResolver(final EntityResolver entityResolver) {
133 this.entityResolver = entityResolver;
134 }
135
136 /**
137 * Returns a SAX parser.
138 *
139 * @return a SAXParser.
140 *
141 * @throws ParserConfigurationException if there is a problem configuring the parser.
142 * @throws SAXException if there is a problem with the parser initialisation
143 */
144 protected SAXParser getParser() throws ParserConfigurationException, SAXException {
145 if (this.factory == null) {
146 this.factory = SAXParserFactory.newInstance();
147 if (isValidateDTD()) {
148 try {
149 // dont touch the validating feature, if not needed ..
150 this.factory.setValidating(true);
151 }
152 catch (Exception ex) {
153 // the parser does not like the idea of validating ...
154 Log.debug("The parser will not validate the xml document.", ex);
155 }
156 }
157 }
158 return this.factory.newSAXParser();
159 }
160
161 /**
162 * Sets the default handler used for parsing reports. This handler is used to
163 * initiate parsing.
164 *
165 * @param handler the handler.
166 */
167 public void setDefaultHandler(final FrontendDefaultHandler handler) {
168 if (handler == null) {
169 throw new NullPointerException();
170 }
171 this.defaulthandler = handler;
172 }
173
174 /**
175 * Returns the ElementDefinitionHandler used for parsing reports.
176 *
177 * @return the report handler.
178 */
179 public FrontendDefaultHandler getDefaultHandler() {
180 return this.defaulthandler;
181 }
182
183 /**
184 * Creates a new instance of the currently set default handler and sets the contentbase
185 * for the handler to <code>contentBase</code>.
186 *
187 * @param contentBase the content base.
188 *
189 * @return the report handler.
190 */
191 protected FrontendDefaultHandler createDefaultHandler(final URL contentBase) {
192 final FrontendDefaultHandler handler = getDefaultHandler().newInstance();
193 if (contentBase != null) {
194 handler.setConfigProperty(Parser.CONTENTBASE_KEY, contentBase.toExternalForm());
195 }
196 return handler;
197 }
198
199 /**
200 * Parses an XML report template file.
201 *
202 * @param input the input source.
203 * @param contentBase the content base.
204 *
205 * @return the report.
206 *
207 * @throws ElementDefinitionException if an error occurred.
208 */
209 protected Object parse(final InputSource input, final URL contentBase)
210 throws ElementDefinitionException {
211 try {
212 final SAXParser parser = getParser();
213 final XMLReader reader = parser.getXMLReader();
214
215 try {
216 reader.setFeature("http://xml.org/sax/features/validation", isValidateDTD());
217 }
218 catch (SAXException se) {
219 Log.debug("The XMLReader will not validate the xml document.", se);
220 }
221 final FrontendDefaultHandler handler = createDefaultHandler(contentBase);
222 configureReader(reader, handler);
223 try {
224 reader.setContentHandler(handler);
225 reader.setDTDHandler(handler);
226 if (getEntityResolver() != null) {
227 reader.setEntityResolver(getEntityResolver());
228 }
229 reader.setErrorHandler(handler);
230 reader.parse(input);
231 return handler.getResult();
232 }
233 catch (IOException e) {
234 throw new ElementDefinitionException(e);
235 }
236 }
237 catch (ParserConfigurationException e) {
238 throw new ElementDefinitionException(e);
239 }
240 catch (SAXException e) {
241 throw new ElementDefinitionException(e);
242 }
243 }
244
245 /**
246 * Configures the xml reader. Use this to set features or properties
247 * before the documents get parsed.
248 *
249 * @param handler the parser implementation that will handle the SAX-Callbacks.
250 * @param reader the xml reader that should be configured.
251 */
252 protected void configureReader(final XMLReader reader, final FrontendDefaultHandler handler) {
253 try {
254 reader.setProperty
255 ("http://xml.org/sax/properties/lexical-handler", handler.getCommentHandler());
256 }
257 catch (SAXException se) {
258 Log.debug("Comments are not supported by this SAX implementation.");
259 }
260 }
261
262 /**
263 * Parses an XML file which is loaded using the given URL. All
264 * needed relative file- and resourcespecification are loaded
265 * using the URL <code>contentBase</code> as base.
266 * <p>
267 * After the report is generated, the ReportDefinition-source and the contentbase are
268 * stored as string in the reportproperties.
269 *
270 * @param file the URL for the report template file.
271 * @param contentBase the URL for the report template content base.
272 *
273 * @return the parsed report.
274 *
275 * @throws IOException if an I/O error occurs.
276 * @throws ElementDefinitionException if there is a problem parsing the report template.
277 */
278 public Object parse(final URL file, final URL contentBase)
279 throws ElementDefinitionException, IOException {
280 if (file == null) {
281 throw new NullPointerException("File may not be null");
282 }
283
284 final BufferedInputStream bin = new BufferedInputStream(file.openStream());
285 final InputSource in = new InputSource(bin);
286 in.setSystemId(file.toString());
287 final Object result = parse(in, contentBase);
288 bin.close();
289 return result;
290 }
291
292 }