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 }