/*
* Copyright (C) 2011 Laurent Caillette
*
* 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 3 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.outfit.xml;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import static com.google.common.base.Preconditions.checkNotNull;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
import org.novelang.outfit.loader.ResourceLoader;
import org.novelang.outfit.loader.ResourceName;
/**
* Resolves an URI into an XML {@link Source}, basing on a
* {@link org.novelang.outfit.loader.ResourceLoader}.
* This class offers a {@link #decorate(org.xml.sax.ContentHandler)} hook} to plug multiple content
* handlers, like for checking if XML element names for Novelang document tree are valid ones.
*
* @author Laurent Caillette
*/
public class LocalUriResolver implements URIResolver {
private static final Logger LOGGER = LoggerFactory.getLogger( LocalUriResolver.class ) ;
private final ResourceLoader resourceLoader ;
private final EntityResolver entityResolver ;
public LocalUriResolver(
final ResourceLoader resourceLoader,
final EntityResolver entityResolver
) {
this.resourceLoader = checkNotNull( resourceLoader ) ;
this.entityResolver = checkNotNull( entityResolver ) ;
}
@Override
public Source resolve( final String href, final String base ) throws TransformerException {
LOGGER.debug( "Resolving URI href='", href, "' base='", base, "'" ) ;
final SAXTransformerFactory saxTransformerFactory =
( SAXTransformerFactory ) TransformerFactory.newInstance() ;
saxTransformerFactory.setURIResolver( this ) ;
final XMLReader reader ;
try {
reader = new ForwardingXmlReader( XMLReaderFactory.createXMLReader() ) {
@Override
public void setContentHandler( final ContentHandler defaultContentHandler ) {
super.setContentHandler( decorate( defaultContentHandler ) ) ;
}
} ;
} catch( SAXException e ) {
throw new RuntimeException( e ) ;
}
reader.setEntityResolver( entityResolver ) ;
return new SAXSource(
reader,
new InputSource( resourceLoader.getInputStream( new ResourceName( href ) ) )
) ;
}
/**
* Hook for decorating original {@code ContentHandler} to tweak or listen to SAX events.
* No way to do that in the constructor, because each included document should have
* its own fresh {@code ContentHandler}s.
*
* @return a possibly null object.
*/
protected ContentHandler decorate( final ContentHandler original ) {
return original ;
}
}