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 }