source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 20.8 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package org.apache.tools.ant.taskdefs.optional;
18
19import java.io.File;
20import java.io.FileInputStream;
21import java.io.IOException;
22import java.util.Vector;
23
24import org.apache.tools.ant.AntClassLoader;
25import org.apache.tools.ant.BuildException;
26import org.apache.tools.ant.DirectoryScanner;
27import org.apache.tools.ant.Project;
28import org.apache.tools.ant.Task;
29import org.apache.tools.ant.types.DTDLocation;
30import org.apache.tools.ant.types.FileSet;
31import org.apache.tools.ant.types.Path;
32import org.apache.tools.ant.types.Reference;
33import org.apache.tools.ant.types.XMLCatalog;
34import org.apache.tools.ant.util.FileUtils;
35import org.apache.tools.ant.util.JAXPUtils;
36
37import org.xml.sax.EntityResolver;
38import org.xml.sax.ErrorHandler;
39import org.xml.sax.InputSource;
40import org.xml.sax.Parser;
41import org.xml.sax.SAXException;
42import org.xml.sax.SAXNotRecognizedException;
43import org.xml.sax.SAXNotSupportedException;
44import org.xml.sax.SAXParseException;
45import org.xml.sax.XMLReader;
46import org.xml.sax.helpers.ParserAdapter;
47
48/**
49 * Checks XML files are valid (or only well formed). The
50 * task uses the SAX2 parser implementation provided by JAXP by default
51 * (probably the one that is used by Ant itself), but one can specify any
52 * SAX1/2 parser if needed.
53 *
54 */
55public class XMLValidateTask extends Task {
56
57 /**
58 * helper for path -> URI and URI -> path conversions.
59 */
60 private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
61
62 protected static final String INIT_FAILED_MSG =
63 "Could not start xml validation: ";
64
65 // ant task properties
66 // defaults
67 protected boolean failOnError = true;
68 protected boolean warn = true;
69 protected boolean lenient = false;
70 protected String readerClassName = null;
71
72 /** file to be validated */
73 protected File file = null;
74 /** sets of file to be validated */
75 protected Vector filesets = new Vector();
76 protected Path classpath;
77
78 /**
79 * the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified,
80 * it's wrapped in an adapter that make it behave as a XMLReader.
81 * a more 'standard' way of doing this would be to use the JAXP1.1 SAXParser
82 * interface.
83 */
84 protected XMLReader xmlReader = null;
85 // XMLReader used to validation process
86 protected ValidatorErrorHandler errorHandler = new ValidatorErrorHandler();
87 // to report sax parsing errors
88
89 /** The vector to store all attributes (features) to be set on the parser. **/
90 private Vector attributeList = new Vector();
91
92 /**
93 * List of properties.
94 */
95 private final Vector propertyList = new Vector();
96
97 private XMLCatalog xmlCatalog = new XMLCatalog();
98
99 /**
100 * Specify how parser error are to be handled.
101 * Optional, default is <code>true</code>.
102 * <p>
103 * If set to <code>true</code> (default), throw a buildException if the
104 * parser yields an error.
105 * @param fail if set to <code>false</code> do not fail on error
106 */
107 public void setFailOnError(boolean fail) {
108 failOnError = fail;
109 }
110
111 /**
112 * Specify how parser error are to be handled.
113 * <p>
114 * If set to <code>true</true> (default), log a warn message for each SAX warn event.
115 * @param bool if set to <code>false</code> do not send warnings
116 */
117 public void setWarn(boolean bool) {
118 warn = bool;
119 }
120
121 /**
122 * Specify whether the parser should be validating. Default
123 * is <code>true</code>.
124 * <p>
125 * If set to false, the validation will fail only if the parsed document
126 * is not well formed XML.
127 * <p>
128 * this option is ignored if the specified class
129 * with {@link #setClassName(String)} is not a SAX2 XMLReader.
130 * @param bool if set to <code>false</code> only fail on malformed XML
131 */
132 public void setLenient(boolean bool) {
133 lenient = bool;
134 }
135
136 /**
137 * Specify the class name of the SAX parser to be used. (optional)
138 * @param className should be an implementation of SAX2
139 * <code>org.xml.sax.XMLReader</code> or SAX2 <code>org.xml.sax.Parser</code>.
140 * <p> if className is an implementation of
141 * <code>org.xml.sax.Parser</code>, {@link #setLenient(boolean)},
142 * will be ignored.
143 * <p> if not set, the default will be used.
144 * @see org.xml.sax.XMLReader
145 * @see org.xml.sax.Parser
146 */
147 public void setClassName(String className) {
148 readerClassName = className;
149 }
150
151 /**
152 * Specify the classpath to be searched to load the parser (optional)
153 * @param classpath the classpath to load the parser
154 */
155 public void setClasspath(Path classpath) {
156 if (this.classpath == null) {
157 this.classpath = classpath;
158 } else {
159 this.classpath.append(classpath);
160 }
161 }
162
163 /**
164 * @see #setClasspath
165 * @return the classpath created
166 */
167 public Path createClasspath() {
168 if (this.classpath == null) {
169 this.classpath = new Path(getProject());
170 }
171 return this.classpath.createPath();
172 }
173
174 /**
175 * Where to find the parser class; optional.
176 * @see #setClasspath
177 * @param r reference to a classpath defined elsewhere
178 */
179 public void setClasspathRef(Reference r) {
180 createClasspath().setRefid(r);
181 }
182
183 /**
184 * specify the file to be checked; optional.
185 * @param file the file to be checked
186 */
187 public void setFile(File file) {
188 this.file = file;
189 }
190
191 /**
192 * add an XMLCatalog as a nested element; optional.
193 * @param catalog XMLCatalog to use
194 */
195 public void addConfiguredXMLCatalog(XMLCatalog catalog) {
196 xmlCatalog.addConfiguredXMLCatalog(catalog);
197 }
198
199 /**
200 * specify a set of file to be checked
201 * @param set the fileset to check
202 */
203 public void addFileset(FileSet set) {
204 filesets.addElement(set);
205 }
206
207 /**
208 * Add an attribute nested element. This is used for setting arbitrary
209 * features of the SAX parser.
210 * Valid attributes
211 * <a href="http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description">include</a>
212 * @return attribute created
213 * @since ant1.6
214 */
215 public Attribute createAttribute() {
216 final Attribute feature = new Attribute();
217 attributeList.addElement(feature);
218 return feature;
219 }
220
221 /**
222 * Creates a property.
223 *
224 * @return a property.
225 * @since ant 1.6.2
226 */
227 public Property createProperty() {
228 final Property prop = new Property();
229 propertyList.addElement(prop);
230 return prop;
231 }
232
233 /**
234 * Called by the project to let the task initialize properly.
235 *
236 * @exception BuildException if something goes wrong with the build
237 */
238 public void init() throws BuildException {
239 super.init();
240 xmlCatalog.setProject(getProject());
241 }
242
243 /**
244 * Create a DTD location record; optional.
245 * This stores the location of a DTD. The DTD is identified
246 * by its public Id.
247 * @return created DTD location
248 */
249 public DTDLocation createDTD() {
250 DTDLocation dtdLocation = new DTDLocation();
251 xmlCatalog.addDTD(dtdLocation);
252 return dtdLocation;
253 }
254 /**
255 * accessor to the xmlCatalog used in the task
256 * @return xmlCatalog reference
257 */
258 protected EntityResolver getEntityResolver() {
259 return xmlCatalog;
260 }
261 /**
262 * execute the task
263 * @throws BuildException if <code>failonerror</code> is true and an error happens
264 */
265 public void execute() throws BuildException {
266
267 int fileProcessed = 0;
268 if (file == null && (filesets.size() == 0)) {
269 throw new BuildException(
270 "Specify at least one source - " + "a file or a fileset.");
271 }
272
273 initValidator();
274
275 if (file != null) {
276 if (file.exists() && file.canRead() && file.isFile()) {
277 doValidate(file);
278 fileProcessed++;
279 } else {
280 String errorMsg = "File " + file + " cannot be read";
281 if (failOnError) {
282 throw new BuildException(errorMsg);
283 } else {
284 log(errorMsg, Project.MSG_ERR);
285 }
286 }
287 }
288
289 for (int i = 0; i < filesets.size(); i++) {
290
291 FileSet fs = (FileSet) filesets.elementAt(i);
292 DirectoryScanner ds = fs.getDirectoryScanner(getProject());
293 String[] files = ds.getIncludedFiles();
294
295 for (int j = 0; j < files.length; j++) {
296 File srcFile = new File(fs.getDir(getProject()), files[j]);
297 doValidate(srcFile);
298 fileProcessed++;
299 }
300 }
301 log(fileProcessed + " file(s) have been successfully validated.");
302 }
303
304 /**
305 * init the parser :
306 * load the parser class, and set features if necessary
307 */
308 private void initValidator() {
309
310 Object reader = null;
311 if (readerClassName == null) {
312 try {
313 reader = JAXPUtils.getXMLReader();
314 } catch (BuildException exc) {
315 reader = JAXPUtils.getParser();
316 }
317 } else {
318
319 Class readerClass = null;
320 try {
321 // load the parser class
322 if (classpath != null) {
323 AntClassLoader loader =
324 getProject().createClassLoader(classpath);
325 readerClass = Class.forName(readerClassName, true, loader);
326 } else {
327 readerClass = Class.forName(readerClassName);
328 }
329
330 reader = readerClass.newInstance();
331 } catch (ClassNotFoundException e) {
332 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
333 } catch (InstantiationException e) {
334 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
335 } catch (IllegalAccessException e) {
336 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
337 }
338 }
339
340 // then check it implements XMLReader
341 if (reader instanceof XMLReader) {
342 xmlReader = (XMLReader) reader;
343 log(
344 "Using SAX2 reader " + reader.getClass().getName(),
345 Project.MSG_VERBOSE);
346 } else {
347
348 // see if it is a SAX1 Parser
349 if (reader instanceof Parser) {
350 xmlReader = new ParserAdapter((Parser) reader);
351 log(
352 "Using SAX1 parser " + reader.getClass().getName(),
353 Project.MSG_VERBOSE);
354 } else {
355 throw new BuildException(
356 INIT_FAILED_MSG
357 + reader.getClass().getName()
358 + " implements nor SAX1 Parser nor SAX2 XMLReader.");
359 }
360 }
361
362 xmlReader.setEntityResolver(getEntityResolver());
363 xmlReader.setErrorHandler(errorHandler);
364
365 if (!(xmlReader instanceof ParserAdapter)) {
366 // turn validation on
367 if (!lenient) {
368 setFeature("http://xml.org/sax/features/validation", true);
369 }
370 // set the feature from the attribute list
371 for (int i = 0; i < attributeList.size(); i++) {
372 Attribute feature = (Attribute) attributeList.elementAt(i);
373 setFeature(feature.getName(), feature.getValue());
374
375 }
376
377 // Sets properties
378 for (int i = 0; i < propertyList.size(); i++) {
379 final Property prop = (Property) propertyList.elementAt(i);
380 setProperty(prop.getName(), prop.getValue());
381 }
382 }
383 }
384
385 /**
386 * Set a feature on the parser.
387 * @param feature the name of the feature to set
388 * @param value the value of the feature
389 */
390 private void setFeature(String feature, boolean value)
391 throws BuildException {
392
393 try {
394 xmlReader.setFeature(feature, value);
395 } catch (SAXNotRecognizedException e) {
396 throw new BuildException(
397 "Parser "
398 + xmlReader.getClass().getName()
399 + " doesn't recognize feature "
400 + feature,
401 e,
402 getLocation());
403 } catch (SAXNotSupportedException e) {
404 throw new BuildException(
405 "Parser "
406 + xmlReader.getClass().getName()
407 + " doesn't support feature "
408 + feature,
409 e,
410 getLocation());
411 }
412 }
413
414 /**
415 * Sets a property.
416 *
417 * @param name a property name
418 * @param value a property value.
419 * @throws BuildException if an error occurs.
420 */
421 private void setProperty(String name, String value) throws BuildException {
422 // Validates property
423 if (name == null || value == null) {
424 throw new BuildException("Property name and value must be specified.");
425 }
426
427 try {
428 xmlReader.setProperty(name, value);
429 } catch (SAXNotRecognizedException e) {
430 throw new BuildException(
431 "Parser "
432 + xmlReader.getClass().getName()
433 + " doesn't recognize property "
434 + name,
435 e,
436 getLocation());
437 } catch (SAXNotSupportedException e) {
438 throw new BuildException(
439 "Parser "
440 + xmlReader.getClass().getName()
441 + " doesn't support property "
442 + name,
443 e,
444 getLocation());
445 }
446 }
447
448 /**
449 * parse the file
450 */
451 private void doValidate(File afile) {
452 try {
453 log("Validating " + afile.getName() + "... ", Project.MSG_VERBOSE);
454 errorHandler.init(afile);
455 InputSource is = new InputSource(new FileInputStream(afile));
456 String uri = FILE_UTILS.toURI(afile.getAbsolutePath());
457 is.setSystemId(uri);
458 xmlReader.parse(is);
459 } catch (SAXException ex) {
460 if (failOnError) {
461 throw new BuildException(
462 "Could not validate document " + afile);
463 } else {
464 log("Could not validate document " + afile + ": " + ex.toString());
465 }
466 } catch (IOException ex) {
467 throw new BuildException(
468 "Could not validate document " + afile,
469 ex);
470 }
471
472 if (errorHandler.getFailure()) {
473 if (failOnError) {
474 throw new BuildException(
475 afile + " is not a valid XML document.");
476 } else {
477 log(afile + " is not a valid XML document", Project.MSG_ERR);
478 }
479 }
480 }
481
482 /**
483 * ValidatorErrorHandler role :
484 * <ul>
485 * <li> log SAX parse exceptions,
486 * <li> remember if an error occurred
487 * </ul>
488 */
489 protected class ValidatorErrorHandler implements ErrorHandler {
490
491 protected File currentFile = null;
492 protected String lastErrorMessage = null;
493 protected boolean failed = false;
494 /**
495 * initialises the class
496 * @param file file used
497 */
498 public void init(File file) {
499 currentFile = file;
500 failed = false;
501 }
502 /**
503 * did an error happen during last parsing ?
504 * @return did an error happen during last parsing ?
505 */
506 public boolean getFailure() {
507 return failed;
508 }
509
510 /**
511 * record a fatal error
512 * @param exception the fatal error
513 */
514 public void fatalError(SAXParseException exception) {
515 failed = true;
516 doLog(exception, Project.MSG_ERR);
517 }
518 /**
519 * receive notification of a recoverable error
520 * @param exception the error
521 */
522 public void error(SAXParseException exception) {
523 failed = true;
524 doLog(exception, Project.MSG_ERR);
525 }
526 /**
527 * receive notification of a warning
528 * @param exception the warning
529 */
530 public void warning(SAXParseException exception) {
531 // depending on implementation, XMLReader can yield hips of warning,
532 // only output then if user explicitly asked for it
533 if (warn) {
534 doLog(exception, Project.MSG_WARN);
535 }
536 }
537
538 private void doLog(SAXParseException e, int logLevel) {
539
540 log(getMessage(e), logLevel);
541 }
542
543 private String getMessage(SAXParseException e) {
544 String sysID = e.getSystemId();
545 if (sysID != null) {
546 String name = sysID;
547 if (sysID.startsWith("file:")) {
548 try {
549 name = FILE_UTILS.fromURI(sysID);
550 } catch (Exception ex) {
551 // if this is not a valid file: just use the uri
552 }
553 }
554 int line = e.getLineNumber();
555 int col = e.getColumnNumber();
556 return name
557 + (line == -1
558 ? ""
559 : (":" + line + (col == -1 ? "" : (":" + col))))
560 + ": "
561 + e.getMessage();
562 }
563 return e.getMessage();
564 }
565 }
566
567 /**
568 * The class to create to set a feature of the parser.
569 * @since ant1.6
570 */
571 public class Attribute {
572 /** The name of the attribute to set.
573 *
574 * Valid attributes <a href="http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description">include.</a>
575 */
576 private String attributeName = null;
577
578 /**
579 * The value of the feature.
580 **/
581 private boolean attributeValue;
582
583 /**
584 * Set the feature name.
585 * @param name the name to set
586 */
587 public void setName(String name) {
588 attributeName = name;
589 }
590 /**
591 * Set the feature value to true or false.
592 * @param value feature value
593 */
594 public void setValue(boolean value) {
595 attributeValue = value;
596 }
597
598 /**
599 * Gets the attribute name.
600 * @return the feature name
601 */
602 public String getName() {
603 return attributeName;
604 }
605
606 /**
607 * Gets the attribute value.
608 * @return the feature value
609 */
610 public boolean getValue() {
611 return attributeValue;
612 }
613 }
614
615 /**
616 * A Parser property.
617 * See <a href="http://xml.apache.org/xerces-j/properties.html">
618 * XML parser properties</a> for usable properties
619 * @since ant 1.6.2
620 */
621 public final class Property {
622
623 private String name;
624 private String value;
625 /**
626 * accessor to the name of the property
627 * @return name of the property
628 */
629 public String getName() {
630 return name;
631 }
632 /**
633 * setter for the name of the property
634 * @param name name of the property
635 */
636 public void setName(String name) {
637 this.name = name;
638 }
639
640 /**
641 * getter for the value of the property
642 * @return value of the property
643 */
644 public String getValue() {
645 return value;
646 }
647 /**
648 * sets the value of the property
649 * @param value value of the property
650 */
651 public void setValue(String value) {
652 this.value = value;
653 }
654
655 } // Property
656
657}
Note: See TracBrowser for help on using the repository browser.