70ef5279ae74cb7db864a12b683fe53bb09ebd19
[org.ibex.core.git] / src / org / ibex / util / Doc.java
1
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
5
6 package org.ibex.util;
7 import java.util.*;
8 import java.io.*;
9 import org.ibex.util.*;
10
11 public class Doc extends XML {
12
13     Root root = null;
14     int skip = 0;
15     Vec nodeStack = new Vec();
16     String pending = "";
17     char[] buffer = new char[1024 * 1024];
18     int preStart = -1;
19     
20     public Doc() { }
21
22     public static void main(String[] s) throws Exception {
23         Doc d = new Doc();
24         Reader r = new InputStreamReader(System.in);
25         int len = 0;
26         while(true) {
27             int numread = r.read(d.buffer, len, d.buffer.length - len);
28             if (numread == -1) break;
29             len += numread;
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);
33                 d.buffer = newbuffer;
34             }
35         }
36         d.parse(new CharArrayReader(d.buffer));
37         StringBuffer sb = new StringBuffer();
38         d.root.dumpLatex(sb);
39         System.out.println(sb);
40     }
41
42     public void startElement(Element e) throws Exn {
43         if (preStart != -1) return;
44         String name = e.getLocalName();
45         Node newGuy = null;
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();
51
52         } else if (name.equals("pre")) {          preStart = getGlobalOffset();
53
54         } else if (name.equals("definition")) {   newGuy = new Definition(e);
55         } else if (name.equals("property")) {     newGuy = new Property(e);
56
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);
62
63         } else {
64             System.err.println("warning: unknown tag " + name);
65             skip++;
66         }
67         if (newGuy != null) {
68             Node parent = (Node)nodeStack.lastElement();
69             if (parent != null) parent.addChild(newGuy);
70             nodeStack.addElement(newGuy);
71         }
72     }
73
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 {
76         if (preStart != -1) {
77             if (!e.getLocalName().equals("pre")) return;
78             ((Node)nodeStack.lastElement()).addChild(new PRE(preStart, getGlobalOffset()));
79             preStart = -1;
80         } else if (skip > 0) {
81             skip--;
82             return;
83         } else {
84             nodeStack.setSize(nodeStack.size() - 1);
85         }
86     }
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));
90     }
91
92     abstract class Node {
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("\"", "''");
112             }
113             return s;
114         }
115     }
116
117
118     // Empty Nodes //////////////////////////////////////////////////////////////////////////////
119
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) { }
125     }
126
127     class Image extends EmptyNode {
128         public String url;
129         public Image(XML.Element e) { url = e.getAttrVal("url"); }
130         public void dumpLatex(StringBuffer sb) { sb.append("\\hyperimage{" + url + "}"); }
131     }
132
133     class Heading extends EmptyNode {
134         public String text;
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) + "}}}"); }
139     }
140
141     class LineBreak extends EmptyNode {
142         public LineBreak() { }
143         public void dumpLatex(StringBuffer sb) { sb.append("\\\n"); }
144     }
145
146     class ParagraphBreak extends EmptyNode {
147         public ParagraphBreak() { }
148         public void dumpLatex(StringBuffer sb) { sb.append("\n\n"); }
149     }
150
151
152
153     // Non-Paragraph Nodes //////////////////////////////////////////////////////////////////////////////
154
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) {
163                 mytext += (String)o;
164                 String[] split = mytext.split("\\n\\s*\\n");
165                 if (split.length <= 1) return;
166                 canAcceptMoreText = false;
167                 mytext = split[0];
168                 parent.addChild(new ParagraphBreak());
169                 o = "";
170                 for(int i=1; i<split.length; i++) o += split[i];
171             }
172             try {
173                 TextNode clone = (TextNode)clone();
174                 clone.mytext = "";
175                 clone.canAcceptMoreText = true;
176                 clone.addText(o);
177                 parent.addChild(clone);
178             } catch (CloneNotSupportedException cnse) {
179                 throw new RuntimeException(cnse);
180             }
181         }
182     }
183
184     class T extends TextNode {
185         public void dumpLatex(StringBuffer sb) {
186             sb.append("{\\texttt{");
187             super.dumpLatex(sb);
188             sb.append("}}");
189         } }
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 {
195         public String url;
196         public String section;
197         public String appendix;
198         public String text;
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");
204         }
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 + "}} ");
212             }
213         }
214     }
215
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--;
220             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--;
224                 end++;
225             }
226             mytext = new String(buffer, start, end - start);
227         }
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");
230             sb.append(mytext);
231             sb.append("\\end{Verbatim}\n\n");
232         }
233     }
234
235
236     // Paragraph Nodes //////////////////////////////////////////////////////////////////////////////
237
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);
244             else
245                 children.setElementAt(children.lastElement() + s, children.size() - 1);
246         }
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);
251             }
252         }
253     }
254
255     class Property extends ParagraphNode {
256         String name = "";
257         String type = "";
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;
265         }
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+" }");
276                 sb.append("\\\\");
277                 sb.append(s);
278             } else {
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(' ')));
283             }
284             if (default_ != null) {
285                 sb.append("\\\\{\\it default: }{\\texttt{" + fixLatex(default_) + "}}\n\n");
286             } else {
287                 sb.append("\n\n");
288             }
289         }
290     }
291
292     class Definition extends ParagraphNode {
293         String name = "";
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(' ')));
303         }
304     }
305
306     class Section extends ParagraphNode {
307         String name;
308         public Section(XML.Element e) { name = e.getAttrVal("title"); }
309         public void dumpLatex(StringBuffer sb) {
310             String secs = "";
311             String base = "section";
312             for(Node n = parent; n != null; n = n.parent)
313                 if (n instanceof Section || n instanceof Appendix) {
314                     base = "section";
315                     secs += "sub";
316                 }
317             if (secs.length() == 0)
318                 sb.append("\\newpage\n\n");
319             sb.append("\n\n\\hypertarget{" + name + "}{\\" + secs + base + "{" + name + "}}\n\n");
320             super.dumpLatex(sb);
321         }
322     }
323
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");
329             hitAppendix = true;
330             super.dumpLatex(sb);
331         }
332     }
333
334     class List extends ParagraphNode {
335         Node textnode = null;
336         public List() { }
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);
341         }
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));
352                 } else {
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"); }
356                     }
357                     ((Node)kid).dumpLatex(sb);
358                 }
359             }
360             sb.append("\n\\end{itemize}%\n");
361         }
362     }
363
364     class Root extends ParagraphNode {
365         String title = "You forgot the title, you idiot!";
366         String author = "Your Mom";
367         String email = null;
368         String subtitle = null;
369         public Root(XML.Element e) {
370             root = this;
371             title = e.getAttrVal("title");
372             author = e.getAttrVal("author");
373             email = e.getAttrVal("email");
374             subtitle = e.getAttrVal("subtitle");
375         }
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?
411             sb.append("\n");
412             sb.append("\\begin{document}\n");
413             sb.append("\\reversemarginpar\n");
414             sb.append("\n");
415             sb.append("\\title{\\textbf{\\textsf{\n");
416             sb.append(title);
417             if (subtitle != null) sb.append("\\\\{\\large " + subtitle + "}\n");
418             sb.append("}}}\n");
419             if (author != null) {
420                 sb.append("\\author{\n");
421                 sb.append(author);
422                 if (email != null) sb.append("\\\\{\\tt " + email + "}\n");
423                 sb.append("}\n");
424             }
425             sb.append("\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");
431             super.dumpLatex(sb);
432             sb.append("\\end{document}");
433         }
434     }
435 }
436