Source for org.jfree.xml.ParserFrontend

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  * 
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * --------------------
  28:  * ReportGenerator.java
  29:  * --------------------
  30:  * (C)opyright 2002-2005, by Thomas Morgner and Contributors.
  31:  *
  32:  * Original Author:  Thomas Morgner (taquera@sherito.org);
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *
  35:  * $Id: ParserFrontend.java,v 1.8 2005/11/14 10:58:19 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 10-May-2002 : Initial version
  40:  * 12-Dec-2002 : Fixed issues reported by Checkstyle (DG);
  41:  * 29-Apr-2003 : Distilled from the JFreeReport project and moved into JCommon
  42:  *
  43:  */
  44: 
  45: package org.jfree.xml;
  46: 
  47: import java.io.BufferedInputStream;
  48: import java.io.IOException;
  49: import java.net.URL;
  50: import javax.xml.parsers.ParserConfigurationException;
  51: import javax.xml.parsers.SAXParser;
  52: import javax.xml.parsers.SAXParserFactory;
  53: 
  54: import org.jfree.util.Log;
  55: import org.xml.sax.EntityResolver;
  56: import org.xml.sax.InputSource;
  57: import org.xml.sax.SAXException;
  58: import org.xml.sax.XMLReader;
  59: 
  60: /**
  61:  * The reportgenerator initializes the parser and provides an interface
  62:  * the the default parser.
  63:  *
  64:  * To create a report from an URL, use
  65:  * <code>
  66:  * ReportGenerator.getInstance().parseReport (URL myURl, URL contentBase);
  67:  * </code>
  68:  *
  69:  * @author Thomas Morgner
  70:  */
  71: public class ParserFrontend {
  72: 
  73:     /** The report handler. */
  74:     private FrontendDefaultHandler defaulthandler;
  75: 
  76:     /** The parser factory. */
  77:     private SAXParserFactory factory;
  78: 
  79:     /** The DTD. */
  80:     private EntityResolver entityResolver;
  81: 
  82:     /** A flag indicating whether to use a DTD to validate the xml input. */
  83:     private boolean validateDTD;
  84: 
  85:     /**
  86:      * Creates a new report generator. The generator uses the singleton pattern by default,
  87:      * so use generator.getInstance() to get the generator.
  88:      *
  89:      * @param parser the parser that is used to coordinate the parsing process.
  90:      */
  91:     protected ParserFrontend(final FrontendDefaultHandler parser) {
  92:         if (parser == null) {
  93:             throw new NullPointerException();
  94:         }
  95:         this.defaulthandler = parser;
  96:     }
  97: 
  98:     /**
  99:      * 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: }