From aa03065dc96ac967d662362b9e506c5bb741386a Mon Sep 17 00:00:00 2001 From: Kevin Smith 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 @@ - + + - + + @@ -93,9 +95,9 @@ sourcepath="${src}" destdir="${doc}" windowtitle="Stroke"> - + 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 events_ = new ArrayBlockingQueue(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