/* * DocBookSlidesWikiReader.java Created on 27. květen 2004, 7:35 */ package net.sf.tomp.docbook.sax; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.sf.tomp.xml.CharReader; import net.sf.tomp.xml.wiki.AbstractWikiReader; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * TODO: nested lists, inline elements * * @author tomp */ public class DocBookSlidesWikiReader extends AbstractWikiReader { private String inList = null; private boolean inProgramlisting = false; private static Map ELEMENTS = new HashMap(); private static Set NAMES = new HashSet(); protected static void depth(String elt, int depth) { ELEMENTS.put(elt, new Integer(depth)); } protected static void name(String name) { NAMES.add(name); } protected static boolean isName(String name) { return NAMES.contains(name); } protected int getDepth(String elt) { Integer ii = (Integer) ELEMENTS.get(elt); if (ii == null) return -1; return ii.intValue(); } static { depth("slides", 0); depth("slidesinfo", 1); depth("foilgroup", 1); depth("foil", 2); depth("title", 3); depth("programlisting", 5); depth("itemizedlist", 5); depth("orderedlist", 5); depth("variablelist", 5); depth("varlistentry", 6); depth("term", 7); depth("listitem", 7); depth("tip", 5); depth("warning", 5); depth("note", 5); depth("caution", 5); depth("important", 5); depth("sidebar", 5); depth("abstract", 5); depth("footnote", 5); depth("author", 5); depth("firstname", 6); depth("surname", 6); depth("para", 8); depth("simpara", 9); depth("figure", 3); depth("mediaobject", 4); depth("caption", 5); depth("imageobject", 5); depth("ulink", 20); depth("xref", 20); depth("emphasis", 20); depth("filename", 21); depth("classname", 21); depth("packagename", 21); depth("methodname", 21); depth("application", 21); depth("command", 21); depth("sgmltag", 21); name("class"); name("package"); name("method"); name("exception"); name("error"); name("var"); name("first"); name("sur"); name("other"); name("file"); name("product"); }; protected boolean isDeeperThan(String elt1, String elt2) { if (elt1 == null || elt2 == null) return super.isDeeperThan(elt1, elt2); if (top.equals("foilgroup") && elt1.equals("foil") && elt2.equals("title")) return false; if (elt1.equals("figure") && elt2.equals("title")) return true; int i1 = getDepth(elt1); int i2 = getDepth(elt2); if (i1 == -1 || i2 == -1) return super.isDeeperThan(elt1, elt2); return i1 < i2; } protected void process(String line) throws SAXException { if (line.startsWith("===")) { // foilgroup begin & title startTitledElement("!foilgroup " + line.substring(3)); } else if (line.startsWith("---")) { // foil begin, id & title startTitledElement("!foil " + line.substring(3)); } else if (line.startsWith("*")) { // itemizedlist listitem itemizedlistItem(line); } else if (line.startsWith("#")) { // orderedlist listitem orderedlistItem(line); } else if (line.startsWith("!mediaobject") || line.startsWith("!figure")) { startTitledFiledElement(line); } else if (line.startsWith("!")) { // tip, caution, important, note, warning, sidebar,... startTitledElement(line); } else if (line.startsWith("{{{")) { // programlisting begin inProgramlisting = true; AttributesImpl atts = new AttributesImpl(); if (line.length() > 3) { atts.addAttribute("", "id", "id", "CDATA", line.substring(3)); } startElement("programlisting", atts); } else if (line.startsWith("}}}")) { // programlisting end endElement(); inProgramlisting = false; } else if (line.startsWith(";")) { // definitionlist variableListItem(line); } else if (line.equals("")) { // empty line emptyLine(); } else { if (inProgramlisting) characters(line + "\n"); else processedTextElement("para", line); } } protected void emptyLine() throws SAXException { inList = null; while (top != null && (top.endsWith("para") || top.endsWith("entry") || top.endsWith("item") || top.endsWith("list"))) { endElement(); } } protected static final Pattern VARLIST_ITEM = Pattern .compile("^\\;([^\\:]+)\\:(.*)"); protected void variableListItem(String line) throws SAXException { Matcher m = VARLIST_ITEM.matcher(line); if (m.matches()) { if (!"variablelist".equals(inList)) { inList = "variablelist"; startElement(inList); } String terms = m.group(1); String definition = m.group(2); System.out.println("term: "+terms+" def: " + definition); startElement("varlistentry"); // TODO: no multiple terms yet processedTextElement("term", terms); startElement("listitem"); processedTextElement("simpara", definition); } } protected void listItem(String listType, String line) throws SAXException { if (!listType.equals(inList)) { startElement(listType + "list"); inList = listType; } startElement("listitem"); processedTextElement("simpara", line.substring(1)); } protected void itemizedlistItem(String line) throws SAXException { listItem("itemized", line); } protected void orderedlistItem(String line) throws SAXException { listItem("ordered", line); } protected static final String ID_TITLE = "\\s*(\\#(\\S*))?\\s*(.*)"; protected static final String ELEMENT_ID_TITLE = "\\!(\\w+)" + ID_TITLE; protected static final Pattern P_ELEMENT_ID_TITLE = Pattern .compile(ELEMENT_ID_TITLE); protected void startTitledElement(String line) throws SAXException { System.out.println("startTitledElement " + line); Matcher m = P_ELEMENT_ID_TITLE.matcher(line); if (m.matches()) { String element = m.group(1); String id = m.group(3); String title = m.group(4); startTitledElement(element, id, title); } } protected static final String ELEMENT_ID_FILE_TITLE = "\\!(\\w+)(\\#(\\S*))?\\s*(\\S+)\\s+(.*)"; protected static final Pattern P_ELEMENT_ID_FILE_TITLE = Pattern .compile(ELEMENT_ID_FILE_TITLE); protected void startTitledFiledElement(String line) throws SAXException { System.out.println("startTitledFiledElement " + line); Matcher m = P_ELEMENT_ID_FILE_TITLE.matcher(line); if (m.matches()) { String element = m.group(1); String id = m.group(3); String file = m.group(4); String title = m.group(5); startTitledFiledElement(element, id, title, file); } } protected AttributesImpl getAttributesWithId(String id) { if (id != null && id.length() > 0) { AttributesImpl atts = new AttributesImpl(); atts.addAttribute("", "id", "id", "CDATA", id); return atts; } else { return EMPTY_ATTRS; } } protected void startTitledElement(String element, String id, String title) throws SAXException { System.out.println("startTitledElement " + element + " id=" + id + " title=" + title); startElement(element, getAttributesWithId(id)); if (element.equals("foilgroup") || element.equals("foil") || element.equals("slidesinfo")) { textElement("title", title); } else { // tip, note, warning, important, sidebar, caution, ... abstract processedTextElement("simpara", title); } if (element.equals("footnote") || element.equals("abstract")) { endElement(); } } protected void startTitledFiledElement(String element, String id, String title, String file) throws SAXException { System.out.println("startTitledFiledElement " + element + " id=" + id + " title=" + title + " file=" + file); if (element.equals("figure")) { startElement("figure", getAttributesWithId(id)); textElement("title", title); startElement("mediaobject"); AttributesImpl atts2 = new AttributesImpl(); atts2.addAttribute("", "fileref", "fileref", "CDATA", file); startElement("imageobject", atts2); endElement(); endElement(); endElement(); } else if (element.equals("mediaobject")) { startElement("mediaobject", getAttributesWithId(id)); AttributesImpl atts2 = new AttributesImpl(); atts2.addAttribute("", "fileref", "fileref", "CDATA", file); startElement("imageobject", atts2); startElement("caption"); textElement("para", title); endElement(); endElement(); endElement(); } } protected void processedTextElement(String element, String text, Attributes atts) throws SAXException { // FIXME parsing of a line startElement(element, atts); processInline(text); endElement(); } protected void processedTextElement(String element, String text) throws SAXException { // FIXME parsing of a line processedTextElement(element, text, null); } protected int listDepth(String line) { if (line.startsWith("*") || line.startsWith("#")) return 1 + listDepth(line.substring(1)); else return 0; } public void setParameter(String k, Object v) { } protected String rootElement() throws SAXException { return "slides"; } protected void processInline(String s) throws SAXException { CharReader r = new CharReader(s); EStack es = new EStack(); Element e = null; boolean emptyElt = false; boolean isInText = true; int depth = 0; int textBegin = r.index(); int afterName = 0; try { while (!r.eof()) { textBegin = r.index(); char c = r.next(); if (c == '%' && (isNamechar(r.at()) || r.at() == '%')) { //boolean goOut = false; do { int afterPercent = r.index(); if (isInText && afterPercent - 1 > textBegin) { text(r.substring(textBegin, afterPercent - 1)); } isInText = false; c = r.next(); if (c == '%') { // %% - empty element emptyElt = true; c = r.next(); while (!r.eof() && isNamechar(c)) { c = r.next(); } afterName = r.eof() ? r.index() + 1 : r.index(); e = new Element(r.substring(afterPercent + 1, afterName - 1), depth); e.emptyTag(); r.pushback(); closeNesting(es, e.depth()); if (markup(r.at())) { if (r.at() == '{') { isInText = true; } } else { isInText = true; } if (isInText) { textBegin = r.index(); } break; } else if (isNamechar(c)) { // nonempty element c = r.next(); while (!r.eof() && isNamechar(c)) { c = r.next(); } afterName = r.eof() ? r.index() + 1 : r.index(); e = new Element(r.substring(afterPercent, afterName - 1), depth); es.push(e); depth++; // to the next nonspace -> it will be attribute name c = r.nextNonspace(c); if (c == '{' || c == '%') { // attrs are finished or nested tag encountered e.startTag(); } else { while (!r.eof() && c != '{' && c != '%') { int anBegin = r.index() - 1; while (!r.eof() && c != '=' && !isWhitespace(c)) { c = r.next(); } e.newAttrName(r.substring(anBegin, r .index() - 1)); while (!r.eof() && !isQuote(c)) { c = r.next(); } r.pushback(); char quote = r.next(); c = ' '; int avBegin = r.index(); while (!r.eof() && c != quote) { c = r.next(); } e.newAttrValue(r.substring(avBegin, r .index() - 1)); c = r.next(); c = r.nextNonspace(c); } // attrs are finished or nested tag encountered e.startTag(); // } } // attrs to the elt on top. } else { // percent but not an element name isInText = true; } } while (c == '%' && !r.eof()); // && !goOut depth = 0; } else if (c == '{') { e = new Brackets(); es.push(e); e.startTag(); } else if (c == '}') { closeNesting(es); isInText = true; } else if (c == '*') { if (r.at() == '*') { if (es.isEmpty() || !isEmphasisBold(es.top())) { // 'emphasis/bold' to be pushed e = new Element("emphasis"); e.newAttrName("role"); e.newAttrValue("bold"); es.push(e); e.startTag(); } else { // 'emphasis' to be popped es.pop().endTag(); } } else { text("*"); } c = r.next(); } else if (c == '_') { if (r.at() == '_') { //System.out.println("Emphasis"); if (es.isEmpty() || !isEmphasis(es.top())) { // 'emphasis' to be pushed e = new Element("emphasis"); es.push(e); e.startTag(); } else { // 'emphasis' to be popped es.pop().endTag(); } } else { text("_"); } c = r.next(); } else if (c == '[') { if (r.at() == '[') { text("["); c = r.next(); } else { c = r.next(); System.out.println("link begin"); boolean xref = false; String text = ""; int textStart = r.index(); // until | or ] not reached while (!r.eof() && c != '|' && c != ']') { c = r.next(); } int textEnd = r.index(); text = r.substring(textStart - 1, textEnd - 1); if (c == '|') { // text now, url will be specified while (!r.eof() && c != ']') { c = r.next(); } int urlEnd = r.index(); String url = r.substring(textEnd, urlEnd - 1); xref = url.startsWith(">>>"); if (xref) { e = new Element("xref"); e.newAttrName("linkend"); e.newAttrValue(url.substring(3)); e.newAttrName("endterm"); e.newAttrValue(text); } else { e = new Element("ulink"); e.newAttrName("url"); e.newAttrValue(url); } es.push(e); } else { xref = text.startsWith(">>>"); if (xref) { e = new Element("xref"); e.newAttrName("linkend"); e.newAttrValue(text.substring(3)); } else { e = new Element("ulink"); e.newAttrName("url"); e.newAttrValue(text); } es.push(e); } e.startTag(); if (!xref) text(text); es.pop().endTag(); } } else if (c == '@') { if (isWhitespace(r.at(r.index() - 2)) && isNamechar(r.at())) { c = r.next(); System.out.println("@name, c=" + c); int eltnameStart = r.index(); // until space not reached while (!r.eof() && !isWhitespace(c)) { c = r.next(); } int eltnameEnd = r.index(); String eltname = r.substring(eltnameStart - 1, eltnameEnd - 1); // eltname has been read, name will follow while (!r.eof() && isWhitespace(c)) { c = r.next(); } int nameStart = r.index() - 1; while (!r.eof() && !isWhitespace(c)) { c = r.next(); } int nameEnd = r.index(); // System.out.println("eltname=" + eltname); // System.out.println("nameStart=" + nameStart); // System.out // .println("chaAt nameStart=" + r.at(nameStart)); // System.out.println("nameEnd - 1=" + (nameEnd - 1)); String name = r.substring(nameStart, nameEnd - 1) .replace('\'', ' '); e = new Element(getEltname(eltname)); es.push(e); e.startTag(); text(name); es.pop().endTag(); } else { c = r.next(); text("@" + c); } } else { // normal char (not % { }) or % and not followed by element // name if (c == '%' && !r.eof()) { c = r.next(); } while (!r.eof() && notMarkup(c)) { c = r.next(); } r.pushback(); int firstMarkup = r.eof() ? r.index() + 1 : r.index(); text(r.substring(textBegin, firstMarkup)); } } // popping elts while (!es.isEmpty()) { es.pop().endTag(); } } catch (StringIndexOutOfBoundsException aioob) { while (!es.isEmpty()) { es.pop().endTag(); } } // out.close(); } protected static String getEltname(String n) { if (isName(n)) return n + "name"; else return n; } protected static boolean isEmphasisBold(Element e) { return e.getName().equals("emphasis") && "bold".equals(e.getAttributes().getValue("role")); } protected static boolean isEmphasis(Element e) { return e.getName().equals("emphasis") && !"bold".equals(e.getAttributes().getValue("role")); } public boolean markup(char c) { return (c == '|') || (c == '@') || super.markup(c); } }