/*
* 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 static org.w3c.dom.Node.ELEMENT_NODE;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import net.sf.orcc.OrccRuntimeException;
import net.sf.orcc.df.Argument;
import net.sf.orcc.df.Connection;
import net.sf.orcc.df.DfFactory;
import net.sf.orcc.df.Entity;
import net.sf.orcc.df.EntityResolver;
import net.sf.orcc.df.Instance;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.Port;
import net.sf.orcc.df.impl.DefaultEntityResolverImpl;
import net.sf.orcc.graph.Vertex;
import net.sf.orcc.ir.Expression;
import net.sf.orcc.ir.IrFactory;
import net.sf.orcc.ir.OpBinary;
import net.sf.orcc.ir.OpUnary;
import net.sf.orcc.ir.Type;
import net.sf.orcc.ir.TypeBool;
import net.sf.orcc.ir.TypeFloat;
import net.sf.orcc.ir.TypeInt;
import net.sf.orcc.ir.TypeList;
import net.sf.orcc.ir.TypeString;
import net.sf.orcc.ir.TypeUint;
import net.sf.orcc.ir.Var;
import net.sf.orcc.ir.util.ExpressionEvaluator;
import net.sf.orcc.util.Attribute;
import net.sf.orcc.util.DomUtil;
import net.sf.orcc.util.UtilFactory;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This class defines an XDF network parser.
*
* @author Matthieu Wipliez
*
*/
public class XdfParser {
/**
* This class defines a type entry.
*
* @author Matthieu Wipliez
*
*/
private static class Entry {
/**
* expression entry
*/
public static final int EXPR = 1;
/**
* type entry
*/
public static final int TYPE = 2;
/**
* the contents of this entry: expression or type.
*/
private Object content;
/**
* the type of this entry
*/
private int type;
/**
* Creates a new expression entry
*
* @param expr
* an expression
*/
public Entry(Expression expr) {
this.content = expr;
this.type = EXPR;
}
/**
* Creates a new type entry
*
* @param type
* a type
*/
public Entry(Type type) {
this.content = type;
this.type = TYPE;
}
/**
* Returns this entry's content as an expression
*
* @return this entry's content as an expression
*/
public Expression getEntryAsExpr() {
if (getType() == EXPR) {
return (Expression) content;
} else {
throw new OrccRuntimeException(
"this entry does not contain an expression");
}
}
/**
* Returns this entry's content as a type
*
* @return this entry's content as a type
*/
public Type getEntryAsType() {
if (getType() == TYPE) {
return (Type) content;
} else {
throw new OrccRuntimeException(
"this entry does not contain a type");
}
}
/**
* Returns the type of this entry.
*
* @return the type of this entry
*/
public int getType() {
return type;
}
}
/**
* This class defines a parser of XDF expressions.
*
* @author Matthieu Wipliez
*
*/
private class ExprParser {
/**
* Parses the given node as an expression and returns the matching
* Expression expression.
*
* @param node
* a node whose expected to be, or whose sibling is expected
* to be, a DOM element named "Expr".
* @return an expression
*/
public Expression parseExpr(Node node) {
ParseContinuation<Expression> cont = parseExprCont(node);
Expression expr = cont.getResult();
if (expr == null) {
throw new OrccRuntimeException("Expected an Expr element");
} else {
return expr;
}
}
/**
* Parses the given node as a binary operator and returns a parse
* continuation with the operator parsed.
*
* @param node
* a node that is expected, or whose sibling is expected, to
* be a DOM element named "Op".
* @return a parse continuation with the operator parsed
*/
private ParseContinuation<OpBinary> parseExprBinaryOp(Node node) {
while (node != null) {
if (node.getNodeName().equals("Op")) {
Element op = (Element) node;
String name = op.getAttribute("name");
return new ParseContinuation<OpBinary>(node,
OpBinary.getOperator(name));
}
node = node.getNextSibling();
}
return new ParseContinuation<OpBinary>(node, null);
}
/**
* Parses the given node and its siblings as a sequence of binary
* operations, aka "BinOpSeq". A BinOpSeq is a sequence of expr, op,
* expr, op, expr...
*
* @param node
* the first child node of a Expr kind="BinOpSeq" element
* @return a parse continuation with a BinaryExpr
*/
private ParseContinuation<Expression> parseExprBinOpSeq(Node node) {
List<Expression> expressions = new ArrayList<Expression>();
List<OpBinary> operators = new ArrayList<OpBinary>();
ParseContinuation<Expression> contE = parseExprCont(node);
expressions.add(contE.getResult());
node = contE.getNode();
while (node != null) {
ParseContinuation<OpBinary> contO = parseExprBinaryOp(node);
OpBinary op = contO.getResult();
node = contO.getNode();
if (op != null) {
operators.add(op);
contE = parseExprCont(node);
Expression expr = contE.getResult();
if (expr == null) {
throw new OrccRuntimeException(
"Expected an Expr element");
}
expressions.add(expr);
node = contE.getNode();
}
}
Expression expr = BinOpSeqParser.parse(expressions, operators);
return new ParseContinuation<Expression>(node, expr);
}
/**
* Parses the given node as an expression and returns the matching
* Expression expression.
*
* @param node
* a node whose sibling is expected to be a DOM element named
* "Expr".
* @return an expression
*/
private ParseContinuation<Expression> parseExprCont(Node node) {
Expression expr = null;
while (node != null) {
if (node.getNodeName().equals("Expr")) {
Element elt = (Element) node;
String kind = elt.getAttribute("kind");
if (kind.equals("BinOpSeq")) {
return parseExprBinOpSeq(elt.getFirstChild());
} else if (kind.equals("Literal")) {
expr = parseExprLiteral(elt);
break;
} else if (kind.equals("List")) {
List<Expression> exprs = parseExprs(node
.getFirstChild());
expr = IrFactory.eINSTANCE.createExprList(exprs);
break;
} else if (kind.equals("UnaryOp")) {
ParseContinuation<OpUnary> cont = parseExprUnaryOp(node
.getFirstChild());
OpUnary op = cont.getResult();
Expression unaryExpr = parseExpr(cont.getNode());
expr = IrFactory.eINSTANCE.createExprUnary(op,
unaryExpr, null);
break;
} else if (kind.equals("Var")) {
String name = elt.getAttribute("name");
// look up variable, in variables scope, and if not
// found in parameters scope
Var var = network.getVariable(name);
if (var == null) {
var = network.getParameter(name);
}
if (var == null) {
throw new OrccRuntimeException("In network \""
+ network.getName()
+ "\": unknown variable: \"" + name + "\"");
}
expr = IrFactory.eINSTANCE.createExprVar(var);
break;
} else {
throw new OrccRuntimeException("In network \""
+ network.getName()
+ "\": Unsupported Expr kind: \"" + kind + "\"");
}
}
node = node.getNextSibling();
}
return new ParseContinuation<Expression>(node, expr);
}
/**
* Parses the given "Expr" element as a literal and returns the matching
* Expression expression.
*
* @param elt
* a DOM element named "Expr"
* @return an expression
*/
private Expression parseExprLiteral(Element elt) {
String kind = elt.getAttribute("literal-kind");
String value = elt.getAttribute("value");
if (kind.equals("Boolean")) {
return IrFactory.eINSTANCE.createExprBool(Boolean
.parseBoolean(value));
} else if (kind.equals("Character")) {
throw new OrccRuntimeException("Characters not supported yet");
} else if (kind.equals("Integer")) {
return IrFactory.eINSTANCE.createExprInt(Long.parseLong(value));
} else if (kind.equals("Real")) {
return IrFactory.eINSTANCE.createExprFloat(Float
.parseFloat(value));
} else if (kind.equals("String")) {
return IrFactory.eINSTANCE.createExprString(value);
} else {
throw new OrccRuntimeException("Unsupported Expr "
+ "literal kind: \"" + kind + "\"");
}
}
private List<Expression> parseExprs(Node node) {
List<Expression> exprs = new ArrayList<Expression>();
while (node != null) {
if (node.getNodeName().equals("Expr")) {
exprs.add(parseExpr(node));
}
node = node.getNextSibling();
}
return exprs;
}
/**
* Parses the given node as a unary operator and returns a parse
* continuation with the operator parsed.
*
* @param node
* a node that is expected, or whose sibling is expected, to
* be a DOM element named "Op".
* @return a parse continuation with the operator parsed
*/
private ParseContinuation<OpUnary> parseExprUnaryOp(Node node) {
while (node != null) {
if (node.getNodeName().equals("Op")) {
Element op = (Element) node;
String name = op.getAttribute("name");
return new ParseContinuation<OpUnary>(node,
OpUnary.getOperator(name));
}
node = node.getNextSibling();
}
throw new OrccRuntimeException("Expected an Op element");
}
}
/**
* This class defines a parse continuation, by storing the next node that
* shall be parsed along with the result already computed.
*
* @author Matthieu Wipliez
*
*/
private static class ParseContinuation<T> {
final private Node node;
final private T result;
/**
* Creates a new parse continuation with the given DOM node and result.
* The constructor stores the next sibling of node.
*
* @param node
* a node that will be used to resume parsing after the
* result has been stored
* @param result
* the result
*/
public ParseContinuation(Node node, T result) {
if (node == null) {
this.node = null;
} else {
this.node = node.getNextSibling();
}
this.result = result;
}
/**
* Returns the node stored in this continuation.
*
* @return the node stored in this continuation
*/
public Node getNode() {
return node;
}
/**
* Returns the result stored in this continuation.
*
* @return the result stored in this continuation
*/
public T getResult() {
return result;
}
}
/**
* This class defines a parser of XDF types.
*
* @author Matthieu Wipliez
*
*/
private class TypeParser {
/**
* Default size of an signed/unsigned integer.
*/
private static final int defaultSize = 32;
/**
* Parses the given node as an Type.
*
* @param node
* the node to parse as a type.
* @return a type
*/
public ParseContinuation<Type> parseType(Node node) {
while (node != null) {
if (node.getNodeName().equals("Type")) {
Element eltType = (Element) node;
String name = eltType.getAttribute("name");
if (name.equals(TypeBool.NAME)) {
return new ParseContinuation<Type>(node,
IrFactory.eINSTANCE.createTypeBool());
} else if (name.equals(TypeInt.NAME)) {
Map<String, Entry> entries = parseTypeEntries(node
.getFirstChild());
Expression expr = parseTypeSize(entries);
int size = new ExpressionEvaluator()
.evaluateAsInteger(expr);
return new ParseContinuation<Type>(node,
IrFactory.eINSTANCE.createTypeInt(size));
} else if (name.equals(TypeFloat.NAME)) {
Map<String, Entry> entries = parseTypeEntries(node
.getFirstChild());
Expression expr = parseTypeSize(entries);
int size = new ExpressionEvaluator()
.evaluateAsInteger(expr);
return new ParseContinuation<Type>(node,
IrFactory.eINSTANCE.createTypeFloat(size));
} else if (name.equals(TypeList.NAME)) {
return new ParseContinuation<Type>(node,
parseTypeList(node));
} else if (name.equals(TypeString.NAME)) {
return new ParseContinuation<Type>(node,
IrFactory.eINSTANCE.createTypeString());
} else if (name.equals(TypeUint.NAME)) {
Map<String, Entry> entries = parseTypeEntries(node
.getFirstChild());
Expression expr = parseTypeSize(entries);
int size = new ExpressionEvaluator()
.evaluateAsInteger(expr);
TypeUint type = IrFactory.eINSTANCE.createTypeUint();
type.setSize(size);
return new ParseContinuation<Type>(node, type);
} else {
throw new OrccRuntimeException("unknown type name: \""
+ name + "\"");
}
}
node = node.getNextSibling();
}
throw new OrccRuntimeException("Expected a Type element");
}
/**
* Parses the node and its siblings as type entries, and returns a map
* of entry names to contents.
*
* @param node
* The first node susceptible to be an entry, or
* <code>null</code>.
* @return A map of entry names to contents.
*/
private Map<String, Entry> parseTypeEntries(Node node) {
Map<String, Entry> entries = new HashMap<String, Entry>();
while (node != null) {
if (node.getNodeName().equals("Entry")) {
Element element = (Element) node;
String name = element.getAttribute("name");
String kind = element.getAttribute("kind");
Entry entry = null;
if (kind.equals("Expr")) {
Expression expr = exprParser.parseExpr(node
.getFirstChild());
entry = new Entry(expr);
} else if (kind.equals("Type")) {
entry = new Entry(parseType(node.getFirstChild())
.getResult());
} else {
throw new OrccRuntimeException(
"unsupported entry type: \"" + kind + "\"");
}
entries.put(name, entry);
}
node = node.getNextSibling();
}
return entries;
}
/**
* Parses a List type.
*
* @param node
* the Type node where this List is defined
* @return a ListType
*/
private Type parseTypeList(Node node) {
Map<String, Entry> entries = parseTypeEntries(node.getFirstChild());
Entry entry = entries.get("size");
if (entry == null) {
throw new OrccRuntimeException(
"List type must have a \"size\" entry");
}
Expression expr = entry.getEntryAsExpr();
entry = entries.get("type");
if (entry == null) {
throw new OrccRuntimeException(
"List type must have a \"type\" entry");
}
Type type = entry.getEntryAsType();
int size = new ExpressionEvaluator().evaluateAsInteger(expr);
return IrFactory.eINSTANCE.createTypeList(size, type);
}
/**
* Gets a "size" entry from the given entry map, if found return its
* value, otherwise return {@link #defaultSize}.
*
* @param entries
* a map of entries
* @return an expression
*/
private Expression parseTypeSize(Map<String, Entry> entries) {
Entry entry = entries.get("size");
if (entry == null) {
return IrFactory.eINSTANCE.createExprInt(defaultSize);
} else {
return entry.getEntryAsExpr();
}
}
}
/**
* a list of entity resolvers
*/
private static final List<EntityResolver> resolvers = new ArrayList<EntityResolver>();
static {
resolvers.add(new DefaultEntityResolverImpl());
}
/**
* Registers a new entity resolver. The resolvers are called in a
* last-in-first-out manner: the last resolver to have been registered is
* used first when trying to resolve entities, then if it cannot resolve,
* the second to last, and so on, until the first resolver (which is the
* {@link DefaultEntityResolverImpl}).
*
* @param resolver
* entity resolver
*/
public static void registerResolver(EntityResolver resolver) {
resolvers.add(resolver);
}
/**
* XDF expression parser.
*/
private final ExprParser exprParser;
private Network network;
/**
* XDF type parser.
*/
private final TypeParser typeParser;
/**
* Creates a new network parser.
*/
public XdfParser(Resource resource, InputStream inputStream) {
exprParser = new ExprParser();
typeParser = new TypeParser();
// initialize resolvers with given resource
for (EntityResolver resolver : resolvers) {
resolver.initialize(resource);
}
// parse network
parseNetwork(resource, inputStream);
}
/**
* If vertexName is not empty, returns a new Port whose name is set to
* portName.
*
* @param vertexName
* the name of a vertex
* @param portName
* the name of a port
* @return a port, or <code>null</code> if no port should be returned
*/
private Port getPort(Vertex vertex, String dir, String portName) {
if (vertex instanceof Port) {
return null;
} else {
Instance instance = (Instance) vertex;
Port port = null;
EObject eObject = instance.getEntity();
if (eObject == null) {
return null;
} else if (eObject.eIsProxy()) {
// if entity is a proxy, create URI of port
URI uri = EcoreUtil.getURI(eObject);
uri = uri.appendFragment("//@" + dir + "." + portName);
// and return a new proxy port with that URI
port = DfFactory.eINSTANCE.createPort();
((InternalEObject) port).eSetProxyURI(uri);
} else {
// if entity is not a proxy, adapt to Entity and find port
Entity entity = instance.getAdapter(Entity.class);
if ("inputs".equals(dir)) {
port = entity.getInput(portName);
} else if ("outputs".equals(dir)) {
port = entity.getOutput(portName);
}
// last resort, create a dummy port with the given name
if (port == null) {
port = DfFactory.eINSTANCE.createPort(null, portName);
// and adds it to the entity
if ("inputs".equals(dir)) {
entity.getInputs().add(port);
} else {
entity.getOutputs().add(port);
}
}
}
return port;
}
}
/**
* If vertexName is empty, returns a new Vertex that contains a port from
* the ports map that has the name portName. If vertexName is not empty,
* returns a new Vertex that contains an instance from the instances map.
*
* @param vertexName
* the name of a vertex
* @param portName
* the name of a port
* @param kind
* the kind of port
* @return a vertex that contains a port or an instance
*/
private Vertex getVertex(String vertexName, String portName, String kind) {
if (vertexName.isEmpty()) {
Port port;
if ("Input".equals(kind)) {
port = network.getInput(portName);
} else {
port = network.getOutput(portName);
}
if (port == null) {
throw new OrccRuntimeException(
"An Connection element has an invalid"
+ " \"src-port\" " + "attribute");
}
return port;
} else {
Vertex vertex = network.getChild(vertexName);
if (vertex == null) {
throw new OrccRuntimeException(
"An Connection element has an invalid"
+ " \"src-port\" " + "attribute");
}
return vertex;
}
}
/**
* Parses the given "Attribute" element.
*
* @param attributes
* a list of attributes to fill
* @param element
* an "Attribute" element
*/
private void parseAttribute(EList<Attribute> attributes, Element element) {
String kind = element.getAttribute("kind");
String attrName = element.getAttribute("name");
Attribute attr = UtilFactory.eINSTANCE.createAttribute(attrName);
if (kind.equals(XdfConstants.CUSTOM)) {
// find the first element child
Node child = element.getFirstChild();
while (child != null && child.getNodeType() != ELEMENT_NODE) {
child = child.getNextSibling();
}
if (child == null) {
return;
}
// serialize it to a String
String value = DomUtil.writeToString(child);
attr.setStringValue(value);
} else if (kind.equals(XdfConstants.FLAG)) {
// nothing to do
} else if (kind.equals(XdfConstants.STRING)) {
String value = element.getAttribute("value");
attr.setStringValue(value);
} else if (kind.equals(XdfConstants.TYPE)) {
Type type = typeParser.parseType(element.getFirstChild())
.getResult();
attr.setEObjectValue(type);
} else if (kind.equals(XdfConstants.VALUE)) {
Expression expr = exprParser.parseExpr(element.getFirstChild());
attr.setEObjectValue(expr);
} else {
throw new OrccRuntimeException("unsupported attribute kind: \""
+ kind + "\"");
}
attributes.add(attr);
}
/**
* Parses the "Attribute" nodes.
*
* @param attributes
* a list of attributes to fill
* @param node
* the first node of a node list, or <code>null</code> if the
* caller had no children.
*/
private void parseAttributes(EList<Attribute> attributes, Node node) {
while (node != null) {
// only parses Attribute nodes, other nodes are ignored.
if (node.getNodeName().equals("Attribute")) {
parseAttribute(attributes, (Element) node);
}
node = node.getNextSibling();
}
}
/**
* Parses the body of the XDF document. The body can contain any element
* among the supported elements. Supported elements are: Connection, Decl
* (kind=Param or kind=Var), Instance, Package, Port.
*
* @param root
*/
private void parseBody(Element root) {
Node node = root.getFirstChild();
while (node != null) {
// this test allows us to skip #text nodes
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String name = node.getNodeName();
if (name.equals("Connection")) {
parseConnection(element);
} else if (name.equals("Decl")) {
parseDecl(element);
} else if (name.equals("Instance")) {
parseInstance(element);
} else if (name.equals("Package")) {
throw new OrccRuntimeException(
"Package elements are not supported by Orcc yet");
} else if (name.equals("Port")) {
parsePort(element);
} else if (name.equals("Attribute")) {
parseAttribute(network.getAttributes(), element);
} else {
throw new OrccRuntimeException("invalid node \"" + name
+ "\"");
}
}
node = node.getNextSibling();
}
}
/**
* Parses the given DOM element as a connection, and adds a matching
* Connection to the graph of the network being parsed.
*
* @param connection
* a DOM element named "Connection"
*/
private void parseConnection(Element connection) {
String src = connection.getAttribute("src");
String src_port = connection.getAttribute("src-port");
String dst = connection.getAttribute("dst");
String dst_port = connection.getAttribute("dst-port");
Vertex source = getVertex(src, src_port, "Input");
Port srcPort = getPort(source, "outputs", src_port);
Vertex target = getVertex(dst, dst_port, "Output");
Port dstPort = getPort(target, "inputs", dst_port);
Node child = connection.getFirstChild();
Connection conn = DfFactory.eINSTANCE.createConnection(source, srcPort,
target, dstPort);
parseAttributes(conn.getAttributes(), child);
network.getConnections().add(conn);
}
/**
* Parses the given Decl element, and adds a parameter or variable to
* {@link #network} depending on Decl's kind.
*
* @param decl
* a Decl element
*/
private void parseDecl(Element decl) {
String kind = decl.getAttribute("kind");
String name = decl.getAttribute("name");
if (name.isEmpty()) {
throw new OrccRuntimeException("Decl has an empty name");
}
if (kind.equals("Param")) {
ParseContinuation<Type> cont = typeParser.parseType(decl
.getFirstChild());
Type type = cont.getResult();
Var var = IrFactory.eINSTANCE.createVar(0, type, name, false);
network.getParameters().add(var);
} else if (kind.equals("Variable")) {
ParseContinuation<Type> cont = typeParser.parseType(decl
.getFirstChild());
Type type = cont.getResult();
Expression expr = exprParser.parseExpr(cont.getNode());
Var var = IrFactory.eINSTANCE.createVar(0, type, name, false, expr);
network.getVariables().add(var);
} else {
throw new OrccRuntimeException("unsupported Decl kind: \"" + kind
+ "\"");
}
}
/**
* Parses an "Instance" element and returns an {@link Instance}.
*
* @param instance
* a DOM element named "Instance".
* @return an instance
*/
private void parseInstance(Element element) {
// instance id
String id = element.getAttribute("id");
if (id.isEmpty()) {
throw new OrccRuntimeException("An Instance element "
+ "must have a valid \"id\" attribute");
}
// create and add instance with id
Instance instance = DfFactory.eINSTANCE.createInstance();
network.add(instance);
instance.setName(id);
// instance class
String clasz = null;
Node child = element.getFirstChild();
while (child != null) {
if (child.getNodeName().equals("Class")) {
clasz = ((Element) child).getAttribute("name");
break;
} else {
child = child.getNextSibling();
}
}
if (clasz != null && !clasz.isEmpty()) {
// resolve the file that defines the given class
resolveEntity(instance, clasz);
}
// instance parameters and attributes
parseParameters(instance, child);
parseAttributes(instance.getAttributes(), child);
}
/**
* Parses the file given to the constructor of this class.
*
* @return a network
*/
public void parseNetwork(Resource resource, InputStream inputStream) {
try {
// input
Document document = DomUtil.parseDocument(inputStream);
// parse the input, return the network
parseXDF(resource, document);
} finally {
try {
inputStream.close();
} catch (IOException e) {
throw new OrccRuntimeException(
"I/O error when parsing network", e);
}
}
}
private void parseParameters(Instance instance, Node node) {
while (node != null) {
if (node.getNodeName().equals("Parameter")) {
String name = ((Element) node).getAttribute("name");
if (name.isEmpty()) {
throw new OrccRuntimeException("A Parameter element "
+ "must have a valid \"name\" attribute");
}
// retrieve param
Var param = null;
EObject eObject = instance.getEntity();
if (eObject.eIsProxy()) {
// if entity is a proxy, create URI of variable
URI uri = EcoreUtil.getURI(eObject);
uri = uri.appendFragment("//@parameters." + name);
// and return a new var port with that URI
param = IrFactory.eINSTANCE.createVar();
((InternalEObject) param).eSetProxyURI(uri);
} else {
// if entity is not a proxy, adapt to Entity and find param
Entity entity = instance.getAdapter(Entity.class);
param = entity.getParameter(name);
}
// just in case, create a dummy if no param was found
if (param == null) {
param = IrFactory.eINSTANCE.createVar();
param.setName(name);
}
// create argument with param and value
Expression expr = exprParser.parseExpr(node.getFirstChild());
Argument argument = DfFactory.eINSTANCE.createArgument(param,
expr);
instance.getArguments().add(argument);
}
node = node.getNextSibling();
}
}
/**
* Parses a port, and adds it to {@link #inputs} or {@link #outputs},
* depending on the port's kind attribute.
*
* @param eltPort
* a DOM element named "Port"
*/
private void parsePort(Element eltPort) {
ParseContinuation<Type> cont = typeParser.parseType(eltPort
.getFirstChild());
Type type = cont.getResult();
String name = eltPort.getAttribute("name");
if (name.isEmpty()) {
throw new OrccRuntimeException("Port has an empty name");
}
Node child = cont.getNode();
// creates a port and parses its attributes
Port port = DfFactory.eINSTANCE.createPort(type, name);
parseAttributes(port.getAttributes(), child);
// DEPRECATED USE OF NOTE ELEMENT
Node node = cont.getNode();
while (node != null) {
if (node.getNodeName().equals("Note")) {
Element note = (Element) node;
if ("native".equals(note.getAttribute("kind"))) {
System.err
.println("Deprecated Note kind=\"native\" found.");
port.setAttribute("native", (Object) null);
break;
}
}
node = node.getNextSibling();
}
// DEPRECATED USE OF NOTE ELEMENT
// adds the port to inputs or outputs depending on its kind
String kind = eltPort.getAttribute("kind");
if (kind.equals("Input")) {
network.addInput(port);
} else if (kind.equals("Output")) {
network.addOutput(port);
} else {
throw new OrccRuntimeException("Port \"" + name
+ "\", invalid kind: \"" + kind + "\"");
}
}
/**
* Parses the given document as an XDF network.
*
* @param doc
* a DOM document that supposedly represent an XDF network
*/
private void parseXDF(Resource resource, Document doc)
throws OrccRuntimeException {
Element xdfElement = doc.getDocumentElement();
if (!xdfElement.getNodeName().equals("XDF")) {
throw new OrccRuntimeException("Expected \"XDF\" start element");
}
String name = xdfElement.getAttribute("name");
if (name.isEmpty()) {
throw new OrccRuntimeException("Expected a \"name\" attribute");
}
// create network and add to resource
network = DfFactory.eINSTANCE.createNetwork();
// set class name with resolvers
int size = resolvers.size();
ListIterator<EntityResolver> it = resolvers.listIterator(size);
while (it.hasPrevious()) {
EntityResolver resolver = it.previous();
if (resolver.setClassName(network)) {
break;
}
}
// set file name based on the type of Resource
URI uri = resource.getURI();
if (uri.isPlatform()) {
network.setFileName(uri.toPlatformString(true));
} else {
network.setFileName(uri.toFileString());
}
// parses body
parseBody(xdfElement);
// add network to resource *after* it has been parsed
// otherwise proxies are solved eagerly
// (which causes all sub-networks to be loaded)
resource.getContents().add(network);
}
/**
* Resolves the reference to an entity with the given class name, and
* updates the given instance accordingly. This method walks the resolvers
* backwards until it finds a resolver that can resolve the entity.
*
* @param instance
* an instance
* @param className
* class name
*/
private void resolveEntity(Instance instance, String className) {
int size = resolvers.size();
ListIterator<EntityResolver> it = resolvers.listIterator(size);
while (it.hasPrevious()) {
EntityResolver resolver = it.previous();
if (resolver.resolve(instance, className)) {
break;
}
}
}
}