| /* DoParse.java -- | |
| Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.xml.util; | |
| import java.io.IOException; | |
| import org.xml.sax.ErrorHandler; | |
| import org.xml.sax.InputSource; | |
| import org.xml.sax.SAXException; | |
| import org.xml.sax.SAXParseException; | |
| import org.xml.sax.XMLReader; | |
| import org.xml.sax.helpers.XMLReaderFactory; | |
| import gnu.xml.pipeline.EventConsumer; | |
| import gnu.xml.pipeline.EventFilter; | |
| import gnu.xml.pipeline.NSFilter; | |
| import gnu.xml.pipeline.PipelineFactory; | |
| import gnu.xml.pipeline.TeeConsumer; | |
| import gnu.xml.pipeline.ValidationConsumer; | |
| import gnu.xml.pipeline.WellFormednessFilter; | |
| /** | |
| * This class provides a driver which may be invoked from the command line | |
| * to process a document using a SAX2 parser and a specified XML processing | |
| * pipeline. | |
| * This facilitates some common types of command line tools, such as parsing an | |
| * XML document in order test it for well formedness or validity. | |
| * | |
| * <p>The SAX2 XMLReaderFactory should return a SAX2 XML parser which | |
| * supports both of the standardized extension handlers (for declaration | |
| * and lexical events). That parser will be used to produce events. | |
| * | |
| * <p>The first parameter to the command gives the name of the document that | |
| * will be given to that processor. If it is a file name, it is converted | |
| * to a URL first. | |
| * | |
| * <p>The second parameter describes a simple processing pipeline, and will | |
| * be used as input to {@link gnu.xml.pipeline.PipelineFactory} | |
| * methods which identify the processing to be done. Examples of such a | |
| * pipeline include <pre> | |
| * | |
| * nsfix | validate <em>to validate the input document </em> | |
| * nsfix | write ( stdout ) <em>to echo the file as XML text</em> | |
| * dom | nsfix | write ( stdout ) <em>parse into DOM, print the result</em> | |
| * </pre> | |
| * | |
| * <p> Relatively complex pipelines can be described on the command line, but | |
| * not all interesting ones will require as little configuration as can be done | |
| * in that way. Put filters like "nsfix", perhaps followed by "validate", | |
| * at the front of the pipeline so they can be optimized out if a parser | |
| * supports those modes natively. | |
| * | |
| * <p> If the parsing is aborted for any reason, the JVM will exit with a | |
| * failure code. If a validating parse was done then both validation and | |
| * well formedness errors will cause a failure. A non-validating parse | |
| * will report failure on well formedness errors. | |
| * | |
| * @see gnu.xml.pipeline.PipelineFactory | |
| * | |
| * @author David Brownell | |
| */ | |
| final public class DoParse | |
| { | |
| private DoParse () { /* no instances allowed */ } | |
| // first reported nonrecoverable error | |
| private static SAXParseException fatal; | |
| // error categories | |
| private static int errorCount; | |
| private static int fatalCount; | |
| /** | |
| * Command line invoker for this class; pass a filename or URL | |
| * as the first argument, and a pipeline description as the second. | |
| * Make sure to use filters to condition the input to stages that | |
| * require it; an <em>nsfix</em> filter will be a common requirement, | |
| * to restore syntax that SAX2 parsers delete by default. Some | |
| * conditioning filters may be eliminated by setting parser options. | |
| * (For example, "nsfix" can set the "namespace-prefixes" feature to | |
| * a non-default value of "true". In the same way, "validate" can set | |
| * the "validation" feature to "true".) | |
| */ | |
| public static void main (String argv []) | |
| throws IOException | |
| { | |
| int exitStatus = 1; | |
| if (argv.length != 2) { | |
| System.err.println ("Usage: DoParse [filename|URL] pipeline-spec"); | |
| System.err.println ("Example pipeline specs:"); | |
| System.err.println (" 'nsfix | validate'"); | |
| System.err.println ( | |
| " ... restore namespace syntax, validate"); | |
| System.err.println (" 'nsfix | write ( stdout )'"); | |
| System.err.println ( | |
| " ... restore namespace syntax, write to stdout as XML" | |
| ); | |
| System.exit (1); | |
| } | |
| try { | |
| // | |
| // Get input source for specified document (or try ;-) | |
| // | |
| argv [0] = Resolver.getURL (argv [0]); | |
| InputSource input = new InputSource (argv [0]); | |
| // | |
| // Get the producer, using the system default parser (which | |
| // can be overridden for this particular invocation). | |
| // | |
| // And the pipeline, using commandline options. | |
| // | |
| XMLReader producer; | |
| EventConsumer consumer; | |
| producer = XMLReaderFactory.createXMLReader (); | |
| // | |
| // XXX pipeline factory now has a pre-tokenized input | |
| // method, use it ... that way at least some params | |
| // can be written using quotes (have spaces, ...) | |
| // | |
| consumer = PipelineFactory.createPipeline (argv [1]); | |
| // | |
| // XXX want commandline option for tweaking error handler. | |
| // Want to be able to present warnings. | |
| // | |
| producer.setErrorHandler (new MyErrorHandler ()); | |
| // XXX need facility enabling resolving to local DTDs | |
| // | |
| // Parse. The pipeline may get optimized a bit, so we | |
| // can't always fail cleanly for validation without taking | |
| // a look at the filter stages. | |
| // | |
| EventFilter.bind (producer, consumer); | |
| producer.parse (input); | |
| try { | |
| if (producer.getFeature ( | |
| "http://org.xml/sax/features/validation")) | |
| exitStatus = ((errorCount + fatalCount) > 0) ? 1 : 0; | |
| else if (fatalCount == 0) | |
| exitStatus = 0; | |
| } catch (SAXException e) { | |
| if (hasValidator (consumer)) | |
| exitStatus = ((errorCount + fatalCount) > 0) ? 1 : 0; | |
| else if (fatalCount == 0) | |
| exitStatus = 0; | |
| } | |
| } catch (java.net.MalformedURLException e) { | |
| System.err.println ("** Malformed URL: " + e.getMessage ()); | |
| System.err.println ("Is '" + argv [0] + "' a non-existent file?"); | |
| e.printStackTrace (); | |
| // e.g. FNF | |
| } catch (SAXParseException e) { | |
| if (e != fatal) { | |
| System.err.print (printParseException ("Parsing Aborted", e)); | |
| e.printStackTrace (); | |
| if (e.getException () != null) { | |
| System.err.println ("++ Wrapped exception:"); | |
| e.getException ().printStackTrace (); | |
| } | |
| } | |
| } catch (SAXException e) { | |
| Exception x = e; | |
| if (e.getException () != null) | |
| x = e.getException (); | |
| x.printStackTrace (); | |
| } catch (Throwable t) { | |
| t.printStackTrace (); | |
| } | |
| System.exit (exitStatus); | |
| } | |
| // returns true if saw a validator (before end or unrecognized node) | |
| // false otherwise | |
| private static boolean hasValidator (EventConsumer e) | |
| { | |
| if (e == null) | |
| return false; | |
| if (e instanceof ValidationConsumer) | |
| return true; | |
| if (e instanceof TeeConsumer) { | |
| TeeConsumer t = (TeeConsumer) e; | |
| return hasValidator (t.getFirst ()) | |
| || hasValidator (t.getRest ()); | |
| } | |
| if (e instanceof WellFormednessFilter | |
| || e instanceof NSFilter | |
| ) | |
| return hasValidator (((EventFilter)e).getNext ()); | |
| // else ... gee, we can't know. Assume not. | |
| return false; | |
| } | |
| static class MyErrorHandler implements ErrorHandler | |
| { | |
| // dump validation errors, but continue | |
| public void error (SAXParseException e) | |
| throws SAXParseException | |
| { | |
| errorCount++; | |
| System.err.print (printParseException ("Error", e)); | |
| } | |
| public void warning (SAXParseException e) | |
| throws SAXParseException | |
| { | |
| // System.err.print (printParseException ("Warning", e)); | |
| } | |
| // try to continue fatal errors, in case a parser reports more | |
| public void fatalError (SAXParseException e) | |
| throws SAXParseException | |
| { | |
| fatalCount++; | |
| if (fatal == null) | |
| fatal = e; | |
| System.err.print (printParseException ("Nonrecoverable Error", e)); | |
| } | |
| } | |
| static private String printParseException ( | |
| String label, | |
| SAXParseException e | |
| ) { | |
| StringBuffer buf = new StringBuffer (); | |
| int temp; | |
| buf.append ("** "); | |
| buf.append (label); | |
| buf.append (": "); | |
| buf.append (e.getMessage ()); | |
| buf.append ('\n'); | |
| if (e.getSystemId () != null) { | |
| buf.append (" URI: "); | |
| buf.append (e.getSystemId ()); | |
| buf.append ('\n'); | |
| } | |
| if ((temp = e.getLineNumber ()) != -1) { | |
| buf.append (" line: "); | |
| buf.append (temp); | |
| buf.append ('\n'); | |
| } | |
| if ((temp = e.getColumnNumber ()) != -1) { | |
| buf.append (" char: "); | |
| buf.append (temp); | |
| buf.append ('\n'); | |
| } | |
| return buf.toString (); | |
| } | |
| } |