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    }