1
2
3
4
5 package com.jcabi.xml;
6
7 import com.jcabi.log.Logger;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.Iterator;
11 import java.util.List;
12 import javax.xml.namespace.NamespaceContext;
13 import lombok.EqualsAndHashCode;
14 import org.cactoos.Scalar;
15 import org.cactoos.scalar.Sticky;
16 import org.cactoos.scalar.Unchecked;
17 import org.w3c.dom.Node;
18 import org.w3c.dom.ls.LSResourceResolver;
19 import org.xml.sax.SAXParseException;
20
21
22
23
24
25
26
27
28
29
30
31 @SuppressWarnings("PMD.TooManyMethods")
32 @EqualsAndHashCode(of = "origin")
33 public final class StrictXML implements XML {
34
35
36
37
38 private final transient Unchecked<XML> origin;
39
40
41
42
43
44 public StrictXML(final XML xml) {
45 this(xml, new ClasspathResolver());
46 }
47
48
49
50
51
52
53
54 public StrictXML(final XML xml, final LSResourceResolver resolver) {
55 this(xml, () -> xml.validate(resolver));
56 }
57
58
59
60
61
62
63 public StrictXML(final XML xml, final XML schema) {
64 this(xml, () -> xml.validate(schema));
65 }
66
67
68
69
70
71
72 private StrictXML(
73 final XML xml,
74 final Scalar<Collection<SAXParseException>> errs
75 ) {
76 this(
77 () -> {
78 final Collection<SAXParseException> errors = errs.value();
79 if (!errors.isEmpty()) {
80 Logger.warn(
81 StrictXML.class,
82 "%d XML validation error(s):\n %s\n%s",
83 errors.size(),
84 StrictXML.join(StrictXML.print(errors), "\n "),
85 xml
86 );
87 throw new IllegalArgumentException(
88 String.format(
89 "%d error(s) in XML document: %s",
90 errors.size(),
91 StrictXML.join(StrictXML.print(errors), ";")
92 )
93 );
94 }
95 return xml;
96 }
97 );
98 }
99
100
101
102
103
104 private StrictXML(final Scalar<XML> xml) {
105 this.origin = new Unchecked<>(new Sticky<>(xml));
106 }
107
108 @Override
109 public String toString() {
110 return this.origin.value().toString();
111 }
112
113 @Override
114 public List<String> xpath(final String query) {
115 return this.origin.value().xpath(query);
116 }
117
118 @Override
119 public List<XML> nodes(final String query) {
120 return this.origin.value().nodes(query);
121 }
122
123 @Override
124 public XML registerNs(final String prefix, final Object uri) {
125 return this.origin.value().registerNs(prefix, uri);
126 }
127
128 @Override
129 public XML merge(final NamespaceContext context) {
130 return this.origin.value().merge(context);
131 }
132
133
134
135
136
137
138
139
140 @Deprecated
141 @Override
142 public Node node() {
143 return this.origin.value().deepCopy();
144 }
145
146 @Override
147 public Node inner() {
148 return this.origin.value().inner();
149 }
150
151 @Override
152 public Node deepCopy() {
153 return this.origin.value().deepCopy();
154 }
155
156 @Override
157 public Collection<SAXParseException> validate(final LSResourceResolver resolver) {
158 return this.origin.value().validate(resolver);
159 }
160
161 @Override
162 public Collection<SAXParseException> validate(final XML xsd) {
163 return this.origin.value().validate(xsd);
164 }
165
166
167
168
169
170
171 private static Iterable<String> print(
172 final Collection<SAXParseException> errors
173 ) {
174 final Collection<String> lines = new ArrayList<>(errors.size());
175 for (final SAXParseException error : errors) {
176 lines.add(StrictXML.asMessage(error));
177 }
178 return lines;
179 }
180
181
182
183
184
185
186 private static String asMessage(final SAXParseException violation) {
187 final StringBuilder msg = new StringBuilder(100);
188 if (violation.getLineNumber() >= 0) {
189 msg.append('#').append(violation.getLineNumber());
190 if (violation.getColumnNumber() >= 0) {
191 msg.append(':').append(violation.getColumnNumber());
192 }
193 msg.append(' ');
194 }
195 msg.append(violation.getLocalizedMessage());
196 if (violation.getException() != null) {
197 msg.append(" (")
198 .append(violation.getException().getClass().getSimpleName())
199 .append(')');
200 }
201 return msg.toString();
202 }
203
204
205
206
207
208
209
210
211 private static String join(final Iterable<?> iterable, final String sep) {
212 final Iterator<?> iterator = iterable.iterator();
213 final Object first = iterator.next();
214 final StringBuilder buf = new StringBuilder(256);
215 if (first != null) {
216 buf.append(first);
217 }
218 while (iterator.hasNext()) {
219 buf.append(sep);
220 final Object obj = iterator.next();
221 if (obj != null) {
222 buf.append(obj);
223 }
224 }
225 return buf.toString();
226 }
227 }