001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Nicolas Modrzyk.
020: * Contributor(s):
021: */package org.continuent.sequoia.common.xml;
022:
023: import java.io.BufferedReader;
024: import java.io.File;
025: import java.io.FileReader;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.io.StringReader;
029: import java.util.ArrayList;
030:
031: import org.continuent.sequoia.common.i18n.Translate;
032: import org.continuent.sequoia.controller.core.ControllerConstants;
033: import org.xml.sax.ErrorHandler;
034: import org.xml.sax.InputSource;
035: import org.xml.sax.SAXException;
036: import org.xml.sax.SAXParseException;
037: import org.xml.sax.XMLReader;
038: import org.xml.sax.ext.LexicalHandler;
039: import org.xml.sax.helpers.DefaultHandler;
040: import org.xml.sax.helpers.XMLReaderFactory;
041:
042: /**
043: * Validate a document and its DTD.
044: *
045: * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
046: */
047: public class XmlValidator extends DefaultHandler implements
048: ErrorHandler, LexicalHandler {
049:
050: /** XML parser. */
051: private XMLReader parser;
052: private String pathToDtd;
053: private boolean isXmlValid = false;
054: private boolean isDtdValid = false;
055: private String xmlContent;
056: private ArrayList errors;
057: private ArrayList warnings;
058:
059: /**
060: * Allow to use the xml validator as an external program
061: *
062: * @param args the xmlfile and the dtd file
063: * @throws Exception if fails
064: */
065: public static void main(String[] args) throws Exception {
066: if (args.length < 1 || args.length > 2) {
067: System.out
068: .println("usage: XmlValidator [xmlFile] ([dtd]) ");
069: System.exit(0);
070: }
071:
072: String fileName = args[0];
073: String dtdName = ControllerConstants.SEQUOIA_DTD_FILE;
074: if (args.length == 2)
075: dtdName = args[1];
076: else
077: System.out.println("Using default DTD:"
078: + ControllerConstants.SEQUOIA_DTD_FILE);
079:
080: File dtd = null;
081: dtd = new File(ClassLoader.getSystemResource(dtdName).getFile());
082: File xmlFile = null;
083: try {
084: xmlFile = new File(ClassLoader.getSystemResource(fileName)
085: .getFile());
086: } catch (RuntimeException e) {
087: xmlFile = new File(fileName);
088: }
089:
090: if (!dtd.exists()) {
091: System.out.println("Cannot find specified dtd");
092: System.exit(1);
093: }
094: if (!xmlFile.exists()) {
095: System.out.println("Cannot find specified xml file");
096: System.exit(1);
097: }
098:
099: System.out.println("Validating:\tFile:" + xmlFile.getName()
100: + " with dtd:" + dtd.getName());
101:
102: // Validate xml and dtd
103: XmlValidator validator = new XmlValidator(
104: dtd.getAbsolutePath(), new FileReader(xmlFile));
105:
106: // Display Results
107: if (!validator.isDtdValid())
108: System.out.println("[FAILED:Dtd is not valid]");
109: else if (!validator.isXmlValid())
110: System.out.println("[FAILED:xml is not valid]");
111: else if (validator.isXmlValid())
112: System.out.println("[OK]");
113:
114: if (validator.getLastException() != null) {
115: ArrayList errors = validator.getExceptions();
116: for (int i = 0; i < errors.size(); i++)
117: System.out.println("\t(parsing error):"
118: + ((Exception) errors.get(i)).getMessage());
119: }
120: }
121:
122: /**
123: * Check the given dtd, and the given xml are valid.
124: *
125: * @param pathToDtd path to dtd
126: * @param xml source to parse as a string
127: */
128: public XmlValidator(String pathToDtd, String xml) {
129: validate(pathToDtd, xml);
130: }
131:
132: /**
133: * @see #XmlValidator(String pathToDtd,String xml)
134: */
135: public XmlValidator(String pathToDtd, FileReader file)
136: throws IOException {
137: // Read the file
138: BufferedReader in = new BufferedReader(file);
139: StringBuffer xml = new StringBuffer();
140: String line;
141: do {
142: line = in.readLine();
143: if (line != null)
144: xml.append(line.trim());
145: } while (line != null);
146: xmlContent = xml.toString();
147: validate(pathToDtd, xmlContent);
148: }
149:
150: /**
151: * get the xml that was formatted
152: *
153: * @return xml
154: */
155: public String getXmlContent() {
156: return xmlContent;
157: }
158:
159: /**
160: * Starts the verification of the xml document AND the dtd
161: *
162: * @param pathToDtd path
163: * @param xml content
164: */
165: public void validate(String pathToDtd, String xml) {
166: System.setProperty("org.xml.sax.driver",
167: "org.apache.crimson.parser.XMLReaderImpl");
168: errors = new ArrayList();
169: warnings = new ArrayList();
170: try {
171: // Store dtd reference
172: this .pathToDtd = pathToDtd;
173: // Instantiate a new parser
174: parser = XMLReaderFactory.createXMLReader();
175: // Activate validation
176: parser.setFeature("http://xml.org/sax/features/validation",
177: true);
178: // Install error handler
179: parser.setErrorHandler(this );
180: // Install document handler
181: parser.setContentHandler(this );
182: parser.setProperty(
183: "http://xml.org/sax/properties/lexical-handler",
184: this );
185: // Install local entity resolver
186: parser.setEntityResolver(this );
187: InputSource input = new InputSource(new StringReader(xml));
188: parser.parse(input);
189: } catch (Exception e) {
190: //throw new Exception("Xml document can not be validated.");
191: //e.printStackTrace();
192: addError(e);
193: isXmlValid = false;
194: }
195: }
196:
197: /**
198: * Allows to parse the document with a local copy of the DTD whatever the
199: * original <code>DOCTYPE</code> found. Warning, this method is called only
200: * if the XML document contains a <code>DOCTYPE</code>.
201: *
202: * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String,
203: * java.lang.String)
204: */
205: public InputSource resolveEntity(String publicId, String systemId)
206: throws SAXException {
207:
208: File dtd = new File(pathToDtd);
209: if (dtd.exists()) {
210: try {
211: FileReader reader = new FileReader(dtd);
212: return new InputSource(reader);
213: } catch (Exception e) { //impossible
214: }
215: }
216:
217: InputStream stream = XmlValidator.class.getResourceAsStream("/"
218: + pathToDtd);
219: if (stream == null) {
220: SAXException sax = new SAXException(
221: Translate
222: .get(
223: "virtualdatabase.xml.dtd.not.found", ControllerConstants.PRODUCT_NAME, pathToDtd)); //$NON-NLS-1$
224: addError(sax);
225: throw sax;
226: }
227:
228: return new InputSource(stream);
229: }
230:
231: /**
232: * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException)
233: */
234: public void error(SAXParseException exception) throws SAXException {
235: addError(exception);
236: }
237:
238: /**
239: * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException)
240: */
241: public void fatalError(SAXParseException exception)
242: throws SAXException {
243: addError(exception);
244: }
245:
246: /**
247: * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException)
248: */
249: public void warning(SAXParseException exception)
250: throws SAXException {
251: warnings.add(exception);
252: }
253:
254: /**
255: * @see org.xml.sax.ContentHandler#endDocument()
256: */
257: public void endDocument() throws SAXException {
258: if (errors.size() == 0)
259: this .isXmlValid = true;
260: }
261:
262: /**
263: * @return Returns the isXmlValid.
264: */
265: public boolean isValid() {
266: return isXmlValid && isDtdValid;
267: }
268:
269: /**
270: * Return the last cause of parsing failure
271: *
272: * @return exception, null if no exception
273: */
274: public Exception getLastException() {
275: if (errors.size() == 0)
276: return null;
277: else
278: return (Exception) errors.get(errors.size() - 1);
279: }
280:
281: /**
282: * Retrieve an <code>ArrayList</code> of all parsing exceptions
283: *
284: * @return an <code>ArrayList</code> of <code>Exception</code>
285: */
286: public ArrayList getExceptions() {
287: return errors;
288: }
289:
290: /**
291: * @see org.xml.sax.ext.LexicalHandler#comment(char[], int, int)
292: */
293: public void comment(char[] ch, int start, int length)
294: throws SAXException {
295: }
296:
297: /**
298: * @see org.xml.sax.ext.LexicalHandler#endCDATA()
299: */
300: public void endCDATA() throws SAXException {
301: }
302:
303: /**
304: * @see org.xml.sax.ext.LexicalHandler#endDTD()
305: */
306: public void endDTD() throws SAXException {
307: if (errors.size() == 0) {
308: isDtdValid = true;
309: } else {
310: isDtdValid = false;
311: }
312: }
313:
314: /**
315: * @see org.xml.sax.ext.LexicalHandler#endEntity(java.lang.String)
316: */
317: public void endEntity(String name) throws SAXException {
318: }
319:
320: /**
321: * @see org.xml.sax.ext.LexicalHandler#startCDATA()
322: */
323: public void startCDATA() throws SAXException {
324: }
325:
326: /**
327: * @see org.xml.sax.ext.LexicalHandler#startDTD(java.lang.String,
328: * java.lang.String, java.lang.String)
329: */
330: public void startDTD(String name, String publicId, String systemId)
331: throws SAXException {
332: }
333:
334: /**
335: * @see org.xml.sax.ext.LexicalHandler#startEntity(java.lang.String)
336: */
337: public void startEntity(String name) throws SAXException {
338: }
339:
340: /**
341: * @return Returns the isDtdValid.
342: */
343: public boolean isDtdValid() {
344: return isDtdValid;
345: }
346:
347: /**
348: * @param isDtdValid The isDtdValid to set.
349: */
350: public void setDtdValid(boolean isDtdValid) {
351: this .isDtdValid = isDtdValid;
352: }
353:
354: /**
355: * @return Returns the isXmlValid.
356: */
357: public boolean isXmlValid() {
358: return isXmlValid;
359: }
360:
361: /**
362: * @param isXmlValid The isXmlValid to set.
363: */
364: public void setXmlValid(boolean isXmlValid) {
365: this .isXmlValid = isXmlValid;
366: }
367:
368: private void addError(Exception e) {
369: errors.add(e);
370: }
371:
372: /**
373: * @return Returns the warnings.
374: */
375: public ArrayList getWarnings() {
376: return warnings;
377: }
378: }
|