/*
* Copyright (c) 2009-2011, IETR/INSA of Rennes
* Copyright (c) 2012, Synflow
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the IETR/INSA of Rennes nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package net.sf.orcc.df.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Comparator;
import java.util.List;
import net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.df.Argument;
import net.sf.orcc.df.Connection;
import net.sf.orcc.df.Entity;
import net.sf.orcc.df.Instance;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Port;
import net.sf.orcc.graph.Vertex;
import net.sf.orcc.ir.ExprBinary;
import net.sf.orcc.ir.ExprBool;
import net.sf.orcc.ir.ExprFloat;
import net.sf.orcc.ir.ExprInt;
import net.sf.orcc.ir.ExprList;
import net.sf.orcc.ir.ExprString;
import net.sf.orcc.ir.ExprUnary;
import net.sf.orcc.ir.ExprVar;
import net.sf.orcc.ir.Expression;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.OpBinary;
import net.sf.orcc.ir.Type;
import net.sf.orcc.ir.TypeInt;
import net.sf.orcc.ir.TypeList;
import net.sf.orcc.ir.TypeUint;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.IrSwitch;
import net.sf.orcc.util.Adaptable;
import net.sf.orcc.util.Attribute;
import net.sf.orcc.util.DomUtil;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This class defines an XDF network writer.
*
* @author Matthieu Wipliez
*
*/
public class XdfWriter {
/**
* This class defines a writer of binary operation sequences. This writer
* translates a binary expression tree to a binary operation sequence with
* respect to operator precedence.
*
* @author Matthieu Wipliez
*
*/
private class BinOpSeqWriter extends IrSwitch<Expression> {
private Element parentElt;
private int parentPrec;
public BinOpSeqWriter(Element elt, int precedence) {
parentElt = elt;
parentPrec = precedence;
}
@Override
public Expression caseExprBinary(ExprBinary expr) {
int currentPrec = expr.getOp().getPrecedence();
if (parentPrec < currentPrec) {
// create a new Expr element
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "BinOpSeq");
parentElt.appendChild(exprElt);
Element oldParent = parentElt;
parentElt = exprElt;
int oldPrec = parentPrec;
parentPrec = currentPrec;
doSwitch(expr.getE1());
writeOperator(expr.getOp());
doSwitch(expr.getE2());
parentElt = oldParent;
parentPrec = oldPrec;
} else {
int oldPrec = parentPrec;
parentPrec = currentPrec;
// append expression 1, operator, expression 2 to the parent
doSwitch(expr.getE1());
writeOperator(expr.getOp());
doSwitch(expr.getE2());
parentPrec = oldPrec;
}
return null;
}
@Override
public Expression caseExprBool(ExprBool expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "Literal");
exprElt.setAttribute("literal-kind", "Boolean");
exprElt.setAttribute("value", String.valueOf(expr.isValue()));
parentElt.appendChild(exprElt);
return null;
}
@Override
public Expression caseExprFloat(ExprFloat expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "Literal");
exprElt.setAttribute("literal-kind", "Real");
exprElt.setAttribute("value", expr.getValue().toString());
parentElt.appendChild(exprElt);
return null;
}
@Override
public Expression caseExprInt(ExprInt expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "Literal");
exprElt.setAttribute("literal-kind", "Integer");
exprElt.setAttribute("value", expr.getValue().toString());
parentElt.appendChild(exprElt);
return null;
}
@Override
public Expression caseExprList(ExprList expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "List");
parentElt.appendChild(exprElt);
Element oldParent = parentElt;
parentElt = exprElt;
for (Expression childExpr : expr.getValue()) {
doSwitch(childExpr);
}
parentElt = oldParent;
return null;
}
@Override
public Expression caseExprString(ExprString expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "Literal");
exprElt.setAttribute("literal-kind", "String");
exprElt.setAttribute("value", expr.getValue());
parentElt.appendChild(exprElt);
return null;
}
@Override
public Expression caseExprUnary(ExprUnary expr) {
Element exprElt = document.createElement("Expr");
exprElt.setAttribute("kind", "UnaryOp");
Element op = document.createElement("Op");
op.setAttribute("name", expr.getOp().getText());
exprElt.appendChild(op);
Element oldParent = parentElt;
parentElt = exprElt;
int oldPrec = parentPrec;
parentPrec = Integer.MIN_VALUE;
// visit the expression of this unary expression
doSwitch(expr.getExpr());
parentElt = oldParent;
parentPrec = oldPrec;
parentElt.appendChild(exprElt);
return null;
}
@Override
public Expression caseExprVar(ExprVar var) {
Element exprElt = document.createElement("Expr");
String value = var.getUse().getVariable().getName();
exprElt.setAttribute("kind", "Var");
exprElt.setAttribute("name", value);
parentElt.appendChild(exprElt);
return null;
}
private void writeOperator(OpBinary op) {
Element opElt = document.createElement("Op");
opElt.setAttribute("name", op.getText());
parentElt.appendChild(opElt);
}
}
/**
* the document being created by this writer.
*/
private Document document;
/**
* Writes the XDF representation of the given network and its descendants in
* the given path.
*
* @param path
* path in which XDF files should be written
* @param network
* a network
* @return the file to which the network was written
*/
public File write(File path, Network network) {
document = DomUtil.createDocument("XDF");
writeXDF(document.getDocumentElement(), network);
File file = new File(path, network.getName() + ".xdf");
OutputStream os;
try {
os = new FileOutputStream(file);
} catch (IOException e) {
throw new OrccRuntimeException("I/O error", e);
}
try {
DomUtil.writeDocument(os, document);
} finally {
try {
os.close();
} catch (IOException e) {
throw new OrccRuntimeException("I/O error", e);
}
}
writeChildren(network, path);
return file;
}
/**
* Writes the XDF representation of the given network to the given output
* stream.
*
* @param network
* a network
* @param os
* an output stream
*/
public void write(Network network, OutputStream os) {
document = DomUtil.createDocument("XDF");
writeXDF(document.getDocumentElement(), network);
try {
DomUtil.writeDocument(os, document);
} finally {
try {
os.close();
} catch (IOException e) {
throw new OrccRuntimeException("I/O error", e);
}
}
}
/**
* Appends Attribute elements to the given parent parent. Each attribute of
* the attributes map is transformed to an Attribute DOM parent.
*
* @param parent
* the parent parent
* @param attributes
* a list of attributes
*/
private void writeAttributes(Element parent, EList<Attribute> attributes) {
// sort attributes by alphabetical order
ECollections.sort(attributes, new Comparator<Attribute>() {
@Override
public int compare(Attribute attr1, Attribute attr2) {
return attr1.getName().compareTo(attr2.getName());
}
});
for (Attribute attribute : attributes) {
Element attributeElt = document.createElement("Attribute");
attributeElt.setAttribute("name", attribute.getName());
String kind;
if (attribute.getStringValue() != null) {
String value = attribute.getStringValue();
if (value.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")) {
// that is somewhat arbitrary, but a String is only
// considered XML content if it starts with that prologue
// (which written by default by DomUtil.writeToString)
Document document = DomUtil.parseDocument(value);
Element docElt = document.getDocumentElement();
kind = XdfConstants.CUSTOM;
Node imported = this.document.importNode(docElt, true);
attributeElt.appendChild(imported);
} else {
kind = XdfConstants.STRING;
attributeElt.setAttribute("value", value);
}
} else if (attribute.getReferencedValue() instanceof Type) {
kind = XdfConstants.TYPE;
Type type = (Type) attribute.getReferencedValue();
attributeElt.appendChild(writeType(type));
} else if (attribute.getReferencedValue() instanceof Expression) {
kind = XdfConstants.VALUE;
Expression expr = (Expression) attribute.getReferencedValue();
writeExpr(attributeElt, expr);
} else {
// default is flag
kind = XdfConstants.FLAG;
}
attributeElt.setAttribute("kind", kind);
parent.appendChild(attributeElt);
}
}
/**
* Writes the children of the network's graph to the output path
*
* @param path
* output path
*/
private void writeChildren(Network network, File path) {
for (Vertex vertex : network.getChildren()) {
Network child = vertex.getAdapter(Network.class);
if (child != null) {
// writes the network
new XdfWriter().write(path, child);
}
}
}
/**
* Returns a Connection parent that represents the given connection.
*
* @param connection
* a connection
* @return a Connection DOM parent
*/
private Element writeConnection(Connection connection) {
Element connectionElt = document.createElement("Connection");
Vertex source = connection.getSource();
Vertex target = connection.getTarget();
Port srcPort = connection.getSourcePort();
Port tgtPort = connection.getTargetPort();
writeConnectionEndpoint(connectionElt, "src", source, srcPort);
writeConnectionEndpoint(connectionElt, "dst", target, tgtPort);
writeAttributes(connectionElt, connection.getAttributes());
return connectionElt;
}
/**
* Writes a connection end-point. An end-point is identified by two
* attributes, <code>name</code>, and <code>name + "-port"</code>.
* <code>name</code> can be "src" or "dst".
*
* @param connection
* a connection
* @return a Connection DOM parent
*/
private void writeConnectionEndpoint(Element connectionElt, String name,
Vertex vertex, Port port) {
String portAttr;
String vertexAttr;
if (port == null) {
vertexAttr = "";
portAttr = vertex.getLabel();
} else {
vertexAttr = vertex.getLabel();
portAttr = port.getName();
}
connectionElt.setAttribute(name, vertexAttr);
connectionElt.setAttribute(name + "-port", portAttr);
}
/**
* Appends Decl elements to the given parent parent with the given kind.
* Each declaration of the variables map is transformed to a Decl DOM
* parent.
*
* @param parent
* the parent parent
* @param kind
* the kind of declarations
* @param variables
* an ordered map of global variables
*/
private void writeDecls(Element parent, String kind, List<Var> variables) {
for (Var variable : variables) {
Element decl = document.createElement("Decl");
parent.appendChild(decl);
decl.setAttribute("kind", kind);
decl.setAttribute("name", variable.getName());
decl.appendChild(writeType(variable.getType()));
if (variable.isInitialized()) {
writeExpr(decl, variable.getInitialValue());
}
}
}
/**
* Returns an Entry parent that represents the given expression entry.
*
* @param name
* the entry name
* @param expr
* the entry value as an expression
* @return an Entry DOM parent
*/
private Element writeEntry(String name, Expression expr) {
Element entry = document.createElement("Entry");
entry.setAttribute("kind", "Expr");
entry.setAttribute("name", name);
writeExpr(entry, expr);
return entry;
}
/**
* Returns an Entry parent that represents the given type entry.
*
* @param name
* the entry name
* @param type
* the entry value as a type
* @return an Entry DOM parent
*/
private Element writeEntry(String name, Type type) {
Element entry = document.createElement("Entry");
entry.setAttribute("kind", "Type");
entry.setAttribute("name", name);
entry.appendChild(writeType(type));
return entry;
}
/**
* Appends an Expr parent that represents the given expression to the given
* parent. This method just creates a {@link BinOpSeqWriter} to fill the
* Expr parent.
*
* @param parent
* the parent parent to which an Expr parent should be added
* @param expr
* an expression
*/
private void writeExpr(Element parent, Expression expr) {
new BinOpSeqWriter(parent, Integer.MIN_VALUE).doSwitch(expr);
}
/**
* Returns an Instance parent that represents the given instance.
*
* @param instance
* an instance
* @return an Instance DOM parent
*/
private Element writeInstance(Instance instance) {
Element instanceElt = document.createElement("Instance");
instanceElt.setAttribute("id", instance.getName());
// class
EObject eObject = instance.getEntity();
if (eObject instanceof Adaptable) {
Entity entity = ((Adaptable) eObject).getAdapter(Entity.class);
if (entity != null) {
Element classElt = document.createElement("Class");
String name = entity.getName();
classElt.setAttribute("name", name);
instanceElt.appendChild(classElt);
}
}
// parameters
for (Argument argument : instance.getArguments()) {
Element parameterElt = document.createElement("Parameter");
parameterElt.setAttribute("name", argument.getVariable().getName());
writeExpr(parameterElt, argument.getValue());
instanceElt.appendChild(parameterElt);
}
// attributes
writeAttributes(instanceElt, instance.getAttributes());
return instanceElt;
}
/**
* Appends Port elements to the given parent parent with the given kind.
* Each port of the ports map is transformed to a Port DOM parent.
*
* @param parent
* the parent parent
* @param kind
* the kind of ports
* @param ports
* an ordered map of ports
*/
private void writePorts(Element parent, String kind, List<Port> ports) {
for (Port port : ports) {
Element portElt = document.createElement("Port");
parent.appendChild(portElt);
portElt.setAttribute("kind", kind);
portElt.setAttribute("name", port.getName());
portElt.appendChild(writeType(port.getType()));
// attributes
writeAttributes(portElt, port.getAttributes());
}
}
/**
* Returns a Type parent that represents the given type.
*
* @param type
* a type
* @return a Type DOM parent
*/
private Element writeType(Type type) {
Element typeElt = document.createElement("Type");
String name;
int size;
if (type.isBool()) {
name = "bool";
} else if (type.isInt()) {
name = "int";
size = ((TypeInt) type).getSize();
typeElt.appendChild(writeEntry("size",
IrFactory.eINSTANCE.createExprInt(size)));
} else if (type.isList()) {
name = "List";
size = ((TypeList) type).getSize();
type = ((TypeList) type).getType();
typeElt.appendChild(writeEntry("type", type));
typeElt.appendChild(writeEntry("size",
IrFactory.eINSTANCE.createExprInt(size)));
} else if (type.isString()) {
name = "String";
} else if (type.isUint()) {
name = "uint";
size = ((TypeUint) type).getSize();
typeElt.appendChild(writeEntry("size",
IrFactory.eINSTANCE.createExprInt(size)));
} else if (type.isVoid()) {
throw new OrccRuntimeException("void type is invalid in XDF");
} else {
throw new OrccRuntimeException("unknown type");
}
typeElt.setAttribute("name", name);
return typeElt;
}
/**
* Writes the top-level XDF parent.
*
* @param xdf
* the XDF parent
* @param network
* the network
*/
private void writeXDF(Element xdf, Network network) {
xdf.setAttribute("name", network.getSimpleName());
writeAttributes(xdf, network.getAttributes());
writePorts(xdf, "Input", network.getInputs());
writePorts(xdf, "Output", network.getOutputs());
writeDecls(xdf, "Param", network.getParameters());
writeDecls(xdf, "Variable", network.getVariables());
for (Vertex vertex : network.getChildren()) {
Instance instance = vertex.getAdapter(Instance.class);
if (instance != null) {
// writes an instance
xdf.appendChild(writeInstance(instance));
}
}
for (Connection connection : network.getConnections()) {
xdf.appendChild(writeConnection(connection));
}
}
}