2 // FEATURE: <code type="c++"/> -- syntax highlighting
3 // FEATURE: <code linenumbers="true"/> -- LaTeX moreverb package can help
4 // FIXME: nest TextNodes within each other for bold+italic
9 import org.ibex.util.*;
11 public class Doc extends XML {
15 Vec nodeStack = new Vec();
17 char[] buffer = new char[1024 * 1024];
22 public static void main(String[] s) throws Exception {
24 Reader r = new InputStreamReader(System.in);
27 int numread = r.read(d.buffer, len, d.buffer.length - len);
28 if (numread == -1) break;
30 if (len >= d.buffer.length) {
31 char[] newbuffer = new char[d.buffer.length * 2];
32 System.arraycopy(d.buffer, 0, newbuffer, 0, d.buffer.length);
36 d.parse(new CharArrayReader(d.buffer));
37 StringBuffer sb = new StringBuffer();
39 System.out.println(sb);
42 public void startElement(Element e) throws Exn {
43 if (preStart != -1) return;
44 String name = e.getLocalName();
46 if (name.equals("ibex-doc")) { newGuy = new Root(e);
47 } else if (name.equals("section")) { newGuy = new Section(e);
48 } else if (name.equals("heading")) { newGuy = new Heading(e);
49 } else if (name.equals("appendix")) { newGuy = new Appendix(e);
50 } else if (name.equals("list")) { newGuy = new List();
52 } else if (name.equals("pre")) { preStart = getGlobalOffset();
54 } else if (name.equals("definition")) { newGuy = new Definition(e);
55 } else if (name.equals("property")) { newGuy = new Property(e);
57 } else if (name.equals("b")) { newGuy = new B();
58 } else if (name.equals("i")) { newGuy = new I();
59 } else if (name.equals("t")) { newGuy = new T();
60 } else if (name.equals("link")) { newGuy = new Link(e);
61 } else if (name.equals("image")) { newGuy = new Image(e);
64 System.err.println("warning: unknown tag " + name);
68 Node parent = (Node)nodeStack.lastElement();
69 if (parent != null) parent.addChild(newGuy);
70 nodeStack.addElement(newGuy);
74 public void whitespace(char[] ch, int start, int length) throws Exn, IOException { characters(ch, start, length); }
75 public void endElement(Element e) throws Exn, IOException {
77 if (!e.getLocalName().equals("pre")) return;
78 ((Node)nodeStack.lastElement()).addChild(new PRE(preStart, getGlobalOffset()));
80 } else if (skip > 0) {
84 nodeStack.setSize(nodeStack.size() - 1);
87 public void characters(char[] ch, int start, int length) throws Exn, IOException {
88 if (preStart != -1) return;
89 ((Node)nodeStack.lastElement()).addText(new String(ch, start, length));
93 public Node parent = null;
94 public abstract void addText(String s);
95 public abstract void addChild(Node s);
96 public abstract void dumpLatex(StringBuffer sb);
97 final String fixLatex(String s) {
98 if (s == null) return "";
99 s = s.replaceAll("\\$", "\\\\\\$ ");
100 s = s.replaceAll("\\\\([^\\$])", "\\$\\\\backslash\\$\\1");
101 s = s.replaceAll("LaTeX", "\\\\LaTeX");
102 s = s.replaceAll("\\%", "\\\\% ");
103 s = s.replaceAll("#", "\\\\#");
104 s = s.replaceAll("\\{", "\\\\{");
105 s = s.replaceAll("\\}", "\\\\}");
106 s = s.replaceAll("\\&", "\\\\&");
107 s = s.replaceAll("\\~", "\\\\~");
108 s = s.replaceAll("_", "\\\\_");
109 if (!(this instanceof T)) {
110 s = s.replaceAll(" \"", " ``");
111 s = s.replaceAll("\"", "''");
118 // Empty Nodes //////////////////////////////////////////////////////////////////////////////
120 class EmptyNode extends Node {
121 public EmptyNode() { }
122 public void addChild(Node o) { throw new RuntimeException(this.getClass().getName() + " cannot have children"); }
123 public void addText(String o) { throw new RuntimeException(this.getClass().getName() + " cannot have content"); }
124 public void dumpLatex(StringBuffer sb) { }
127 class Image extends EmptyNode {
129 public Image(XML.Element e) { url = e.getAttrVal("url"); }
130 public void dumpLatex(StringBuffer sb) { sb.append("\\hyperimage{" + url + "}"); }
133 class Heading extends EmptyNode {
135 public Heading(XML.Element e) { text = e.getAttrVal("title"); }
136 public void dumpLatex(StringBuffer sb) { sb.append("\\vspace{.6cm}\\hypertarget{" +
137 fixLatex(text) + "}{\\textbf{\\textsf{" +
138 fixLatex(text) + "}}}"); }
141 class LineBreak extends EmptyNode {
142 public LineBreak() { }
143 public void dumpLatex(StringBuffer sb) { sb.append("\\\n"); }
146 class ParagraphBreak extends EmptyNode {
147 public ParagraphBreak() { }
148 public void dumpLatex(StringBuffer sb) { sb.append("\n\n"); }
153 // Non-Paragraph Nodes //////////////////////////////////////////////////////////////////////////////
155 /** Nodes which contain only text; they split themselves if anything else is added */
156 class TextNode extends Node implements Cloneable {
157 protected String mytext = "";
158 private boolean canAcceptMoreText = true;
159 public void dumpLatex(StringBuffer sb) { sb.append(fixLatex(mytext)); }
160 public void addChild(Node o) { canAcceptMoreText = false; parent.addChild(o); }
161 public void addText(String o) {
162 if (canAcceptMoreText) {
164 String[] split = mytext.split("\\n\\s*\\n");
165 if (split.length <= 1) return;
166 canAcceptMoreText = false;
168 parent.addChild(new ParagraphBreak());
170 for(int i=1; i<split.length; i++) o += split[i];
173 TextNode clone = (TextNode)clone();
175 clone.canAcceptMoreText = true;
177 parent.addChild(clone);
178 } catch (CloneNotSupportedException cnse) {
179 throw new RuntimeException(cnse);
184 class T extends TextNode {
185 public void dumpLatex(StringBuffer sb) {
186 sb.append("{\\texttt{");
190 class B extends TextNode {
191 public void dumpLatex(StringBuffer sb) {sb.append("{\\textbf{"); super.dumpLatex(sb); sb.append("}}");}}
192 class I extends TextNode {
193 public void dumpLatex(StringBuffer sb) {sb.append("{\\it{"); super.dumpLatex(sb); sb.append("}}");}}
194 class Link extends TextNode {
196 public String section;
197 public String appendix;
199 public Link(XML.Element e) {
200 url = e.getAttrVal("url");
201 text = e.getAttrVal("text");
202 appendix = e.getAttrVal("appendix");
203 section = e.getAttrVal("section");
205 public void dumpLatex(StringBuffer sb) {
206 // FIXME: dotted underline for section/appendix, solid underline for url
207 String text = fixLatex(this.text);
208 if (text == null) text = "\\tt " + (url != null ? url : section != null ? section : appendix);
209 if (url != null) { sb.append("\\href{" + url + "}{\\uline{" + text + "}} ");
210 } else if (section != null) { sb.append("\\hyperlink{" + section + "}{\\uwave{" + text + "}} ");
211 } else if (appendix != null) { sb.append("\\hyperlink{" + appendix + "}{\\uwave{" + text + "}} ");
216 class PRE extends TextNode {
217 public PRE(int start, int end) {
218 while(Character.isWhitespace(buffer[start])) start++;
219 while(buffer[start] != '\n') start--;
221 end -= 6; // ugly hack since </pre> is 6 chars long...
222 if (Character.isWhitespace(buffer[end])) {
223 while(Character.isWhitespace(buffer[end])) end--;
226 mytext = new String(buffer, start, end - start);
228 public void dumpLatex(StringBuffer sb) {
229 sb.append("\n\n\\begin{Verbatim}[fontfamily=courier,fontsize=\\footnotesize,frame=single,rulecolor=\\color{CodeBorder},resetmargins=true]\n");
231 sb.append("\\end{Verbatim}\n\n");
236 // Paragraph Nodes //////////////////////////////////////////////////////////////////////////////
238 class ParagraphNode extends Node {
239 protected Vec children = new Vec();
240 public void addChild(Node n) { children.addElement(n); n.parent = this; }
241 public void addText(String s) {
242 if (children.size() == 0 || !(children.lastElement() instanceof String))
243 children.addElement(s);
245 children.setElementAt(children.lastElement() + s, children.size() - 1);
247 public void dumpLatex(StringBuffer sb) {
248 for(int i=0; i<children.size(); i++) {
249 if (children.elementAt(i) instanceof String) sb.append(fixLatex(children.elementAt(i).toString()));
250 else ((Node)children.elementAt(i)).dumpLatex(sb);
255 class Property extends ParagraphNode {
258 String default_ = null;
259 public Property(XML.Element e) {
260 name = e.getAttrVal("name");
261 if (name == null || name.equals("")) name = "ERROR";
262 type = e.getAttrVal("type");
263 default_ = e.getAttrVal("default");
264 if (default_ != null && default_.trim().length() == 0) default_ = null;
266 public void dumpLatex(StringBuffer sb) {
267 StringBuffer sb2 = new StringBuffer();
268 super.dumpLatex(sb2);
269 String s = sb2.toString();
270 while(Character.isSpace(s.charAt(0))) s = s.substring(1);
271 String fname = fixLatex(name);
272 type = type == null || type.equals("") ? "" : "({\\it{" + fixLatex(type) + "}})";
273 if (name.trim().length() > 13) {
274 sb.append("\n\n{\\color{CodeBorder}\\hspace{-2cm}\\dotfill\\\\\\color{black}}");
275 sb.append("\\marginpar{\\raggedleft{\\texttt{\\textbf{\\footnotesize{"+fname+"}}}}\\\\"+type+" }");
279 sb.append("\n\n{\\color{CodeBorder}\\hspace{-2cm}\\dotfill\\\\\\color{black}}");
280 sb.append(s.substring(0, s.indexOf(' ')));
281 sb.append("\\marginpar{\\raggedleft{\\texttt{\\textbf{\\footnotesize{"+fname+"}}}}\\\\"+type+" }");
282 sb.append(s.substring(s.indexOf(' ')));
284 if (default_ != null) {
285 sb.append("\\\\{\\it default: }{\\texttt{" + fixLatex(default_) + "}}\n\n");
292 class Definition extends ParagraphNode {
294 public Definition(XML.Element e) { name = e.getAttrVal("term"); }
295 public void dumpLatex(StringBuffer sb) {
296 StringBuffer sb2 = new StringBuffer();
297 super.dumpLatex(sb2);
298 String s = sb2.toString();
299 while(Character.isSpace(s.charAt(0))) s = s.substring(1);
300 sb.append(s.substring(0, s.indexOf(' ')));
301 sb.append("\\marginpar{\\raggedleft{\\textbf{"+fixLatex(name)+"}}}");
302 sb.append(s.substring(s.indexOf(' ')));
306 class Section extends ParagraphNode {
308 public Section(XML.Element e) { name = e.getAttrVal("title"); }
309 public void dumpLatex(StringBuffer sb) {
311 String base = "section";
312 for(Node n = parent; n != null; n = n.parent)
313 if (n instanceof Section || n instanceof Appendix) {
317 if (secs.length() == 0)
318 sb.append("\\newpage\n\n");
319 sb.append("\n\n\\hypertarget{" + name + "}{\\" + secs + base + "{" + name + "}}\n\n");
324 static boolean hitAppendix = false;
325 class Appendix extends Section {
326 public Appendix(XML.Element e) { super(e); }
327 public void dumpLatex(StringBuffer sb) {
328 if (!hitAppendix) sb.append("\\appendix\n");
334 class List extends ParagraphNode {
335 Node textnode = null;
337 public void addText(String s) {
338 if (!(children.lastElement() == textnode) || (textnode == null && children.lastElement() == null))
339 addChild(textnode = new TextNode());
340 ((TextNode)children.lastElement()).addText(s);
342 public void dumpLatex(StringBuffer sb) {
343 sb.append("\n\\begin{itemize}%\n");
344 boolean unusedItem = false;
345 for(int i=0; i<children.size(); i++) {
346 Object kid = children.elementAt(i);
347 if (kid instanceof ParagraphBreak || kid instanceof List) unusedItem = false;
348 if (kid instanceof String) {
349 if (kid.toString().trim().length() == 0) continue;
350 if (!unusedItem) { unusedItem = true; sb.append("\n\n\\item\n"); }
351 sb.append(children.elementAt(i));
353 if (kid instanceof TextNode) {
354 if (((TextNode)kid).mytext.trim().length() == 0) continue;
355 if (!unusedItem) { unusedItem = true; sb.append("\n\n\\item\n"); }
357 ((Node)kid).dumpLatex(sb);
360 sb.append("\n\\end{itemize}%\n");
364 class Root extends ParagraphNode {
365 String title = "You forgot the title, you idiot!";
366 String author = "Your Mom";
368 String subtitle = null;
369 public Root(XML.Element e) {
371 title = e.getAttrVal("title");
372 author = e.getAttrVal("author");
373 email = e.getAttrVal("email");
374 subtitle = e.getAttrVal("subtitle");
376 public void dumpLatex(StringBuffer sb) {
377 sb.append("\\documentclass{article}\n");
378 sb.append("\\def\\ninept{\\def\\baselinestretch{.95}\\let\\normalsize\\small\\normalsize}\n");
379 sb.append("\\ninept\n");
380 sb.append("\\usepackage{color}\n");
381 sb.append("\\definecolor{CodeBorder}{rgb}{0.6,0.6,0.6}\n");
382 sb.append("\\definecolor{CodeBackground}{rgb}{0.93,0.93,0.93}\n");
383 sb.append("\\usepackage{graphicx}\n");
384 sb.append("\\usepackage{courier}\n");
385 sb.append("\\usepackage{fancyvrb}\n");
386 sb.append("\\usepackage{fvrb-ex}\n");
387 sb.append("\\usepackage{bold-extra}\n");
388 sb.append("\\usepackage{ulem}\n");
389 sb.append("\\usepackage{appendix}\n");
390 sb.append("\\usepackage{amssymb,amsmath,epsfig,alltt}\n");
391 sb.append("\\sloppy\n");
392 sb.append("\\usepackage{palatino}\n");
393 sb.append("\\usepackage{sectsty}\n");
394 sb.append("\\allsectionsfont{\\sffamily}\n");
395 sb.append("\\sectionfont{\\color{black}\\leftskip=-2cm\\hrulefill\\\\\\sffamily\\bfseries\\raggedleft\\vspace{1cm}}\n");
396 sb.append("\\subsectionfont{\\color{black}\\dotfill\\\\\\sffamily\\raggedright\\hspace{-4cm}}\n");
397 sb.append("\\newdimen\\sectskip\n");
398 sb.append("\\newdimen\\subsectskip\n");
399 sb.append("\\newdimen\\saveskip\n");
400 sb.append("\\saveskip=\\leftskip\n");
401 sb.append("\\sectskip=-2cm\n");
402 sb.append("\\subsectskip=0cm\n");
403 sb.append("\\let\\oldsection\\section\n");
404 sb.append("\\let\\oldsubsection\\subsection\n");
405 sb.append("\\def\\subsection#1{\\leftskip=\\sectskip\\oldsubsection{#1}\\leftskip=0cm}\n");
406 sb.append("\\usepackage{parskip}\n");
407 sb.append("\\usepackage{tabularx}\n");
408 sb.append("\\usepackage{alltt}\n");
409 sb.append("\\usepackage[pdftex,colorlinks=true,urlcolor=blue,linkcolor=blue,bookmarks=true]{hyperref}\n");
410 // FIXME: pdfauthor, pdftitle, pdfsubject, pdfkeywords?
412 sb.append("\\begin{document}\n");
413 sb.append("\\reversemarginpar\n");
415 sb.append("\\title{\\textbf{\\textsf{\n");
417 if (subtitle != null) sb.append("\\\\{\\large " + subtitle + "}\n");
419 if (author != null) {
420 sb.append("\\author{\n");
422 if (email != null) sb.append("\\\\{\\tt " + email + "}\n");
426 sb.append("\\maketitle\n");
427 sb.append("\\clearpage\n");
428 sb.append("\\tableofcontents\n");
429 sb.append("\\clearpage\n");
430 sb.append("\\onecolumn\n");
432 sb.append("\\end{document}");