/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
package org.ebayopensource.turmeric.runtime.binding.impl.parser.nv.ordered;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Map;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import org.ebayopensource.turmeric.runtime.binding.BindingConstants;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.BaseXMLStreamReader;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.NamespaceConvention;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.nv.NVLine;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.nv.NVPathPart;
import org.ebayopensource.turmeric.runtime.binding.impl.parser.nv.NVStreamParser;
/**
* @author ichernyshev
*/
public class OrderedNVStreamReader extends BaseXMLStreamReader {
private final InputStream m_is;
private final NVStreamParser m_parser;
private final NVPathPart m_rootXmlName;
private NVLineSwinger m_swinger;
private ArrayList<AttributeHolder> m_attributes = new ArrayList<AttributeHolder>();
public OrderedNVStreamReader(InputStream is, NamespaceConvention convention,
Charset charset, QName rootXmlName, Map<String, String> option)
{
super(convention, option);
if (is == null || charset == null || rootXmlName == null) {
throw new NullPointerException();
}
m_is = is;
m_parser = new NVStreamParser(is, charset, convention, m_doubleQuoteDelimited);
m_rootXmlName = new NVPathPart(rootXmlName, 0, false);
m_event = START_DOCUMENT;
}
/*
* (non-Javadoc)
*
* @see javax.xml.stream.XMLStreamReader#close()
*/
public void close() throws XMLStreamException {
try {
m_is.close();
} catch (IOException e) {
throw new XMLStreamException(e);
}
}
/*
* (non-Javadoc)
*
* @see javax.xml.stream.XMLStreamReader#getElementText()
*/
public String getElementText() throws XMLStreamException {
if (m_swinger == null) {
throw new IllegalStateException("No NV line currently selected");
}
return m_swinger.getCurrentValue();
}
/*
* (non-Javadoc)
*
* @see javax.xml.stream.XMLStreamReader#getNamespaceContext()
*/
public NamespaceContext getNamespaceContext() {
return m_convention;
}
/*
* (non-Javadoc)
*
* @see javax.xml.stream.XMLStreamReader#getText()
*/
public String getText() {
if (m_swinger == null) {
throw new IllegalStateException("No NV line currently selected");
}
return m_swinger.getCurrentValue();
}
@Override
public int getAttributeCount() {
return m_attributes.size();
}
@Override
public QName getAttributeName(int n) {
AttributeHolder info = m_attributes.get(n);
return info.m_name;
}
@Override
public String getAttributeLocalName(int n) {
AttributeHolder info = m_attributes.get(n);
return info.m_name.getLocalPart();
}
@Override
public String getAttributeNamespace(int n) {
AttributeHolder info = m_attributes.get(n);
return info.m_name.getNamespaceURI();
}
@Override
public String getAttributePrefix(int n) {
AttributeHolder info = m_attributes.get(n);
return info.m_name.getPrefix();
}
@Override
public String getAttributeValue(int n) {
AttributeHolder info = m_attributes.get(n);
return info.m_value;
}
@Override
protected QName internalGetName() {
if (m_swinger == null) {
throw new IllegalStateException("No NV line currently selected");
}
return m_swinger.getQName();
}
/*
* (non-Javadoc)
*
* @see javax.xml.stream.XMLStreamReader#next()
*/
public int next() throws XMLStreamException {
m_attributes.clear();
if (m_event == START_DOCUMENT) {
NVLine firstLine = readLine(null, null);
if (firstLine == null) {
// empty document
m_event = END_DOCUMENT;
return m_event;
}
m_swinger = new NVLineSwinger(firstLine, NVLine.createEmpty());
QName name = getNextName();
if (name == null) {
// TODO: must an attribute only, this is probavly an error condition
m_event = END_DOCUMENT;
return m_event;
}
m_event = START_ELEMENT;
return m_event;
}
if (m_event == START_ELEMENT) {
QName name = getNextName();
if (name == null) {
// reached the value
m_event = CHARACTERS;
return m_event;
}
return m_event;
}
if (m_event == CHARACTERS) {
// we are done with one swing, need to get ready for the next one
NVLine previousLine = m_swinger.getCurrentLine();
NVLine currentLine = readLine(previousLine, m_swinger.getPreviousLine());
if (currentLine == null) {
// end of file, use empty line
currentLine = NVLine.createEmpty();
}
m_swinger = new NVLineSwinger(currentLine, previousLine);
QName name = getNextName();
if (name == null) {
// TODO: must an attribute only, this is probavly an error condition
m_event = END_DOCUMENT;
return m_event;
}
m_event = m_swinger.passedValley() ? START_ELEMENT : END_ELEMENT;
return m_event;
}
if (m_event == END_ELEMENT) {
QName name = getNextName();
if (name == null) {
// the next line does not exist...
m_event = END_DOCUMENT;
return m_event;
}
m_event = m_swinger.passedValley() ? START_ELEMENT : END_ELEMENT;
return m_event;
}
throw new XMLStreamException("Not able to find the next event, " +
"unexpected event type " + m_event);
}
private QName getNextName() throws XMLStreamException {
if (m_swinger == null) {
return null;
}
QName result = m_swinger.getNextName();
if (m_swinger.shouldAddAsAttribute()) {
// found attributes, read them and switch to real element after them
createAttributeList();
if (m_swinger == null) {
return null;
}
result = m_swinger.getQName();
} else if (m_swinger.isAttributeLevelElement()) {
// add nil attribute if the value is NULL
String currentValue = m_swinger.getCurrentValue();
if (BindingConstants.NULL_VALUE_STR.equals(currentValue)) {
m_attributes.add(new AttributeHolder(
BindingConstants.NILLABLE_ATTRIBUTE_QNAME, "true"));
}
}
return result;
}
/**
* Reads all the attributes NVLines from the input until it hits
* the next NV Line that is not an attribute. returns the QName.
*/
private void createAttributeList() throws XMLStreamException {
NVLine oldLine = m_swinger.getCurrentLine();
NVLine newLine = oldLine;
NVLine prevLine = newLine;
boolean hasNilAttribute = false;
do {
AttributeHolder attr = new AttributeHolder(newLine.peek(), newLine.getValue());
if (!hasNilAttribute) {
hasNilAttribute = BindingConstants.NILLABLE_ATTRIBUTE_QNAME.equals(attr.m_name) && "true".equalsIgnoreCase(attr.m_value);
}
m_attributes.add(attr);
prevLine = newLine;
newLine = readLine(newLine, m_swinger.getPreviousLine());
} while (newLine != null && newLine.isAttributeAtSameLevel());
if (newLine != null) {
m_swinger = new NVLineSwinger(newLine, oldLine);
m_swinger.swingToLevel(oldLine.getDepth() - 2);
} else {
int elementPathDepth = prevLine.getDepth() - 1;
/*
* In the case of nil = true, create an element with NULL value. otherwise create an empty line.
*/
if (hasNilAttribute){
NVPathPart[] elementPathParts = new NVPathPart[elementPathDepth];
for (int i=0; i< elementPathDepth; i++) {
elementPathParts[i] = prevLine.getPathPart(i);
}
NVLine elementLine = new NVLine(elementPathParts, elementPathDepth, BindingConstants.NULL_VALUE_STR, elementPathDepth, false);
m_swinger = new NVLineSwinger(elementLine, prevLine);
} else {
m_swinger = new NVLineSwinger(NVLine.createEmpty(), prevLine);
}
m_swinger.swingToLevel(elementPathDepth - 1);
}
}
private NVLine readLine(NVLine prevLine, NVLine reuseLine) throws XMLStreamException {
boolean hasLine = m_parser.parseLine();
if (!hasLine) {
// end of stream
return null;
}
return NVLine.createNext(m_parser, prevLine, reuseLine, m_rootXmlName);
}
private static class AttributeHolder {
final QName m_name;
final String m_value;
AttributeHolder(QName name, String value) {
m_name = name;
m_value = value;
}
}
}