/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.libraries.xmlns.parser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; /** * Resolves the JFreeReport DTD specification and routes the parser to a local copy. * * @author Thomas Morgner */ public final class ParserEntityResolver implements EntityResolver { private static final Log logger = LogFactory.getLog( ParserEntityResolver.class ); /** * The hashtable for the known entities (deprecated DTDs). */ private final HashMap deprecatedDTDs; /** * The hashtable for the known entities. */ private final HashMap dtds; /** * The singleton instance of this entity resolver. */ private static ParserEntityResolver singleton; /** * Creates a new, uninitialized ParserEntityResolver. */ private ParserEntityResolver() { dtds = new HashMap(); deprecatedDTDs = new HashMap(); } /** * Defines a DTD used to validate the report definition. Your XMLParser must be a validating parser for this feature * to work. * * @param publicID the public ID. * @param location the URL. * @return A boolean. */ public boolean setDTDLocation( final String publicID, final URL location ) { if ( isValid( location ) ) { this.dtds.put( publicID, location ); return true; } else { logger.warn( "Validate location failed for " + publicID + " location: " + location ); return false; } } /** * Defines a DTD used to validate the report definition. Your XMLParser must be a validating parser for this feature * to work. * * @param systemId the system ID for the DTD. * @param publicID the public ID. * @param location the URL. * @return A boolean. */ public boolean setDTDLocation( final String publicID, final String systemId, final URL location ) { if ( isValid( location ) ) { this.dtds.put( publicID, location ); this.dtds.put( systemId, location ); return true; } else { logger.warn( "Validate location failed for " + publicID + " location: " + location ); return false; } } /** * Sets the location of the DTD. This is used for validating XML parsers to validate the structure of the report * definition. * * @param publicID the id. * @return the URL for the DTD. */ public URL getDTDLocation( final String publicID ) { return (URL) dtds.get( publicID ); } /** * Checks whether the speficied URL is readable. * * @param reportDtd the url pointing to the local DTD copy. * @return true, if the URL can be read, false otherwise. */ private boolean isValid( final URL reportDtd ) { if ( reportDtd == null ) { return false; } try { final InputStream uc = reportDtd.openStream(); uc.close(); return true; } catch ( IOException ioe ) { return false; } } /** * Allow the application to resolve external entities. * <p/> * Resolves the DTD definition to point to a local copy, if the specified public ID is known to this resolver. * * @param publicId the public ID. * @param systemId the system ID. * @return The input source. */ public InputSource resolveEntity( final String publicId, final String systemId ) { try { // cannot validate without public id ... if ( publicId == null ) { //Log.debug ("No PUBLIC ID, cannot continue"); if ( systemId != null ) { final URL location = getDTDLocation( systemId ); if ( location != null ) { final InputSource inputSource = new InputSource( location.openStream() ); inputSource.setSystemId( systemId ); return inputSource; } } return null; } final URL location = getDTDLocation( publicId ); if ( location != null ) { final InputSource inputSource = new InputSource( location.openStream() ); inputSource.setSystemId( systemId ); inputSource.setPublicId( publicId ); return inputSource; } final String message = getDeprecatedDTDMessage( publicId ); if ( message != null ) { logger.info( message ); } else { logger.info( "A public ID was given for the document, but it was unknown or invalid." ); } return null; } catch ( IOException ioe ) { logger.warn( "Unable to open specified DTD", ioe ); } return null; } /** * Returns a default resolver, which is initialized to redirect the parser to a local copy of the JFreeReport DTDs. * * @return the default entity resolver. */ public static synchronized ParserEntityResolver getDefaultResolver() { if ( singleton == null ) { singleton = new ParserEntityResolver(); } return singleton; } /** * Defines that the given public ID should be deprecated and provides a log-message along with the deprecation. * * @param publicID the public id that should be considered deprecated. * @param message the message to present to the user to warn them about their use of deprecated DTDs. */ public void setDeprecatedDTDMessage( final String publicID, final String message ) { deprecatedDTDs.put( publicID, message ); } /** * Returns deprecation message for the given public ID. * * @param publicID the public id that should be considered deprecated. * @return the deprecation message or null if the ID is not considered deprecated. */ public String getDeprecatedDTDMessage( final String publicID ) { return (String) deprecatedDTDs.get( publicID ); } }