package com.scrivenvar.processors;
import com.scrivenvar.Services;
import com.scrivenvar.service.Snitch;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.ProcessingInstruction;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.TransformerFactoryImpl;
import static net.sf.saxon.tree.util.ProcInstParser.getPseudoAttribute;
public class XMLProcessor extends AbstractProcessor<String>
implements ErrorListener {
private final Snitch snitch = Services.load( Snitch.class );
private XMLInputFactory xmlInputFactory;
private TransformerFactory transformerFactory;
private Transformer transformer;
private Path path;
public XMLProcessor( final Processor<String> processor, final Path path ) {
super( processor );
setPath( path );
}
@Override
public String processLink( final String text ) {
try {
return text.isEmpty() ? text : transform( text );
} catch( final Exception ex ) {
throw new RuntimeException( ex );
}
}
private String transform( final String text ) throws Exception {
final String template = getXsltFilename( text );
final Path xsl = getXslPath( template );
try(
final StringWriter output = new StringWriter( text.length() );
final StringReader input = new StringReader( text ) ) {
getSnitch().listen( xsl );
getTransformer( xsl ).transform(
new StreamSource( input ),
new StreamResult( output )
);
return output.toString();
}
}
private Transformer getTransformer( final Path xsl )
throws TransformerConfigurationException, IOException {
if( this.transformer == null ) {
this.transformer = createTransformer( xsl );
}
return this.transformer;
}
protected Transformer createTransformer( final Path xsl )
throws TransformerConfigurationException {
final Source xslt = new StreamSource( xsl.toFile() );
return getTransformerFactory().newTransformer( xslt );
}
private Path getXslPath( final String filename ) {
final Path xmlPath = getPath();
final File xmlDirectory = xmlPath.toFile().getParentFile();
return Paths.get( xmlDirectory.getPath(), filename );
}
private String getXsltFilename( final String xml )
throws XMLStreamException, ParseException {
String result = "";
try( final StringReader sr = new StringReader( xml ) ) {
boolean found = false;
int count = 0;
final XMLEventReader reader = createXMLEventReader( sr );
while( !found && reader.hasNext() && count++ < 10 ) {
final XMLEvent event = reader.nextEvent();
if( event.isProcessingInstruction() ) {
final ProcessingInstruction pi = (ProcessingInstruction)event;
final String target = pi.getTarget();
if( "xml-stylesheet".equalsIgnoreCase( target ) ) {
result = getPseudoAttribute( pi.getData(), "href" );
found = true;
}
}
}
}
return result;
}
private XMLEventReader createXMLEventReader( final Reader reader )
throws XMLStreamException {
return getXMLInputFactory().createXMLEventReader( reader );
}
private synchronized XMLInputFactory getXMLInputFactory() {
if( this.xmlInputFactory == null ) {
this.xmlInputFactory = createXMLInputFactory();
}
return this.xmlInputFactory;
}
private XMLInputFactory createXMLInputFactory() {
return XMLInputFactory.newInstance();
}
private synchronized TransformerFactory getTransformerFactory() {
if( this.transformerFactory == null ) {
this.transformerFactory = createTransformerFactory();
}
return this.transformerFactory;
}
private TransformerFactory createTransformerFactory() {
final TransformerFactory factory = new TransformerFactoryImpl();
factory.setErrorListener( this );
return factory;
}
@Override
public void warning( final TransformerException ex ) {
throw new RuntimeException( ex );
}
@Override
public void error( final TransformerException ex ) {
throw new RuntimeException( ex );
}
@Override
public void fatalError( final TransformerException ex ) {
throw new RuntimeException( ex );
}
private void setPath( final Path path ) {
this.path = path;
}
private Path getPath() {
return this.path;
}
private Snitch getSnitch() {
return this.snitch;
}
}