/*
* Copyright 2011, 2012 Odysseus Software GmbH
*
* Licensed 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.synapse.commons.staxon.core.xml;
import java.io.IOException;
import java.io.Reader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import org.apache.synapse.commons.staxon.core.base.AbstractXMLStreamReader;
import org.apache.synapse.commons.staxon.core.base.XMLStreamReaderScope;
/**
* Simple XML Stream Reader.
* <p/>
* The scope info is the element tag name.
*/
public class SimpleXMLStreamReader extends AbstractXMLStreamReader<String> {
private final Reader reader;
private int ch;
public SimpleXMLStreamReader(Reader reader) throws XMLStreamException {
super(null);
this.reader = reader;
try {
nextChar();
} catch (IOException e) {
throw new XMLStreamException(e);
}
initialize();
}
private void readAttrNsDecl(String name, String value) throws XMLStreamException {
int separator = name.indexOf(':');
if (separator < 0) {
if (XMLConstants.XMLNS_ATTRIBUTE.equals(name)) {
readNsDecl(XMLConstants.DEFAULT_NS_PREFIX, value);
} else {
readAttr(XMLConstants.DEFAULT_NS_PREFIX, name, null, value);
}
} else {
if (name.startsWith(XMLConstants.XMLNS_ATTRIBUTE) && separator == XMLConstants.XMLNS_ATTRIBUTE.length()) {
readNsDecl(name.substring(separator + 1), value);
} else {
readAttr(name.substring(0, separator), name.substring(separator + 1), null, value);
}
}
}
@Override
protected boolean consume() throws XMLStreamException, IOException {
XMLStreamReaderScope<String> scope = getScope();
skipWhitespace();
if (ch == -1) {
if (isStartDocumentRead()) {
readEndDocument();
}
return false;
}
if (ch == '<') {
nextChar();
if (ch == '/') { // END_ELEMENT
nextChar();
String tagName = readName('>');
if (scope.getInfo().equals(tagName)) {
readEndElementTag();
} else {
throw new XMLStreamException("not well-formed");
}
} else if (ch == '?') { // START_DOPCUMENT | PROCESSING_INSTRUCTION
nextChar();
String target = readName('?');
String data = null;
if (ch != '?') {
data = readText('?');
}
nextChar(); // please, let it be '>'
if ("xml".equals(target)) {
String version = null;
String encoding = null;
Boolean standalone = null;
if (data != null) {
Matcher matcher = Pattern.compile("(\\w+)\\s*=\\s*\"(\\S+)\"").matcher(data);
while (matcher.find()) {
String name = matcher.group(1);
String value = matcher.group(2);
if ("version".equals(name)) {
if (!"1.0".equals(value) && !"1.1".equals(value)) {
throw new XMLStreamException("Bad XML version: " + value);
}
version = value;
} else if ("encoding".equals(name)) {
encoding = value;
} else if ("standalone".equals(name)) {
if (!"yes".equals(value) && !"no".equals(value)) {
throw new XMLStreamException("Bad XML version: " + value);
}
standalone = value.equals("yes");
} else {
throw new XMLStreamException("Bad xml XML declaration attribute: " + name);
}
}
}
readStartDocument(version, encoding, standalone);
} else {
readPI(target, data);
}
} else if (ch == '!') { // COMMENT | CDATA
nextChar();
switch (ch) {
case '-': // COMMENT
String comment = readData('-');
if (!comment.startsWith("-")) {
throw new XMLStreamException("expected comment");
}
readData(comment.substring(1, comment.length() - 3), null, XMLStreamConstants.COMMENT);
break;
case '[': // CDATA
String cdata = readData(']');
if (!cdata.startsWith("CDATA[")) {
throw new XMLStreamException("expected cdata");
}
readData(cdata.substring(6, cdata.length() - 3), null, XMLStreamConstants.CDATA);
break;
}
} else { // START_ELEMENT
String tagName = readName(' ');
int colon = tagName.indexOf(':');
if (colon < 0) {
readStartElementTag(XMLConstants.DEFAULT_NS_PREFIX, tagName, null, tagName);
} else {
readStartElementTag(tagName.substring(0, colon), tagName.substring(colon + 1), null, tagName);
}
scope = getScope();
while (ch != '>' && ch != '/') {
String name = readName('=');
nextChar();
skipWhitespace();
int quote = ch;
nextChar();
String value = readText(quote);
nextChar();
skipWhitespace();
readAttrNsDecl(name, value);
}
if (ch == '/') {
nextChar(); // please, let it be '>'
readEndElementTag();
} else {
nextChar();
return consume();
}
}
nextChar();
} else {
String text = readText('<');
readData(text, null, XMLStreamConstants.CHARACTERS);
}
return true;
}
private void nextChar() throws IOException {
ch = reader.read();
}
private void skipWhitespace() throws IOException {
if (Character.isWhitespace(ch)) {
do {
nextChar();
} while (Character.isWhitespace(ch));
}
}
private String readData(final int end) throws IOException {
StringBuilder data = new StringBuilder();
int state = 0;
do {
nextChar();
data.append((char) ch);
if ((ch == end && state != 2) || (ch == '>' && state == 2)) {
state++;
} else {
state = 0;
}
} while (state < 3);
return data.toString();
}
private String readText(final int end) throws IOException {
final StringBuilder builder = new StringBuilder();
while (ch != end && ch >= 0) {
if (ch == '&') {
nextChar();
String entity = readName(';');
if ("lt".equals(entity)) {
builder.append('<');
} else if ("gt".equals(entity)) {
builder.append('>');
} else if ("amp".equals(entity)) {
builder.append('&');
} else if ("quot".equals(entity)) {
builder.append('"');
} else if ("apos".equals(entity)) {
builder.append('\'');
} else {
builder.append('?');
}
} else {
builder.append((char) ch);
}
nextChar();
}
return builder.toString();
}
private String readName(final int end) throws IOException {
skipWhitespace();
final StringBuilder builder = new StringBuilder();
do {
builder.append((char) ch);
nextChar();
} while (ch != end && ch != '>' && ch != '/' && !Character.isWhitespace(ch));
skipWhitespace();
return builder.toString();
}
}