/*******************************************************************************
* Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://robocode.sourceforge.net/license/epl-v10.html
*
* Contributors:
* Pavel Savara
* - Initial implementation
*******************************************************************************/
package net.sf.robocode.serialization;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Stack;
/**
* @author Pavel Savara (original)
*/
public class XmlReader {
SAXParser parser;
final InputStream input;
final Stack<Element> elements = new Stack<Element>();
final Stack<IXmlSerializable> items = new Stack<IXmlSerializable>();
final Stack<Dictionary<String, Element>> elementNames = new Stack<Dictionary<String, Element>>();
final Stack<Dictionary<String, Attribute>> attributeNames = new Stack<Dictionary<String, Attribute>>();
IXmlSerializable result;
public XmlReader(InputStream input) throws SAXException, ParserConfigurationException {
this.input = input;
SAXParserFactory factory = SAXParserFactory.newInstance();
parser = factory.newSAXParser();
}
private Object deserialize(IXmlSerializable prototype) throws IOException, SAXException {
elementNames.push(new Hashtable<String, Element>());
attributeNames.push(new Hashtable<String, Attribute>());
items.push(null);
elements.push(new ListElement() {
public IXmlSerializable read(XmlReader reader) {
return null;
}
public void add(IXmlSerializable child) {
result = child;
}
public void close() {}
});
prototype.readXml(this);
parser.parse(input, new Handler(this));
items.pop();
elements.pop();
elementNames.pop();
attributeNames.pop();
return result;
}
private class Handler extends DefaultHandler {
final XmlReader parent;
public Handler(XmlReader parent) {
this.parent = parent;
}
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
final Dictionary<String, Element> names = XmlReader.this.elementNames.peek();
final Element element = names == null ? null : names.get(qName);
if (element != null) {
elements.push(element);
XmlReader.this.elementNames.push(new Hashtable<String, Element>());
attributeNames.push(new Hashtable<String, Attribute>());
final IXmlSerializable item = element.read(parent);
item.readXml(parent);
for (int i = 0; i < attributes.getLength(); i++) {
Attribute attribute = attributeNames.peek().get(attributes.getQName(i));
if (attribute != null) {
attribute.read(attributes.getValue(i));
}
}
items.push(item);
} else {
items.push(null);
elements.push(null);
XmlReader.this.elementNames.push(null);
attributeNames.push(null);
}
}
public void endElement(String uri, String localName, String qName) throws SAXException {
elements.pop();
final IXmlSerializable item = items.peek();
final Element parentElement = elements.peek();
if (parentElement instanceof ListElement) {
ListElement le = (ListElement) parentElement;
le.add(item);
}
items.pop();
elementNames.pop();
attributeNames.pop();
final Dictionary<String, Element> names = XmlReader.this.elementNames.peek();
final Element element = names == null ? null : names.get(qName);
if (element != null) {
if (element instanceof ElementClose) {
ElementClose ec = (ElementClose) element;
ec.close();
}
}
}
}
public Element expect(String name, Element element) {
elementNames.peek().put(name, element);
return element;
}
public Attribute expect(String name, Attribute attribute) {
attributeNames.peek().put(name, attribute);
return attribute;
}
public interface Element {
IXmlSerializable read(XmlReader reader);
}
public interface ElementClose extends Element {
void close();
}
public interface ListElement extends ElementClose {
void add(IXmlSerializable child);
}
public interface Attribute {
void read(String value);
}
public static Object deserialize(InputStream input, IXmlSerializable prototype) throws IOException {
try {
XmlReader xr = new XmlReader(input);
return xr.deserialize(prototype);
} catch (SAXException e) {
throw new IOException();
} catch (ParserConfigurationException e) {
throw new IOException();
}
}
}