001/* A validating parser. 002 003 Copyright (c) 2010-2015 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.configs.test; 029 030import java.io.StringReader; 031 032import javax.xml.parsers.SAXParser; 033import javax.xml.parsers.SAXParserFactory; 034 035import org.xml.sax.Attributes; 036import org.xml.sax.EntityResolver; 037import org.xml.sax.ErrorHandler; 038import org.xml.sax.InputSource; 039import org.xml.sax.Locator; 040import org.xml.sax.SAXException; 041import org.xml.sax.SAXParseException; 042import org.xml.sax.XMLReader; 043import org.xml.sax.helpers.DefaultHandler; 044 045import ptolemy.moml.MoMLParser; 046import ptolemy.util.FileUtilities; 047 048/** 049 A validating XML parser. 050 <p>Note that MoML files are not always valid XML because of the 051 <configure> tag, so that tag should be removed.</p> 052 053 @author Christopher Brooks 054 @version $Id$ 055 @since Ptolemy II 10.0 056 @Pt.ProposedRating Red (cxh) 057 @Pt.AcceptedRating Red (cxh) 058 */ 059public class ValidatingXMLParser extends DefaultHandler { 060 /** Parse an XML parser using a validating parser. 061 * The DTD http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd 062 * is replaced with the value of MoMLParser.MoML_1.dtd. 063 * @param args A single element array that names the xml file to 064 * be parsed. The file should have 065 * <configure>...<configure> removed. One workaround 066 * is to remove the configure tags with a script before 067 * validating, see $PTII/ptolemy/moml/test/removeconfigure. 068 * See also <a href="http://ptolemy.eecs.berkeley.edu/ptolemyII/ptIIfaq.htm#MoML">http://ptolemy.eecs.berkeley.edu/ptolemyII/ptIIfaq.htm#MoML</a> 069 */ 070 public static void main(String[] args) { 071 try { 072 parse(args[0]); 073 } catch (Throwable throwable) { 074 throwable.printStackTrace(); 075 System.exit(1); 076 } 077 System.exit(0); 078 } 079 080 /** Parse an XML parser using a validating parser. 081 * The DTD http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd 082 * is replaced with the value of MoMLParser.MoML_1.dtd. 083 * @param fileName The xml file to be parsed. The file should 084 * have <configure>...<configure> removed. 085 * @exception Throwable If the XML is invalid XML. 086 */ 087 public static void parse(String fileName) throws Throwable { 088 DefaultHandler handler = new ValidatingXMLParser(); 089 090 // Use the validating parser 091 SAXParserFactory factory = SAXParserFactory.newInstance(); 092 factory.setValidating(true); 093 try { 094 SAXParser saxParser = factory.newSAXParser(); 095 // Instead of using saxParser.parser, we create our own 096 // entity resolver so that we do not beat up the ptolemy 097 // web server. 098 //saxParser.parse(new File(fileName), handler); 099 100 XMLReader xmlReader = saxParser.getXMLReader(); 101 // Use error handlers so that if parsing fails, we throw 102 // an exception that can be caught by the test, which will 103 // show which .xml file is failing. Validating the 104 // accessors required this. 105 xmlReader.setErrorHandler(new ErrorHandler() { 106 @Override 107 public void fatalError(SAXParseException exception) 108 throws SAXException { 109 System.err.println("fatalError: " + exception); 110 throw exception; 111 } 112 113 @Override 114 public void error(SAXParseException exception) 115 throws SAXException { 116 System.err.println("error: " + exception); 117 throw exception; 118 } 119 120 @Override 121 public void warning(SAXParseException exception) 122 throws SAXException { 123 System.err.println("warning: " + exception); 124 throw exception; 125 } 126 }); 127 MoMLEntityResolver resolver = new MoMLEntityResolver(); 128 xmlReader.setEntityResolver(resolver); 129 xmlReader.setContentHandler(handler); 130 xmlReader.parse(new InputSource(fileName)); 131 } catch (SAXParseException ex) { 132 System.out.println("\n Error parsing line " + ex.getLineNumber() 133 + ", uri " + ex.getSystemId()); 134 System.out.println(" " + ex.getMessage()); 135 throw ex; 136 } 137 } 138 139 /** Resolve an entity by checking for 140 * http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd 141 * and, if found, return the value of MoMLParser.MoML_1.dtd. 142 */ 143 public static class MoMLEntityResolver implements EntityResolver { 144 /** Resolve the entity. 145 * @param publicID Ignored. 146 * @param systemID The systemID. 147 * @return If systemID equals 148 * http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd then 149 * return an InputSource based on the value of 150 * MoMLParser.MoML_DTD_1, If the systemID equals 151 * https://accessors.org/Accessor_1.dtd, then return the 152 * contents of 153 * $CLASSPATH/org/terraswarm/accessor/accessors/web/Accessor_1.dtd 154 * otherwise return null. 155 * @exception SAXException If the MoML DTD cannot be created. 156 */ 157 @Override 158 public InputSource resolveEntity(String publicID, String systemID) 159 throws SAXException { 160 if (systemID.equals( 161 "http://ptolemy.eecs.berkeley.edu/xml/dtd/MoML_1.dtd")) { 162 InputSource source = new InputSource( 163 new StringReader(MoMLParser.MoML_DTD_1)); 164 source.setPublicId(publicID); 165 source.setSystemId(systemID); 166 return source; 167 } else if (publicID.equals("-//TerraSwarm//DTD Accessor 1//EN") 168 && systemID 169 .equals("https://accessors.org/Accessor_1.dtd")) { 170 try { 171 // See also org/terraswarm/accessor/JSAccessor.java 172 String dtd = FileUtilities.getFileAsString( 173 "$CLASSPATH/org/terraswarm/accessor/accessors/web/Accessor_1.dtd"); 174 InputSource source = new InputSource(new StringReader(dtd)); 175 source.setPublicId(publicID); 176 source.setSystemId(systemID); 177 return source; 178 } catch (Exception ex) { 179 throw new SAXException( 180 "Failed to read Accessor_1.dtd from local file system", 181 ex); 182 } 183 } 184 return null; 185 } 186 } 187 188 //=========================================================== 189 // SAX DocumentHandler methods 190 //=========================================================== 191 @Override 192 public void setDocumentLocator(Locator l) { 193 } 194 195 @Override 196 public void startDocument() throws SAXException { 197 } 198 199 @Override 200 public void endDocument() throws SAXException { 201 } 202 203 @Override 204 public void startElement(String namespaceURI, String sName, // simple name 205 String qName, // qualified name 206 Attributes attrs) throws SAXException { 207 } 208 209 @Override 210 public void endElement(String namespaceURI, String sName, // simple name 211 String qName // qualified name 212 ) throws SAXException { 213 } 214 215 @Override 216 public void characters(char[] buf, int offset, int len) 217 throws SAXException { 218 } 219 220 @Override 221 public void ignorableWhitespace(char[] buf, int offset, int len) 222 throws SAXException { 223 } 224 225 @Override 226 public void processingInstruction(String target, String data) 227 throws SAXException { 228 } 229 230 @Override 231 public void error(SAXParseException exception) throws SAXParseException { 232 warning(exception); 233 throw exception; 234 } 235 236 @Override 237 public void warning(SAXParseException exception) throws SAXParseException { 238 System.out.println("Warning: line " + exception.getLineNumber() 239 + ", uri " + exception.getSystemId()); 240 System.out.println(" " + exception.getMessage()); 241 } 242}