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.w3c;
31
32 import com.jcabi.aspects.Immutable;
33 import com.jcabi.http.Request;
34 import com.jcabi.http.Response;
35 import com.jcabi.http.request.JdkRequest;
36 import com.jcabi.http.response.XmlResponse;
37 import com.jcabi.log.Logger;
38 import com.jcabi.xml.XML;
39 import java.io.IOException;
40 import java.net.HttpURLConnection;
41 import java.net.URI;
42 import java.util.Arrays;
43 import java.util.List;
44 import java.util.regex.Pattern;
45 import javax.ws.rs.core.HttpHeaders;
46 import javax.ws.rs.core.MediaType;
47 import javax.ws.rs.core.UriBuilder;
48 import lombok.EqualsAndHashCode;
49 import lombok.ToString;
50
51
52
53
54
55
56
57 @Immutable
58 @ToString
59 @EqualsAndHashCode(callSuper = false, of = "uri")
60 final class DefaultCssValidator
61 extends AbstractBaseValidator implements Validator {
62
63
64
65
66 private final transient String uri;
67
68
69
70
71
72 DefaultCssValidator(final URI entry) {
73 super();
74 this.uri = entry.toString();
75 }
76
77 @Override
78 public ValidationResponse validate(final String css)
79 throws IOException {
80 final ValidationResponse response;
81 final Pattern pattern = Pattern.compile(
82 ".*^/\\* JIGSAW IGNORE: [^\\n]+\\*/$.*",
83 Pattern.MULTILINE | Pattern.DOTALL
84 );
85 try {
86 if (pattern.matcher(css).matches()) {
87 response = AbstractBaseValidator.success("");
88 } else {
89 response = this.processed(css);
90 }
91 } catch (final IllegalArgumentException ex) {
92 throw new IOException(ex);
93 }
94 return response;
95 }
96
97
98
99
100
101
102
103 private ValidationResponse processed(final String css) throws IOException {
104 final Request req = this.request(
105 AbstractBaseValidator.entity(
106 "file", DefaultCssValidator.filter(css), "text/css"
107 )
108 );
109 final Response response = DefaultCssValidator.correct(req.fetch());
110 return DefaultCssValidator.build(
111 response.as(XmlResponse.class)
112 .registerNs("env", "http://www.w3.org/2003/05/soap-envelope")
113 .registerNs("m", "http://www.w3.org/2005/07/css-validator")
114 .assertXPath("//m:validity")
115 .assertXPath("//m:checkedby")
116 .xml()
117 );
118 }
119
120
121
122
123
124
125 private Request request(final String entity) {
126 return new JdkRequest(this.uri)
127 .method(Request.POST)
128 .body().set(entity).back()
129 .header(HttpHeaders.USER_AGENT, AbstractBaseValidator.USER_AGENT)
130 .header(HttpHeaders.ACCEPT, "application/soap+xml")
131 .header(
132 HttpHeaders.CONTENT_TYPE,
133 Logger.format(
134 "%s; boundary=%s",
135 MediaType.MULTIPART_FORM_DATA,
136 AbstractBaseValidator.BOUNDARY
137 )
138 );
139 }
140
141
142
143
144
145
146 private static ValidationResponse build(final XML soap) {
147 final DefaultValidationResponse resp = new DefaultValidationResponse(
148 "true".equals(
149 AbstractBaseValidator.textOf(soap.xpath("//m:validity/text()"))
150 ),
151 UriBuilder.fromUri(
152 AbstractBaseValidator.textOf(soap.xpath("//m:checkedby/text()"))
153 ).build(),
154 AbstractBaseValidator.textOf(soap.xpath("//m:doctype/text()")),
155 AbstractBaseValidator.charset(
156 AbstractBaseValidator.textOf(soap.xpath("//m:charset/text()"))
157 )
158 );
159 for (final XML node : soap.nodes("//m:error")) {
160 resp.addError(DefaultCssValidator.defect(node));
161 }
162 for (final XML node : soap.nodes("//m:warning")) {
163 resp.addWarning(DefaultCssValidator.defect(node));
164 }
165 return resp;
166 }
167
168
169
170
171
172
173 private static Defect defect(final XML node) {
174 return new Defect(
175 AbstractBaseValidator.intOf(node.xpath("m:line/text()")),
176 AbstractBaseValidator.intOf(node.xpath("m:col/text()")),
177 AbstractBaseValidator.textOf(node.xpath("m:source/text()")),
178 AbstractBaseValidator.textOf(node.xpath("m:explanation/text()")),
179 AbstractBaseValidator.textOf(node.xpath("m:messageid/text()")),
180 AbstractBaseValidator.textOf(node.xpath("m:message/text()"))
181 );
182 }
183
184
185
186
187
188
189
190 private static Response correct(final Response response)
191 throws IOException {
192 final List<Integer> statuses = Arrays.asList(
193 HttpURLConnection.HTTP_INTERNAL_ERROR,
194 HttpURLConnection.HTTP_NOT_IMPLEMENTED,
195 HttpURLConnection.HTTP_BAD_GATEWAY,
196 HttpURLConnection.HTTP_UNAVAILABLE,
197 HttpURLConnection.HTTP_GATEWAY_TIMEOUT,
198 HttpURLConnection.HTTP_VERSION
199 );
200 if (statuses.contains(response.status())) {
201 throw new IOException(
202 String.format(
203 "Bad status from W3C server: %1d",
204 response.status()
205 )
206 );
207 }
208 return response;
209 }
210
211
212
213
214
215
216 private static String filter(final String css) {
217 return Pattern.compile(
218 "^/\\* JIGSAW: [^\\n]+\\*/$",
219 Pattern.MULTILINE | Pattern.DOTALL
220 ).matcher(css).replaceAll("");
221 }
222
223 }