Skip to content

Commit

Permalink
#276 .validate() added
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Dec 1, 2024
1 parent 2296cff commit b08adb3
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 298 deletions.
39 changes: 15 additions & 24 deletions src/it/saxon/src/test/java/com/jcabi/saxon/SaxonSampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
*/
package com.jcabi.saxon;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.jcabi.xml.XSD;
import com.jcabi.xml.XSDDocument;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -65,7 +64,7 @@ public void validatesInMultipleThreads() throws Exception {
@Override
public Void call() throws Exception {
final int cnt = rand.nextInt(random);
final XSD xsd = new XSDDocument(
final XML xsd = new XMLDocument(
StringUtils.join(
"<xs:schema ",
"xmlns:xs='http://www.w3.org/2001/XMLSchema' >",
Expand All @@ -78,17 +77,13 @@ public Void call() throws Exception {
)
);
MatcherAssert.assertThat(
xsd.validate(
new DOMSource(
new XMLDocument(
StringUtils.join(
"<root>",
StringUtils.repeat("<a>hey you</a>", cnt),
"</root>"
)
).node()
new XMLDocument(
StringUtils.join(
"<root>",
StringUtils.repeat("<a>hey you</a>", cnt),
"</root>"
)
),
).validate(xsd),
Matchers.hasSize(cnt << 1)
);
return null;
Expand Down Expand Up @@ -116,7 +111,7 @@ public void validatesInMultipleThreadsAgain() throws Exception {
final int random = 100;
final int loop = 50;
final Random rand = new SecureRandom();
final XSD xsd = new XSDDocument(
final XML xsd = new XMLDocument(
StringUtils.join(
"<xs:schema xmlns:xs ='http://www.w3.org/2001/XMLSchema' >",
"<xs:element name='r'><xs:complexType><xs:sequence>",
Expand All @@ -130,17 +125,13 @@ public void validatesInMultipleThreadsAgain() throws Exception {
public Void call() throws Exception {
final int cnt = rand.nextInt(random);
MatcherAssert.assertThat(
xsd.validate(
new DOMSource(
new XMLDocument(
StringUtils.join(
"<r>",
StringUtils.repeat("<x>hey</x>", cnt),
"</r>"
)
).node()
new XMLDocument(
StringUtils.join(
"<r>",
StringUtils.repeat("<x>hey</x>", cnt),
"</r>"
)
),
).validate(xsd),
Matchers.hasSize(cnt << 1)
);
return null;
Expand Down
21 changes: 8 additions & 13 deletions src/it/xerces/src/test/java/com/jcabi/xerces/XercesSampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@
*/
package com.jcabi.xerces;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import com.jcabi.xml.XSD;
import com.jcabi.xml.XSDDocument;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -60,7 +59,7 @@ public void validatesXmlForSchemaValidity() throws Exception {
final int random = 100;
final int loop = 50;
final Random rand = new SecureRandom();
final XSD xsd = new XSDDocument(
final XML xsd = new XMLDocument(
StringUtils.join(
"<xs:schema xmlns:xs ='http://www.w3.org/2001/XMLSchema' >",
"<xs:element name='r'><xs:complexType>",
Expand All @@ -77,17 +76,13 @@ public void validatesXmlForSchemaValidity() throws Exception {
public Void call() throws Exception {
final int cnt = rand.nextInt(random);
MatcherAssert.assertThat(
xsd.validate(
new DOMSource(
new XMLDocument(
StringUtils.join(
"<r>",
StringUtils.repeat("<x>hey</x>", cnt),
"</r>"
)
).node()
new XMLDocument(
StringUtils.join(
"<r>",
StringUtils.repeat("<x>hey</x>", cnt),
"</r>"
)
),
).validate(xsd),
Matchers.hasSize(cnt << 1)
);
return null;
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/com/jcabi/xml/SaxonDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import javax.xml.namespace.NamespaceContext;
Expand All @@ -49,6 +50,7 @@
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import org.w3c.dom.Node;
import org.xml.sax.SAXParseException;

/**
* Saxon XML document.
Expand Down Expand Up @@ -204,6 +206,20 @@ public Node node() {
);
}

@Override
public Collection<SAXParseException> validate() {
throw new UnsupportedOperationException(
String.format(SaxonDocument.UNSUPPORTED, "validate")
);
}

@Override
public Collection<SAXParseException> validate(final XML xsd) {
throw new UnsupportedOperationException(
String.format(SaxonDocument.UNSUPPORTED, "validate")
);
}

/**
* Build Saxon XML document node from XML string text.
* @param text XML string text.
Expand Down
26 changes: 23 additions & 3 deletions src/main/java/com/jcabi/xml/StrictXML.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ public StrictXML(final XML xml, final Validator val) {
* @param xml XML document
* @param schema XSD schema
*/
public StrictXML(final XML xml, final XSD schema) {
this(xml, schema.validate(new DOMSource(xml.node())));
public StrictXML(final XML xml, final XML schema) {
this(xml, StrictXML.check(xml, schema));
}

/**
Expand Down Expand Up @@ -159,6 +159,26 @@ public Node node() {
return this.origin.node();
}

@Override
public Collection<SAXParseException> validate() {
return this.origin.validate();
}

@Override
public Collection<SAXParseException> validate(final XML xsd) {
return this.origin.validate(xsd);
}

/**
* Check and return list of errors.
* @param xml The XML to check
* @param xsd Schema to use
* @return List of errors
*/
private static Collection<SAXParseException> check(final XML xml, final XML xsd) {
return xml.validate(xsd);
}

/**
* Convert errors to lines.
* @param errors The errors
Expand Down Expand Up @@ -218,7 +238,7 @@ private static Collection<SAXParseException> validate(
final int max = 3;
try {
validator.setErrorHandler(
new XSDDocument.ValidationHandler(errors)
new XMLDocument.ValidationHandler(errors)
);
final DOMSource dom = new DOMSource(xml.node());
for (int retry = 1; retry <= max; ++retry) {
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/com/jcabi/xml/XML.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
*/
package com.jcabi.xml;

import java.util.Collection;
import java.util.List;
import javax.xml.namespace.NamespaceContext;
import org.w3c.dom.Node;
import org.xml.sax.SAXParseException;

/**
* XML document.
Expand Down Expand Up @@ -149,7 +151,7 @@ public interface XML {
* Append this namespace context to the existing one.
*
* <p>The existing context (inside this object) and the new one provided
* will be merged together. The existing context will be have higher
* will be merged together. The existing context will have higher
* priority.
*
* @param context The context to append
Expand All @@ -163,4 +165,19 @@ public interface XML {
*/
Node node();

/**
* Validate this XML against the XSD schema inside it.
* @return List of errors found
* @since 0.31.0
*/
Collection<SAXParseException> validate();

/**
* Validate this XML against the provided XSD schema.
* @param xsd The Schema
* @return List of errors found
* @since 0.31.0
*/
Collection<SAXParseException> validate(XML xsd);

}
104 changes: 103 additions & 1 deletion src/main/java/com/jcabi/xml/XMLDocument.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,22 @@
*/
package com.jcabi.xml;

import com.jcabi.log.Logger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
Expand All @@ -55,15 +60,21 @@
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 javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import net.sf.saxon.xpath.XPathFactoryImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
* Implementation of {@link XML}.
Expand Down Expand Up @@ -404,6 +415,62 @@ public XML merge(final NamespaceContext ctx) {
);
}

@Override
public Collection<SAXParseException> validate(final XML xsd) {
final Schema schema;
try {
schema = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
.newSchema(new StreamSource(new StringReader(xsd.toString())));
} catch (final SAXException ex) {
throw new IllegalStateException(
String.format("Failed to create XSD schema from %s", xsd),
ex
);
}
return this.validate(schema);
}

@Override
public Collection<SAXParseException> validate() {
final Schema schema;
try {
schema = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
.newSchema();
} catch (final SAXException ex) {
throw new IllegalStateException(
"Failed to create XSD schema",
ex
);
}
return this.validate(schema);
}

/**
* Validate against schema.
* @param schema The XSD schema
* @return List of errors
*/
public Collection<SAXParseException> validate(final Schema schema) {
final Collection<SAXParseException> errors =
new CopyOnWriteArrayList<>();
final Validator validator = schema.newValidator();
validator.setErrorHandler(new XMLDocument.ValidationHandler(errors));
try {
validator.validate(new DOMSource(this.cache));
} catch (final SAXException | IOException ex) {
throw new IllegalStateException(ex);
}
if (Logger.isDebugEnabled(this)) {
Logger.debug(
this, "%s detected %d error(s)",
schema.getClass().getName(), errors.size()
);
}
return errors;
}

/**
* Clones a node and imports it in a new document.
* @param node A node to clone.
Expand Down Expand Up @@ -550,4 +617,39 @@ private static DocumentBuilderFactory configuredDFactory() {
factory.setNamespaceAware(true);
return factory;
}

/**
* Validation error handler.
*
* @since 0.1
*/
static final class ValidationHandler implements ErrorHandler {
/**
* Errors.
*/
private final transient Collection<SAXParseException> errors;

/**
* Constructor.
* @param errs Collection of errors
*/
ValidationHandler(final Collection<SAXParseException> errs) {
this.errors = errs;
}

@Override
public void warning(final SAXParseException error) {
this.errors.add(error);
}

@Override
public void error(final SAXParseException error) {
this.errors.add(error);
}

@Override
public void fatalError(final SAXParseException error) {
this.errors.add(error);
}
}
}
Loading

0 comments on commit b08adb3

Please sign in to comment.