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 }