Skip to content

Commit

Permalink
Make it possible to configure the type of exceptions that can be thro…
Browse files Browse the repository at this point in the history
…wn when transforming
  • Loading branch information
joeha480 committed May 22, 2019
1 parent 1ca88de commit 81af6cb
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 64 deletions.
132 changes: 132 additions & 0 deletions src/org/daisy/dotify/common/xml/TransformerEnvironment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package org.daisy.dotify.common.xml;

import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;

/**
* Provides a transformer environment.
* @author Joel Håkansson
*
* @param <T> the type of throwable that this environment prefers
*/
public final class TransformerEnvironment<T extends Throwable> {
private final TransformerFactory factory;
private final Function<? super Throwable, T> throwableProcessor;
private final Map<String, Object> params;

/**
* Provides a builder for a {@link TransformerEnvironment}.
*/
public static class Builder {
private TransformerFactory factory = null;
private Map<String, Object> params = Collections.emptyMap();

private Builder() {
}

/**
* Sets the transformer factory for this environment.
* @param factory the transformer factory
* @return this builder
*/
public Builder transformerFactory(TransformerFactory factory) {
this.factory = factory;
return this;
}

/**
* Sets the xslt parameters for this environment.
* @param params the xslt parameters
* @return this builder
*/
public Builder parameters(Map<String, Object> params) {
this.params = params;
return this;
}

/**
* Builds a new environment using the current configuration of this builder.
* @return a new {@link TransformerEnvironment}
*/
public TransformerEnvironment<XMLToolsException> build() {
return build(th-> {
if (th instanceof XMLToolsException) {
return (XMLToolsException)th;
} else {
return new XMLToolsException(th);
}
});
}

/**
* Builds a new environment with the specified type of throwable that can be thrown when
* using the environment.
* @param <Y> the type of throwable to throw in case of an error
* @param throwableProcessor a function that processes a throwable and returns another throwable
* @return a new {@link TransformerEnvironment}
*/
public <Y extends Throwable> TransformerEnvironment<Y> build(Function<? super Throwable, Y> throwableProcessor) {
return new TransformerEnvironment<Y>(this, throwableProcessor);
}
}

/**
* Creates a new {@link TransformerEnvironment.Builder}
* @return a new builder
*/
public static Builder builder() {
return new Builder();
}

private TransformerEnvironment(Builder builder, Function<? super Throwable, T> throwableProcessor) {
this.throwableProcessor = throwableProcessor;
this.params = builder.params;
this.factory = Optional.ofNullable(builder.factory).orElse(TransformerFactory.newInstance());
}

/**
* Processes the supplied throwable and returns another throwable
* @param cause the throwable
* @return another throwable
*/
T toThrowable(Throwable cause) {
return throwableProcessor.apply(cause);
}

Transformer newTransformer(Source xslt) throws T {
try {
return factory.newTransformer(xslt);
} catch (TransformerConfigurationException e) {
throw toThrowable(e);
}
}

Map<String, Object> getParameters() {
return params;
}

Source asSource(Object source) throws T {
try {
return TransformerTools.toSource(source);
} catch (XMLToolsException e) {
throw toThrowable(e);
}
}

Result asResult(Object result) throws T {
try {
return TransformerTools.toResult(result);
} catch (XMLToolsException e) {
throw toThrowable(e);
}
}

}
58 changes: 58 additions & 0 deletions src/org/daisy/dotify/common/xml/TransformerTools.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.daisy.dotify.common.xml;

import java.io.File;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

class TransformerTools {

static Source toSource(Object source) throws XMLToolsException {
if (source instanceof File) {
return new StreamSource((File) source);
} else if (source instanceof String) {
return new StreamSource((String) source);
} else if (source instanceof URL) {
try {
// Compare to {@link StreamSource#StreamSource(File)}
return new StreamSource(((URL) source).toURI().toASCIIString());
} catch (URISyntaxException e) {
throw new XMLToolsException(e);
}
} else if (source instanceof URI) {
return new StreamSource(((URI) source).toASCIIString());
} else if (source instanceof Source) {
return (Source) source;
} else {
throw new XMLToolsException("Failed to create source: " + source);
}
}

static Result toResult(Object result) throws XMLToolsException {
if (result instanceof File) {
return new StreamResult((File) result);
} else if (result instanceof OutputStream) {
return new StreamResult((OutputStream) result);
} else if (result instanceof String) {
return new StreamResult((String) result);
} else if (result instanceof URL) {
try {
return new StreamResult(((URL) result).toURI().toASCIIString());
} catch (URISyntaxException e) {
throw new XMLToolsException(e);
}
} else if (result instanceof URI) {
return new StreamResult(((URI) result).toASCIIString());
} else if (result instanceof Result) {
return (Result) result;
} else {
throw new XMLToolsException("Failed to create result: " + result);
}
}
}
103 changes: 39 additions & 64 deletions src/org/daisy/dotify/common/xml/XMLTools.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
Expand All @@ -26,13 +23,9 @@
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.TransformerFactoryConfigurationError;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
Expand Down Expand Up @@ -317,7 +310,7 @@ private static Optional<Charset> forName(String charset) {
* @throws XMLToolsException if the transformation is unsuccessful
*/
public static void transform(Object source, Object result, Object xslt, Map<String, Object> params) throws XMLToolsException {
transform(toSource(source), toResult(result), toSource(xslt), params);
transform(TransformerTools.toSource(source), TransformerTools.toResult(result), TransformerTools.toSource(xslt), params);
}

/**
Expand All @@ -336,7 +329,7 @@ public static void transform(Object source, Object result, Object xslt, Map<Stri
* @throws XMLToolsException if the transformation is unsuccessful
*/
public static void transform(Object source, Object result, Object xslt, Map<String, Object> params, TransformerFactory factory) throws XMLToolsException {
transform(toSource(source), toResult(result), toSource(xslt), params, factory);
transform(TransformerTools.toSource(source), TransformerTools.toResult(result), TransformerTools.toSource(xslt), params, factory);
}

/**
Expand Down Expand Up @@ -365,21 +358,46 @@ public static void transform(Source source, Result result, Source xslt, Map<Stri
* @throws XMLToolsException if the transformation is unsuccessful
*/
public static void transform(Source source, Result result, Source xslt, Map<String, Object> params, TransformerFactory factory) throws XMLToolsException {
Transformer transformer;
try {
transformer = factory.newTransformer(xslt);
} catch (TransformerConfigurationException e) {
throw new XMLToolsException(e);
} catch (TransformerFactoryConfigurationError e) {
throw new XMLToolsException(e);
}
transform(source, result, xslt, TransformerEnvironment.builder().transformerFactory(factory).parameters(params).build());
}

/**
* <p>Transforms the xml with the specified parameters. By default, this method will set up a caching entity resolver, which
* will reduce the amount of fetching of dtd's from the Internet.</p>
* @param source the source xml
* @param result the result xml
* @param xslt the xslt
* @param env the transformer environment
* @param <T> the type of exception thrown
* @throws T if the transformation is unsuccessful
*/
public static <T extends Exception> void transform(Object source, Object result, Object xslt, TransformerEnvironment<T> env) throws T {
transform(env.asSource(source), env.asResult(result), env.asSource(xslt), env);
}

/**
* <p>Transforms the xml with the specified parameters. By default, this method will set up a caching entity resolver, which
* will reduce the amount of fetching of dtd's from the Internet.</p>
* @param source the source xml
* @param result the result xml
* @param xslt the xslt
* @param env the transformer environment
* @param <T> the type of exception thrown
* @throws T if the transformation is unsuccessful
*/
public static <T extends Exception> void transform(Source source, Result result, Source xslt, TransformerEnvironment<T> env) throws T {
Transformer transformer = env.newTransformer(xslt);

for (String name : params.keySet()) {
transformer.setParameter(name, params.get(name));
for (String name : env.getParameters().keySet()) {
transformer.setParameter(name, env.getParameters().get(name));
}

SAXParserFactory parserFactory = SAXParserFactory.newInstance();
transformer.setURIResolver(new CachingURIResolver(parserFactory));
try {
transformer.setURIResolver(new CachingURIResolver(parserFactory));
} catch (XMLToolsException e) {
env.toThrowable(e);
}
//Create a SAXSource, hook up an entityresolver
if(source.getSystemId()!=null && source.getSystemId().length()>0) {
try {
Expand All @@ -397,7 +415,7 @@ public static void transform(Source source, Result result, Source xslt, Map<Stri
}
}
} catch (TransformerException e) {
throw new XMLToolsException(e);
throw env.toThrowable(e);
} catch (Exception e) {
//TODO: really catch everything?
e.printStackTrace();
Expand All @@ -412,49 +430,6 @@ private static SAXSource setEntityResolver(SAXSource source) {
return source;
}

private static Source toSource(Object source) throws XMLToolsException {
if (source instanceof File) {
return new StreamSource((File) source);
} else if (source instanceof String) {
return new StreamSource((String) source);
} else if (source instanceof URL) {
try {
// Compare to {@link StreamSource#StreamSource(File)}
return new StreamSource(((URL) source).toURI().toASCIIString());
} catch (URISyntaxException e) {
throw new XMLToolsException(e);
}
} else if (source instanceof URI) {
return new StreamSource(((URI) source).toASCIIString());
} else if (source instanceof Source) {
return (Source) source;
} else {
throw new XMLToolsException("Failed to create source: " + source);
}
}

private static Result toResult(Object result) throws XMLToolsException {
if (result instanceof File) {
return new StreamResult((File) result);
} else if (result instanceof OutputStream) {
return new StreamResult((OutputStream) result);
} else if (result instanceof String) {
return new StreamResult((String) result);
} else if (result instanceof URL) {
try {
return new StreamResult(((URL) result).toURI().toASCIIString());
} catch (URISyntaxException e) {
throw new XMLToolsException(e);
}
} else if (result instanceof URI) {
return new StreamResult(((URI) result).toASCIIString());
} else if (result instanceof Result) {
return (Result) result;
} else {
throw new XMLToolsException("Failed to create result: " + result);
}
}

/**
* Returns true if the specified file is well formed XML.
* @param f the file
Expand Down
43 changes: 43 additions & 0 deletions test/org/daisy/dotify/common/xml/TransformerEnvironmentTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.daisy.dotify.common.xml;

import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;

import org.junit.Test;

@SuppressWarnings("javadoc")
public class TransformerEnvironmentTest {

@Test
public void testThrowable_01() {
TransformerEnvironment<XMLToolsException> t = TransformerEnvironment.builder().build();
XMLToolsException ex1 = new XMLToolsException("test");
XMLToolsException ex2 = t.toThrowable(ex1);
assertSame(ex1, ex2);
}

@Test
public void testThrowable_02() {
TransformerEnvironment<XMLToolsException> t = TransformerEnvironment.builder().build();
Exception ex1 = new Exception();
XMLToolsException ex2 = t.toThrowable(ex1);
assertNotSame(ex1, ex2);
}

@Test
public void testThrowable_03() {
TransformerEnvironment<XMLToolsException> t = TransformerEnvironment.builder().build();
XMLToolsExceptionExt ex1 = new XMLToolsExceptionExt();
XMLToolsException ex2 = t.toThrowable(ex1);
assertSame(ex1, ex2);
}

static class XMLToolsExceptionExt extends XMLToolsException {

/**
*
*/
private static final long serialVersionUID = 3633188503742794479L;

}
}

0 comments on commit 81af6cb

Please sign in to comment.