Support for DOMSource and other Sources

This commit is contained in:
Andreas Penski (init) 2020-05-07 17:10:33 +02:00
parent b02126c1cc
commit 9e3b12685b
4 changed files with 72 additions and 23 deletions

View file

@ -164,8 +164,10 @@ public class InputFactory {
}
/**
* Reads a test document from a {@link Source}.
*
* Reads a test document from a {@link Source}. <br/>
* Note: computing the hashcode is only supported for {@link StreamSource}. You can not directly use other {@link Source
* Soures}. You need to supply the hashcode for identification then.
*
* @param source source
* @return an {@link Input}
*/
@ -175,6 +177,9 @@ public class InputFactory {
/**
* Reads a test document from a {@link Source} using a specified digest algorithm.
*
* Note: computing the hashcode is only supported for {@link StreamSource}. You can not directly use other {@link Source
* Soures}. You need to supply the hashcode for identification then.
*
* @param source source
* @param digestAlgorithm the digest algorithm

View file

@ -7,13 +7,25 @@ import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.lang3.NotImplementedException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
* A validator {@link de.kosit.validationtool.api.Input} based an on a {@link Source}.
* A validator {@link de.kosit.validationtool.api.Input} based on a {@link Source}. <br/>
* <p>
* Note: The various implementations of {@link Source} varies wether the can be read twice or no. This implementation
* tries to handle this with respect document identification (hashcode).
*
* This class is know to work with:
* <ul>
* <li>{@link StreamSource} - both {@link java.io.InputStream} based and {@link java.io.Reader} based</li>
* <li>{@link javax.xml.transform.dom.DOMSource}</li>
* <li>{@link javax.xml.bind.util.JAXBSource}</li>
* </ul>
*
* Other {@link Source Sources} may work as well, please try and let us know.
* </p>
*
* @author Andreas Penski
*/
@ -40,26 +52,23 @@ public class SourceInput extends AbstractInput {
}
private void validate() {
if (!isSupported()) {
if (!isHashcodeComputed() && !isSupported()) {
throw new IllegalStateException("Unsupported source. Only StreamSource supported yet");
}
if (((StreamSource) this.source).getInputStream() == null && !isHashcodeComputed()) {
if (!isHashcodeComputed() && ((StreamSource) this.source).getInputStream() == null) {
log.warn("No hashcode supplied, will wrap the reader using system default charset");
}
}
@Override
public Source getSource() throws IOException {
if (!isSupported()) {
if (!isHashcodeComputed() && !isSupported()) {
throw new IllegalStateException("Unsupported source. Only InputStream-based StreamSource supported yet");
}
if (isWrappingRequired()) {
return wrap();
}
if (isConsumed()) {
throw new IllegalStateException("A SourceInput can only read once");
}
return this.source;
return isHashcodeComputed() ? this.source : wrappedSource();
}
private boolean isSupported() {
@ -67,24 +76,25 @@ public class SourceInput extends AbstractInput {
}
private boolean isConsumed() throws IOException {
if (!isStreamSource()) {
throw new NotImplementedException("Supports only StreamSource yet");
}
final StreamSource ss = (StreamSource) this.source;
try {
return (ss.getInputStream() != null && ss.getInputStream().available() == 0)
|| (ss.getReader() != null && !ss.getReader().ready());
} catch (final IOException e) {
log.error("Error checking consumed state", e);
return true;
if (isStreamSource()) {
final StreamSource ss = (StreamSource) this.source;
try {
return (ss.getInputStream() != null && ss.getInputStream().available() == 0)
|| (ss.getReader() != null && !ss.getReader().ready());
} catch (final IOException e) {
log.error("Error checking consumed state", e);
return true;
}
}
return false;
}
private boolean isStreamSource() {
return this.source instanceof StreamSource;
}
private Source wrap() {
private Source wrappedSource() {
Source result = this.source;
if (isStreamSource()) {
final StreamSource ss = (StreamSource) this.source;

View file

@ -31,14 +31,29 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import de.kosit.validationtool.impl.Helper;
import de.kosit.validationtool.impl.Helper.Simple;
import de.kosit.validationtool.impl.TestObjectFactory;
import de.kosit.validationtool.impl.input.SourceInput;
import de.kosit.validationtool.impl.model.Result;
import de.kosit.validationtool.model.reportInput.XMLSyntaxError;
import net.sf.saxon.dom.NodeOverNodeInfo;
import net.sf.saxon.s9api.BuildingContentHandler;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmNode;
/**
* Testet den Hashcode-Service.
@ -163,4 +178,23 @@ public class InputFactoryTest {
InputFactory.read(Simple.NOT_EXISTING);
}
@Test
public void testDomSource() throws SaxonApiException, SAXException, IOException {
final DocumentBuilder builder = TestObjectFactory.createProcessor().newDocumentBuilder();
final BuildingContentHandler handler = builder.newBuildingContentHandler();
handler.startDocument();
handler.startElement("http://some.ns", "mynode", "mynode", new AttributesImpl());
final Document dom = NodeOverNodeInfo.wrap(handler.getDocumentNode().getUnderlyingNode()).getOwnerDocument();
final Input domInput = InputFactory.read(new DOMSource(dom), "MD5", "id".getBytes());
assertThat(domInput).isNotNull();
final Source source = domInput.getSource();
assertThat(source).isNotNull();
final Result<XdmNode, XMLSyntaxError> parsed = Helper.parseDocument(domInput);
assertThat(parsed.isValid()).isTrue();
// read twice
assertThat(Helper.parseDocument(domInput).getObject()).isNotNull();
}
}