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("<");
247 break;
248 case '>':
249 write(">");
250 break;
251 case '&':
252 write("&");
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 }