You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ng...@apache.org on 2010/01/01 22:11:44 UTC

svn commit: r895076 - in /mina/sandbox/vysper/branches/nbxml-sax: ./ src/main/java/org/apache/vysper/xml/decoder/ src/main/java/org/apache/vysper/xml/sax/impl/ src/test/java/org/apache/vysper/xml/decoder/ src/test/java/org/apache/vysper/xml/sax/ src/te...

Author: ngn
Date: Fri Jan  1 21:11:43 2010
New Revision: 895076

URL: http://svn.apache.org/viewvc?rev=895076&view=rev
Log:
Parser now based on a tokenizer/state machine combo. Attribute tests now all succeed. Next up, namespace support

Added:
    mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/decoder/XMLToken.java
    mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/Parser.java
    mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/ParticleTokenizer.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseAttributesTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/
    mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/log4j.properties
Removed:
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/decoder/
Modified:
    mina/sandbox/vysper/branches/nbxml-sax/pom.xml
    mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/DefaultAsyncXMLReader.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/AbstractAsyncXMLReaderTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/DefaultAsyncXMLReaderTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/NameTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseElementsTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseTextTestCase.java
    mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/TestHandler.java

Modified: mina/sandbox/vysper/branches/nbxml-sax/pom.xml
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/pom.xml?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/pom.xml (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/pom.xml Fri Jan  1 21:11:43 2010
@@ -41,6 +41,18 @@
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <scope>test</scope>
+    </dependency>
   
   </dependencies>
 </project>

Added: mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/decoder/XMLToken.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/decoder/XMLToken.java?rev=895076&view=auto
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/decoder/XMLToken.java (added)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/decoder/XMLToken.java Fri Jan  1 21:11:43 2010
@@ -0,0 +1,52 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.vysper.xml.decoder;
+
+
+/**
+ * holds a particle of XML, either representing an start or end element, or an elements body, or other text nodes.
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class XMLToken {
+
+	public static enum Type {
+		START_NAME,
+		END_NAME,
+		ATTRIBUTE_NAME,
+		ATTRIBUTE_VALUE,
+		COMMENT,
+		TEXT
+	}
+	
+	private Type type;
+	private String value;
+	public XMLToken(Type type, String value) {
+		this.type = type;
+		this.value = value;
+	}
+
+	public Type getType() {
+		return type;
+	}
+	public String getValue() {
+		return value;
+	}
+}

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/DefaultAsyncXMLReader.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/DefaultAsyncXMLReader.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/DefaultAsyncXMLReader.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/DefaultAsyncXMLReader.java Fri Jan  1 21:11:43 2010
@@ -21,12 +21,8 @@
 
 import java.io.IOException;
 import java.nio.charset.CharsetDecoder;
-import java.util.Stack;
 
 import org.apache.mina.common.ByteBuffer;
-import org.apache.vysper.xml.decoder.DecodingException;
-import org.apache.vysper.xml.decoder.ParticleDecoder;
-import org.apache.vysper.xml.decoder.XMLParticle;
 import org.apache.vysper.xml.sax.AsyncXMLReader;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.DTDHandler;
@@ -35,7 +31,6 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXNotRecognizedException;
 import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.SAXParseException;
 import org.xml.sax.helpers.DefaultHandler;
 
 
@@ -46,12 +41,8 @@
 
 	private ErrorHandler errorHandler = new DefaultHandler();
 	private ContentHandler contentHandler = new DefaultHandler();
-	
-	private boolean documentStarted = false;
-	private boolean parserClosed = false;
-	
-	// element names as {uri}qname
-	private Stack<String> elements = new Stack<String>();
+
+	private Parser parser;
 	
 	/**
 	 * {@inheritDoc}
@@ -140,74 +131,15 @@
     	return errorHandler;
     }
 
-    private String toFQEN(XMLParticle particle) throws DecodingException {
-    	return "{}" + particle.getElementName();
-    }
-    
-    private void fatalError(String msg) throws SAXException {
-    	parserClosed = true;
-    	errorHandler.fatalError(new SAXParseException(msg, null));
-    }
-    
 	/**
 	 * {@inheritDoc}
 	 */
     public void parse (ByteBuffer buffer, CharsetDecoder decoder) throws IOException, SAXException {
-    	if(parserClosed) {
-    		throw new SAXException("Parser closed");
+    	if(parser == null) {
+    		parser = new Parser(contentHandler, errorHandler);
     	}
     	
-    	try {
-			XMLParticle particle = ParticleDecoder.decodeParticle(buffer, decoder);
-			while(particle != null) {
-				if(!documentStarted) {
-					// TODO handle exception
-					contentHandler.startDocument();
-					documentStarted = true;
-				}
-				
-				if(particle.isOpeningElement()) {
-					// TODO handle exception
-					contentHandler.startElement("", particle.getElementName(), particle.getElementName(), new DefaultAttributes());
-					elements.push(toFQEN(particle));
-				}
-
-				if(particle.isClosingElement()) {
-					String fqen = elements.pop();
-					if(!fqen.equals(toFQEN(particle))) {
-						fatalError("Incorrect closing element");
-						return;
-					}
-
-					// TODO handle exception
-					contentHandler.endElement("", particle.getElementName(), particle.getElementName());
-				}
-
-				if(particle.isText()) {
-					if(elements.size() == 0) {
-						fatalError("Illegal placement of text");
-						return;
-					}
-					
-					
-					char[] ch = particle.getContent().toCharArray();
-					// TODO handle exception
-					contentHandler.characters(ch, 0, ch.length);
-				}
-				
-				if(elements.size() == 0) {
-					parserClosed = true;
-					// TODO handle exception
-					contentHandler.endDocument();
-				}
-				
-				particle = ParticleDecoder.decodeParticle(buffer, decoder);
-			}
-		} catch (IOException e) {
-			throw e;
-		} catch(Exception e) {
-			fatalError(e.getMessage());
-		}
+		parser.parse(buffer, decoder);
     }
 
 

Added: mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/Parser.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/Parser.java?rev=895076&view=auto
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/Parser.java (added)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/Parser.java Fri Jan  1 21:11:43 2010
@@ -0,0 +1,250 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.vysper.xml.sax.impl;
+
+import java.nio.charset.CharsetDecoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.vysper.xml.sax.impl.ParticleTokenizer.TokenListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class Parser implements TokenListener {
+
+	private Logger LOG = LoggerFactory.getLogger(Parser.class);
+	
+    private static final String nameStartChar = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
+    private static final String nameChar = nameStartChar + "-\\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
+    public static final Pattern NAME_PATTERN = Pattern.compile("^[" + nameStartChar + "][" + nameChar + "]*$");
+    public static final Pattern NAME_PREFIX_PATTERN = Pattern.compile("^xml", Pattern.CASE_INSENSITIVE);
+    
+    public static final Pattern UNESCAPE_UNICODE_PATTERN = Pattern.compile("\\&\\#(x?)(.+);");
+	
+	private ContentHandler contentHandler;
+	private ErrorHandler errorHandler;
+
+	private static enum State {
+		START,
+		IN_TAG,
+		IN_END_TAG,
+		AFTER_START_NAME,
+		AFTER_END_NAME,
+		IN_EMPTY_TAG,
+		AFTER_ATTRIBUTE_NAME,
+		AFTER_ATTRIBUTE_EQUALS,
+		CLOSED
+	}
+	
+	private ParticleTokenizer tokenizer;
+	private State state = State.START;
+	private String qname;
+	private List<Attribute> attributes;
+	private String attributeName;
+
+	// element names as {uri}qname
+	private Stack<String> elements = new Stack<String>();
+
+	
+	public Parser(ContentHandler contentHandler, ErrorHandler errorHandler) {
+		this.contentHandler = contentHandler;
+		this.errorHandler = errorHandler;
+		
+		this.tokenizer = new ParticleTokenizer(this);
+	}
+	
+    public void parse(ByteBuffer byteBuffer, CharsetDecoder charsetDecoder) throws SAXException {
+    	if(state == State.CLOSED) throw new SAXException("Parser is closed");
+    	
+    	tokenizer.parse(byteBuffer, charsetDecoder);
+    }
+
+	public void token(String token) throws SAXException {
+		LOG.debug("Parser got token {} in state {}", token, state);
+		
+		if(state == State.START) {
+			if(token.equals("<")) {
+				state = State.IN_TAG;
+				attributes = new ArrayList<Attribute>();
+			} else {
+				characters(token);
+			}
+		} else if(state == State.IN_TAG) {
+			// token must be element name or / for a end tag
+			if(token.equals("/")) {
+				state = State.IN_END_TAG;
+			} else {
+				qname = token;
+				state = State.AFTER_START_NAME;
+			}
+		} else if(state == State.IN_END_TAG) {
+			// token must be element name
+			qname = token;
+			state = State.AFTER_END_NAME;
+		} else if(state == State.AFTER_START_NAME) {
+			// token must be attribute name or > or /
+			if(token.equals(">")) {
+				// end of start or end tag
+				if(state == State.AFTER_START_NAME) {
+					startElement();
+					state = State.START;
+					attributes = null;
+				} else if(state == State.AFTER_END_NAME) {
+					state = State.START;
+					endElement();
+				}
+			} else if(token.equals("/")) {
+				state = State.IN_EMPTY_TAG;
+			} else {
+				// must be attribute name
+				attributeName = token;
+				state = State.AFTER_ATTRIBUTE_NAME;
+			}
+		} else if(state == State.AFTER_ATTRIBUTE_NAME) {
+			// token must be =
+			if(token.equals("=")) {
+				state = State.AFTER_ATTRIBUTE_EQUALS;
+			}
+		} else if(state == State.AFTER_ATTRIBUTE_EQUALS) {
+			// token must be attribute value
+			attributes.add(new Attribute(attributeName, "", attributeName, unescape(token)));
+			state = State.AFTER_START_NAME;
+		} else if(state == State.AFTER_END_NAME) {
+			// token must be >
+			if(token.equals(">")) {
+				state = State.START;
+				endElement();
+			}
+		} else if(state == State.IN_EMPTY_TAG) {
+			// token must be >
+			if(token.equals(">")) {
+				startElement();
+				attributes = null;
+				
+				if(state != State.CLOSED) {
+					state = State.START;
+					endElement();
+				}
+			}
+		}
+	}
+	
+	private void characters(String s) throws SAXException {
+		// text only allowed in element
+		if(!elements.isEmpty()) {
+			String unescaped = unescape(s);
+			LOG.debug("Parser emitting characters \"{}\"", unescaped);
+			contentHandler.characters(unescaped.toCharArray(), 0, unescaped.length());
+		} else {
+			// must start document, even that document is not wellformed
+			contentHandler.startDocument();
+			fatalError("Text only allowed in element");
+		}
+	}
+	
+	private void startElement() throws SAXException {
+		LOG.debug("StartElement {}", qname);
+		
+		if(elements.isEmpty()) {
+			contentHandler.startDocument();
+		}
+		
+        if(!NAME_PATTERN.matcher(qname).find()) {
+        	fatalError("Invalid element name: " + qname);
+        	return;
+        }
+
+        // element names must not begin with "xml" in any casing
+        if(NAME_PREFIX_PATTERN.matcher(qname).find()) {
+        	fatalError("Names must not start with 'xml': " + qname);
+        	return;
+        }
+
+		String uri = "";
+		elements.add(fullyQualifiedName(uri, qname));
+		contentHandler.startElement(uri, qname, qname, new DefaultAttributes(attributes));
+	}
+
+	private String fullyQualifiedName(String uri, String qname) {
+		return "{" + uri + "}" + qname;
+	}
+	
+	private void endElement() throws SAXException {	
+		LOG.debug("EndElement {}", qname);
+
+		if(state == State.CLOSED) return;
+		
+		String uri = "";
+		String fqn = elements.pop();
+		if(fqn.equals(fullyQualifiedName(uri, qname))) {
+			contentHandler.endElement(uri, qname, qname);
+			
+			if(elements.isEmpty()) {
+				contentHandler.endDocument();
+				state = State.CLOSED;
+			}
+		} else {
+			fatalError("Invalid element name " + qname);
+		}
+	}
+	
+	private void fatalError(String message) throws SAXException {
+		LOG.debug("Fatal error: {}", message);
+		state = State.CLOSED;
+		tokenizer.close();
+		
+		errorHandler.fatalError(new SAXParseException(message, null));
+	}
+	
+    private String unescape(String s) {
+    	s = s.replace("&amp;", "&").replace("&gt;", ">").replace("&lt;", "<").replace("&apos;", "'").replace("&quot;", "\"");
+    
+    	StringBuffer sb = new StringBuffer();
+
+    	Matcher matcher = UNESCAPE_UNICODE_PATTERN.matcher(s);
+    	int end = 0;
+    	while(matcher.find()) {
+    		boolean isHex = matcher.group(1).equals("x");
+    		String unicodeCode = matcher.group(2);
+    		
+    		int base = isHex ? 16: 10;
+    		int i = Integer.valueOf(unicodeCode, base).intValue();
+    		char[] c = Character.toChars(i);
+    		sb.append(s.substring(end, matcher.start()));
+    		end = matcher.end();
+    		sb.append(c);
+    	}
+    	sb.append(s.substring(end, s.length()));
+    	
+    	return sb.toString();
+    }
+}

Added: mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/ParticleTokenizer.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/ParticleTokenizer.java?rev=895076&view=auto
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/ParticleTokenizer.java (added)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/main/java/org/apache/vysper/xml/sax/impl/ParticleTokenizer.java Fri Jan  1 21:11:43 2010
@@ -0,0 +1,166 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.vysper.xml.sax.impl;
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.mina.common.ByteBuffer;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class ParticleTokenizer {
+	
+	private enum State { 
+		START, 
+		IN_TAG,
+		IN_DOUBLE_ATTRIBUTE_VALUE,
+		IN_SINGLE_ATTRIBUTE_VALUE,
+		IN_TEXT,
+		CLOSED
+	}
+	
+	private int lastPosition = 0;
+	private State state = State.START;
+	
+	public static interface TokenListener {
+		void token(String token) throws SAXException;
+	}
+	
+	private TokenListener listener;
+	
+	public ParticleTokenizer(TokenListener listeners) {
+		this.listener = listeners;
+	}
+	
+    /**
+     * @param byteBuffer
+     * @param charsetDecoder
+     * @return the new particle or NULL, if the buffer was exhausted before the particle was completed
+     * @throws Exception
+     */
+    public void parse(ByteBuffer byteBuffer, CharsetDecoder decoder) throws SAXException {
+        lastPosition = byteBuffer.position();
+//        StringBuffer sb = new StringBuffer();
+        while (byteBuffer.hasRemaining() && state != State.CLOSED) {
+            char c = (char)byteBuffer.get();
+
+            if(state == State.START) {
+            	if(c == '<') {
+            		emit("<", byteBuffer);
+            		state = State.IN_TAG;
+            	} else {
+            		state = State.IN_TEXT;
+//            		sb.append(c);
+            	}
+            } else if(state == State.IN_TEXT) {
+            	if(c == '<') {
+            		emit(byteBuffer, decoder);
+            		emit("<", byteBuffer);
+            		state = State.IN_TAG;
+            	} else {
+//            		sb.append(c);
+            	}
+            } else if(state == State.IN_TAG) {
+            	if(c == '/') {
+            		if(checkEmit(byteBuffer)) {
+            			emit(byteBuffer, decoder);
+            		}
+            		emit("/", byteBuffer);
+            	} else if(c == '>') {
+            		if(checkEmit(byteBuffer)) {
+            			emit(byteBuffer, decoder);
+            		}
+                	emit(">", byteBuffer);
+                	state = State.START;
+            	} else if(Character.isWhitespace(c)) {
+            		if(checkEmit(byteBuffer)) {
+            			emit(byteBuffer, decoder);
+            		} else {
+            			// ignore whitespace
+            			lastPosition = byteBuffer.position();
+            		}
+            	} else if(c == '=') {
+            		emit(byteBuffer, decoder);
+            		emit("=", byteBuffer);
+            	} else if(c == '"') {
+//            		emit("\"", byteBuffer);
+            		state = State.IN_DOUBLE_ATTRIBUTE_VALUE;
+            	} else if(c == '\'') {
+//            		emit("\'", byteBuffer);
+            		state = State.IN_SINGLE_ATTRIBUTE_VALUE;
+            	} else {
+            		// non-whitespace char
+            	}
+            } else if(state == State.IN_DOUBLE_ATTRIBUTE_VALUE) {
+            	if(c == '"') {
+            		emit(byteBuffer, decoder);
+//            		emit("\"", byteBuffer);
+            		state = State.IN_TAG;
+            	}
+            } else if(state == State.IN_SINGLE_ATTRIBUTE_VALUE) {
+            	if(c == '\'') {
+            		emit(byteBuffer, decoder);
+//            		emit("'", byteBuffer);
+            		state = State.IN_TAG;
+            	}
+            } 
+        }
+
+        byteBuffer.position(lastPosition);
+    }
+    
+    public void close() {
+    	state = State.CLOSED;
+    }
+    
+    private boolean checkEmit(ByteBuffer buffer) {
+    	return buffer.position() > lastPosition + 1;
+    }
+    
+    private void emit(String token, ByteBuffer byteBuffer) throws SAXException {
+    	listener.token(token);
+    	
+    	lastPosition = byteBuffer.position();
+    }
+
+    private void emit(ByteBuffer byteBuffer, CharsetDecoder decoder) throws SAXException {
+    	int endPosition = byteBuffer.position();
+    	int oldLimit = byteBuffer.limit();
+    	byteBuffer.position(lastPosition);
+    	byteBuffer.limit(endPosition - 1);
+		
+    	try {
+			listener.token(byteBuffer.getString(decoder));
+		} catch (CharacterCodingException e) {
+			throw new SAXException(e);
+		}
+		byteBuffer.limit(oldLimit);
+		byteBuffer.position(endPosition);
+		lastPosition = byteBuffer.position();
+		
+		
+    }
+}

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/AbstractAsyncXMLReaderTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/AbstractAsyncXMLReaderTestCase.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/AbstractAsyncXMLReaderTestCase.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/AbstractAsyncXMLReaderTestCase.java Fri Jan  1 21:11:43 2010
@@ -20,6 +20,7 @@
 package org.apache.vysper.xml.sax;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import junit.framework.TestCase;
@@ -61,14 +62,25 @@
 	
 	protected void assertStartElement(String expectedUri, String expectedLocalName, String expectedQName, Attributes expectedAttributes, 
 			TestEvent actual) {
-		if(!(actual instanceof StartElementEvent)) fail("Event must be StartElementEvent");
+		if(!(actual instanceof StartElementEvent)) fail("Event must be StartElementEvent but was " + actual.getClass());
 		StartElementEvent startElementEvent = (StartElementEvent) actual;
 		assertEquals("URI", expectedUri, startElementEvent.getURI());
 		assertEquals("local name", expectedLocalName, startElementEvent.getLocalName());
 		assertEquals("qName", expectedQName, startElementEvent.getQName());
-		assertEquals("Attributes", expectedAttributes, startElementEvent.getAtts());
+		assertAttributes(expectedAttributes, startElementEvent.getAtts());
 	}
 
+	protected void assertAttributes(Attributes expectedAttrs, Attributes actualAttrs) {
+		assertEquals("Attribute count", expectedAttrs.getLength(), actualAttrs.getLength());
+		
+		for(int i = 0; i<expectedAttrs.getLength(); i++) {
+			assertEquals("Local name[" + i + "]", expectedAttrs.getLocalName(i), actualAttrs.getLocalName(i));
+			assertEquals("Qname[" + i + "]", expectedAttrs.getQName(i), actualAttrs.getQName(i));
+			assertEquals("URI[" + i + "]", expectedAttrs.getURI(i), actualAttrs.getURI(i));
+			assertEquals("Value[" + i + "]", expectedAttrs.getValue(i), actualAttrs.getValue(i));
+		}
+	}
+	
 	protected void assertEndElement(String expectedUri, String expectedLocalName, String expectedQName, 
 			TestEvent actual) {
 		if(!(actual instanceof EndElementEvent)) fail("Event must be EndElementEvent");
@@ -107,4 +119,9 @@
 		if(!(actual instanceof FatalErrorEvent)) fail("Event must be FatalErrorEvent but is "+ actual.getClass());
 	}
 
+	protected void assertNoMoreevents(Iterator events) {
+		if(events.hasNext()) {
+			fail("Must not be any more evens, but found one " + events.next().getClass());
+		}
+	}
 }
\ No newline at end of file

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/DefaultAsyncXMLReaderTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/DefaultAsyncXMLReaderTestCase.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/DefaultAsyncXMLReaderTestCase.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/DefaultAsyncXMLReaderTestCase.java Fri Jan  1 21:11:43 2010
@@ -22,6 +22,7 @@
 import org.apache.mina.common.ByteBuffer;
 import org.apache.vysper.charset.CharsetUtil;
 import org.apache.vysper.xml.sax.impl.DefaultAsyncXMLReader;
+import org.xml.sax.DTDHandler;
 import org.xml.sax.SAXException;
 
 
@@ -66,5 +67,30 @@
 			// OK
 		}
 	}
+	
+	public void testSetDtdHandlerNotSupported() {
+		try {
+			new DefaultAsyncXMLReader().setDTDHandler(new DTDHandler() {
+				public void unparsedEntityDecl(String name, String publicId,
+						String systemId, String notationName) throws SAXException {
+				}
+				public void notationDecl(String name, String publicId, String systemId)
+						throws SAXException {
+				}
+			});
+			fail("Not supported, must throw RuntimeException");
+		} catch(RuntimeException e) {
+			// OK
+		}
+	}
+
+	public void testGetDtdHandlerNotSupported() {
+		try {
+			new DefaultAsyncXMLReader().getDTDHandler();
+			fail("Not supported, must throw RuntimeException");
+		} catch(RuntimeException e) {
+			// OK
+		}
+	}
 
 }
\ No newline at end of file

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/NameTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/NameTestCase.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/NameTestCase.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/NameTestCase.java Fri Jan  1 21:11:43 2010
@@ -21,7 +21,7 @@
 
 import junit.framework.TestCase;
 
-import org.apache.vysper.xml.decoder.XMLParticle;
+import org.apache.vysper.xml.sax.impl.Parser;
 
 
 
@@ -31,17 +31,17 @@
 public class NameTestCase extends TestCase {
 
 	public void testValidName() {
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("_abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher(":abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("Aabc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("\u00C8abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("\u00C8abc").find());
-		assertFalse(XMLParticle.NAME_PATTERN.matcher("3abc").find());
-		assertFalse(XMLParticle.NAME_PATTERN.matcher("\u2001abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("a3bc").find());
-		assertFalse(XMLParticle.NAME_PATTERN.matcher("-abc").find());
-		assertTrue(XMLParticle.NAME_PATTERN.matcher("ab-c").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("_abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher(":abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("Aabc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("\u00C8abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("\u00C8abc").find());
+		assertFalse(Parser.NAME_PATTERN.matcher("3abc").find());
+		assertFalse(Parser.NAME_PATTERN.matcher("\u2001abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("a3bc").find());
+		assertFalse(Parser.NAME_PATTERN.matcher("-abc").find());
+		assertTrue(Parser.NAME_PATTERN.matcher("ab-c").find());
 		
 	}
 

Added: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseAttributesTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseAttributesTestCase.java?rev=895076&view=auto
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseAttributesTestCase.java (added)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseAttributesTestCase.java Fri Jan  1 21:11:43 2010
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.vysper.xml.sax;
+
+import java.util.Iterator;
+
+import org.apache.vysper.xml.sax.TestHandler.TestEvent;
+import org.apache.vysper.xml.sax.impl.Attribute;
+
+
+
+/**
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class ParseAttributesTestCase extends AbstractAsyncXMLReaderTestCase {
+
+	public void testSimpleAttribute() throws Exception {
+		Iterator<TestEvent> events = parse("<root att='foo' />").iterator();
+
+		assertStartDocument(events.next());
+		assertStartElement("", "root", "root", attributes(new Attribute("att", "", "att", "foo")), events.next());
+		assertEndElement("", "root", "root", events.next());
+		assertEndDocument(events.next());
+		
+		assertFalse(events.hasNext());
+	}
+
+	public void testMultipleAttribute() throws Exception {
+		Iterator<TestEvent> events = parse("<root att='foo' att2='bar' />").iterator();
+
+		assertStartDocument(events.next());
+		assertStartElement("", "root", "root", 
+				attributes(new Attribute("att", "", "att", "foo"), new Attribute("att2", "", "att2", "bar")), events.next());
+		assertEndElement("", "root", "root", events.next());
+		assertEndDocument(events.next());
+		
+		assertFalse(events.hasNext());
+	}
+
+	public void testAttributeWithDoubleQuote() throws Exception {
+		Iterator<TestEvent> events = parse("<root att='f\"oo' />").iterator();
+
+		assertStartDocument(events.next());
+		assertStartElement("", "root", "root", 
+				attributes(new Attribute("att", "", "att", "f\"oo")), events.next());
+		assertEndElement("", "root", "root", events.next());
+		assertEndDocument(events.next());
+		
+		assertFalse(events.hasNext());
+	}
+
+	public void testAttributeWithSingleQuote() throws Exception {
+		Iterator<TestEvent> events = parse("<root att=\"f'oo\" />").iterator();
+
+		assertStartDocument(events.next());
+		assertStartElement("", "root", "root", 
+				attributes(new Attribute("att", "", "att", "f'oo")), events.next());
+		assertEndElement("", "root", "root", events.next());
+		assertEndDocument(events.next());
+		
+		assertFalse(events.hasNext());
+	}
+
+	public void testAttributeWithEscapedAmp() throws Exception {
+		Iterator<TestEvent> events = parse("<root att='f&amp;oo' />").iterator();
+
+		assertStartDocument(events.next());
+		assertStartElement("", "root", "root", 
+				attributes(new Attribute("att", "", "att", "f&oo")), events.next());
+		assertEndElement("", "root", "root", events.next());
+		assertEndDocument(events.next());
+		
+		assertFalse(events.hasNext());
+	}
+}
\ No newline at end of file

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseElementsTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseElementsTestCase.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseElementsTestCase.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseElementsTestCase.java Fri Jan  1 21:11:43 2010
@@ -37,7 +37,7 @@
 		assertEndElement("", "root", "root", events.next());
 		assertEndDocument(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testElement() throws Exception {
@@ -48,7 +48,7 @@
 		assertEndElement("", "root", "root", events.next());
 		assertEndDocument(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testElements() throws Exception {
@@ -64,7 +64,7 @@
 		assertEndElement("", "root", "root", events.next());
 		assertEndDocument(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testIllegalClosingElement() throws Exception {
@@ -76,7 +76,7 @@
 		assertEndElement("", "child", "child", events.next());
 		assertFatalError(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testNumberAsFirstCharInName() throws Exception {
@@ -85,7 +85,7 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testDashAsFirstCharInName() throws Exception {
@@ -94,7 +94,7 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testNumberInName() throws Exception {
@@ -105,7 +105,7 @@
 		assertEndElement("", "r1oot", "r1oot", events.next());
 		assertEndDocument(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testInvalidUnicodeInName() throws Exception {
@@ -114,7 +114,7 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testValidUnicodeInName() throws Exception {
@@ -126,7 +126,7 @@
 		assertEndDocument(events.next());
 
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testXmlBeginName() throws Exception {
@@ -135,7 +135,7 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	public void testXmlInsideName() throws Exception {
@@ -146,7 +146,7 @@
 		assertEndElement("", "roxmlot", "roxmlot", events.next());
 		assertEndDocument(events.next());
 
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 	
@@ -156,6 +156,6 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 }
\ No newline at end of file

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseTextTestCase.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseTextTestCase.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseTextTestCase.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/ParseTextTestCase.java Fri Jan  1 21:11:43 2010
@@ -97,7 +97,7 @@
 		assertStartDocument(events.next());
 		assertFatalError(events.next());
 		
-		assertFalse(events.hasNext());
+		assertNoMoreevents(events);
 	}
 
 

Modified: mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/TestHandler.java
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/TestHandler.java?rev=895076&r1=895075&r2=895076&view=diff
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/TestHandler.java (original)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/java/org/apache/vysper/xml/sax/TestHandler.java Fri Jan  1 21:11:43 2010
@@ -162,16 +162,19 @@
 	
 	public void characters(char[] ch, int start, int length)
 			throws SAXException {
+		System.out.println("sax characters: " + new String(ch));
 		events.add(new CharacterEvent(ch, start, length));
 		
 	}
 
 	public void endDocument() throws SAXException {
+		System.out.println("sax end document");
 		events.add(new EndDocumentEvent());
 	}
 
 	public void endElement(String uri, String localName, String qName)
 			throws SAXException {
+		System.out.println("sax end element: "+ qName);
 		events.add(new EndElementEvent(uri, localName, qName));
 		
 	}
@@ -204,6 +207,7 @@
 	}
 
 	public void startDocument() throws SAXException {
+		System.out.println("sax start document");
 		events.add(new StartDocumentEvent());		
 	}
 

Added: mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/log4j.properties?rev=895076&view=auto
==============================================================================
--- mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/log4j.properties (added)
+++ mina/sandbox/vysper/branches/nbxml-sax/src/test/resources/log4j.properties Fri Jan  1 21:11:43 2010
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+log4j.rootLogger=DEBUG, C
+
+log4j.logger.org.apache.vysper.mina.XmppIoHandlerAdapter=WARN,C
+log4j.logger.org.apache.mina.filter.executor.ExecutorFilter=WARN,C
+
+log4j.appender.C=org.apache.log4j.ConsoleAppender 
+log4j.appender.C.layout=org.apache.log4j.PatternLayout 
+log4j.appender.C.layout.ConversionPattern=%m%n
+