Skip to content

Commit b08adb3

Browse files
committed
#276 .validate() added
1 parent 2296cff commit b08adb3

11 files changed

Lines changed: 338 additions & 298 deletions

File tree

src/it/saxon/src/test/java/com/jcabi/saxon/SaxonSampleTest.java

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
*/
3030
package com.jcabi.saxon;
3131

32+
import com.jcabi.xml.XML;
3233
import com.jcabi.xml.XMLDocument;
33-
import com.jcabi.xml.XSD;
34-
import com.jcabi.xml.XSDDocument;
3534
import java.security.SecureRandom;
3635
import java.util.Random;
3736
import java.util.concurrent.Callable;
@@ -65,7 +64,7 @@ public void validatesInMultipleThreads() throws Exception {
6564
@Override
6665
public Void call() throws Exception {
6766
final int cnt = rand.nextInt(random);
68-
final XSD xsd = new XSDDocument(
67+
final XML xsd = new XMLDocument(
6968
StringUtils.join(
7069
"<xs:schema ",
7170
"xmlns:xs='http://www.w3.org/2001/XMLSchema' >",
@@ -78,17 +77,13 @@ public Void call() throws Exception {
7877
)
7978
);
8079
MatcherAssert.assertThat(
81-
xsd.validate(
82-
new DOMSource(
83-
new XMLDocument(
84-
StringUtils.join(
85-
"<root>",
86-
StringUtils.repeat("<a>hey you</a>", cnt),
87-
"</root>"
88-
)
89-
).node()
80+
new XMLDocument(
81+
StringUtils.join(
82+
"<root>",
83+
StringUtils.repeat("<a>hey you</a>", cnt),
84+
"</root>"
9085
)
91-
),
86+
).validate(xsd),
9287
Matchers.hasSize(cnt << 1)
9388
);
9489
return null;
@@ -116,7 +111,7 @@ public void validatesInMultipleThreadsAgain() throws Exception {
116111
final int random = 100;
117112
final int loop = 50;
118113
final Random rand = new SecureRandom();
119-
final XSD xsd = new XSDDocument(
114+
final XML xsd = new XMLDocument(
120115
StringUtils.join(
121116
"<xs:schema xmlns:xs ='http://www.w3.org/2001/XMLSchema' >",
122117
"<xs:element name='r'><xs:complexType><xs:sequence>",
@@ -130,17 +125,13 @@ public void validatesInMultipleThreadsAgain() throws Exception {
130125
public Void call() throws Exception {
131126
final int cnt = rand.nextInt(random);
132127
MatcherAssert.assertThat(
133-
xsd.validate(
134-
new DOMSource(
135-
new XMLDocument(
136-
StringUtils.join(
137-
"<r>",
138-
StringUtils.repeat("<x>hey</x>", cnt),
139-
"</r>"
140-
)
141-
).node()
128+
new XMLDocument(
129+
StringUtils.join(
130+
"<r>",
131+
StringUtils.repeat("<x>hey</x>", cnt),
132+
"</r>"
142133
)
143-
),
134+
).validate(xsd),
144135
Matchers.hasSize(cnt << 1)
145136
);
146137
return null;

src/it/xerces/src/test/java/com/jcabi/xerces/XercesSampleTest.java

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@
2929
*/
3030
package com.jcabi.xerces;
3131

32+
import com.jcabi.xml.XML;
3233
import com.jcabi.xml.XMLDocument;
33-
import com.jcabi.xml.XSD;
34-
import com.jcabi.xml.XSDDocument;
3534
import java.security.SecureRandom;
3635
import java.util.Random;
3736
import java.util.concurrent.Callable;
@@ -60,7 +59,7 @@ public void validatesXmlForSchemaValidity() throws Exception {
6059
final int random = 100;
6160
final int loop = 50;
6261
final Random rand = new SecureRandom();
63-
final XSD xsd = new XSDDocument(
62+
final XML xsd = new XMLDocument(
6463
StringUtils.join(
6564
"<xs:schema xmlns:xs ='http://www.w3.org/2001/XMLSchema' >",
6665
"<xs:element name='r'><xs:complexType>",
@@ -77,17 +76,13 @@ public void validatesXmlForSchemaValidity() throws Exception {
7776
public Void call() throws Exception {
7877
final int cnt = rand.nextInt(random);
7978
MatcherAssert.assertThat(
80-
xsd.validate(
81-
new DOMSource(
82-
new XMLDocument(
83-
StringUtils.join(
84-
"<r>",
85-
StringUtils.repeat("<x>hey</x>", cnt),
86-
"</r>"
87-
)
88-
).node()
79+
new XMLDocument(
80+
StringUtils.join(
81+
"<r>",
82+
StringUtils.repeat("<x>hey</x>", cnt),
83+
"</r>"
8984
)
90-
),
85+
).validate(xsd),
9186
Matchers.hasSize(cnt << 1)
9287
);
9388
return null;

src/main/java/com/jcabi/xml/SaxonDocument.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.net.URL;
3838
import java.nio.charset.StandardCharsets;
3939
import java.nio.file.Path;
40+
import java.util.Collection;
4041
import java.util.List;
4142
import java.util.stream.Collectors;
4243
import javax.xml.namespace.NamespaceContext;
@@ -49,6 +50,7 @@
4950
import net.sf.saxon.s9api.XdmItem;
5051
import net.sf.saxon.s9api.XdmNode;
5152
import org.w3c.dom.Node;
53+
import org.xml.sax.SAXParseException;
5254

5355
/**
5456
* Saxon XML document.
@@ -204,6 +206,20 @@ public Node node() {
204206
);
205207
}
206208

209+
@Override
210+
public Collection<SAXParseException> validate() {
211+
throw new UnsupportedOperationException(
212+
String.format(SaxonDocument.UNSUPPORTED, "validate")
213+
);
214+
}
215+
216+
@Override
217+
public Collection<SAXParseException> validate(final XML xsd) {
218+
throw new UnsupportedOperationException(
219+
String.format(SaxonDocument.UNSUPPORTED, "validate")
220+
);
221+
}
222+
207223
/**
208224
* Build Saxon XML document node from XML string text.
209225
* @param text XML string text.

src/main/java/com/jcabi/xml/StrictXML.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ public StrictXML(final XML xml, final Validator val) {
9898
* @param xml XML document
9999
* @param schema XSD schema
100100
*/
101-
public StrictXML(final XML xml, final XSD schema) {
102-
this(xml, schema.validate(new DOMSource(xml.node())));
101+
public StrictXML(final XML xml, final XML schema) {
102+
this(xml, StrictXML.check(xml, schema));
103103
}
104104

105105
/**
@@ -159,6 +159,26 @@ public Node node() {
159159
return this.origin.node();
160160
}
161161

162+
@Override
163+
public Collection<SAXParseException> validate() {
164+
return this.origin.validate();
165+
}
166+
167+
@Override
168+
public Collection<SAXParseException> validate(final XML xsd) {
169+
return this.origin.validate(xsd);
170+
}
171+
172+
/**
173+
* Check and return list of errors.
174+
* @param xml The XML to check
175+
* @param xsd Schema to use
176+
* @return List of errors
177+
*/
178+
private static Collection<SAXParseException> check(final XML xml, final XML xsd) {
179+
return xml.validate(xsd);
180+
}
181+
162182
/**
163183
* Convert errors to lines.
164184
* @param errors The errors
@@ -218,7 +238,7 @@ private static Collection<SAXParseException> validate(
218238
final int max = 3;
219239
try {
220240
validator.setErrorHandler(
221-
new XSDDocument.ValidationHandler(errors)
241+
new XMLDocument.ValidationHandler(errors)
222242
);
223243
final DOMSource dom = new DOMSource(xml.node());
224244
for (int retry = 1; retry <= max; ++retry) {

src/main/java/com/jcabi/xml/XML.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
*/
3030
package com.jcabi.xml;
3131

32+
import java.util.Collection;
3233
import java.util.List;
3334
import javax.xml.namespace.NamespaceContext;
3435
import org.w3c.dom.Node;
36+
import org.xml.sax.SAXParseException;
3537

3638
/**
3739
* XML document.
@@ -149,7 +151,7 @@ public interface XML {
149151
* Append this namespace context to the existing one.
150152
*
151153
* <p>The existing context (inside this object) and the new one provided
152-
* will be merged together. The existing context will be have higher
154+
* will be merged together. The existing context will have higher
153155
* priority.
154156
*
155157
* @param context The context to append
@@ -163,4 +165,19 @@ public interface XML {
163165
*/
164166
Node node();
165167

168+
/**
169+
* Validate this XML against the XSD schema inside it.
170+
* @return List of errors found
171+
* @since 0.31.0
172+
*/
173+
Collection<SAXParseException> validate();
174+
175+
/**
176+
* Validate this XML against the provided XSD schema.
177+
* @param xsd The Schema
178+
* @return List of errors found
179+
* @since 0.31.0
180+
*/
181+
Collection<SAXParseException> validate(XML xsd);
182+
166183
}

src/main/java/com/jcabi/xml/XMLDocument.java

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,22 @@
2929
*/
3030
package com.jcabi.xml;
3131

32+
import com.jcabi.log.Logger;
3233
import java.io.File;
3334
import java.io.FileNotFoundException;
3435
import java.io.IOException;
3536
import java.io.InputStream;
37+
import java.io.StringReader;
3638
import java.io.StringWriter;
3739
import java.net.URI;
3840
import java.net.URL;
3941
import java.nio.file.Path;
4042
import java.util.ArrayList;
43+
import java.util.Collection;
4144
import java.util.Collections;
4245
import java.util.List;
46+
import java.util.concurrent.CopyOnWriteArrayList;
47+
import javax.xml.XMLConstants;
4348
import javax.xml.namespace.NamespaceContext;
4449
import javax.xml.namespace.QName;
4550
import javax.xml.parsers.DocumentBuilder;
@@ -55,15 +60,21 @@
5560
import javax.xml.transform.dom.DOMResult;
5661
import javax.xml.transform.dom.DOMSource;
5762
import javax.xml.transform.stream.StreamResult;
63+
import javax.xml.transform.stream.StreamSource;
64+
import javax.xml.validation.Schema;
65+
import javax.xml.validation.SchemaFactory;
66+
import javax.xml.validation.Validator;
5867
import javax.xml.xpath.XPath;
5968
import javax.xml.xpath.XPathConstants;
6069
import javax.xml.xpath.XPathExpressionException;
6170
import javax.xml.xpath.XPathFactory;
6271
import net.sf.saxon.xpath.XPathFactoryImpl;
6372
import org.w3c.dom.Document;
64-
import org.w3c.dom.Element;
6573
import org.w3c.dom.Node;
6674
import org.w3c.dom.NodeList;
75+
import org.xml.sax.ErrorHandler;
76+
import org.xml.sax.SAXException;
77+
import org.xml.sax.SAXParseException;
6778

6879
/**
6980
* Implementation of {@link XML}.
@@ -404,6 +415,62 @@ public XML merge(final NamespaceContext ctx) {
404415
);
405416
}
406417

418+
@Override
419+
public Collection<SAXParseException> validate(final XML xsd) {
420+
final Schema schema;
421+
try {
422+
schema = SchemaFactory
423+
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
424+
.newSchema(new StreamSource(new StringReader(xsd.toString())));
425+
} catch (final SAXException ex) {
426+
throw new IllegalStateException(
427+
String.format("Failed to create XSD schema from %s", xsd),
428+
ex
429+
);
430+
}
431+
return this.validate(schema);
432+
}
433+
434+
@Override
435+
public Collection<SAXParseException> validate() {
436+
final Schema schema;
437+
try {
438+
schema = SchemaFactory
439+
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
440+
.newSchema();
441+
} catch (final SAXException ex) {
442+
throw new IllegalStateException(
443+
"Failed to create XSD schema",
444+
ex
445+
);
446+
}
447+
return this.validate(schema);
448+
}
449+
450+
/**
451+
* Validate against schema.
452+
* @param schema The XSD schema
453+
* @return List of errors
454+
*/
455+
public Collection<SAXParseException> validate(final Schema schema) {
456+
final Collection<SAXParseException> errors =
457+
new CopyOnWriteArrayList<>();
458+
final Validator validator = schema.newValidator();
459+
validator.setErrorHandler(new XMLDocument.ValidationHandler(errors));
460+
try {
461+
validator.validate(new DOMSource(this.cache));
462+
} catch (final SAXException | IOException ex) {
463+
throw new IllegalStateException(ex);
464+
}
465+
if (Logger.isDebugEnabled(this)) {
466+
Logger.debug(
467+
this, "%s detected %d error(s)",
468+
schema.getClass().getName(), errors.size()
469+
);
470+
}
471+
return errors;
472+
}
473+
407474
/**
408475
* Clones a node and imports it in a new document.
409476
* @param node A node to clone.
@@ -550,4 +617,39 @@ private static DocumentBuilderFactory configuredDFactory() {
550617
factory.setNamespaceAware(true);
551618
return factory;
552619
}
620+
621+
/**
622+
* Validation error handler.
623+
*
624+
* @since 0.1
625+
*/
626+
static final class ValidationHandler implements ErrorHandler {
627+
/**
628+
* Errors.
629+
*/
630+
private final transient Collection<SAXParseException> errors;
631+
632+
/**
633+
* Constructor.
634+
* @param errs Collection of errors
635+
*/
636+
ValidationHandler(final Collection<SAXParseException> errs) {
637+
this.errors = errs;
638+
}
639+
640+
@Override
641+
public void warning(final SAXParseException error) {
642+
this.errors.add(error);
643+
}
644+
645+
@Override
646+
public void error(final SAXParseException error) {
647+
this.errors.add(error);
648+
}
649+
650+
@Override
651+
public void fatalError(final SAXParseException error) {
652+
this.errors.add(error);
653+
}
654+
}
553655
}

0 commit comments

Comments
 (0)