KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > XMLValidateTask


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional;
19
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.util.Vector JavaDoc;
24
25 import org.apache.tools.ant.AntClassLoader;
26 import org.apache.tools.ant.BuildException;
27 import org.apache.tools.ant.DirectoryScanner;
28 import org.apache.tools.ant.Project;
29 import org.apache.tools.ant.Task;
30 import org.apache.tools.ant.types.DTDLocation;
31 import org.apache.tools.ant.types.FileSet;
32 import org.apache.tools.ant.types.Path;
33 import org.apache.tools.ant.types.Reference;
34 import org.apache.tools.ant.types.XMLCatalog;
35 import org.apache.tools.ant.util.FileUtils;
36 import org.apache.tools.ant.util.JAXPUtils;
37 import org.apache.tools.ant.util.XmlConstants;
38
39 import org.xml.sax.EntityResolver JavaDoc;
40 import org.xml.sax.ErrorHandler JavaDoc;
41 import org.xml.sax.InputSource JavaDoc;
42 import org.xml.sax.Parser JavaDoc;
43 import org.xml.sax.SAXException JavaDoc;
44 import org.xml.sax.SAXNotRecognizedException JavaDoc;
45 import org.xml.sax.SAXNotSupportedException JavaDoc;
46 import org.xml.sax.SAXParseException JavaDoc;
47 import org.xml.sax.XMLReader JavaDoc;
48 import org.xml.sax.helpers.ParserAdapter JavaDoc;
49
50 /**
51  * Checks XML files are valid (or only well formed). The
52  * task uses the SAX2 parser implementation provided by JAXP by default
53  * (probably the one that is used by Ant itself), but one can specify any
54  * SAX1/2 parser if needed.
55  *
56  */

57 public class XMLValidateTask extends Task {
58
59     /**
60      * helper for path -> URI and URI -> path conversions.
61      */

62     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
63
64     protected static final String JavaDoc INIT_FAILED_MSG =
65         "Could not start xml validation: ";
66
67     // ant task properties
68
// defaults
69
// CheckStyle:VisibilityModifier OFF - bc
70
protected boolean failOnError = true;
71     protected boolean warn = true;
72     protected boolean lenient = false;
73     protected String JavaDoc readerClassName = null;
74
75     /** file to be validated */
76     protected File JavaDoc file = null;
77     /** sets of file to be validated */
78     protected Vector JavaDoc filesets = new Vector JavaDoc();
79     protected Path classpath;
80
81     /**
82      * the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified,
83      * it's wrapped in an adapter that make it behave as a XMLReader.
84      * a more 'standard' way of doing this would be to use the JAXP1.1 SAXParser
85      * interface.
86      */

87     protected XMLReader JavaDoc xmlReader = null;
88     // XMLReader used to validation process
89
protected ValidatorErrorHandler errorHandler = new ValidatorErrorHandler();
90     // to report sax parsing errors
91
// CheckStyle:VisibilityModifier ON
92

93     /** The vector to store all attributes (features) to be set on the parser. **/
94     private Vector JavaDoc attributeList = new Vector JavaDoc();
95
96     /**
97      * List of properties.
98      */

99     private final Vector JavaDoc propertyList = new Vector JavaDoc();
100
101     private XMLCatalog xmlCatalog = new XMLCatalog();
102     /** Message for sucessfull validation */
103     public static final String JavaDoc MESSAGE_FILES_VALIDATED
104         = " file(s) have been successfully validated.";
105
106     /**
107      * Specify how parser error are to be handled.
108      * Optional, default is <code>true</code>.
109      * <p>
110      * If set to <code>true</code> (default), throw a buildException if the
111      * parser yields an error.
112      * @param fail if set to <code>false</code> do not fail on error
113      */

114     public void setFailOnError(boolean fail) {
115         failOnError = fail;
116     }
117
118     /**
119      * Specify how parser error are to be handled.
120      * <p>
121      * If set to <code>true</true> (default), log a warn message for each SAX warn event.
122      * @param bool if set to <code>false</code> do not send warnings
123      */

124     public void setWarn(boolean bool) {
125         warn = bool;
126     }
127
128     /**
129      * Specify whether the parser should be validating. Default
130      * is <code>true</code>.
131      * <p>
132      * If set to false, the validation will fail only if the parsed document
133      * is not well formed XML.
134      * <p>
135      * this option is ignored if the specified class
136      * with {@link #setClassName(String)} is not a SAX2 XMLReader.
137      * @param bool if set to <code>false</code> only fail on malformed XML
138      */

139     public void setLenient(boolean bool) {
140         lenient = bool;
141     }
142
143     /**
144      * Specify the class name of the SAX parser to be used. (optional)
145      * @param className should be an implementation of SAX2
146      * <code>org.xml.sax.XMLReader</code> or SAX2 <code>org.xml.sax.Parser</code>.
147      * <p> if className is an implementation of
148      * <code>org.xml.sax.Parser</code>, {@link #setLenient(boolean)},
149      * will be ignored.
150      * <p> if not set, the default will be used.
151      * @see org.xml.sax.XMLReader
152      * @see org.xml.sax.Parser
153      */

154     public void setClassName(String JavaDoc className) {
155         readerClassName = className;
156     }
157
158     /**
159      * Specify the classpath to be searched to load the parser (optional)
160      * @param classpath the classpath to load the parser
161      */

162     public void setClasspath(Path classpath) {
163         if (this.classpath == null) {
164             this.classpath = classpath;
165         } else {
166             this.classpath.append(classpath);
167         }
168     }
169
170     /**
171      * @see #setClasspath
172      * @return the classpath created
173      */

174     public Path createClasspath() {
175         if (this.classpath == null) {
176             this.classpath = new Path(getProject());
177         }
178         return this.classpath.createPath();
179     }
180
181     /**
182      * Where to find the parser class; optional.
183      * @see #setClasspath
184      * @param r reference to a classpath defined elsewhere
185      */

186     public void setClasspathRef(Reference r) {
187         createClasspath().setRefid(r);
188     }
189
190     /**
191      * specify the file to be checked; optional.
192      * @param file the file to be checked
193      */

194     public void setFile(File JavaDoc file) {
195         this.file = file;
196     }
197
198     /**
199      * add an XMLCatalog as a nested element; optional.
200      * @param catalog XMLCatalog to use
201      */

202     public void addConfiguredXMLCatalog(XMLCatalog catalog) {
203         xmlCatalog.addConfiguredXMLCatalog(catalog);
204     }
205
206     /**
207      * specify a set of file to be checked
208      * @param set the fileset to check
209      */

210     public void addFileset(FileSet set) {
211         filesets.addElement(set);
212     }
213
214     /**
215      * Add an attribute nested element. This is used for setting arbitrary
216      * features of the SAX parser.
217      * Valid attributes
218      * <a HREF=
219      * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description"
220      * >include</a>
221      * @return attribute created
222      * @since ant1.6
223      */

224     public Attribute createAttribute() {
225         final Attribute feature = new Attribute();
226         attributeList.addElement(feature);
227         return feature;
228     }
229
230     /**
231      * Creates a property.
232      *
233      * @return a property.
234      * @since ant 1.6.2
235      */

236     public Property createProperty() {
237         final Property prop = new Property();
238         propertyList.addElement(prop);
239         return prop;
240     }
241
242     /**
243      * Called by the project to let the task initialize properly.
244      *
245      * @exception BuildException if something goes wrong with the build
246      */

247     public void init() throws BuildException {
248         super.init();
249         xmlCatalog.setProject(getProject());
250     }
251
252     /**
253      * Create a DTD location record; optional.
254      * This stores the location of a DTD. The DTD is identified
255      * by its public Id.
256      * @return created DTD location
257      */

258     public DTDLocation createDTD() {
259         DTDLocation dtdLocation = new DTDLocation();
260         xmlCatalog.addDTD(dtdLocation);
261         return dtdLocation;
262     }
263     /**
264      * accessor to the xmlCatalog used in the task
265      * @return xmlCatalog reference
266      */

267     protected EntityResolver JavaDoc getEntityResolver() {
268         return xmlCatalog;
269     }
270
271     /**
272      * get the XML reader. Non-null only after {@link #initValidator()}.
273      * If the reader is an instance of {@link ParserAdapter} then
274      * the parser is a SAX1 parser, and you cannot call
275      * {@link #setFeature(String, boolean)} or {@link #setProperty(String, String)}
276      * on it.
277      * @return the XML reader or null.
278      */

279     protected XMLReader JavaDoc getXmlReader() {
280         return xmlReader;
281     }
282
283     /**
284      * execute the task
285      * @throws BuildException if <code>failonerror</code> is true and an error happens
286      */

287     public void execute() throws BuildException {
288
289         int fileProcessed = 0;
290         if (file == null && (filesets.size() == 0)) {
291             throw new BuildException(
292                 "Specify at least one source - " + "a file or a fileset.");
293         }
294
295
296
297         if (file != null) {
298             if (file.exists() && file.canRead() && file.isFile()) {
299                 doValidate(file);
300                 fileProcessed++;
301             } else {
302                 String JavaDoc errorMsg = "File " + file + " cannot be read";
303                 if (failOnError) {
304                     throw new BuildException(errorMsg);
305                 } else {
306                     log(errorMsg, Project.MSG_ERR);
307                 }
308             }
309         }
310
311         for (int i = 0; i < filesets.size(); i++) {
312
313             FileSet fs = (FileSet) filesets.elementAt(i);
314             DirectoryScanner ds = fs.getDirectoryScanner(getProject());
315             String JavaDoc[] files = ds.getIncludedFiles();
316
317             for (int j = 0; j < files.length; j++) {
318                 File JavaDoc srcFile = new File JavaDoc(fs.getDir(getProject()), files[j]);
319                 doValidate(srcFile);
320                 fileProcessed++;
321             }
322         }
323         onSuccessfulValidation(fileProcessed);
324     }
325
326     /**
327      * handler called on successful file validation.
328      * @param fileProcessed number of files processed.
329      */

330     protected void onSuccessfulValidation(int fileProcessed) {
331         log(fileProcessed + MESSAGE_FILES_VALIDATED);
332     }
333
334     /**
335      * init the parser :
336      * load the parser class, and set features if necessary
337      * It is only after this that the reader is valid
338      * @throws BuildException if something went wrong
339      */

340     protected void initValidator() {
341
342         xmlReader = createXmlReader();
343
344         xmlReader.setEntityResolver(getEntityResolver());
345         xmlReader.setErrorHandler(errorHandler);
346
347         if (!isSax1Parser()) {
348             // turn validation on
349
if (!lenient) {
350                 setFeature(XmlConstants.FEATURE_VALIDATION, true);
351             }
352             // set the feature from the attribute list
353
for (int i = 0; i < attributeList.size(); i++) {
354                 Attribute feature = (Attribute) attributeList.elementAt(i);
355                 setFeature(feature.getName(), feature.getValue());
356
357             }
358             // Sets properties
359
for (int i = 0; i < propertyList.size(); i++) {
360                 final Property prop = (Property) propertyList.elementAt(i);
361                 setProperty(prop.getName(), prop.getValue());
362             }
363         }
364     }
365
366     /**
367      * test that returns true if we are using a SAX1 parser.
368      * @return true when a SAX1 parser is in use
369      */

370     protected boolean isSax1Parser() {
371         return (xmlReader instanceof ParserAdapter JavaDoc);
372     }
373
374     /**
375      * create the XML reader.
376      * This is one by instantiating anything specified by {@link #readerClassName},
377      * falling back to a default reader if not.
378      * If the returned reader is an instance of {@link ParserAdapter} then
379      * we have created and wrapped a SAX1 parser.
380      * @return the new XMLReader.
381      */

382     protected XMLReader JavaDoc createXmlReader() {
383         Object JavaDoc reader = null;
384         if (readerClassName == null) {
385             reader = createDefaultReaderOrParser();
386         } else {
387
388             Class JavaDoc readerClass = null;
389             try {
390                 // load the parser class
391
if (classpath != null) {
392                     AntClassLoader loader =
393                         getProject().createClassLoader(classpath);
394                     readerClass = Class.forName(readerClassName, true, loader);
395                 } else {
396                     readerClass = Class.forName(readerClassName);
397                 }
398
399                 reader = readerClass.newInstance();
400             } catch (ClassNotFoundException JavaDoc e) {
401                 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
402             } catch (InstantiationException JavaDoc e) {
403                 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
404             } catch (IllegalAccessException JavaDoc e) {
405                 throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
406             }
407         }
408
409         // then check it implements XMLReader
410
XMLReader JavaDoc newReader;
411         if (reader instanceof XMLReader JavaDoc) {
412             newReader = (XMLReader JavaDoc) reader;
413             log(
414                 "Using SAX2 reader " + reader.getClass().getName(),
415                 Project.MSG_VERBOSE);
416         } else {
417
418             // see if it is a SAX1 Parser
419
if (reader instanceof Parser JavaDoc) {
420                 newReader = new ParserAdapter JavaDoc((Parser JavaDoc) reader);
421                 log(
422                     "Using SAX1 parser " + reader.getClass().getName(),
423                     Project.MSG_VERBOSE);
424             } else {
425                 throw new BuildException(
426                     INIT_FAILED_MSG
427                         + reader.getClass().getName()
428                         + " implements nor SAX1 Parser nor SAX2 XMLReader.");
429             }
430         }
431         return newReader;
432     }
433
434     /**
435      *
436      * @return
437      */

438     private Object JavaDoc createDefaultReaderOrParser() {
439         Object JavaDoc reader;
440         try {
441             reader = createDefaultReader();
442         } catch (BuildException exc) {
443             reader = JAXPUtils.getParser();
444         }
445         return reader;
446     }
447
448     /**
449      * create a reader if the use of the class did not specify another one.
450      * If a BuildException is thrown, the caller may revert to an alternate
451      * reader.
452      * @return a new reader.
453      * @throws BuildException if something went wrong
454      */

455     protected XMLReader JavaDoc createDefaultReader() {
456         return JAXPUtils.getXMLReader();
457     }
458
459     /**
460      * Set a feature on the parser.
461      * @param feature the name of the feature to set
462      * @param value the value of the feature
463      * @throws BuildException if the feature was not supported
464      */

465     protected void setFeature(String JavaDoc feature, boolean value)
466         throws BuildException {
467         log("Setting feature " + feature + "=" + value, Project.MSG_DEBUG);
468         try {
469             xmlReader.setFeature(feature, value);
470         } catch (SAXNotRecognizedException JavaDoc e) {
471             throw new BuildException(
472                 "Parser "
473                     + xmlReader.getClass().getName()
474                     + " doesn't recognize feature "
475                     + feature,
476                 e,
477                 getLocation());
478         } catch (SAXNotSupportedException JavaDoc e) {
479             throw new BuildException(
480                 "Parser "
481                     + xmlReader.getClass().getName()
482                     + " doesn't support feature "
483                     + feature,
484                 e,
485                 getLocation());
486         }
487     }
488
489     /**
490      * Sets a property.
491      *
492      * @param name a property name
493      * @param value a property value.
494      * @throws BuildException if an error occurs.
495      * @throws BuildException if the property was not supported
496      */

497     protected void setProperty(String JavaDoc name, String JavaDoc value) throws BuildException {
498         // Validates property
499
if (name == null || value == null) {
500             throw new BuildException("Property name and value must be specified.");
501         }
502
503         try {
504             xmlReader.setProperty(name, value);
505         } catch (SAXNotRecognizedException JavaDoc e) {
506             throw new BuildException(
507                 "Parser "
508                     + xmlReader.getClass().getName()
509                     + " doesn't recognize property "
510                     + name,
511                 e,
512                 getLocation());
513         } catch (SAXNotSupportedException JavaDoc e) {
514             throw new BuildException(
515                 "Parser "
516                     + xmlReader.getClass().getName()
517                     + " doesn't support property "
518                     + name,
519                 e,
520                 getLocation());
521         }
522     }
523
524     /**
525      * parse the file
526      * @param afile the file to validate.
527      * @return true if the file validates.
528      */

529     protected boolean doValidate(File JavaDoc afile) {
530         //for every file, we have a new instance of the validator
531
initValidator();
532         boolean result = true;
533         try {
534             log("Validating " + afile.getName() + "... ", Project.MSG_VERBOSE);
535             errorHandler.init(afile);
536             InputSource JavaDoc is = new InputSource JavaDoc(new FileInputStream JavaDoc(afile));
537             String JavaDoc uri = FILE_UTILS.toURI(afile.getAbsolutePath());
538             is.setSystemId(uri);
539             xmlReader.parse(is);
540         } catch (SAXException JavaDoc ex) {
541             log("Caught when validating: " + ex.toString(), Project.MSG_DEBUG);
542             if (failOnError) {
543                 throw new BuildException(
544                     "Could not validate document " + afile);
545             }
546             log("Could not validate document " + afile + ": " + ex.toString());
547             result = false;
548         } catch (IOException JavaDoc ex) {
549             throw new BuildException(
550                 "Could not validate document " + afile,
551                 ex);
552         }
553         if (errorHandler.getFailure()) {
554             if (failOnError) {
555                 throw new BuildException(
556                     afile + " is not a valid XML document.");
557             }
558             result = false;
559             log(afile + " is not a valid XML document", Project.MSG_ERR);
560         }
561         return result;
562     }
563
564     /**
565      * ValidatorErrorHandler role :
566      * <ul>
567      * <li> log SAX parse exceptions,
568      * <li> remember if an error occurred
569      * </ul>
570      */

571     protected class ValidatorErrorHandler implements ErrorHandler JavaDoc {
572
573         // CheckStyle:VisibilityModifier OFF - bc
574
protected File JavaDoc currentFile = null;
575         protected String JavaDoc lastErrorMessage = null;
576         protected boolean failed = false;
577         // CheckStyle:VisibilityModifier ON
578
/**
579          * initialises the class
580          * @param file file used
581          */

582         public void init(File JavaDoc file) {
583             currentFile = file;
584             failed = false;
585         }
586         /**
587          * did an error happen during last parsing ?
588          * @return did an error happen during last parsing ?
589          */

590         public boolean getFailure() {
591             return failed;
592         }
593
594         /**
595          * record a fatal error
596          * @param exception the fatal error
597          */

598         public void fatalError(SAXParseException JavaDoc exception) {
599             failed = true;
600             doLog(exception, Project.MSG_ERR);
601         }
602         /**
603          * receive notification of a recoverable error
604          * @param exception the error
605          */

606         public void error(SAXParseException JavaDoc exception) {
607             failed = true;
608             doLog(exception, Project.MSG_ERR);
609         }
610         /**
611          * receive notification of a warning
612          * @param exception the warning
613          */

614         public void warning(SAXParseException JavaDoc exception) {
615             // depending on implementation, XMLReader can yield hips of warning,
616
// only output then if user explicitly asked for it
617
if (warn) {
618                 doLog(exception, Project.MSG_WARN);
619             }
620         }
621
622         private void doLog(SAXParseException JavaDoc e, int logLevel) {
623
624             log(getMessage(e), logLevel);
625         }
626
627         private String JavaDoc getMessage(SAXParseException JavaDoc e) {
628             String JavaDoc sysID = e.getSystemId();
629             if (sysID != null) {
630                 String JavaDoc name = sysID;
631                 if (sysID.startsWith("file:")) {
632                     try {
633                         name = FILE_UTILS.fromURI(sysID);
634                     } catch (Exception JavaDoc ex) {
635                         // if this is not a valid file: just use the uri
636
}
637                 }
638                 int line = e.getLineNumber();
639                 int col = e.getColumnNumber();
640                 return name
641                     + (line == -1
642                        ? ""
643                        : (":" + line + (col == -1 ? "" : (":" + col))))
644                     + ": "
645                     + e.getMessage();
646             }
647             return e.getMessage();
648         }
649     }
650
651     /**
652      * The class to create to set a feature of the parser.
653      * @since ant1.6
654      */

655     public static class Attribute {
656         /** The name of the attribute to set.
657          *
658          * Valid attributes <a HREF=
659          * "http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description"
660          * >include.</a>
661          */

662         private String JavaDoc attributeName = null;
663
664         /**
665          * The value of the feature.
666          **/

667         private boolean attributeValue;
668
669         /**
670          * Set the feature name.
671          * @param name the name to set
672          */

673         public void setName(String JavaDoc name) {
674             attributeName = name;
675         }
676         /**
677          * Set the feature value to true or false.
678          * @param value feature value
679          */

680         public void setValue(boolean value) {
681             attributeValue = value;
682         }
683
684         /**
685          * Gets the attribute name.
686          * @return the feature name
687          */

688         public String JavaDoc getName() {
689             return attributeName;
690         }
691
692         /**
693          * Gets the attribute value.
694          * @return the feature value
695          */

696         public boolean getValue() {
697             return attributeValue;
698         }
699     }
700
701     /**
702      * A Parser property.
703      * See <a HREF="http://xml.apache.org/xerces-j/properties.html">
704      * XML parser properties</a> for usable properties
705      * @since ant 1.6.2
706      */

707     public static final class Property {
708
709         private String JavaDoc name;
710         private String JavaDoc value;
711         /**
712          * accessor to the name of the property
713          * @return name of the property
714          */

715         public String JavaDoc getName() {
716             return name;
717         }
718         /**
719          * setter for the name of the property
720          * @param name name of the property
721          */

722         public void setName(String JavaDoc name) {
723             this.name = name;
724         }
725
726         /**
727          * getter for the value of the property
728          * @return value of the property
729          */

730         public String JavaDoc getValue() {
731             return value;
732         }
733         /**
734          * sets the value of the property
735          * @param value value of the property
736          */

737         public void setValue(String JavaDoc value) {
738             this.value = value;
739         }
740
741     } // Property
742

743
744
745 }
746
Popular Tags