/* * AbstractWikiReader.java Created on 27. květen 2004, 7:19 */ package net.sf.tomp.xml.wiki; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import net.sf.tomp.xml.CharReader; import net.sf.tomp.xml.sax.AbstractXMLReader; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; /** * @author tomp */ public abstract class AbstractWikiReader extends AbstractXMLReader { protected class Brackets extends Element { public Brackets() { super("{"); } public void emptyTag() throws SAXException { characters("{"); } public void endTag() throws SAXException { characters("}"); } public void startTag() throws SAXException { characters("{"); } } protected class Element { private String attrName, attrValue; private AttributesImpl attrs; private char avDelim; private int depth; private String name; public Element(String name) { attrs = new AttributesImpl(); this.name = name; } public Element(String name, int d) { this(name); this.depth = d; // System.out.println("creating elt="+name+", depth="+d); } protected void addAttribute() { attrs.addAttribute("", attrName, attrName, "CDATA", attrValue); // System.out.println("attrs size="+attrs.size()); } public int depth() { return depth; } public void emptyTag() throws SAXException { startTag(); endTag(); } public void endTag() throws SAXException { endElement(); } public Attributes getAttributes() { return attrs; } public String getName() { return name; } public void newAttrName(String name) { attrName = name; } public void newAttrValue(String value) { attrValue = value; addAttribute(); } public void startTag() throws SAXException { startElement(name, attrs); } } public class EStack { List st = new ArrayList(); public boolean isEmpty() { return st.isEmpty(); } public Element pop() { int top = st.size() - 1; Element e = (Element) st.remove(top); return e; } public void push(Element e) { // System.err.println("pushing "+e.getName()+" depth="+e.depth()); st.add(e); } public Element top() { int top = st.size() - 1; Element e = (Element) st.get(top); return e; } } protected static final AttributesImpl EMPTY_ATTRS = new AttributesImpl(); public static AttributesImpl appendAttributes(AttributesImpl a, Attributes b) { if (b != null) { for (int i = 0; i < b.getLength(); i++) { a.addAttribute(b.getURI(i), b.getLocalName(i), b.getQName(i), b .getType(i), b.getValue(i)); } } return a; } private String currentAttributeName; private AttributesImpl currentAttributes; private String namespacePrefix; private String namespaceURI; private List stack; protected String top; private boolean verbose = true; /** Creates a new instance of AbstractWikiReader */ public AbstractWikiReader() { stack = new ArrayList(); top = null; namespaceURI = ""; namespacePrefix = ""; currentAttributes = null; currentAttributeName = null; } public void closeNesting(EStack es) throws SAXException { while (!es.isEmpty() && es.top().depth() > 0) { es.pop().endTag(); } if (!es.isEmpty()) es.pop().endTag(); } public void closeNesting(EStack es, int depth) throws SAXException { while (!es.isEmpty() && depth > 0) { es.pop().endTag(); depth--; } } protected void doEndElement() throws SAXException { if ("".equals(namespacePrefix)) contentHandler.endElement(namespaceURI, top, top); else contentHandler.endElement(namespaceURI, top, namespacePrefix + ":" + top); System.out.println("Element " + top + " ended"); } protected void doStartElement(String element, Attributes atts) throws SAXException { // System.out.println("About to start Element " + element+" atts="+atts); if ("".equals(namespacePrefix)) contentHandler.startElement(namespaceURI, element, element, atts); else contentHandler.startElement(namespaceURI, element, namespacePrefix + ":" + element, atts); System.out.println("Element " + element + " started"); } protected void endElement() throws SAXException { doEndElement(); // pop one element pop(); } protected void endElementsOnStack() throws SAXException { while (!isEmpty()) { // pop all on-stack elements doEndElement(); pop(); } } /** * @return Returns the currentAttributeName. */ public String getCurrentAttributeName() { return currentAttributeName; } /** * @return Returns the currentAttributes. */ public AttributesImpl getCurrentAttributes() { return currentAttributes; } protected void characters(String s) throws SAXException { char[] cha = s.toCharArray(); contentHandler.characters(cha, 0, cha.length); } protected boolean isDeeperThan(String elt1, String elt2) { if (elt1 == null) return true; else if (elt2 == null) return false; throw new IllegalArgumentException( "Don't know how to compare depth of '" + elt1 + "' and '" + elt2 + "'"); } protected boolean isEmpty() { return stack.isEmpty(); } public boolean isNamechar(char c) { return Character.isLetterOrDigit(c) || c == '_' || c == ':' || c == '-' || c == '.'; } public boolean isQuote(char c) { return c == '\'' || c == '"'; } public boolean isTagMark(char c) { return c == '%'; } public boolean isWhitespace(char c) { return Character.isWhitespace(c); } public boolean markup(char c) { return (c == '%') || c == '{' || c == '}' || c == '_' || c == '*' || c == '['; // && (isNamechar(next) || next == '%') } public void msg(String s) { if (verbose) System.err.print(s); } protected void newAttrName(String s) { setCurrentAttributeName(s); } protected void newAttrValue(String s) { if (currentAttributeName != null) { // FIXME: no namespaces!!! System.out.println("attribute: " + currentAttributeName + "='" + s + "'"); currentAttributes.addAttribute("", currentAttributeName, currentAttributeName, "CDATA", s); } } public boolean notMarkup(char c) { return !markup(c); } public void parse(InputSource input) throws IOException, SAXException { if (contentHandler == null) { return; } BufferedReader br = getBufferedReader(input); contentHandler.startDocument(); startElement(rootElement(), EMPTY_ATTRS); String line = br.readLine(); while (line != null) { System.out.println("Line:" + line); if (line.startsWith(".")) { // always (?) attributes processAttributes(line); } else { process(line); } line = br.readLine(); } endElementsOnStack(); contentHandler.endDocument(); } protected void pop() { stack.remove(stack.size() - 1); if (stack.isEmpty()) top = null; else top = (String) stack.get(stack.size() - 1); } protected abstract void process(String line) throws SAXException; protected void processAttributes(String line) throws SAXException { if (currentAttributes == null) { setCurrentAttributes(new AttributesImpl()); } CharReader r = new CharReader(line); r.next(); char c = r.next(); while (!r.eof()) { int anBegin = r.index() - 1; // System.out.println("processAttributes c="+c); // System.out.println("processAttributes anBegin="+anBegin); while (!r.eof() && c != '=' && !isWhitespace(c)) { c = r.next(); } // System.out.println("processAttributes r.index() - 1="+(r.index() - 1)); 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(); } newAttrValue(r.substring(avBegin, r.index() - 1)); c = r.next(); c = r.nextNonspace(c); } } 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(); } else { //r.pushback(); //System.out.println("not in text->step // back="+r.at()); } 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 { // 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 void push(String s) { stack.add(s); top = s; } protected abstract String rootElement() throws SAXException; /** * @param currentAttributeName The currentAttributeName to set. */ public void setCurrentAttributeName(String currentAttributeName) { this.currentAttributeName = currentAttributeName; } /** * @param currentAttributes The currentAttributes to set. */ public void setCurrentAttributes(AttributesImpl currentAttributes) { this.currentAttributes = currentAttributes; } /** * DOCUMENT ME! * * @param k DOCUMENT ME! * @param v DOCUMENT ME! */ public void setParameter(String k, Object v) { // FIXME } protected void startElement(String element) throws SAXException { startElement(element, null); } protected void startElement(String element, Attributes atts) throws SAXException { while (!isDeeperThan(top, element)) { // pop all on-stack elements that are deeper than the new one doEndElement(); pop(); } AttributesImpl currentAttributes = getCurrentAttributes(); if (currentAttributes == null) { if (atts == null) { atts = EMPTY_ATTRS; } } else { atts = appendAttributes(currentAttributes, atts); setCurrentAttributes(null); } doStartElement(element, atts); push(element); } public void text(String s) throws SAXException { // System.out.println("text="+s); characters(s); } protected void textElement(String element, String text) throws SAXException { textElement(element, text, null); } protected void textElement(String element, String text, Attributes atts) throws SAXException { startElement(element, atts); characters(text); endElement(); } protected String top() { return top; } }