/*******************************************************************************
* Copyright (c) 2008 CWI.
* 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://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* jurgen@vinju.org - initial API and implementation
*******************************************************************************/
package org.rascalmpl.value.io;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IExternalValue;
import org.rascalmpl.value.IInteger;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.INode;
import org.rascalmpl.value.IRational;
import org.rascalmpl.value.IReal;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.exceptions.UnsupportedTypeException;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeStore;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This IValueWriter serializes values to XML documents.
* It will not serialize all IValues, see <code>XMLReader</code> for limitations.
*/
public class XMLWriter implements IValueTextWriter {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
private DocumentBuilder fBuilder;
public void write(IValue value, java.io.Writer stream) throws IOException {
try {
fBuilder = dbf.newDocumentBuilder();
Document doc = fBuilder.newDocument();
Node top = yield(value, doc);
doc.appendChild(top);
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.transform(new DOMSource(doc), new StreamResult(stream));
} catch (ParserConfigurationException e) {
throw new IOException("XML configuration is invalid: " + e.getMessage());
} catch (TransformerException e) {
throw new IOException("Exception while serializing XML: " + e.getMessage());
}
}
public void write(IValue value, java.io.Writer stream, TypeStore typeStore) throws IOException {
write(value, stream);
}
private Node yield(IValue value, Document doc) {
Type type = value.getType();
if (type.isAbstractData()) {
Type node = ((IConstructor) value).getConstructorType();
if (isListWrapper(node)) {
return yieldList((INode) value, doc);
}
else if (isSetWrapper(node)) {
return yieldSet((INode) value, doc);
}
else if (isRelationWrapper(node)) {
return yieldRelation((INode) value, doc);
}
else if (isMapWrapper(node)) {
return yieldMap((INode) value, doc);
}
else {
return yieldTree((INode) value, doc);
}
}
else if (type.isString()) {
return yieldString((IString) value, doc);
}
else if (type.isInteger()) {
return yieldInt((IInteger) value, doc);
}
else if (type.isRational()) {
return yieldRational((IRational) value, doc);
}
else if (type.isReal()) {
return yieldDouble((IReal) value, doc);
}
else if (type.isExternalType()) {
return yieldExternal((IExternalValue) value, doc);
}
throw new UnsupportedTypeException(
"Outermost or nested tuples, lists, sets, relations or maps are not allowed.", type);
}
private boolean isListWrapper(Type nodeType) {
return nodeType.getArity() == 1
&& nodeType.getFieldTypes().getFieldType(0).isList();
}
private boolean isSetWrapper(Type nodeType) {
return nodeType.getArity() == 1
&& nodeType.getFieldTypes().getFieldType(0).isSet();
}
private boolean isRelationWrapper(Type nodeType) {
return nodeType.getArity() == 1
&& nodeType.getFieldTypes().getFieldType(0)
.isRelation();
}
private boolean isMapWrapper(Type nodeType) {
return nodeType.getArity() == 1
&& nodeType.getFieldTypes().getFieldType(0).isMap();
}
private Node yieldDouble(IReal value, Document doc) {
return doc.createTextNode(value.toString());
}
private Node yieldInt(IInteger value, Document doc) {
return doc.createTextNode(value.toString());
}
private Node yieldRational(IRational value, Document doc) {
/* Element element = doc.createElementNS("values", "rat");
element.setAttribute("num", value.numerator().toString());
element.setAttribute("denom", value.denominator().toString());
return element;
*/
return null;
}
private Node yieldString(IString value, Document doc) {
return doc.createTextNode(value.getValue());
}
private Node yieldExternal(IExternalValue value, Document doc) {
return doc.createTextNode(value.toString());
}
private Node yieldMap(INode node, Document doc) {
Element treeNode = doc.createElement(node.getName());
IMap map = (IMap) node.get(0);
for (IValue key : map) {
IValue value = map.get(key);
if (key.getType().isTuple()) {
appendTupleElements(doc, treeNode, key);
}
else {
treeNode.appendChild(yield(key, doc));
}
if (value.getType().isTuple()) {
appendTupleElements(doc, treeNode, value);
}
else {
treeNode.appendChild(yield(value, doc));
}
}
return treeNode;
}
private void appendTupleElements(Document doc, Element treeNode, IValue tupleValue) {
ITuple tuple = (ITuple) tupleValue;
for (IValue element : tuple) {
treeNode.appendChild(yield(element, doc));
}
}
private Node yieldRelation(INode node, Document doc) {
Element treeNode = doc.createElement(node.getName());
ISet relation = (ISet) node.get(0);
assert (relation.getType().isRelation());
for (IValue tuple : relation) {
appendTupleElements(doc, treeNode, tuple);
}
return treeNode;
}
private Node yieldSet(INode node, Document doc) {
Element treeNode = doc.createElement(node.getName());
ISet set = (ISet) node.get(0);
for (IValue elem : set) {
if (elem.getType().isTuple()) {
appendTupleElements(doc, treeNode, elem);
}
else {
treeNode.appendChild(yield(elem, doc));
}
}
return treeNode;
}
private Node yieldList(INode node, Document doc) {
Element treeNode = doc.createElement(node.getName());
IList list = (IList) node.get(0);
for (IValue elem : list) {
if (elem.getType().isTuple()) {
appendTupleElements(doc, treeNode, elem);
}
else {
treeNode.appendChild(yield(elem, doc));
}
}
return treeNode;
}
private Node yieldTree(INode value, Document doc) {
Element treeNode = doc.createElement(value.getName());
for (IValue child : value) {
if (child.getType().isTuple()) {
appendTupleElements(doc, treeNode, child);
}
else {
treeNode.appendChild(yield(child, doc));
}
}
return treeNode;
}
}