Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
XSDDocument |
|
| 1.6923076923076923;1.692 | ||||
XSDDocument$ValidationHandler |
|
| 1.6923076923076923;1.692 |
1 | /** | |
2 | * Copyright (c) 2012-2017, jcabi.com | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: 1) Redistributions of source code must retain the above | |
8 | * copyright notice, this list of conditions and the following | |
9 | * disclaimer. 2) Redistributions in binary form must reproduce the above | |
10 | * copyright notice, this list of conditions and the following | |
11 | * disclaimer in the documentation and/or other materials provided | |
12 | * with the distribution. 3) Neither the name of the jcabi.com nor | |
13 | * the names of its contributors may be used to endorse or promote | |
14 | * products derived from this software without specific prior written | |
15 | * permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT | |
19 | * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
20 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | |
21 | * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
22 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
24 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
26 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
28 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | package com.jcabi.xml; | |
31 | ||
32 | import com.jcabi.log.Logger; | |
33 | import java.io.IOException; | |
34 | import java.io.InputStream; | |
35 | import java.io.StringReader; | |
36 | import java.net.URI; | |
37 | import java.net.URL; | |
38 | import java.util.Collection; | |
39 | import java.util.concurrent.CopyOnWriteArrayList; | |
40 | import javax.xml.XMLConstants; | |
41 | import javax.xml.transform.Source; | |
42 | import javax.xml.transform.stream.StreamSource; | |
43 | import javax.xml.validation.Schema; | |
44 | import javax.xml.validation.SchemaFactory; | |
45 | import javax.xml.validation.Validator; | |
46 | import lombok.EqualsAndHashCode; | |
47 | import org.xml.sax.ErrorHandler; | |
48 | import org.xml.sax.SAXException; | |
49 | import org.xml.sax.SAXParseException; | |
50 | ||
51 | /** | |
52 | * Implementation of {@link XSD}. | |
53 | * | |
54 | * <p>Objects of this class are immutable and thread-safe. | |
55 | * | |
56 | * @author Yegor Bugayenko (yegor@teamed.io) | |
57 | * @version $Id: 0fc5670d929cb6f6f8ce51038a17ae56ac6dacbb $ | |
58 | * @since 0.5 | |
59 | */ | |
60 | 0 | @EqualsAndHashCode(of = "xsd") |
61 | public final class XSDDocument implements XSD { | |
62 | ||
63 | /** | |
64 | * XSD document. | |
65 | */ | |
66 | private final transient String xsd; | |
67 | ||
68 | /** | |
69 | * Public ctor, from XSD as a source. | |
70 | * @param src XSD document body | |
71 | */ | |
72 | public XSDDocument(final XML src) { | |
73 | 0 | this(src.toString()); |
74 | 0 | } |
75 | ||
76 | /** | |
77 | * Public ctor, from XSD as a string. | |
78 | * @param src XSD document body | |
79 | */ | |
80 | 12 | public XSDDocument(final String src) { |
81 | 12 | this.xsd = src; |
82 | 12 | } |
83 | ||
84 | /** | |
85 | * Public ctor, from URL. | |
86 | * @param url Location of document | |
87 | * @throws IOException If fails to read | |
88 | * @since 0.7.4 | |
89 | */ | |
90 | public XSDDocument(final URL url) throws IOException { | |
91 | 1 | this(new TextResource(url).toString()); |
92 | 1 | } |
93 | ||
94 | /** | |
95 | * Public ctor, from URI. | |
96 | * @param uri Location of document | |
97 | * @throws IOException If fails to read | |
98 | * @since 0.15 | |
99 | */ | |
100 | public XSDDocument(final URI uri) throws IOException { | |
101 | 0 | this(new TextResource(uri).toString()); |
102 | 0 | } |
103 | ||
104 | /** | |
105 | * Public ctor, from XSD as an input stream. | |
106 | * @param stream XSD input stream | |
107 | */ | |
108 | public XSDDocument(final InputStream stream) { | |
109 | 1 | this(new TextResource(stream).toString()); |
110 | 1 | } |
111 | ||
112 | /** | |
113 | * Make an instance of XSD schema without I/O exceptions. | |
114 | * | |
115 | * <p>This factory method is useful when you need to create | |
116 | * an instance of XSD schema as a static final variable. In this | |
117 | * case you can't catch an exception but this method can help, for example: | |
118 | * | |
119 | * <pre> class Foo { | |
120 | * private static final XSD SCHEMA = XSDDocument.make( | |
121 | * Foo.class.getResourceAsStream("my-schema.xsd") | |
122 | * ); | |
123 | * }</pre> | |
124 | * | |
125 | * @param stream Input stream | |
126 | * @return XSD schema | |
127 | */ | |
128 | public static XSD make(final InputStream stream) { | |
129 | 0 | return new XSDDocument(stream); |
130 | } | |
131 | ||
132 | /** | |
133 | * Make an instance of XSD schema without I/O exceptions. | |
134 | * @param url URL with content | |
135 | * @return XSD schema | |
136 | * @see #make(InputStream) | |
137 | * @since 0.7.4 | |
138 | */ | |
139 | public static XSD make(final URL url) { | |
140 | try { | |
141 | 0 | return new XSDDocument(url); |
142 | 0 | } catch (final IOException ex) { |
143 | 0 | throw new IllegalStateException(ex); |
144 | } | |
145 | } | |
146 | ||
147 | @Override | |
148 | public String toString() { | |
149 | 0 | return new XMLDocument(this.xsd).toString(); |
150 | } | |
151 | ||
152 | @Override | |
153 | public Collection<SAXParseException> validate(final Source xml) { | |
154 | final Schema schema; | |
155 | try { | |
156 | 71 | synchronized (XSDDocument.class) { |
157 | 71 | schema = SchemaFactory |
158 | .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI) | |
159 | .newSchema(new StreamSource(new StringReader(this.xsd))); | |
160 | 71 | } |
161 | 0 | } catch (final SAXException ex) { |
162 | 0 | throw new IllegalStateException( |
163 | String.format("failed to create XSD schema from %s", this.xsd), | |
164 | ex | |
165 | ); | |
166 | 71 | } |
167 | 71 | final Collection<SAXParseException> errors = |
168 | new CopyOnWriteArrayList<SAXParseException>(); | |
169 | 71 | final Validator validator = schema.newValidator(); |
170 | 71 | validator.setErrorHandler(new ValidationHandler(errors)); |
171 | try { | |
172 | 71 | synchronized (XSDDocument.class) { |
173 | 71 | validator.validate(xml); |
174 | 71 | } |
175 | 0 | } catch (final SAXException ex) { |
176 | 0 | throw new IllegalStateException(ex); |
177 | 0 | } catch (final IOException ex) { |
178 | 0 | throw new IllegalStateException(ex); |
179 | 71 | } |
180 | 71 | Logger.debug( |
181 | this, "%s detected %d error(s)", | |
182 | schema.getClass().getName(), errors.size() | |
183 | ); | |
184 | 71 | return errors; |
185 | } | |
186 | ||
187 | /** | |
188 | * Validation error handler. | |
189 | */ | |
190 | static final class ValidationHandler implements ErrorHandler { | |
191 | /** | |
192 | * Errors. | |
193 | */ | |
194 | private final transient Collection<SAXParseException> errors; | |
195 | /** | |
196 | * Constructor. | |
197 | * @param errs Collection of errors | |
198 | */ | |
199 | 77 | ValidationHandler(final Collection<SAXParseException> errs) { |
200 | 77 | this.errors = errs; |
201 | 77 | } |
202 | @Override | |
203 | public void warning(final SAXParseException error) { | |
204 | 5 | this.errors.add(error); |
205 | 5 | } |
206 | @Override | |
207 | public void error(final SAXParseException error) { | |
208 | 890 | this.errors.add(error); |
209 | 890 | } |
210 | @Override | |
211 | public void fatalError(final SAXParseException error) { | |
212 | 0 | this.errors.add(error); |
213 | 0 | } |
214 | } | |
215 | ||
216 | } |