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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 package org.apache.maven.sitevalidator;
58
59 import java.io.File;
60 import java.io.FileInputStream;
61 import java.io.FileNotFoundException;
62 import java.io.FileOutputStream;
63 import java.io.IOException;
64 import java.io.UnsupportedEncodingException;
65 import java.net.UnknownHostException;
66 import java.util.ArrayList;
67 import java.util.Iterator;
68 import java.util.List;
69
70 import org.apache.tools.ant.BuildException;
71 import org.apache.tools.ant.DirectoryScanner;
72 import org.apache.tools.ant.Project;
73 import org.apache.tools.ant.Task;
74 import org.apache.tools.ant.types.DTDLocation;
75 import org.apache.tools.ant.types.FileSet;
76 import org.apache.tools.ant.types.XMLCatalog;
77 import org.dom4j.Element;
78 import org.dom4j.io.OutputFormat;
79 import org.dom4j.io.XMLWriter;
80 import org.dom4j.tree.DefaultElement;
81 import org.xml.sax.InputSource;
82 import org.xml.sax.SAXException;
83 import org.xml.sax.SAXNotRecognizedException;
84 import org.xml.sax.SAXNotSupportedException;
85 import org.xml.sax.XMLReader;
86 import org.xml.sax.helpers.XMLReaderFactory;
87
88 /***
89 * Validator task.
90 * This task is a modified version of the standard ant validator task, specialized for checking
91 * xhtml files and logging errors (without beaking the build)
92 * @author fgiust
93 * @version $Revision: 1.4 $ ($Author: fgiust $)
94 */
95 public class Validator extends Task
96 {
97 /***
98 * validation feature
99 */
100 private static final String VALIDATION_FEATURE = "http://xml.org/sax/features/validation";
101
102 /***
103 * encoding - defaults to ISO-8859-1
104 */
105 private String encoding = "ISO-8859-1";
106
107 /***
108 * output file
109 */
110 private File outputFile = null;
111
112 /***
113 * XmlWriter used to write to outputFile
114 */
115 private XMLWriter out;
116
117 /***
118 * sets of file to be validated
119 */
120 private List filesets = new ArrayList();
121
122 /***
123 * XMLReader used to validation process
124 */
125 private XMLReader xmlReader = null;
126
127 /***
128 * Validator handler: implements both ErrorHandler and DTDHandler
129 */
130 private ValidatorHandler validatorHandler = new ValidatorHandler();
131
132 /***
133 * XMLCatalog containing references to local dtds and entities
134 */
135 private XMLCatalog xmlCatalog = new XMLCatalog();
136
137 /***
138 * sets the input/output files encoding
139 * @param value encoding
140 */
141 public void setEncoding(String value)
142 {
143 encoding = value;
144 }
145
146 /***
147 * sets the output file
148 * @param file output file
149 */
150 public void setOUtput(File file)
151 {
152 outputFile = file;
153 }
154
155 /***
156 * add an XMLCatalog as a nested element.
157 * @param catalog XMLCatalog
158 */
159 public void addConfiguredXMLCatalog(XMLCatalog catalog)
160 {
161 xmlCatalog.addConfiguredXMLCatalog(catalog);
162 }
163
164 /***
165 * specify a set of file to be checked
166 * @param set Fileset
167 */
168 public void addFileset(FileSet set)
169 {
170 filesets.add(set);
171 }
172
173 /***
174 * initialize task
175 */
176 public void init()
177 {
178 super.init();
179 xmlCatalog.setProject(project);
180 }
181
182 /***
183 * Create a DTD location record.
184 * This stores the location of a DTD. The DTD is identified by its public Id
185 * @return DTDLocation
186 */
187 public DTDLocation createDTD()
188 {
189 DTDLocation dtdLocation = new DTDLocation();
190 xmlCatalog.addDTD(dtdLocation);
191 return dtdLocation;
192 }
193
194 /***
195 * Init the parser. Load the parser class and enable validation
196 */
197 private void initValidator()
198 {
199
200
201 try
202 {
203 xmlReader = XMLReaderFactory.createXMLReader();
204 xmlReader.setFeature(VALIDATION_FEATURE, true);
205
206 }
207 catch (SAXNotRecognizedException e)
208 {
209 throw new BuildException("Could not start validation: " + xmlReader + " doesn't provide validation");
210 }
211 catch (SAXNotSupportedException e)
212 {
213 throw new BuildException("Could not start validation: " + xmlReader + " doesn't provide validation");
214 }
215 catch (SAXException e)
216 {
217 throw new BuildException("Could not start validation: " + xmlReader + " not found");
218 }
219
220 log(xmlReader.getClass().getName(), Project.MSG_DEBUG);
221
222
223 xmlReader.setEntityResolver(xmlCatalog);
224
225
226 xmlReader.setErrorHandler(validatorHandler);
227 xmlReader.setEntityResolver(validatorHandler);
228
229 }
230
231 /***
232 * execute the task
233 * @throws BuildException if unable to build a validating SAX2 parser
234 */
235 public void execute() throws BuildException
236 {
237
238
239 File dir = outputFile.getParentFile();
240
241
242 if (dir != null)
243 {
244 dir.mkdirs();
245 }
246
247
248 OutputFormat format = OutputFormat.createPrettyPrint();
249 format.setEncoding(encoding);
250
251
252 try
253 {
254 out = new XMLWriter(new FileOutputStream(outputFile), format);
255 }
256 catch (UnsupportedEncodingException e)
257 {
258 throw new BuildException("Unsopported encoding: [" + encoding + "]");
259 }
260 catch (FileNotFoundException e)
261 {
262 throw new BuildException("Unable to write to [" + outputFile.getAbsolutePath() + "]");
263 }
264
265
266 initValidator();
267
268
269 validatorHandler.setOut(out);
270
271
272 Element validation = new DefaultElement("validation");
273 try
274 {
275
276 out.startDocument();
277 out.writeOpen(validation);
278
279
280
281
282 Iterator fileIterator = filesets.iterator();
283
284 while (fileIterator.hasNext())
285 {
286
287 FileSet fs = (FileSet) fileIterator.next();
288
289
290 DirectoryScanner ds = fs.getDirectoryScanner(project);
291
292
293 String[] files = ds.getIncludedFiles();
294
295
296 for (int j = 0; j < files.length; j++)
297 {
298
299 Element xmlfile = new DefaultElement("file");
300
301
302 File srcFile = new File(fs.getDir(project), files[j]);
303
304
305 xmlfile.addAttribute("name", files[j]);
306
307
308 out.writeOpen(xmlfile);
309
310
311 doValidate(srcFile);
312
313
314 out.writeClose(xmlfile);
315 }
316 }
317
318
319 out.writeClose(validation);
320
321
322 out.endDocument();
323 }
324
325 catch (SAXException e)
326 {
327 log(e.getClass() + " " + e.getMessage(), Project.MSG_WARN);
328 throw new BuildException(e.getMessage());
329 }
330 catch (IOException e)
331 {
332 log(e.getClass() + " " + e.getMessage(), Project.MSG_WARN);
333 throw new BuildException(e.getMessage());
334 }
335
336 finally
337 {
338 try
339 {
340
341 if (out != null)
342 {
343 out.close();
344 }
345 }
346 catch (IOException e)
347 {
348 e.printStackTrace();
349 }
350 }
351 }
352
353 /***
354 * parse the file
355 * @param afile File to validate
356 */
357 private void doValidate(File afile)
358 {
359 log("Validating " + afile.getName() + "... ", Project.MSG_INFO);
360
361
362 validatorHandler.init(afile);
363
364 try
365 {
366
367 InputSource is = new InputSource(new FileInputStream(afile));
368 String uri = "file:" + afile.getAbsolutePath().replace('//', '/');
369 for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#'))
370 {
371 uri = uri.substring(0, index) + "%23" + uri.substring(index + 1);
372 }
373 is.setSystemId(uri);
374 is.setEncoding(encoding);
375
376 xmlReader.parse(is);
377 }
378 catch (SAXException e)
379 {
380 Element error = new DefaultElement("fatalerror");
381 error.addText(e.getMessage());
382 writeToLog(error);
383
384 }
385 catch (UnknownHostException e)
386 {
387 Element error = new DefaultElement("fatalerror");
388
389 error.addText(
390 "Could not validate document " + afile + "; dtd missing from repository and unable to connect");
391 writeToLog(error);
392 }
393 catch (IOException e)
394 {
395 Element error = new DefaultElement("fatalerror");
396 error.addText(e.getMessage());
397 writeToLog(error);
398
399 }
400
401 if (validatorHandler.getErrors() > 0)
402 {
403 log(afile + ", errors=" + validatorHandler.getErrors(), Project.MSG_INFO);
404 }
405
406 }
407
408 /***
409 * write an element to the xml log file
410 * @param element Element to write
411 */
412 private void writeToLog(Element element)
413 {
414 try
415 {
416 out.write(element);
417 }
418 catch (IOException e)
419 {
420 e.printStackTrace();
421 }
422 }
423
424 }