/*
* Copyright (c) 2014 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.werval.modules.xml.internal;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.validation.Schema;
import io.werval.modules.xml.SAX;
import io.werval.modules.xml.UncheckedXMLException;
import org.xml.sax.HandlerBase;
import org.xml.sax.InputSource;
import org.xml.sax.Parser;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import static io.werval.modules.xml.internal.Internal.ACCESS_EXTERNAL_ALL;
import static io.werval.modules.xml.internal.Internal.ACCESS_EXTERNAL_NONE;
import static io.werval.modules.xml.internal.Internal.LOG;
/**
* SAXParserFactory implementation for XMLPlugin.
* <p>
* Factory API that enables applications to configure and obtain a SAX based parser to parse XML documents.
*
* @see SAXParserFactory
*/
public final class SAXParserFactoryImpl
extends SAXParserFactory
{
// Aalto
// private final SAXParserFactory delegate = new com.fasterxml.aalto.sax.SAXParserFactoryImpl();
// Woodstox
// private final SAXParserFactory delegate = new com.ctc.wstx.sax.WstxSAXParserFactory();
// Xerces
private final SAXParserFactory delegate = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
public SAXParserFactoryImpl()
throws ParserConfigurationException, SAXException
{
// Aalto & Woodstox & Xerces
delegate.setFeature( SAX.Features.EXTERNAL_GENERAL_ENTITIES, Internal.EXTERNAL_ENTITIES.get() );
delegate.setFeature( SAX.Features.EXTERNAL_PARAMETER_ENTITIES, Internal.EXTERNAL_ENTITIES.get() );
// Xerces
delegate.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
delegate.setFeature( "http://apache.org/xml/features/standard-uri-conformant", true );
setValidating( false );
// No support but should be disabled anyway, belt'n braces
delegate.setXIncludeAware( false );
}
@Override
public void setNamespaceAware( boolean namespaceAware )
{
delegate.setNamespaceAware( namespaceAware );
}
@Override
public void setValidating( boolean dtdValidation )
{
try
{
// Xerces
delegate.setFeature( "http://apache.org/xml/features/validation/balance-syntax-trees", dtdValidation );
delegate.setFeature( "http://apache.org/xml/features/nonvalidating/load-dtd-grammar", dtdValidation );
delegate.setFeature(
"http://apache.org/xml/features/nonvalidating/load-external-dtd",
dtdValidation && Internal.EXTERNAL_ENTITIES.get()
);
delegate.setFeature( "http://apache.org/xml/features/disallow-doctype-decl", !dtdValidation );
}
catch( ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException ex )
{
throw new UncheckedXMLException( ex );
}
delegate.setValidating( dtdValidation );
if( dtdValidation )
{
LOG.warn( "SAXParserFactory.setValidating( true ) Unsafe DTD support enabled" );
}
}
@Override
public boolean isNamespaceAware()
{
return delegate.isNamespaceAware();
}
@Override
public boolean isValidating()
{
return delegate.isValidating();
}
@Override
public Schema getSchema()
{
return delegate.getSchema();
}
@Override
public void setSchema( Schema schema )
{
delegate.setSchema( schema );
}
@Override
public void setXIncludeAware( boolean xIncludeAware )
{
delegate.setXIncludeAware( xIncludeAware );
}
@Override
public boolean isXIncludeAware()
{
return delegate.isXIncludeAware();
}
@Override
public SAXParser newSAXParser()
throws ParserConfigurationException, SAXException
{
return new SAXParserImpl( delegate.newSAXParser() );
}
@Override
public void setFeature( String name, boolean value )
throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException
{
delegate.setFeature( name, value );
}
@Override
public boolean getFeature( String name )
throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException
{
return delegate.getFeature( name );
}
private static final class SAXParserImpl
extends SAXParser
{
private final SAXParser parser;
protected SAXParserImpl( SAXParser saxParser )
throws SAXException
{
this.parser = saxParser;
try
{
this.parser.setProperty(
XMLConstants.ACCESS_EXTERNAL_DTD,
Internal.EXTERNAL_ENTITIES.get() ? ACCESS_EXTERNAL_ALL : ACCESS_EXTERNAL_NONE
);
}
catch( SAXException ex )
{
LOG.trace( "JAXP<1.5 - {} on {}", ex.getMessage(), this.parser );
}
try
{
this.parser.setProperty(
XMLConstants.ACCESS_EXTERNAL_SCHEMA,
Internal.EXTERNAL_ENTITIES.get() ? ACCESS_EXTERNAL_ALL : ACCESS_EXTERNAL_NONE
);
}
catch( SAXException ex )
{
LOG.trace( "JAXP<1.5 - {} on {}", ex.getMessage(), this.parser );
}
}
@Override
public void reset()
{
parser.reset();
}
@Override
public void parse( InputStream inputStream, HandlerBase handlerBase )
throws SAXException, IOException
{
parser.parse( inputStream, handlerBase );
}
@Override
public void parse( InputStream inputStream, HandlerBase handlerBase, String systemId )
throws SAXException, IOException
{
parser.parse( inputStream, handlerBase, systemId );
}
@Override
public void parse( InputStream inputStream, DefaultHandler defaultHandler )
throws SAXException, IOException
{
parser.parse( inputStream, defaultHandler );
}
@Override
public void parse( InputStream inputStream, DefaultHandler defaultHandler, String systemId )
throws SAXException, IOException
{
parser.parse( inputStream, defaultHandler, systemId );
}
@Override
public void parse( String s, HandlerBase handlerBase )
throws SAXException, IOException
{
parser.parse( s, handlerBase );
}
@Override
public void parse( String s, DefaultHandler defaultHandler )
throws SAXException, IOException
{
parser.parse( s, defaultHandler );
}
@Override
public void parse( File file, HandlerBase handlerBase )
throws SAXException, IOException
{
parser.parse( file, handlerBase );
}
@Override
public void parse( File file, DefaultHandler defaultHandler )
throws SAXException, IOException
{
parser.parse( file, defaultHandler );
}
@Override
public void parse( InputSource inputSource, HandlerBase handlerBase )
throws SAXException, IOException
{
parser.parse( inputSource, handlerBase );
}
@Override
public void parse( InputSource inputSource, DefaultHandler defaultHandler )
throws SAXException, IOException
{
parser.parse( inputSource, defaultHandler );
}
@Override
public Parser getParser()
throws SAXException
{
return parser.getParser();
}
@Override
public XMLReader getXMLReader()
throws SAXException
{
XMLReader reader = parser.getXMLReader();
try
{
reader.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
}
catch( SAXNotRecognizedException ex )
{
LOG.trace( "JAXP<1.5 - {} on {}", ex.getMessage(), reader );
}
reader.setFeature( SAX.Features.EXTERNAL_GENERAL_ENTITIES, Internal.EXTERNAL_ENTITIES.get() );
reader.setFeature( SAX.Features.EXTERNAL_PARAMETER_ENTITIES, Internal.EXTERNAL_ENTITIES.get() );
reader.setEntityResolver( Internal.RESOLVER.get() );
reader.setErrorHandler( Errors.INSTANCE );
return reader;
}
@Override
public boolean isNamespaceAware()
{
return parser.isNamespaceAware();
}
@Override
public boolean isValidating()
{
return parser.isValidating();
}
@Override
public void setProperty( String name, Object value )
throws SAXNotRecognizedException, SAXNotSupportedException
{
parser.setProperty( name, value );
}
@Override
public Object getProperty( String name )
throws SAXNotRecognizedException, SAXNotSupportedException
{
return parser.getProperty( name );
}
@Override
public Schema getSchema()
{
return parser.getSchema();
}
@Override
public boolean isXIncludeAware()
{
return parser.isXIncludeAware();
}
}
}