Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#215): Add several useful constructors for SaxonDocument #216

Merged
merged 4 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 76 additions & 3 deletions src/main/java/com/jcabi/xml/SaxonDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@
*/
package com.jcabi.xml;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.namespace.NamespaceContext;
Expand Down Expand Up @@ -81,14 +88,72 @@ public final class SaxonDocument implements XML {
/**
* Public constructor from XML as string text.
* @param text XML document body.
* @since 0.28.0
*/
public SaxonDocument(final String text) {
this(SaxonDocument.node(text));
}

/**
* Public constructor from XML as byte array.
* @param data XML document body as byte array.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@volodya-lombrozo it's a good practice to add @since to a method or a class when you introduce it to the code base: https://stackoverflow.com/questions/578363/javadoc-version-and-since

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yegor256 I've added the "since" tag

* @since 0.28.1
*/
public SaxonDocument(final byte[] data) {
this(SaxonDocument.node(new String(data, StandardCharsets.UTF_8)));
}

/**
* Public constructor from XML saved in a filesystem.
* @param path Path to XML file in a filesystem.
* @since 0.28.1
*/
public SaxonDocument(final Path path) {
this(path.toFile());
}

/**
* Public constructor from XML saved in a filesystem.
* @param file XML file in a filesystem.
* @since 0.28.1
*/
public SaxonDocument(final File file) {
this(SaxonDocument.node(new StreamSource(file)));
}

/**
* Public constructor from XML reached by URL.
* @param url URL of XML document.
* @throws IOException If fails.
* @since 0.28.1
*/
public SaxonDocument(final URL url) throws IOException {
this(SaxonDocument.node(new TextResource(url).toString()));
}

/**
* Public constructor from XML reached by URI.
* @param uri URI of XML document.
* @throws IOException If fails.
* @since 0.28.1
*/
public SaxonDocument(final URI uri) throws IOException {
this(SaxonDocument.node(new TextResource(uri).toString()));
}

/**
* Public constructor from XML as input stream.
* @param stream Input stream with XML document.
* @since 0.28.1
*/
public SaxonDocument(final InputStream stream) {
this(SaxonDocument.node(new StreamSource(stream)));
}

/**
* Public constructor from Saxon XML document node.
* @param xml Saxon XML document node.
* @since 0.28.0
*/
public SaxonDocument(final XdmNode xml) {
this.xdm = xml;
Expand Down Expand Up @@ -145,12 +210,20 @@ public Node node() {
* @return Saxon XML document node.
*/
private static XdmNode node(final String text) {
return SaxonDocument.node(new StreamSource(new StringReader(text)));
}

/**
* Build Saxon XML document node from XML source.
* @param source XML.
* @return Saxon XML document node.
*/
private static XdmNode node(final StreamSource source) {
try {
return SaxonDocument.DOC_BUILDER
.build(new StreamSource(new StringReader(text)));
return SaxonDocument.DOC_BUILDER.build(source);
} catch (final SaxonApiException exception) {
throw new IllegalArgumentException(
String.format("SaxonDocument can't parse XML: %s", text),
String.format("SaxonDocument can't parse XML from source '%s'", source),
exception
);
}
Expand Down
98 changes: 98 additions & 0 deletions src/test/java/com/jcabi/xml/SaxonDocumentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,102 @@
*/
package com.jcabi.xml;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
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.file.Files;
import java.nio.file.Path;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

/**
* Test case for {@link SaxonDocument}.
* @since 0.28
*/
final class SaxonDocumentTest {

/**
* Default XML content for tests.
*/
private static final String DEFAULT_XML = "<o><o base='a'/></o>";

/**
* Default assertion message that will be used in many test assertions.
*/
private static final String ASSERTION_MSG = "Can't create Saxon XML document from '%s'";

@Test
void createsFromFile(@TempDir final Path temp) throws IOException {
final Path xml = SaxonDocumentTest.xmlFile(temp);
final File file = xml.toFile();
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, file),
new SaxonDocument(file).xpath("//o[@base]"),
Matchers.hasSize(1)
);
}

@Test
void createsFromPath(@TempDir final Path temp) throws IOException {
final Path xml = SaxonDocumentTest.xmlFile(temp);
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, xml),
new SaxonDocument(xml).xpath("//o[@base]"),
Matchers.hasSize(1)
);
}

@Test
void createsFromByteArray() {
final Charset utf = StandardCharsets.UTF_8;
final byte[] bytes = SaxonDocumentTest.DEFAULT_XML.getBytes(utf);
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, new String(bytes, utf)),
new SaxonDocument(bytes).xpath("//o[@base]"),
Matchers.hasSize(1)
);
}

@Test
void createsFromUrl() throws IOException {
final URL resource = this.getClass().getResource("simple.xml");
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, resource),
new SaxonDocument(resource).xpath("//simple"),
Matchers.hasSize(1)
);
}

@Test
void createsFromUri() throws URISyntaxException, IOException {
final URI resource = this.getClass().getResource("simple.xml").toURI();
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, resource),
new SaxonDocument(resource).xpath("//simple"),
Matchers.hasSize(1)
);
}

@Test
void createsFromStream() {
MatcherAssert.assertThat(
String.format(SaxonDocumentTest.ASSERTION_MSG, SaxonDocumentTest.DEFAULT_XML),
new SaxonDocument(
new ByteArrayInputStream(
SaxonDocumentTest.DEFAULT_XML.getBytes(StandardCharsets.UTF_8)
)
).xpath("//o[@base]"),
Matchers.hasSize(1)
);
}

@Test
void findsXpathWithConcatFunctionThatReturnsSeveralItems() {
MatcherAssert.assertThat(
Expand All @@ -60,4 +146,16 @@ void findsXpathWithStringJoinFunctionThatReturnsSeveralItems() {
Matchers.hasItems("a", "b|2", "c")
);
}

/**
* Creates XML file.
* @param temp Temporary directory where file will be created
* @return Path to created file
* @throws IOException If something goes wrong
*/
private static Path xmlFile(final Path temp) throws IOException {
final Path xml = temp.resolve("test.xml");
Files.write(xml, SaxonDocumentTest.DEFAULT_XML.getBytes(StandardCharsets.UTF_8));
return xml;
}
}