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 045package org.jfree.xml; 046 047import java.io.BufferedInputStream; 048import java.io.IOException; 049import java.net.URL; 050import javax.xml.parsers.ParserConfigurationException; 051import javax.xml.parsers.SAXParser; 052import javax.xml.parsers.SAXParserFactory; 053 054import org.jfree.util.Log; 055import org.xml.sax.EntityResolver; 056import org.xml.sax.InputSource; 057import org.xml.sax.SAXException; 058import 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 */ 071public 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}