/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library 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;
* version 2.1 of the License.
*
* This library 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.
*/
package org.geotools.xml;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.geotools.xml.handlers.DocumentHandler;
import org.geotools.xml.schema.Schema;
import org.xml.sax.SAXException;
/**
* This is the main entry point into the XSI parsing routines.
* <p>
* Example Use:
* <pre>
* Object x = DocumentFactory.getInstance(new URI("MyInstanceDocumentURI");
* </pre>
* </p>
* <p>
* A selection of the hints available to configure parsing:
* </p>
* <ul>
* <li>{@link #VALIDATION_HINT} - Boolean.FALSE to disable validation</li>
* <li>{@link DocumentHandler#DEFAULT_NAMESPACE_HINT_KEY} - {@link Schema} for parsing and validation</li>
* <li>{@link XMLHandlerHints#FLOW_HANDLER_HINT}</li>
* <li>{@link XMLHandlerHints#NAMESPACE_MAPPING} - Map<String,URL> namespace mapping</li>
* <li>{@link XMLHandlerHints#ENTITY_RESOLVER} - control entry resolution<li>
* <li>{@link #DISABLE_EXTERNAL_ENTITIES} - Boolean.TRUE to disable entity resolution<li>
* <li>{@link XMLHandlerHints#SAX_PARSER_FACTORY} - supply factory used by {@link #getParser(Map)}</li>
* </ul>
*
* @author dzwiers, Refractions Research, Inc. http://www.refractions.net
*
* @source $URL$
* http://svn.osgeo.org/geotools/trunk/modules/library/xml/src/main/java/org/geotools/xml
* /DocumentFactory.java $
* @version $Id$
*/
public class DocumentFactory {
/**
* When this hint is contained and set to Boolean.FALSE, element ordering will not be validated.
* This key may also affect data validation within the parse routines. The inherent safety of
* the resulting objects is weekend by turning this param to false.
*/
public static final String VALIDATION_HINT = "DocumentFactory_VALIDATION_HINT";
/**
* When this hint is contained and set to Boolean.TRUE, external entities will be disabled. This
* setting is used to alivate XXE attacks, preventing both {@link #VALIDATION_HINT} and
* {@link XMLHandlerHints#ENTITY_RESOLVER} from being effective.
*/
public static final String DISABLE_EXTERNAL_ENTITIES = "DocumentFactory_DISABLE_EXTERNAL_ENTITIES";
/**
* <p>
* calls getInstance(URI,Level) with Level.WARNING
* </p>
*
* @param desiredDocument
* @param hints
* May be null.
*
* @return Object
*
* @throws SAXException
*
* @see DocumentFactory#getInstance(URI, Map, Level)
*/
public static Object getInstance(URI desiredDocument, Map<String,Object> hints) throws SAXException {
return getInstance(desiredDocument, hints, Level.WARNING);
}
/**
* <p>
* Parses the instance data provided. This method assumes that the XML document is fully
* described using XML Schemas. Failure to be fully described as Schemas will result in errors,
* as opposed to a vid parse.
* </p>
*
* @param desiredDocument
* @param hints
* May be null.
* @param level
*
* @return Object
*
* @throws SAXException
*
* @see DocumentFactory#getInstance(URI, Map, Level, boolean)
*/
public static Object getInstance(URI desiredDocument, @SuppressWarnings("rawtypes") Map hints, Level level)
throws SAXException {
@SuppressWarnings("unchecked")
SAXParser parser = getParser(hints);
XMLSAXHandler xmlContentHandler = new XMLSAXHandler(desiredDocument, hints);
XMLSAXHandler.setLogLevel(level);
try {
parser.parse(desiredDocument.toString(), xmlContentHandler);
} catch (IOException e) {
throw new SAXException(e);
}
return xmlContentHandler.getDocument();
}
/**
* <p>
* Parses the instance data provided. This method assumes that the XML document is fully
* described using XML Schemas. Failure to be fully described as Schemas will result in errors,
* as opposed to a vid parse.
* </p>
*
* @param is
* @param hints
* May be null.
* @param level
*
* @return Object
*
* @throws SAXException
*
* @see DocumentFactory#getInstance(InputStream, Map, Level, boolean)
*/
public static Object getInstance(InputStream is, Map<String,Object> hints, Level level) throws SAXException {
SAXParser parser = getParser(hints);
XMLSAXHandler xmlContentHandler = new XMLSAXHandler(hints);
XMLSAXHandler.setLogLevel(level);
try {
parser.parse(is, xmlContentHandler);
} catch (IOException e) {
XMLSAXHandler.logger.warning(e.toString());
throw new SAXException(e);
}
return xmlContentHandler.getDocument();
}
/*
* Convenience method to create an instance of a SAXParser if it is null.
*/
private static SAXParser getParser(Map<String,Object> hints) throws SAXException {
SAXParserFactory spf = null;
if(hints != null && hints.containsKey(XMLHandlerHints.SAX_PARSER_FACTORY)) {
spf = (SAXParserFactory) hints.get(XMLHandlerHints.SAX_PARSER_FACTORY);
} else {
spf = SAXParserFactory.newInstance();
}
spf.setNamespaceAware(true);
spf.setValidating(false);
try {
// Extra precaution to reduce/prevent XXE attacks
//
// For more info: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
// https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet
// Step 1 distable DTD support - not needed for schema driven parser
//
// Note: XMLSaxHandler will reject all DTD references - but we may as well avoid early
// spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
// Step 2 optionally disable external entities
//
if (hints != null && hints.containsKey(DISABLE_EXTERNAL_ENTITIES)
&& Boolean.TRUE.equals(hints.get(DISABLE_EXTERNAL_ENTITIES))) {
spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
}
SAXParser sp = spf.newSAXParser();
return sp;
} catch (ParserConfigurationException e) {
throw new SAXException(e);
}
}
}