From aa03065dc96ac967d662362b9e506c5bb741386a Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Wed, 9 May 2012 11:39:14 +0100
Subject: Use the Aalto XML parser instead of XPP

Also adds a 'make test' target for the Makefile. Set the JUNIT environment variable to point to your jar if it doesn't find it.

diff --git a/Makefile b/Makefile
index 2880ade..30c97a4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,9 @@
 all: dist/lib/stroke.jar
 
+DEFINES = -Dxpp-dir=third-party/xpp -Djzlib-dir=third-party/jzlib -Dicu4j-dir=third-party/ -Dstax2-dir=third-party/stax2/ -Daalto-dir=third-party/aalto/
+
+JUNIT ?= /usr/share/junit/junit.jar
+
 .PHONY : clean
 clean:
 	ant clean
@@ -10,12 +14,20 @@ distclean: clean
 	rm -rf third-party
 
 .PHONY : dist/lib/stroke.jar
-dist/lib/stroke.jar: third-party/xpp/xpp.jar third-party/jzlib/jzlib.jar third-party/icu4j.jar
-	ant -Dxpp-dir=third-party/xpp -Djzlib-dir=third-party/jzlib -Dicu4j-dir=third-party/
+dist/lib/stroke.jar: third-party/jzlib/jzlib.jar third-party/icu4j.jar third-party/aalto/aalto-xml.jar third-party/stax2/stax2-api.jar
+	ant ${DEFINES}
+
+.PHONY : test
+test: dist/lib/stroke.jar
+	ant ${DEFINES} -DJUNIT_JAR=${JUNIT} test
+
+third-party/aalto/aalto-xml.jar:
+	mkdir -p third-party/aalto
+	curl http://repo2.maven.org/maven2/com/fasterxml/aalto-xml/0.9.8/aalto-xml-0.9.8.jar -o third-party/aalto/aalto-xml.jar
 
-third-party/xpp/xpp.jar:
-	mkdir -p third-party/xpp
-	curl http://www.extreme.indiana.edu/dist/java-repository/xpp3/jars/xpp3-1.1.4c.jar -o third-party/xpp/xpp.jar
+third-party/stax2/stax2-api.jar:
+	mkdir -p third-party/stax2
+	curl http://repository.codehaus.org/org/codehaus/woodstox/stax2-api/3.0.3/stax2-api-3.0.3.jar -o third-party/stax2/stax2-api.jar
 
 third-party/jzlib/jzlib.jar:
 	mkdir -p third-party
diff --git a/build.xml b/build.xml
index 6aaf392..454325d 100644
--- a/build.xml
+++ b/build.xml
@@ -16,12 +16,14 @@
   <property name="main-class" value="com.isode.stroke.examples.gui.StrokeGUI"/>
   <property name="compile.debug" value="true"/>
   <property name="testsuiteclass" value="com.isode.stroke.unittest.StrokeTestSuite" />
-  <property name="xpp-dir" value="../third-party/xpp"/>
+  <property name="aalto-dir" value="../third-party/aalto"/>
+  <property name="stax2-dir" value="../third-party/stax2"/>
   <property name="jzlib-dir" value="../third-party/jzlib"/>
   <property name="icu4j-dir" value="../third-party/icu4j"/>
 
   <path id="classpath">
-    <fileset dir="${xpp-dir}" includes="xpp.jar"/>
+    <fileset dir="${aalto-dir}" includes="aalto-xml.jar"/>
+    <fileset dir="${stax2-dir}" includes="stax2-api.jar"/>
     <fileset dir="${jzlib-dir}" includes="jzlib.jar"/>
     <fileset dir="${icu4j-dir}" includes="icu4j.jar"/>
   </path>
@@ -93,9 +95,9 @@
      sourcepath="${src}"
      destdir="${doc}"
      windowtitle="Stroke">
-     <classpath> 
+     <!--<classpath>
        <fileset dir="${xpp-dir}" includes="xpp.jar"/>
-     </classpath>
+     </classpath>-->
       <link href="http://docs.oracle.com/javase/6/docs/api/"/>
    </javadoc>
   </target>
diff --git a/src/com/isode/stroke/parser/AaltoXMLParser.java b/src/com/isode/stroke/parser/AaltoXMLParser.java
new file mode 100644
index 0000000..041dc1e
--- /dev/null
+++ b/src/com/isode/stroke/parser/AaltoXMLParser.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010-2012, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser;
+
+import com.fasterxml.aalto.AsyncInputFeeder;
+import com.fasterxml.aalto.AsyncXMLStreamReader;
+import com.fasterxml.aalto.stax.InputFactoryImpl;
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.eventloop.EventLoop;
+import java.util.logging.Logger;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * Parser based around the Aalto XML parser
+ */
+class AaltoXMLParser extends XMLParser {
+
+    private final Logger logger_ = Logger.getLogger(this.getClass().getName());
+    private boolean error_ = false;
+    private final EventLoop eventLoop_;
+    private final AsyncXMLStreamReader xmlReader_ = new InputFactoryImpl().createAsyncXMLStreamReader();
+
+    public AaltoXMLParser(XMLParserClient client, EventLoop eventLoop) {
+        super(client);
+        eventLoop_ = eventLoop;
+    }
+
+
+    /**
+     * Cause the parser thread to parse these data.
+     */
+    @Override
+    public boolean parse(String data) {
+        if (data.isEmpty()) {
+            return false;
+        }
+        final AsyncInputFeeder inputFeeder = xmlReader_.getInputFeeder();
+        final byte[] xmlBytes = new ByteArray(data).getData();
+        int type = 0;
+        boolean error = false;
+
+        try {
+            inputFeeder.feedInput(xmlBytes, 0, xmlBytes.length);
+        } catch (XMLStreamException ex) {
+            error = true;
+            throw new IllegalStateException(ex);
+        }
+
+        try {
+            while ((type = xmlReader_.next()) != XMLStreamConstants.END_DOCUMENT && type != AsyncXMLStreamReader.EVENT_INCOMPLETE) {
+                QName name;
+                switch (type) {
+                    case XMLStreamConstants.START_ELEMENT:
+                        name = xmlReader_.getName();
+                        AttributeMap attributes = new AttributeMap();
+                        for (int i = 0; i < xmlReader_.getAttributeCount(); i++) {
+                            QName attributeName = xmlReader_.getAttributeName(i);
+                            attributes.addAttribute(attributeName.getLocalPart(), attributeName.getNamespaceURI(), xmlReader_.getAttributeValue(i));
+                        }
+                        getClient().handleStartElement(name.getLocalPart(), name.getNamespaceURI(), attributes);
+                        break;
+                    case XMLStreamConstants.END_ELEMENT:
+                        name = xmlReader_.getName();
+                        getClient().handleEndElement(name.getLocalPart(), name.getNamespaceURI());
+                        break;
+                    case XMLStreamConstants.CHARACTERS:
+                        getClient().handleCharacterData(xmlReader_.getText());
+                        break;
+                }
+
+            }
+        } catch (XMLStreamException e) {
+            error = true;
+            throw new IllegalStateException(e);
+        }
+        if (type == XMLStreamConstants.END_DOCUMENT) {
+            try {
+                xmlReader_.close();
+            } catch (XMLStreamException ex) {
+                /* If the parser errors while we're shutting down, it's not much of an error.*/
+            }
+        }
+        return !error;
+    }
+
+    
+}
diff --git a/src/com/isode/stroke/parser/PlatformXMLParserFactory.java b/src/com/isode/stroke/parser/PlatformXMLParserFactory.java
index c1e44d6..000eb7f 100644
--- a/src/com/isode/stroke/parser/PlatformXMLParserFactory.java
+++ b/src/com/isode/stroke/parser/PlatformXMLParserFactory.java
@@ -16,6 +16,6 @@ public class PlatformXMLParserFactory {
      * Unlike Swiften, this may be threaded, and therefore needs an eventloop.
      */
     public static XMLParser createXMLParser(XMLParserClient client, EventLoop eventLoop) {
-        return new PullXMLParser(client, eventLoop);
+        return new AaltoXMLParser(client, eventLoop);
     }
 }
diff --git a/src/com/isode/stroke/parser/PullXMLParser.java b/src/com/isode/stroke/parser/PullXMLParser.java
deleted file mode 100644
index 845dfbc..0000000
--- a/src/com/isode/stroke/parser/PullXMLParser.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (c) 2010, Isode Limited, London, England.
- * All rights reserved.
- */
-package com.isode.stroke.parser;
-
-import com.isode.stroke.base.ByteArray;
-import com.isode.stroke.eventloop.Event.Callback;
-import com.isode.stroke.eventloop.EventLoop;
-import java.io.IOException;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import org.xmlpull.mxp1.MXParser;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-/**
- * Parser based around the XmlPullParser
- */
-class PullXMLParser extends XMLParser {
-
-    private final Logger logger_ = Logger.getLogger(this.getClass().getName());
-    private final XmlPullParser parser_ = new MXParser();
-    private final PipedInputStream reader_;
-    private final PipedOutputStream writer_;
-    private final ArrayBlockingQueue<Event> events_ = new ArrayBlockingQueue<Event>(20);
-    private final Thread parserThread_;
-    private boolean error_ = false;
-    private final EventLoop eventLoop_;
-
-
-    private enum EventType {Start, End, Text};
-
-    /**
-     * XML Event struct.
-     */
-    private class Event {
-        private final EventType type;
-        private final String name;
-        private final String namespace;
-        private final AttributeMap attributes;
-        public Event(EventType type, String name, String namespace, AttributeMap attributes) {
-            this.type = type;
-            this.name = name;
-            this.namespace = namespace;
-            this.attributes = attributes;
-        }
-
-        public Event(String name) {
-            this(EventType.Text, name, null, null);
-        }
-
-
-        public Event(String name, String namespace) {
-            this(EventType.End, name, namespace, null);
-        }
-
-        public Event(String name, String namespace, AttributeMap attributes) {
-            this(EventType.Start, name, namespace, attributes);
-        }
-
-    }
-
-    /**
-     * Put an XML event onto the queue ready for the main thread to pick up later.
-     */
-    private void addEvent(Event event) throws InterruptedException {
-        events_.put(event);
-    }
-
-    /**
-     * Deal with whatever was just read out of the parser_.
-     */
-    private void handleEvent(int eventType) throws XmlPullParserException, InterruptedException {
-        if (eventType == XmlPullParser.START_TAG) {
-            AttributeMap map = new AttributeMap();
-            for (int i = 0; i < parser_.getAttributeCount(); i++) {
-                map.addAttribute(parser_.getAttributeName(i), parser_.getAttributeNamespace(i), parser_.getAttributeValue(i));
-            }
-            addEvent(new Event(parser_.getName(), parser_.getNamespace(), map));
-            bump();
-        } else if (eventType == XmlPullParser.END_TAG) {
-            addEvent(new Event(parser_.getName(), parser_.getNamespace()));
-            bump();
-        } else if (eventType == XmlPullParser.TEXT) {
-            StringBuilder text = new StringBuilder();
-            int holderForStartAndLength[] = new int[2];
-            char ch[] = parser_.getTextCharacters(holderForStartAndLength);
-            int start = holderForStartAndLength[0];
-            int length = holderForStartAndLength[1];
-            for (int i = start; i < start + length; i++) {
-                text.append(ch[i]);
-            }
-            addEvent(new Event(text.toString()));
-            bump();
-        } else if (eventType == XmlPullParser.START_DOCUMENT) {
-            //System.out.println("Starting document");
-        } else if (eventType == XmlPullParser.END_DOCUMENT) {
-            //System.out.println("Ending document");
-
-        } else {
-            //System.out.println("Unhandled event");
-        }
-    }
-
-    /**
-     * Cause the main thread to process any outstanding events.
-     */
-    private void bump() {
-        eventLoop_.postEvent(new Callback() {
-            public void run() {
-                processEvents();
-            }
-        });
-    }
-
-    public PullXMLParser(XMLParserClient client, EventLoop eventLoop) {
-        super(client);
-        eventLoop_ = eventLoop;
-        writer_ = new PipedOutputStream();
-        try {
-            reader_ = new PipedInputStream(writer_, 128000);
-        } catch (IOException ex) {
-            Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-            throw new IllegalStateException(ex);
-        }
-        try {
-            parser_.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-            parser_.setInput(reader_, "UTF-8");
-        } catch (XmlPullParserException ex) {
-            Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-            throw new IllegalStateException(ex);
-        }
-        Runnable parserRunnable = new Runnable() {
-            public void run() {
-                int eventType = XmlPullParser.END_DOCUMENT - 1; /* Anything to make the following not true*/
-                while (eventType != XmlPullParser.END_DOCUMENT) {
-                    try {
-                        parser_.next();
-                        eventType = parser_.getEventType();
-                        handleEvent(eventType);
-                    } catch (XmlPullParserException ex) {
-                        error_ = true;
-                        Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-                        break;
-                    } catch (IOException ex) {
-                        error_ = true;
-                        Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-                        break;
-                    } catch (InterruptedException ex) {
-                        /* The thread was interrupted while trying to process an event - presumably this is because we're shutting down.*/
-                        error_ = true;
-                        Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-                        break;
-                    }
-                }
-            }
-        };
-        parserThread_ = new Thread(parserRunnable);
-        parserThread_.setDaemon(true);
-        parserThread_.start();
-    }
-
-    /**
-     * Do not do this!
-     * This is only to allow the unit tests to join onto it.
-     * @return
-     */
-    Thread getParserThread() {
-        return parserThread_;
-    }
-
-    /**
-     * Process outstanding events.
-     * Call in the main thread only.
-     */
-    private void processEvents() {
-        while (events_.size() > 0) {
-            processEvent(events_.poll());
-        }
-    }
-
-    /**
-     * Main thread only.
-     */
-    private void processEvent(Event event) {
-        String name = event.name;
-        String namespace = event.namespace;
-        AttributeMap attributes = event.attributes;
-        switch (event.type) {
-            case Start: getClient().handleStartElement(name, namespace, attributes); break;
-            case End: getClient().handleEndElement(name, namespace); break;
-            case Text: getClient().handleCharacterData(name); break;
-        }
-    }
-
-    /**
-     * Cause the parser thread to parse these data later.
-     * Note that the return code is a best guess based on previous parsing,
-     * and will almost always give a false negative on a stanza before a
-     * true negative. True negatives will always mean an error in the stream.
-     */
-    @Override
-    public boolean parse(String data) {
-        try {
-            writer_.write(new ByteArray(data).getData());
-            writer_.flush();
-        } catch (IOException ex) {
-            error_ = true;
-            Logger.getLogger(PullXMLParser.class.getName()).log(Level.SEVERE, null, ex);
-        }
-        return !error_;
-    }
-
-    
-}
diff --git a/test/com/isode/stroke/parser/XMLParserTest.java b/test/com/isode/stroke/parser/XMLParserTest.java
index e3f054b..a1b5d1c 100644
--- a/test/com/isode/stroke/parser/XMLParserTest.java
+++ b/test/com/isode/stroke/parser/XMLParserTest.java
@@ -31,11 +31,7 @@ public class XMLParserTest {
     }
 
     private void join(XMLParser parser) {
-        try {
-            ((PullXMLParser) parser).getParserThread().join(300);
-        } catch (InterruptedException ex) {
-            Logger.getLogger(XMLParserTest.class.getName()).log(Level.SEVERE, null, ex);
-        }
+       
     }
 
     @Test
-- 
cgit v0.10.2-6-g49f6