1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package com.jcabi.xml;
31
32 import com.jcabi.log.Logger;
33 import java.io.File;
34 import java.io.FileNotFoundException;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.StringReader;
38 import java.net.URI;
39 import java.net.URL;
40 import java.nio.file.Path;
41 import java.util.Collection;
42 import java.util.concurrent.CopyOnWriteArrayList;
43 import javax.xml.XMLConstants;
44 import javax.xml.transform.Source;
45 import javax.xml.transform.stream.StreamSource;
46 import javax.xml.validation.Schema;
47 import javax.xml.validation.SchemaFactory;
48 import javax.xml.validation.Validator;
49 import lombok.EqualsAndHashCode;
50 import org.xml.sax.ErrorHandler;
51 import org.xml.sax.SAXException;
52 import org.xml.sax.SAXParseException;
53
54
55
56
57
58
59
60
61
62 @EqualsAndHashCode(of = "xsd")
63 public final class XSDDocument implements XSD {
64
65
66
67
68 private final transient String xsd;
69
70
71
72
73
74 public XSDDocument(final XML src) {
75 this(src.toString());
76 }
77
78
79
80
81
82 public XSDDocument(final String src) {
83 this.xsd = src;
84 }
85
86
87
88
89
90
91
92 public XSDDocument(final URL url) throws IOException {
93 this(new TextResource(url).toString());
94 }
95
96
97
98
99
100
101
102 public XSDDocument(final Path file) throws FileNotFoundException {
103 this(file.toFile());
104 }
105
106
107
108
109
110
111
112 public XSDDocument(final File file) throws FileNotFoundException {
113 this(new TextResource(file).toString());
114 }
115
116
117
118
119
120
121
122 public XSDDocument(final URI uri) throws IOException {
123 this(new TextResource(uri).toString());
124 }
125
126
127
128
129
130 public XSDDocument(final InputStream stream) {
131 this(new TextResource(stream).toString());
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 @SuppressWarnings("PMD.ProhibitPublicStaticMethods")
151 public static XSD make(final InputStream stream) {
152 return new XSDDocument(stream);
153 }
154
155
156
157
158
159
160
161
162 @SuppressWarnings("PMD.ProhibitPublicStaticMethods")
163 public static XSD make(final URL url) {
164 try {
165 return new XSDDocument(url);
166 } catch (final IOException ex) {
167 throw new IllegalStateException(ex);
168 }
169 }
170
171 @Override
172 public String toString() {
173 return new XMLDocument(this.xsd).toString();
174 }
175
176 @Override
177 public Collection<SAXParseException> validate(final Source xml) {
178 final Schema schema;
179 try {
180 synchronized (XSDDocument.class) {
181 schema = SchemaFactory
182 .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
183 .newSchema(new StreamSource(new StringReader(this.xsd)));
184 }
185 } catch (final SAXException ex) {
186 throw new IllegalStateException(
187 String.format("Failed to create XSD schema from %s", this.xsd),
188 ex
189 );
190 }
191 final Collection<SAXParseException> errors =
192 new CopyOnWriteArrayList<>();
193 final Validator validator = schema.newValidator();
194 validator.setErrorHandler(new XSDDocument.ValidationHandler(errors));
195 try {
196 synchronized (XSDDocument.class) {
197 validator.validate(xml);
198 }
199 } catch (final SAXException | IOException ex) {
200 throw new IllegalStateException(ex);
201 }
202 if (Logger.isDebugEnabled(this)) {
203 Logger.debug(
204 this, "%s detected %d error(s)",
205 schema.getClass().getName(), errors.size()
206 );
207 }
208 return errors;
209 }
210
211
212
213
214
215
216 static final class ValidationHandler implements ErrorHandler {
217
218
219
220 private final transient Collection<SAXParseException> errors;
221
222
223
224
225
226 ValidationHandler(final Collection<SAXParseException> errs) {
227 this.errors = errs;
228 }
229
230 @Override
231 public void warning(final SAXParseException error) {
232 this.errors.add(error);
233 }
234
235 @Override
236 public void error(final SAXParseException error) {
237 this.errors.add(error);
238 }
239
240 @Override
241 public void fatalError(final SAXParseException error) {
242 this.errors.add(error);
243 }
244 }
245
246 }