Skip to content

Commit

Permalink
feat(jcabi#277): fix most of the code offences
Browse files Browse the repository at this point in the history
  • Loading branch information
volodya-lombrozo committed Dec 5, 2024
1 parent a457fb2 commit befa879
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 99 deletions.
34 changes: 28 additions & 6 deletions src/main/java/com/jcabi/xml/DomParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,9 @@

import com.jcabi.log.Logger;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import javax.print.Doc;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
Expand Down Expand Up @@ -100,18 +96,33 @@ final class DomParser {
this(fct, new BytesSource(bytes));
}


/**
* Public ctor.
*
* <p>An {@link IllegalArgumentException} may be thrown if the parameter
* passed is not in XML format. It doesn't perform a strict validation
* and is not guaranteed that an exception will be thrown whenever
* the parameter is not XML.
*
* @param fct Document builder factory to use
* @param file The XML as a file
*/
DomParser(final DocumentBuilderFactory fct, final File file) {
this(fct, new FileSource(file));
}

/**
* Private ctor.
* @param factory Document builder factory to use
* @param source Source of XML
*/
private DomParser(final DocumentBuilderFactory factory, final DocSource source) {
this.factory = factory;
this.source = source;
}

/**
* Get document of body.
* Get the document body.
* @return The document
*/
public Document document() {
Expand Down Expand Up @@ -158,8 +169,19 @@ public Document document() {
*/
private interface DocSource {

/**
* Parse XML by the builder.
* @param builder The builder to use during parsing.
* @return The document.
* @throws IOException If fails.
* @throws SAXException If fails.
*/
Document apply(DocumentBuilder builder) throws IOException, SAXException;

/**
* The length of the source.
* @return The length.
*/
long length();
}

Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/jcabi/xml/SaxonDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
*
* @since 0.28
*/
@SuppressWarnings("PMD.TooManyMethods")
public final class SaxonDocument implements XML {

/**
Expand Down
162 changes: 69 additions & 93 deletions src/test/java/com/jcabi/xml/XMLDocumentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
package com.jcabi.xml;

import com.google.common.collect.Iterables;
import com.jcabi.log.Logger;
import com.jcabi.matchers.XhtmlMatchers;
import java.io.ByteArrayInputStream;
import java.io.File;
Expand All @@ -50,9 +49,9 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.cactoos.io.ResourceOf;
Expand All @@ -69,8 +68,6 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
Expand Down Expand Up @@ -680,102 +677,81 @@ void validatesMultipleXmlsInThreads() throws Exception {
service.shutdownNow();
}


@Test
@Disabled
void createsManyXmlDocuments() throws ParserConfigurationException, IOException, SAXException {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
String xml = this.large();
final long startSimple = System.nanoTime();
final String expected = "root";
for (int i = 0; i < 10_000; ++i) {
final Document parse = factory.newDocumentBuilder()
.parse(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
final String actual = parse.getFirstChild().getNodeName();
MatcherAssert.assertThat(
actual,
Matchers.equalTo(expected)
);
}
final long endSimple = System.nanoTime();
System.out.println(
"Default approach to create XML timing: " + (endSimple - startSimple) / 1_000_000 + " ms");
Logger.info(this, "Time: %[ms]s", (endSimple - startSimple) / 1000);
final long start = System.nanoTime();
for (int i = 0; i < 10_000; ++i) {
;
final String actual = new XMLDocument(
new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)))
.node()
.getFirstChild().getNodeName();
MatcherAssert.assertThat(
actual,
Matchers.equalTo(expected)
);
}
final long end = System.nanoTime();
System.out.println(
"jcabi-xml approach to create XML timing: " + (end - start) / 1_000_000 + " ms"
);
}


// ~ 1.6
/**
* This test is disabled because it is a performance test that might be flaky.
* @param temp Temporary directory.
* @throws IOException If something goes wrong.
*/
@RepeatedTest(10)
@Disabled
void createsXmlFromFile(
@TempDir final Path temp
) throws IOException, ParserConfigurationException, SAXException {
void createsXmlFromFile(@TempDir final Path temp) throws IOException {
final Path xml = temp.resolve("test.xml");
String content = this.large();
Files.write(xml, content.getBytes(StandardCharsets.UTF_8));
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final long startSimple = System.nanoTime();
final String expected = "root";
for (int i = 0; i < 10_000; ++i) {
final Document parse = factory.newDocumentBuilder().parse(xml.toFile());
final String actual = parse.getFirstChild().getNodeName();
MatcherAssert.assertThat(
actual,
Matchers.equalTo(expected)
);
}
final long endSimple = System.nanoTime();
final double simple = (endSimple - startSimple) / 1_000_000.0d;
System.out.println(
"Default approach to create XML timing: " + simple + " ms");
final long start = System.nanoTime();
for (int i = 0; i < 10_000; ++i) {
final String actual = new XMLDocument(xml).node()
.getFirstChild().getNodeName();
MatcherAssert.assertThat(
actual,
Matchers.equalTo(expected)
);
}
final long end = System.nanoTime();
final double actual = (end - start) / 1_000_000.0d;
System.out.println(
"jcabi-xml approach to create XML timing: " + actual + " ms"
Files.write(xml, XMLDocumentTest.large().getBytes(StandardCharsets.UTF_8));
final long clear = XMLDocumentTest.measure(
() -> DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(xml.toFile())
.getFirstChild()
.getNodeName()
);
System.out.println(
"Simple approach is " + (actual / simple) + " times faster"
final long wrapped = XMLDocumentTest.measure(
() -> new XMLDocument(xml.toFile()).inner().getFirstChild().getNodeName()
);

MatcherAssert.assertThat(
String.format(
"We expect that jcabi-xml is at max 2 times slower than default approach, time spend on jcabi-xml: %d ms, time spend on default approach: %d ms",
wrapped,
clear
),
wrapped / clear,
Matchers.lessThan(2L)
);
}

/**
* Measure the time of execution.
* @param run The callable to run.
* @return Time in milliseconds.
* @checkstyle IllegalCatchCheck (20 lines)
*/
@SuppressWarnings({"PMD.AvoidCatchingGenericException", "PMD.PrematureDeclaration"})
private static long measure(final Callable<String> run) {
final long start = System.nanoTime();
if (!IntStream.range(0, 1000).mapToObj(
each -> {
try {
return run.call();
} catch (final Exception exception) {
throw new IllegalStateException(
String.format("Failed to run %s", run), exception
);
}
}
).allMatch("root"::equals)) {
throw new IllegalStateException("Invalid result");
}
return System.nanoTime() - start / 1_000_000;
}

private String large() {
final StringBuilder builder = new StringBuilder(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>").append("<root>");
final String payment = StringUtils.join(
"<payment><id>333</id>",
"<date>1-Jan-2013</date>",
"<debit>test-1</debit>",
"<credit>test-2</credit>",
"</payment>"
);
IntStream.range(0, 1_000).mapToObj(i -> payment).forEach(builder::append);
return builder.append("</root>").toString();
/**
* Generate large XML for tests.
* @return Large XML string.
*/
private static String large() {
return IntStream.range(0, 100)
.mapToObj(
i -> StringUtils.join(
"<payment><id>333</id>",
"<date>1-Jan-2013</date>",
"<debit>test-1</debit>",
"<credit>test-2</credit>",
"</payment>"
)
).collect(
Collectors.joining(
"", "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root>", "</root>"
)
);
}

}

0 comments on commit befa879

Please sign in to comment.