    001    package org.apache.tapestry5.javadoc;
    002
    003    import java.io.BufferedInputStream;
    004    import java.io.File;
    005    import java.io.FileInputStream;
    006    import java.io.IOException;
    007    import java.io.InputStream;
    008    import java.io.Writer;
    009
    010    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    011    import org.apache.tapestry5.ioc.util.Stack;
    012    import org.xml.sax.Attributes;
    013    import org.xml.sax.ContentHandler;
    014    import org.xml.sax.InputSource;
    015    import org.xml.sax.Locator;
    016    import org.xml.sax.SAXException;
    017    import org.xml.sax.XMLReader;
    018    import org.xml.sax.ext.LexicalHandler;
    019    import org.xml.sax.helpers.XMLReaderFactory;
    020
    021    /**
    022     * Reads an XDOC file using SAX and streams its content (with some modifications) to
    023     * an output stream.
    024     */
    025    public class XDocStreamer
    026    {
    027        final File xdoc;
    028
    029        final Writer writer;
    030
    031        private static final Runnable NO_OP = new Runnable()
    032        {
    033            public void run()
    034            {
    035            }
    036        };
    037
    038        private void write(String text)
    039        {
    040            try
    041            {
    042                writer.write(text);
    043            }
    044            catch (IOException ex)
    045            {
    046                throw new RuntimeException(ex);
    047            }
    048        }
    049
    050        private Runnable writeClose(final String elementName)
    051        {
    052            return new Runnable()
    053            {
    054                public void run()
    055                {
    056                    write("</");
    057                    write(elementName);
    058                    write(">");
    059                }
    060            };
    061        }
    062
    063        public XDocStreamer(File xdoc, Writer writer)
    064        {
    065            this.xdoc = xdoc;
    066            this.writer = writer;
    067        }
    068
    069        enum ParserState
    070        {
    071            IGNORING, COPYING, COPYING_CDATA
    072        };
    073
    074        class SaxHandler implements ContentHandler, LexicalHandler
    075        {
    076            final Stack<Runnable> endElementHandlers = CollectionFactory.newStack();
    077
    078            ParserState state = ParserState.IGNORING;
    079
    080            public void startDTD(String name, String publicId, String systemId) throws SAXException
    081            {
    082            }
    083
    084            public void endDTD() throws SAXException
    085            {
    086            }
    087
    088            public void startEntity(String name) throws SAXException
    089            {
    090            }
    091
    092            public void endEntity(String name) throws SAXException
    093            {
    094            }
    095
    096            public void startCDATA() throws SAXException
    097            {
    098                if (state == ParserState.IGNORING)
    099                {
    100                    endElementHandlers.push(NO_OP);
    101                    return;
    102                }
    103
    104                state = ParserState.COPYING_CDATA;
    105
    106                endElementHandlers.push(new Runnable()
    107                {
    108                    public void run()
    109                    {
    110                        state = ParserState.COPYING;
    111                    }
    112                });
    113            }
    114
    115            public void endCDATA() throws SAXException
    116            {
    117                endElementHandlers.pop().run();
    118            }
    119
    120            /** Does nothing; comments are always stripped out. */
    121            public void comment(char[] ch, int start, int length) throws SAXException
    122            {
    123            }
    124
    125            public void setDocumentLocator(Locator locator)
    126            {
    127            }
    128
    129            public void startDocument() throws SAXException
    130            {
    131            }
    132
    133            public void endDocument() throws SAXException
    134            {
    135            }
    136
    137            public void startPrefixMapping(String prefix, String uri) throws SAXException
    138            {
    139            }
    140
    141            public void endPrefixMapping(String prefix) throws SAXException
    142            {
    143            }
    144
    145            public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
    146            {
    147                if (state == ParserState.IGNORING)
    148                {
    149                    if (localName.equals("body"))
    150                    {
    151                        state = ParserState.COPYING;
    152                    }
    153
    154                    endElementHandlers.push(NO_OP);
    155
    156                    return;
    157                }
    158
    159                if (localName.equals("section"))
    160                {
    161
    162                    String name = getAttribute(atts, "name");
    163
    164                    // More JavaDoc ugliness; this makes sections fit in well with the main
    165                    // output.
    166
    167                    write(String.format("<dt><b>%s:</b></dt><dd>", name));
    168
    169                    endElementHandlers.push(writeClose("dd"));
    170
    171                    return;
    172                }
    173
    174                if (localName.equals("subsection"))
    175                {
    176                    writeSectionHeader(atts, "h3");
    177                    return;
    178                }
    179
    180                if (localName.equals("source"))
    181                {
    182                    write("<pre>");
    183                    endElementHandlers.push(writeClose("pre"));
    184                    return;
    185                }
    186
    187                write("<");
    188                write(localName);
    189
    190                for (int i = 0; i < atts.getLength(); i++)
    191                {
    192                    write(String.format(" %s=\"%s\"", atts.getLocalName(i), atts.getValue(i)));
    193                }
    194
    195                write(">");
    196
    197                endElementHandlers.push(writeClose(localName));
    198            }
    199
    200            private void writeSectionHeader(Attributes atts, String elementName)
    201            {
    202                String name = getAttribute(atts, "name");
    203
    204                write(String.format("<%s>%s</%1$s>", elementName, name));
    205
    206                endElementHandlers.push(NO_OP);
    207                return;
    208            }
    209
    210            private String getAttribute(Attributes atts, String name)
    211            {
    212                for (int i = 0; i < atts.getLength(); i++)
    213                {
    214                    if (atts.getLocalName(i).equals(name))
    215                        return atts.getValue(i);
    216                }
    217
    218                throw new RuntimeException(String.format("No '%s' attribute present.", name));
    219            }
    220
    221            public void endElement(String uri, String localName, String qName) throws SAXException
    222            {
    223                endElementHandlers.pop().run();
    224            }
    225
    226            public void characters(char[] ch, int start, int length) throws SAXException
    227            {
    228                try
    229                {
    230                    switch (state)
    231                    {
    232                        case IGNORING:
    233                            break;
    234
    235                        case COPYING:
    236                            writer.write(ch, start, length);
    237                            break;
    238
    239                        case COPYING_CDATA:
    240
    241                            for (int i = start; i < start + length; i++)
    242                            {
    243                                switch (ch[i])
    244                                {
    245                                    case '<':
    246                                        write("&lt;");
    247                                        break;
    248                                    case '>':
    249                                        write("&gt;");
    250                                        break;
    251                                    case '&':
    252                                        write("&amp;");
    253                                        break;
    254                                    default:
    255                                        writer.write(ch[i]);
    256                                }
    257                            }
    258
    259                            break;
    260                    }
    261                }
    262                catch (IOException ex)
    263                {
    264                    throw new SAXException(ex);
    265                }
    266            }
    267
    268            public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
    269            {
    270            }
    271
    272            public void processingInstruction(String target, String data) throws SAXException
    273            {
    274            }
    275
    276            public void skippedEntity(String name) throws SAXException
    277            {
    278            }
    279
    280        }
    281
    282        /** Parse the file and write its transformed content to the Writer. */
    283        public void writeContent() throws SAXException
    284        {
    285            SaxHandler handler = new SaxHandler();
    286
    287            XMLReader reader = XMLReaderFactory.createXMLReader();
    288
    289            reader.setContentHandler(handler);
    290            reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
    291
    292            try
    293            {
    294                InputStream is = new BufferedInputStream(new FileInputStream(xdoc));
    295
    296                reader.parse(new InputSource(is));
    297            }
    298            catch (IOException ex)
    299            {
    300                throw new RuntimeException(ex);
    301            }
    302        }
    303    }