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    }