/*
* 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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
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.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import io.werval.api.context.CurrentContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import static io.werval.util.Maps.fromMap;
/**
* XML.
* <p>
* Manage configured instances of XML APIs providing utility methods for frequent use cases.
* Application configuration allow to set up thoses managed instances.
*/
public class XML
{
/**
* Active XML Plugin API.
*
* @return Active XML Plugin API
*
* @throws IllegalArgumentException if no {@literal XML Plugin} is found
* @throws IllegalStateException if the {@literal Application} is not active
*/
public static XML xml()
{
return CurrentContext.plugin( XML.class );
}
private final Charset defaultCharset;
public XML( Charset defaultCharset )
{
this.defaultCharset = defaultCharset;
}
// TODO Add shorthands to the Plugin's API for every XML APIs, like this one, or not
public XMLInputFactory newXMLInputFactory()
{
return XMLInputFactory.newFactory();
}
public String asString( Document document )
{
return asString( document, defaultCharset );
}
public String asString( Document document, Charset charset )
{
return new String( asBytes( document ), charset );
}
public byte[] asBytes( Document document )
{
return asBytes(
document,
fromMap( new HashMap<String, String>() )
.put( OutputKeys.ENCODING, defaultCharset.name() )
.put( OutputKeys.METHOD, "xml" )
.put( OutputKeys.OMIT_XML_DECLARATION, "no" )
.put( OutputKeys.INDENT, "yes" )
.toMap()
);
}
public String asString( Node node )
{
return asString( node, defaultCharset );
}
public String asString( Node node, Charset charset )
{
return new String( asBytes( node ), charset );
}
public byte[] asBytes( Node node )
{
return asBytes(
node,
fromMap( new HashMap<String, String>() )
.put( OutputKeys.ENCODING, defaultCharset.name() )
.put( OutputKeys.METHOD, "xml" )
.put( OutputKeys.OMIT_XML_DECLARATION, "yes" )
.put( OutputKeys.INDENT, "no" )
.toMap()
);
}
private byte[] asBytes( Node node, Map<String, String> outputProperties )
{
try
{
Transformer transformer = TransformerFactory.newInstance().newTransformer();
outputProperties.forEach(
(name, value) ->
{
transformer.setOutputProperty( name, value );
}
);
ByteArrayOutputStream output = new ByteArrayOutputStream();
transformer.transform( new DOMSource( node ), new StreamResult( output ) );
return output.toByteArray();
}
catch( TransformerException ex )
{
throw new UncheckedXMLException( ex );
}
}
// ______
// | __ \.---.-.----.-----.-----.
// | __/| _ | _|__ --| -__|
// |___| |___._|__| |_____|_____|
// _________________________________________________________________________________________________________________
//
public XMLEventReader staxEvents( InputStream input )
{
try
{
XMLInputFactory factory = XMLInputFactory.newInstance();
return factory.createXMLEventReader( input );
}
catch( XMLStreamException ex )
{
throw new UncheckedXMLException( ex );
}
}
public XMLStreamReader staxStream( InputStream input )
{
try
{
XMLInputFactory factory = XMLInputFactory.newInstance();
return factory.createXMLStreamReader( input );
}
catch( XMLStreamException ex )
{
throw new UncheckedXMLException( ex );
}
}
public void sax( InputStream input, DefaultHandler handler )
{
sax( input, handler, false );
}
public void sax( InputStream input, DefaultHandler handler, boolean dtdValidation )
{
try
{
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
saxFactory.setValidating( dtdValidation );
saxFactory.newSAXParser().parse( input, handler );
}
catch( ParserConfigurationException | SAXException ex )
{
throw new UncheckedXMLException( ex );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
}
public Document document( String string )
{
return document( string, defaultCharset );
}
public Document document( String string, Charset charset )
{
return document( string.getBytes( charset ) );
}
public Document document( byte[] bytes )
{
try( InputStream xmlDocStream = new ByteArrayInputStream( bytes ) )
{
return document( xmlDocStream );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
}
public Document document( File file )
{
try( InputStream xmlDocStream = new FileInputStream( file ) )
{
return document( xmlDocStream );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
}
public Document document( Path path )
{
try( InputStream xmlDocStream = Files.newInputStream( path ) )
{
return document( xmlDocStream );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
}
public Document document( InputStream input )
{
return document( input, false, true, null );
}
public Document document( InputStream input, boolean dtdValidation )
{
return document( input, dtdValidation, false, null );
}
public Document document( InputStream input, Schema schema )
{
return document( input, false, true, schema );
}
private Document document( InputStream input, boolean dtdValidation, boolean namespaceAware, Schema schema )
{
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating( dtdValidation );
factory.setNamespaceAware( namespaceAware );
factory.setSchema( schema );
return factory.newDocumentBuilder().parse( input );
}
catch( ParserConfigurationException | SAXException ex )
{
throw new UncheckedXMLException( ex );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
}
public Node node( InputStream input )
{
return document( input, null );
}
public Node node( InputStream input, Schema schema )
{
return document( input, schema );
}
public Schema xsd( InputStream input )
{
return schema( input, XMLConstants.W3C_XML_SCHEMA_NS_URI );
}
public Schema xsd_1_1( InputStream input )
{
return schema( input, "http://www.w3.org/XML/XMLSchema/v1.1" );
}
public Schema relaxNg( InputStream input )
{
return schema( input, XMLConstants.RELAXNG_NS_URI );
}
public Schema schema( InputStream input, String schemaLanguage )
{
try
{
return SchemaFactory.newInstance( schemaLanguage ).newSchema( new StreamSource( input ) );
}
catch( SAXException ex )
{
throw new UncheckedXMLException( ex );
}
}
public Transformer xslt( String string )
{
return xslt( string, defaultCharset );
}
public Transformer xslt( String string, Charset charset )
{
return xslt( new ByteArrayInputStream( string.getBytes( charset ) ) );
}
public Transformer xslt( InputStream input )
{
try
{
return TransformerFactory.newInstance().newTransformer( new StreamSource( input ) );
}
catch( TransformerConfigurationException ex )
{
throw new UncheckedXMLException( ex );
}
}
//
// _______ ___
// |_ _|.----.---.-.-----.-----.' _|.-----.----.--------.
// | | | _| _ | |__ --| _|| _ | _| |
// |___| |__| |___._|__|__|_____|__| |_____|__| |__|__|__|
// _________________________________________________________________________________________________________________
//
public String toText( Document document, String xsltString )
{
return toText( document, xsltString, defaultCharset );
}
public String toText( Document document, String xsltString, Charset xsltCharset )
{
return toText( document, xsltString.getBytes( xsltCharset ) );
}
public String toText( Document document, byte[] xsltBytes )
{
return toText( document, new ByteArrayInputStream( xsltBytes ) );
}
public String toText( Document document, InputStream xsltStream )
{
return toText( document, xslt( xsltStream ) );
}
public String toText( Document document, Transformer transformer )
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
to( document, transformer, output );
return new String( output.toByteArray() );
}
public Document toDocument( Document document, InputStream xsltStream )
{
return (Document) toNode( document, xsltStream );
}
public Node toNode( Node node, InputStream xsltStream )
{
DOMResult domResult = new DOMResult();
to( new DOMSource( node ), xslt( xsltStream ), domResult );
return domResult.getNode();
}
public void to( Node node, Transformer transformer, OutputStream output )
{
to( new DOMSource( node ), transformer, new StreamResult( output ) );
}
public void to( InputStream input, Transformer transformer, OutputStream output )
{
to( new StreamSource( input ), transformer, new StreamResult( output ) );
}
private void to( Source input, Transformer transformer, Result output )
{
try
{
transformer.transform( input, output );
}
catch( TransformerException ex )
{
throw new UncheckedXMLException( ex );
}
}
}