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