001 /* 002 * Copyright (c) 2005 Henri Sivonen 003 * Copyright (c) 2007 Mozilla Foundation 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining a 006 * copy of this software and associated documentation files (the "Software"), 007 * to deal in the Software without restriction, including without limitation 008 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 009 * and/or sell copies of the Software, and to permit persons to whom the 010 * Software is furnished to do so, subject to the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be included in 013 * all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 018 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 020 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 021 * DEALINGS IN THE SOFTWARE. 022 */ 023 024 package nu.validator.tools; 025 026 import java.io.FileInputStream; 027 import java.io.FileOutputStream; 028 import java.io.IOException; 029 import java.io.OutputStreamWriter; 030 import java.io.Writer; 031 032 import javax.xml.parsers.SAXParserFactory; 033 034 import nu.validator.java.StringLiteralUtil; 035 036 import org.xml.sax.Attributes; 037 import org.xml.sax.ContentHandler; 038 import org.xml.sax.InputSource; 039 import org.xml.sax.Locator; 040 import org.xml.sax.SAXException; 041 import org.xml.sax.XMLReader; 042 043 /** 044 * Please refer to http://hsivonen.iki.fi/saxcompiler/ 045 * 046 * @version $Id: SaxCompiler.java,v 1.3 2006/11/18 00:05:24 hsivonen Exp $ 047 * @author hsivonen 048 */ 049 public class SaxCompiler implements ContentHandler { 050 051 private StringBuilder sb = new StringBuilder(); 052 053 private Writer w; 054 055 private int start = 0; 056 057 private int state = 0; 058 059 // 0 initial 060 // 1 package written 061 // 2 class written 062 // 3 method written 063 064 private boolean omitRoot = false; 065 066 private int level = 0; 067 068 /** 069 * Instantiates a <code>SaxCompiler</code> 070 * 071 * @param w 072 * the <code>Writer</code> to which generated code is written 073 */ 074 public SaxCompiler(Writer w) { 075 this.w = w; 076 } 077 078 /** 079 * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) 080 */ 081 public void setDocumentLocator(Locator arg0) { 082 } 083 084 /** 085 * @see org.xml.sax.ContentHandler#startDocument() 086 */ 087 public void startDocument() throws SAXException { 088 try { 089 w.write("/" 090 + "* This code was generated by nu.validator.tools.SaxCompiler. Please regenerate instead of editing. *" 091 + "/\n"); 092 } catch (IOException e) { 093 throw new SAXException(e); 094 } 095 } 096 097 /** 098 * @see org.xml.sax.ContentHandler#endDocument() 099 */ 100 public void endDocument() throws SAXException { 101 try { 102 if (!omitRoot) { 103 w.write("} finally {\ncontentHandler.endDocument();\n}\n"); 104 } 105 w.write("}\n"); 106 w.write("private static final char[] __chars__ = "); 107 w.write(StringLiteralUtil.charArrayLiteral(sb)); 108 w.write(";\n}\n"); 109 w.flush(); 110 w.close(); 111 } catch (IOException e) { 112 throw new SAXException(e); 113 } 114 } 115 116 /** 117 * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, 118 * java.lang.String) 119 */ 120 public void startPrefixMapping(String arg0, String arg1) 121 throws SAXException { 122 ensureState(); 123 try { 124 w.write("contentHandler.startPrefixMapping("); 125 w.write(StringLiteralUtil.stringLiteral(arg0)); 126 w.write(", "); 127 w.write(StringLiteralUtil.stringLiteral(arg1)); 128 w.write(");\n"); 129 } catch (IOException e) { 130 throw new SAXException(e); 131 } 132 } 133 134 /** 135 * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) 136 */ 137 public void endPrefixMapping(String arg0) throws SAXException { 138 try { 139 w.write("contentHandler.endPrefixMapping("); 140 w.write(StringLiteralUtil.stringLiteral(arg0)); 141 w.write(");\n"); 142 } catch (IOException e) { 143 throw new SAXException(e); 144 } 145 } 146 147 /** 148 * @see org.xml.sax.ContentHandler#startElement(java.lang.String, 149 * java.lang.String, java.lang.String, org.xml.sax.Attributes) 150 */ 151 public void startElement(String arg0, String arg1, String arg2, 152 Attributes attrs) throws SAXException { 153 ensureState(); 154 level++; 155 if (omitRoot && level == 1) { 156 return; 157 } 158 try { 159 w.write("__attrs__.clear();\n"); 160 for (int i = 0; i < attrs.getLength(); i++) { 161 w.write("__attrs__.addAttribute("); 162 w.write(StringLiteralUtil.stringLiteral(attrs.getURI(i))); 163 w.write(", "); 164 w.write(StringLiteralUtil.stringLiteral(attrs.getLocalName(i))); 165 w.write(", "); 166 w.write(StringLiteralUtil.stringLiteral(attrs.getQName(i))); 167 w.write(", "); 168 w.write(StringLiteralUtil.stringLiteral(attrs.getType(i))); 169 w.write(", "); 170 w.write(StringLiteralUtil.stringLiteral(attrs.getValue(i))); 171 w.write(");\n"); 172 } 173 w.write("contentHandler.startElement("); 174 w.write(StringLiteralUtil.stringLiteral(arg0)); 175 w.write(", "); 176 w.write(StringLiteralUtil.stringLiteral(arg1)); 177 w.write(", "); 178 w.write(StringLiteralUtil.stringLiteral(arg2)); 179 w.write(", __attrs__);\n"); 180 } catch (IOException e) { 181 throw new SAXException(e); 182 } 183 } 184 185 /** 186 * @see org.xml.sax.ContentHandler#endElement(java.lang.String, 187 * java.lang.String, java.lang.String) 188 */ 189 public void endElement(String arg0, String arg1, String arg2) 190 throws SAXException { 191 if (omitRoot && level == 1) { 192 return; 193 } 194 level--; 195 try { 196 w.write("contentHandler.endElement("); 197 w.write(StringLiteralUtil.stringLiteral(arg0)); 198 w.write(", "); 199 w.write(StringLiteralUtil.stringLiteral(arg1)); 200 w.write(", "); 201 w.write(StringLiteralUtil.stringLiteral(arg2)); 202 w.write(");\n"); 203 } catch (IOException e) { 204 throw new SAXException(e); 205 } 206 } 207 208 /** 209 * @see org.xml.sax.ContentHandler#characters(char[], int, int) 210 */ 211 public void characters(char[] buf, int offset, int length) 212 throws SAXException { 213 sb.append(buf, offset, length); 214 try { 215 w.write("contentHandler.characters(__chars__, "); 216 w.write("" + start); 217 w.write(", "); 218 w.write("" + length); 219 w.write(");\n"); 220 } catch (IOException e) { 221 throw new SAXException(e); 222 } 223 start += length; 224 } 225 226 /** 227 * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 228 */ 229 public void ignorableWhitespace(char[] arg0, int arg1, int arg2) 230 throws SAXException { 231 } 232 233 /** 234 * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, 235 * java.lang.String) 236 */ 237 public void processingInstruction(String target, String data) 238 throws SAXException { 239 try { 240 if ("SaxCompiler-package".equals(target)) { 241 assertState(0); 242 w.write("package "); 243 w.write(data); 244 w.write(";\n"); 245 state = 1; 246 } else if ("SaxCompiler-class".equals(target)) { 247 assertStateLEQ(1); 248 w.write("public final class "); 249 w.write(data); 250 w.write(" {\n"); 251 w.write("private "); 252 w.write(data); 253 w.write("() {}\n"); 254 state = 2; 255 } else if ("SaxCompiler-args".equals(target)) { 256 assertState(2); 257 w.write("public static void emit(org.xml.sax.ContentHandler contentHandler, "); 258 w.write(data); 259 w.write(") throws org.xml.sax.SAXException {\n"); 260 state = 3; 261 writeStart(); 262 } else if ("SaxCompiler-omitRoot".equals(target)) { 263 assertStateLEQ(2); 264 omitRoot = true; 265 } else if ("SaxCompiler-code".equals(target)) { 266 ensureState(); 267 w.write(data); 268 w.write("\n"); 269 } else { 270 ensureState(); 271 w.write("contentHandler.processingInstruction("); 272 w.write(StringLiteralUtil.stringLiteral(target)); 273 w.write(", "); 274 w.write(StringLiteralUtil.stringLiteral(data)); 275 w.write(");\n"); 276 } 277 } catch (IOException e) { 278 throw new SAXException(e); 279 } 280 } 281 282 /** 283 * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) 284 */ 285 public void skippedEntity(String arg0) throws SAXException { 286 throw new SAXException("skippedEntity not supported"); 287 } 288 289 private void assertState(int s) throws SAXException { 290 if (state != s) { 291 throw new SAXException("Illegal state."); 292 } 293 } 294 295 private void assertStateLEQ(int s) throws SAXException { 296 if (state > s) { 297 throw new SAXException("Illegal state."); 298 } 299 } 300 301 private void writeStart() throws SAXException { 302 try { 303 w.write("org.xml.sax.helpers.AttributesImpl __attrs__ = new org.xml.sax.helpers.AttributesImpl();\n"); 304 if (!omitRoot) { 305 w.write("try {\n"); 306 w.write("contentHandler.startDocument();\n"); 307 } 308 } catch (IOException e) { 309 throw new SAXException(e); 310 } 311 312 } 313 314 private void ensureState() throws SAXException { 315 if (state == 2) { 316 try { 317 w.write("public static void emit(org.xml.sax.ContentHandler contentHandler) throws org.xml.sax.SAXException {\n"); 318 writeStart(); 319 } catch (IOException e) { 320 throw new SAXException(e); 321 } 322 state = 3; 323 } else if (state != 3) { 324 throw new SAXException("Illegal state."); 325 } 326 } 327 328 public static void main(String[] args) { 329 try { 330 SAXParserFactory factory = SAXParserFactory.newInstance(); 331 factory.setNamespaceAware(true); 332 factory.setValidating(false); 333 XMLReader reader = factory.newSAXParser().getXMLReader(); 334 InputSource in = new InputSource(new FileInputStream(args[0])); 335 SaxCompiler sc = new SaxCompiler(new OutputStreamWriter( 336 new FileOutputStream(args[1]), "UTF-8")); 337 reader.setContentHandler(sc); 338 reader.parse(in); 339 } catch (Exception e) { 340 throw new RuntimeException(e); 341 } 342 } 343 }