com.openhtmltopdf.resource.FSCatalog Maven / Gradle / Ivy
Show all versions of openhtmltopdf-core Show documentation
/*
* {{{ header & license
* FSCatalog.java
* Copyright (c) 2004, 2005 Patrick Wright
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* }}}
*/
package com.openhtmltopdf.resource;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import com.openhtmltopdf.util.XRLog;
import com.openhtmltopdf.util.XRRuntimeException;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
/**
* FSCatalog loads an XML catalog file to read mappings of public IDs for
* XML schemas/dtds, to resolve those mappings to a local store for the schemas.
* The catalog file allows one to have a single mapping of schema IDs to local
* files, and is useful when there are many schemas, or when schemas are broken
* into many smaller files. Currently FSCatalog only supports the very simple
* mapping of public id to local URI using the public element in the catalog XML.
*
* FSCatalog is not an EntityResolver; it only parses a catalog file. See
* {@link FSEntityResolver} for entity resolution.
*
* To use, instantiate the class, and call {@link #parseCatalog(InputSource)}
* to retrieve a {@link java.util.Map} keyed by public ids. The class uses
* an XMLReader instance retrieved via {@link XMLResource#newXMLReader()}, so
* XMLReader configuration (and specification) follows that of the standard XML
* parsing in Flying Saucer.
*
* This class is not safe for multi-threaded access.
*
* @author Patrick Wright
*/
public class FSCatalog {
/**
* Default constructor
*/
public FSCatalog() {
}
/**
* Parses an XML catalog file and returns a Map of public ids to local URIs read
* from the catalog. Only the catalog public elements are parsed.
*
* @param catalogURI A String URI to a catalog XML file on the classpath.
*/
public Map parseCatalog(String catalogURI) {
URL url;
Map map = null;
InputStream s = null;
try {
url = FSCatalog.class.getClassLoader().getResource(catalogURI);
s = new BufferedInputStream(url.openStream());
map = parseCatalog(new InputSource(s));
} catch (Exception ex) {
XRLog.xmlEntities(Level.WARNING, "Could not open XML catalog from URI '" + catalogURI + "'", ex);
map = new HashMap();
} finally {
try {
if (s != null) {
s.close();
}
} catch (IOException e) {
// ignore..
}
}
return map;
}
/**
* Parses an XML catalog file and returns a Map of public ids to local URIs read
* from the catalog. Only the catalog public elements are parsed.
*
* @param inputSource A SAX InputSource to a catalog XML file on the classpath.
*/
public Map parseCatalog(InputSource inputSource) {
XMLReader xmlReader = XMLResource.newXMLReader();
CatalogContentHandler ch = new CatalogContentHandler();
addHandlers(xmlReader, ch);
setFeature(xmlReader, "http://xml.org/sax/features/validation", false);
try {
xmlReader.parse(inputSource);
} catch (Exception ex) {
throw new RuntimeException("Failed on configuring SAX to DOM transformer.", ex);
}
return ch.getEntityMap();
}
/**
* Adds the default EntityResolved and ErrorHandler for the SAX parser.
*/
private void addHandlers(XMLReader xmlReader, ContentHandler ch) {
try {
// add our own entity resolver
xmlReader.setContentHandler(ch);
xmlReader.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException ex) {
if (XRLog.isLoggingEnabled()) {
XRLog.xmlEntities(Level.WARNING, ex.getMessage());
}
}
public void fatalError(SAXParseException ex) {
if (XRLog.isLoggingEnabled()) {
XRLog.xmlEntities(Level.WARNING, ex.getMessage());
}
}
public void warning(SAXParseException ex) {
if (XRLog.isLoggingEnabled()) {
XRLog.xmlEntities(Level.WARNING, ex.getMessage());
}
}
});
} catch (Exception ex) {
throw new XRRuntimeException("Failed on configuring SAX parser/XMLReader.", ex);
}
}
/**
* A SAX ContentHandler that reads an XML catalog file and builds a Map of
* public IDs to local URIs. Currently only handles the element and attributes.
* To use, just call XMLReader.setContentHandler() with an instance of the class,
* parse, then call getEntityMap().
*/
private static class CatalogContentHandler extends DefaultHandler {
private Map entityMap;
public CatalogContentHandler() {
this.entityMap = new HashMap();
}
/**
* Returns a Map of public Ids to local URIs
*/
public Map getEntityMap() {
return entityMap;
}
/**
* Receive notification of the beginning of an element; here used to pick up the mappings
* for public IDs to local URIs in the catalog.
*/
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) {
if (localName.equalsIgnoreCase("public") ||
(localName.equals("") && qName.equalsIgnoreCase("public"))) {
entityMap.put(atts.getValue("publicId"), atts.getValue("uri"));
}
}
}
/**
* Attempts to set requested feature on the parser; logs exception if not supported
* or not recognized.
*/
private void setFeature(XMLReader xmlReader, String featureUri, boolean value) {
try {
xmlReader.setFeature(featureUri, value);
XRLog.xmlEntities(Level.FINE, "SAX Parser feature: " +
featureUri.substring(featureUri.lastIndexOf("/")) +
" set to " +
xmlReader.getFeature(featureUri));
} catch (SAXNotSupportedException ex) {
XRLog.xmlEntities(Level.WARNING,
"SAX feature not supported on this XMLReader: " + featureUri);
} catch (SAXNotRecognizedException ex) {
XRLog.xmlEntities(Level.WARNING,
"SAX feature not recognized on this XMLReader: " +
featureUri + ". Feature may be properly named, but not recognized by this parser.");
}
}
}// end class