From ae94a7824458b7f3c1957740fc764978dd477b44 Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Mon, 29 Jan 2018 12:27:52 +0100
Subject: [PATCH] parser of sbml function

---
 .../celldesigner/CellDesignerXmlParser.java   | 1225 +++++++++--------
 .../function/FunctionCollectionXmlParser.java |   24 +
 .../function/FunctionXmlParser.java           |   44 +
 .../celldesigner/ComplexParserTests.java      |  692 +++++-----
 .../function/FunctionXmlParserTest.java       |   49 +
 converter-CellDesigner/testFiles/function.xml |   70 +
 .../testFiles/function/invalid_definition.xml |    2 +
 .../testFiles/function/invalid_math.xml       |    4 +
 .../testFiles/function/simple.xml             |   17 +
 9 files changed, 1184 insertions(+), 943 deletions(-)
 create mode 100644 converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionCollectionXmlParser.java
 create mode 100644 converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParser.java
 create mode 100644 converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParserTest.java
 create mode 100644 converter-CellDesigner/testFiles/function.xml
 create mode 100644 converter-CellDesigner/testFiles/function/invalid_definition.xml
 create mode 100644 converter-CellDesigner/testFiles/function/invalid_math.xml
 create mode 100644 converter-CellDesigner/testFiles/function/simple.xml

diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
index 296e2bd330..05794c33d6 100644
--- a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParser.java
@@ -9,6 +9,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.lang3.StringEscapeUtils;
@@ -28,6 +29,7 @@ import lcsb.mapviewer.converter.InvalidInputDataExecption;
 import lcsb.mapviewer.converter.model.celldesigner.alias.AliasCollectionXmlParser;
 import lcsb.mapviewer.converter.model.celldesigner.annotation.RestAnnotationParser;
 import lcsb.mapviewer.converter.model.celldesigner.compartment.CompartmentCollectionXmlParser;
+import lcsb.mapviewer.converter.model.celldesigner.function.FunctionCollectionXmlParser;
 import lcsb.mapviewer.converter.model.celldesigner.reaction.ReactionCollectionXmlParser;
 import lcsb.mapviewer.converter.model.celldesigner.reaction.UnknownReactionClassException;
 import lcsb.mapviewer.converter.model.celldesigner.species.InternalModelSpeciesData;
@@ -37,6 +39,7 @@ import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerElement
 import lcsb.mapviewer.converter.model.celldesigner.structure.CellDesignerSpecies;
 import lcsb.mapviewer.model.graphics.PolylineData;
 import lcsb.mapviewer.model.map.InconsistentModelException;
+import lcsb.mapviewer.model.map.SbmlFunction;
 import lcsb.mapviewer.model.map.compartment.Compartment;
 import lcsb.mapviewer.model.map.layout.graphics.Layer;
 import lcsb.mapviewer.model.map.layout.graphics.LayerOval;
@@ -73,609 +76,621 @@ import lcsb.mapviewer.model.map.species.Species;
  */
 public class CellDesignerXmlParser extends XmlParser implements IConverter {
 
-	/**
-	 * Default class logger.
-	 */
-	private static Logger									 logger			 = Logger.getLogger(CellDesignerXmlParser.class.getName());
-
-	/**
-	 * CellDesigner parser for layers.
-	 */
-	private LayerXmlParser								 layerParser = new LayerXmlParser();
-
-	/**
-	 * CellDesigner parser for compartments collections.
-	 */
-	private CompartmentCollectionXmlParser compartmentCollectionXmlParser;
-
-	/**
-	 * CellDesigner parser for species collections.
-	 */
-	private SpeciesCollectionXmlParser		 speciesSbmlParser;
-
-	/**
-	 * CellDesigner parser for alias collections.
-	 */
-	private AliasCollectionXmlParser			 aliasCollectionParser;
-
-	/**
-	 * Annotation parser.
-	 */
-	private RestAnnotationParser					 rap				 = new RestAnnotationParser();
-
-	@Override
-	public Model createModel(ConverterParams params) throws InvalidInputDataExecption {
-		CellDesignerElementCollection elements = new CellDesignerElementCollection();
-
-		Model model = new ModelFullIndexed(null);
-
-		if (params.getFilename() != null) {
-			model.setName(FilenameUtils.getBaseName(params.getFilename()));
-		}
-		speciesSbmlParser = new SpeciesCollectionXmlParser(elements);
-		aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
-		compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
-
-		DOMParser parser = new DOMParser();
-		try {
-			parser.parse(params.getSource());
-		} catch (IOException e) {
-			throw new InvalidInputDataExecption("IO Problem with a file: " + params.getSource().getSystemId(), e);
-		}
-		Document doc = parser.getDocument();
-		try {
-
-			// Get the document's root XML node
-			NodeList root = doc.getChildNodes();
-
-			// Navigate down the hierarchy to get to the CEO node
-			Node sbmlNode = getNode("SBML", root);
-			if (sbmlNode == null) {
-				throw new InvalidInputDataExecption("No SBML node");
-			}
-
-			Node modelNode = getNode("model", sbmlNode.getChildNodes());
-			if (modelNode == null) {
-				throw new InvalidInputDataExecption("No model node in SBML");
-			}
-			// we ignore metaid - it's useless and obstruct data model
-			// model.setMetaId(getNodeAttr("metaId", modelNode));
-			model.setIdModel(getNodeAttr("id", modelNode));
-
-			Node compartmentNode = getNode("listOfCompartments", modelNode.getChildNodes());
-			if (compartmentNode != null) {
-				List<CellDesignerCompartment> compartments = compartmentCollectionXmlParser.parseXmlCompartmentCollection(compartmentNode);
-				elements.addElements(compartments);
-			}
-
-			InternalModelSpeciesData modelData = new InternalModelSpeciesData();
-
-			Node speciesNode = getNode("listOfSpecies", modelNode.getChildNodes());
-			if (speciesNode != null) {
-				List<Pair<String, ? extends CellDesignerSpecies<?>>> species = speciesSbmlParser.parseSbmlSpeciesCollection(speciesNode);
-				modelData.updateSpecies(species);
-			}
-			Node reactionsNode = null;
-
-			NodeList nodes = modelNode.getChildNodes();
-			for (int x = 0; x < nodes.getLength(); x++) {
-				Node node = nodes.item(x);
-				if (node.getNodeType() == Node.ELEMENT_NODE) {
-					if (node.getNodeName().equalsIgnoreCase("annotation")) {
-						continue;
-					} else if (node.getNodeName().equalsIgnoreCase("listOfSpecies")) {
-						continue;
-					} else if (node.getNodeName().equalsIgnoreCase("listOfReactions")) {
-						reactionsNode = node;
-					} else if (node.getNodeName().equalsIgnoreCase("listOfCompartments")) {
-						// we already parsed compartemnts
-						continue;
-					} else if (node.getNodeName().equalsIgnoreCase("notes")) {
-						String notes = rap.getNotes(node);
-						if (notes != null) {
-							notes = StringEscapeUtils.unescapeHtml4(notes);
-						}
-						model.setNotes(notes);
-					} else if (node.getNodeName().equalsIgnoreCase("listOfUnitDefinitions")) {
-						continue; // we can ignore unit definitions
-					} else {
-						throw new InvalidInputDataExecption("Unknown element of model: " + node.getNodeName());
-					}
-				}
-			}
-
-			Node annotationNode = getNode("annotation", modelNode.getChildNodes());
-			if (annotationNode == null) {
-				throw new InvalidInputDataExecption("No annotation node in SBML/model");
-			}
-
-			parseAnnotation(model, annotationNode, modelData, elements);
-
-			if (speciesNode != null) {
-				List<Pair<String, ? extends CellDesignerSpecies<?>>> species = speciesSbmlParser.parseSbmlSpeciesCollection(speciesNode);
-				modelData.updateSpecies(species);
-			}
-
-			if (reactionsNode != null) {
-				ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements, params.isSbgnFormat());
-				List<Reaction> reactions = reactionCollectionXmlParser.parseXmlReactionCollection(reactionsNode);
-				model.addReactions(reactions);
-			}
-
-			if (params.isSizeAutoAdjust()) {
-				Rectangle2D bound = getModelBound(model);
-				double width = bound.getWidth() + 2 * (Math.max(0, bound.getX()));
-				double height = bound.getHeight() + 2 * (Math.max(0, bound.getY()));
-
-				model.setWidth(width);
-				model.setHeight(height);
-			}
-		} catch (InvalidXmlSchemaException e) {
-			throw new InvalidInputDataExecption(e);
-		} catch (UnknownReactionClassException e) {
-			String type = e.getReactionType();
-			String reactionId = e.getReactionId();
-			String newType = null;
-			if ("CATALYSIS".equalsIgnoreCase(type)) {
-				newType = "positive influence";
-			} else if ("INHIBITION".equalsIgnoreCase(type)) {
-				newType = "negative influence";
-			} else if ("UNKNOWN_CATALYSIS".equalsIgnoreCase(type)) {
-				newType = "unknown positive influence";
-			} else if ("UNKNOWN_INHIBITION".equalsIgnoreCase(type)) {
-				newType = "unknown negative influence";
-			} else if ("PHYSICAL_STIMULATION".equalsIgnoreCase(type)) {
-				newType = "reduced physical stimulation";
-			} else if ("MODULATION".equalsIgnoreCase(type)) {
-				newType = "reduced modulation";
-			} else if ("TRIGGER".equalsIgnoreCase(type)) {
-				newType = "reduced trigger";
-			} else {
-				throw new InvalidInputDataExecption(e);
-			}
-			throw new InvalidInputDataExecption(
-					"Reaction type \"" + type + "\" is inappropriate for reaction " + reactionId + ". Suggested type: " + newType
-							+ " in the \"Reduced\" notation of CellDesigner.",
-					e);
-		} catch (CellDesignerParserException e) {
-			throw new InvalidInputDataExecption(e);
-		}
-
-		return model;
-	}
-
-	/**
-	 * Computes bound of the model.
-	 * 
-	 * @param model
-	 *          object for which computaion is done
-	 * @return bound of the model
-	 */
-	Rectangle2D getModelBound(Model model) {
-		double maxX = 0;
-		double maxY = 0;
-		double minX = model.getWidth();
-		double minY = model.getHeight();
-		for (Element alias : model.getElements()) {
-			maxX = Math.max(maxX, alias.getWidth() + alias.getX());
-			maxY = Math.max(maxY, alias.getHeight() + alias.getY());
-
-			minX = Math.min(minX, alias.getX());
-			minY = Math.min(minY, alias.getY());
-
-		}
-
-		for (Reaction reaction : model.getReactions()) {
-			for (Line2D line : reaction.getLines()) {
-				maxX = Math.max(maxX, line.getX1());
-				maxX = Math.max(maxX, line.getX2());
-				maxY = Math.max(maxY, line.getY1());
-				maxY = Math.max(maxY, line.getY2());
-
-				minX = Math.min(minX, line.getX1());
-				minX = Math.min(minX, line.getX2());
-				minY = Math.min(minY, line.getY1());
-				minY = Math.min(minY, line.getY2());
-			}
-		}
-
-		for (Layer layer : model.getLayers()) {
-			for (PolylineData lline : layer.getLines()) {
-				for (Line2D line : lline.getLines()) {
-					maxX = Math.max(maxX, line.getX1());
-					maxX = Math.max(maxX, line.getX2());
-					maxY = Math.max(maxY, line.getY1());
-					maxY = Math.max(maxY, line.getY2());
-
-					minX = Math.min(minX, line.getX1());
-					minX = Math.min(minX, line.getX2());
-					minY = Math.min(minY, line.getY1());
-					minY = Math.min(minY, line.getY2());
-				}
-			}
-			for (LayerRect rect : layer.getRectangles()) {
-				maxX = Math.max(maxX, rect.getX());
-				maxX = Math.max(maxX, rect.getX() + rect.getWidth());
-				maxY = Math.max(maxY, rect.getY());
-				maxY = Math.max(maxY, rect.getY() + rect.getHeight());
-
-				minX = Math.min(minX, rect.getX());
-				minX = Math.min(minX, rect.getX() + rect.getWidth());
-				minY = Math.min(minY, rect.getY());
-				minY = Math.min(minY, rect.getY() + rect.getHeight());
-			}
-			for (LayerOval oval : layer.getOvals()) {
-				maxX = Math.max(maxX, oval.getX());
-				maxX = Math.max(maxX, oval.getX() + oval.getWidth());
-				maxY = Math.max(maxY, oval.getY());
-				maxY = Math.max(maxY, oval.getY() + oval.getHeight());
-
-				minX = Math.min(minX, oval.getX());
-				minX = Math.min(minX, oval.getX() + oval.getWidth());
-				minY = Math.min(minY, oval.getY());
-				minY = Math.min(minY, oval.getY() + oval.getHeight());
-			}
-			for (LayerText text : layer.getTexts()) {
-				maxX = Math.max(maxX, text.getX());
-				maxX = Math.max(maxX, text.getX() + text.getWidth());
-				maxY = Math.max(maxY, text.getY());
-				maxY = Math.max(maxY, text.getY() + text.getHeight());
-
-				minX = Math.min(minX, text.getX());
-				minX = Math.min(minX, text.getX() + text.getWidth());
-				minY = Math.min(minY, text.getY());
-				minY = Math.min(minY, text.getY() + text.getHeight());
-			}
-		}
-
-		Rectangle2D result = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
-		return result;
-	}
-
-	/**
-	 * Parse annotation part of CellDesigner xml.
-	 * 
-	 * @param model
-	 *          model that is parsed (and will be updated)
-	 * @param modelData
-	 *          object conmtaining infoirmation about species during CellDesigner
-	 *          parsing
-	 * @param elements
-	 *          collection of {@link CellDesignerElement cell designer elements}
-	 *          parsed from xml
-	 * @param annotationNode
-	 *          xml node to parse
-	 * @throws InvalidXmlSchemaException
-	 *           thrown when xmlString is invalid
-	 */
-	private void parseAnnotation(Model model, Node annotationNode, InternalModelSpeciesData modelData, CellDesignerElementCollection elements)
-			throws InvalidXmlSchemaException {
-		SpeciesCollectionXmlParser parser = new SpeciesCollectionXmlParser(elements);
-
-		Node extensionNode = getNode("celldesigner:extension", annotationNode.getChildNodes());
-		if (extensionNode == null) {
-			throw new InvalidXmlSchemaException("No celldesigner:extension node in SBML/model/annotation");
-		}
-
-		NodeList nodes = extensionNode.getChildNodes();
-		Node includedSpecies = null;
-		Node listofSpeciesAlias = null;
-		Node listOfComplexSpeciesAlias = null;
-		Node listOfComparmentAlias = null;
-		Node listOfProteins = null;
-		Node listOfGenes = null;
-		Node listOfRnas = null;
-		Node listOfGroups = null;
-		Node listOfAntisenseRnas = null;
-		for (int x = 0; x < nodes.getLength(); x++) {
-			Node node = nodes.item(x);
-			if (node.getNodeType() == Node.ELEMENT_NODE) {
-				if (node.getNodeName().equalsIgnoreCase("celldesigner:modelVersion")) {
-					// we ignore map version (there is no use for us)
-					// model.setVersion(getNodeValue(node));
-					continue;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:modelDisplay")) {
-					model.setWidth(getNodeAttr("sizeX", node));
-					model.setHeight(getNodeAttr("sizeY", node));
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfSpeciesAliases")) {
-					listofSpeciesAlias = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfComplexSpeciesAliases")) {
-					listOfComplexSpeciesAlias = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfProteins")) {
-					listOfProteins = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfGenes")) {
-					listOfGenes = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfRNAs")) {
-					listOfRnas = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfAntisenseRNAs")) {
-					listOfAntisenseRnas = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfIncludedSpecies")) {
-					includedSpecies = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfCompartmentAliases")) {
-					listOfComparmentAlias = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfLayers")) {
-					model.addLayers(layerParser.parseLayers(node));
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfGroups")) {
-					listOfGroups = node;
-				} else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfBlockDiagrams")) {
-					layerParser.parseBlocks(model, node);
-				} else {
-					throw new InvalidXmlSchemaException("Unknown element of annotation: " + node.getNodeName());
-				}
-			}
-		}
-
-		if (listOfComparmentAlias != null) {
-			List<Compartment> aliases = aliasCollectionParser.parseXmlCompartmentAliasCollection(listOfComparmentAlias);
-			for (Element alias : aliases) {
-				rap.processNotes(alias);
-				model.addElement(alias);
-			}
-		}
-
-		if (includedSpecies != null) {
-			List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseIncludedSpeciesCollection(includedSpecies);
-			modelData.updateSpecies(species);
-		}
-
-		if (listOfProteins != null) {
-			List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlProteinCollection(listOfProteins);
-			modelData.updateSpecies(species);
-		}
-		if (listOfGenes != null) {
-			List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlGeneCollection(listOfGenes);
-			modelData.updateSpecies(species);
-		}
-		if (listOfRnas != null) {
-			List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlRnaCollection(listOfRnas);
-			modelData.updateSpecies(species);
-		}
-		if (listOfAntisenseRnas != null) {
-			List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlAntisenseRnaCollection(listOfAntisenseRnas);
-			modelData.updateSpecies(species);
-		}
-
-		for (CellDesignerSpecies<?> species : modelData.getAll()) {
-			if (!species.getElementId().equals("")) {
-				elements.addElement(species);
-			} else {
-				logger.warn(
-						"Species (class: " + species.getClass().getName() + ", name: " + species.getName()
-								+ ") exists in CD file, but is never instantiated. It's CellDesigner file problem.");
-			}
-		}
-
-		if (listOfComplexSpeciesAlias != null) {
-			List<Complex> aliases = aliasCollectionParser.parseXmlComplexAliasCollection(listOfComplexSpeciesAlias);
-			for (Element alias : aliases) {
-				rap.processNotes(alias);
-				model.addElement(alias);
-			}
-		}
-
-		if (listofSpeciesAlias != null) {
-			List<Species> aliases = aliasCollectionParser.parseXmlSpeciesAliasCollection(listofSpeciesAlias);
-			for (Element alias : aliases) {
-				rap.processNotes(alias);
-				model.addElement(alias);
-			}
-		}
-
-		if (includedSpecies != null) {
-			parseAnnotationAliasesConnections(model, getNode("celldesigner:listOfSpeciesAliases", nodes));
-			parseAnnotationComplexAliasesConnections(model, getNode("celldesigner:listOfComplexSpeciesAliases", nodes));
-		}
-
-		if (listOfGroups != null) {
-			layerParser.parseGroups(model, listOfGroups);
-		}
-
-	}
-
-	/**
-	 * Parses celldesigner:listOfComplexSpeciesAliases node for annotation part of
-	 * the CellDEsigner format.
-	 * 
-	 * @param model
-	 *          model that is parsed
-	 * @param aliasNode
-	 *          node to parse
-	 * @throws InvalidXmlSchemaException
-	 *           thrown when xml node contains data that is not supported by xml
-	 *           schema
-	 */
-	void parseAnnotationComplexAliasesConnections(Model model, Node aliasNode) throws InvalidXmlSchemaException {
-		NodeList nodes = aliasNode.getChildNodes();
-		for (int x = 0; x < nodes.getLength(); x++) {
-			Node node = nodes.item(x);
-			if (node.getNodeType() == Node.ELEMENT_NODE) {
-				if (node.getNodeName().equalsIgnoreCase("celldesigner:complexSpeciesAlias")) {
-					String aliasId = getNodeAttr("id", node);
-					String complexId = getNodeAttr("complexSpeciesAlias", node);
-					Complex alias = model.getElementByElementId(aliasId);
-					if (alias == null) {
-						throw new InvalidXmlSchemaException("Alias does not exist " + aliasId);
-					}
-					Complex parentComplex = model.getElementByElementId(complexId);
-					if (parentComplex != null) {
-						parentComplex.addSpecies(alias);
-						alias.setComplex(parentComplex);
-					}
-				} else {
-					throw new InvalidXmlSchemaException("Unknown element of celldesigner:listOfComplexSpeciesAliases: " + node.getNodeName());
-				}
-			}
-		}
-
-	}
-
-	/**
-	 * Parses celldesigner:listOfSpeciesAliases node for annotation part of the
-	 * CellDesigner format.
-	 * 
-	 * @param model
-	 *          model that is parsed
-	 * @param aliasNode
-	 *          node to parse
-	 * @throws InvalidXmlSchemaException
-	 *           thrown when xml node contains data that is not supported by xml
-	 *           schema
-	 */
-	void parseAnnotationAliasesConnections(Model model, Node aliasNode) throws InvalidXmlSchemaException {
-		NodeList nodes = aliasNode.getChildNodes();
-		for (int x = 0; x < nodes.getLength(); x++) {
-			Node node = nodes.item(x);
-			if (node.getNodeType() == Node.ELEMENT_NODE) {
-				if (node.getNodeName().equalsIgnoreCase("celldesigner:speciesAlias")) {
-					String aliasId = getNodeAttr("id", node);
-					String complexId = getNodeAttr("complexSpeciesAlias", node);
-					Element alias = model.getElementByElementId(aliasId);
-					if (alias == null) {
-						throw new InvalidXmlSchemaException("Alias does not exist " + aliasId);
-					} else if (alias instanceof Species) {
-						Species species = ((Species) alias);
-						Complex complex = model.getElementByElementId(complexId);
-						if (complex != null) {
-							species.setComplex(complex);
-							complex.addSpecies(species);
-						}
-					}
-				} else {
-					throw new InvalidXmlSchemaException("Unknown element of celldesigner:listOfSpeciesAliases: " + node.getNodeName());
-				}
-			}
-		}
-	}
-
-	/**
-	 * Transforms model into CellDesigner xml.
-	 * 
-	 * @param model
-	 *          model that should be transformed
-	 * @return CellDesigner xml string for the model
-	 * @throws InconsistentModelException
-	 *           thrown when then model is invalid
-	 */
-	public String toXml(Model model) throws InconsistentModelException {
-		CellDesignerElementCollection elements = new CellDesignerElementCollection();
-
-		SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
-		ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements, false);
-		aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
-
-		StringBuilder result = new StringBuilder();
-		result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-		result.append(
-				"<sbml xmlns=\"http://www.sbml.org/sbml/level2/version4\" "
-						+ "xmlns:celldesigner=\"http://www.sbml.org/2001/ns/celldesigner\" level=\"2\" version=\"4\">\n");
-		// metaid is a strang cell designer id, usually it's model id and as far as
-		// we can tell it's not used at all
-		result.append("<model metaid=\"" + model.getIdModel() + "\" id=\"" + model.getIdModel() + "\">\n");
-
-		if (model.getNotes() != null) {
-			result.append("<notes>");
-			result.append(escapeXml(model.getNotes()));
-			result.append("</notes>");
-		}
-
-		result.append(annotationToXml(model, elements));
-
-		compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
-
-		result.append(compartmentCollectionXmlParser.toXml(model.getCompartments()));
-
-		result.append(speciesCollectionXmlParser.speciesCollectionToSbmlString(model.getSpeciesList()));
-
-		result.append(reactionCollectionXmlParser.reactionCollectionToXmlString(model.getReactions()));
-
-		result.append("</model>");
-		result.append("</sbml>");
-		return result.toString();
-	}
-
-	/**
-	 * Generates xml node that should be in annotation part of the model.
-	 * 
-	 * @param model
-	 *          model to transform
-	 * @param elements
-	 *          collection of {@link CellDesignerElement cell designer elements}
-	 *          parsed from xml
-	 * @return annotation xml string for the model
-	 */
-	private String annotationToXml(Model model, CellDesignerElementCollection elements) {
-		SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
-
-		StringBuilder result = new StringBuilder();
-		result.append("<annotation>\n");
-		result.append("<celldesigner:extension>\n");
-		result.append("<celldesigner:modelVersion>4.0</celldesigner:modelVersion>\n");
-		result.append("<celldesigner:modelDisplay sizeX=\"" + model.getWidth().intValue() + "\" sizeY=\"" + model.getHeight().intValue() + "\"/>\n");
-
-		result.append(speciesCollectionXmlParser.speciesCollectionToXmlIncludedString(model.getSpeciesList()));
-
-		result.append(aliasCollectionParser.compartmentAliasCollectionToXmlString(model.getCompartments()));
-		result.append(aliasCollectionParser.complexAliasCollectionToXmlString(model.getComplexList()));
-		result.append(aliasCollectionParser.speciesAliasCollectionToXmlString(model.getNotComplexSpeciesList()));
-
-		List<Protein> proteins = new ArrayList<>();
-		List<Gene> genes = new ArrayList<>();
-		List<Rna> rnas = new ArrayList<>();
-		List<AntisenseRna> antisenseRnas = new ArrayList<>();
-		for (Element element : model.getElements()) {
-			if (element instanceof Protein) {
-				proteins.add((Protein) element);
-			} else if (element instanceof Gene) {
-				genes.add((Gene) element);
-			} else if (element instanceof AntisenseRna) {
-				antisenseRnas.add((AntisenseRna) element);
-			} else if (element instanceof Rna) {
-				rnas.add((Rna) element);
-			}
-		}
-
-		result.append(speciesCollectionXmlParser.proteinCollectionToXmlString(proteins));
-		result.append(speciesCollectionXmlParser.geneCollectionToXmlString(genes));
-		result.append(speciesCollectionXmlParser.rnaCollectionToXmlString(rnas));
-		result.append(speciesCollectionXmlParser.antisenseRnaCollectionToXmlString(antisenseRnas));
-		result.append(layerParser.layerCollectionToXml(model.getLayers()));
-
-		result.append("</celldesigner:extension>\n");
-		result.append("</annotation>\n");
-		return result.toString();
-	}
-
-	@Override
-	public InputStream exportModelToInputStream(Model model) throws InconsistentModelException {
-		String exportedString = toXml(model);
-		InputStream inputStream = new ByteArrayInputStream(exportedString.getBytes());
-		return inputStream;
-	}
-
-	@Override
-	public File exportModelToFile(Model model, String filePath) throws InconsistentModelException, IOException {
-		File file = new File(filePath);
-		String exportedString = toXml(model);
-		FileWriter fileWriter = new FileWriter(file);
-		fileWriter.write(exportedString);
-		fileWriter.flush();
-		fileWriter.close();
-
-		return file;
-	}
-
-	@Override
-	public String getCommonName() {
-		return "CellDesigner SBML";
-	}
-
-	@Override
-	public MimeType getMimeType() {
-		return MimeType.SBML;
-	}
-
-	@Override
-	public String getFileExtension() {
-		return "xml";
-	}
+  /**
+   * Default class logger.
+   */
+  private static Logger logger = Logger.getLogger(CellDesignerXmlParser.class.getName());
+
+  /**
+   * CellDesigner parser for layers.
+   */
+  private LayerXmlParser layerParser = new LayerXmlParser();
+
+  /**
+   * CellDesigner parser for compartments collections.
+   */
+  private CompartmentCollectionXmlParser compartmentCollectionXmlParser;
+
+  /**
+   * CellDesigner parser for species collections.
+   */
+  private SpeciesCollectionXmlParser speciesSbmlParser;
+
+  /**
+   * CellDesigner parser for alias collections.
+   */
+  private AliasCollectionXmlParser aliasCollectionParser;
+
+  /**
+   * Annotation parser.
+   */
+  private RestAnnotationParser rap = new RestAnnotationParser();
+
+  @Override
+  public Model createModel(ConverterParams params) throws InvalidInputDataExecption {
+    CellDesignerElementCollection elements = new CellDesignerElementCollection();
+    FunctionCollectionXmlParser functionParser = new FunctionCollectionXmlParser();
+
+    Model model = new ModelFullIndexed(null);
+
+    if (params.getFilename() != null) {
+      model.setName(FilenameUtils.getBaseName(params.getFilename()));
+    }
+    speciesSbmlParser = new SpeciesCollectionXmlParser(elements);
+    aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
+    compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
+
+    DOMParser parser = new DOMParser();
+    try {
+      parser.parse(params.getSource());
+    } catch (IOException e) {
+      throw new InvalidInputDataExecption("IO Problem with a file: " + params.getSource().getSystemId(), e);
+    }
+    Document doc = parser.getDocument();
+    try {
+
+      // Get the document's root XML node
+      NodeList root = doc.getChildNodes();
+
+      // Navigate down the hierarchy to get to the CEO node
+      Node sbmlNode = getNode("SBML", root);
+      if (sbmlNode == null) {
+        throw new InvalidInputDataExecption("No SBML node");
+      }
+
+      Node modelNode = getNode("model", sbmlNode.getChildNodes());
+      if (modelNode == null) {
+        throw new InvalidInputDataExecption("No model node in SBML");
+      }
+      // we ignore metaid - it's useless and obstruct data model
+      // model.setMetaId(getNodeAttr("metaId", modelNode));
+      model.setIdModel(getNodeAttr("id", modelNode));
+
+      Node compartmentNode = getNode("listOfCompartments", modelNode.getChildNodes());
+      if (compartmentNode != null) {
+        List<CellDesignerCompartment> compartments = compartmentCollectionXmlParser
+            .parseXmlCompartmentCollection(compartmentNode);
+        elements.addElements(compartments);
+      }
+
+      InternalModelSpeciesData modelData = new InternalModelSpeciesData();
+
+      Node speciesNode = getNode("listOfSpecies", modelNode.getChildNodes());
+      if (speciesNode != null) {
+        List<Pair<String, ? extends CellDesignerSpecies<?>>> species = speciesSbmlParser
+            .parseSbmlSpeciesCollection(speciesNode);
+        modelData.updateSpecies(species);
+      }
+      Node reactionsNode = null;
+      Node functionsNode = null;
+
+      NodeList nodes = modelNode.getChildNodes();
+      for (int x = 0; x < nodes.getLength(); x++) {
+        Node node = nodes.item(x);
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+          if (node.getNodeName().equalsIgnoreCase("annotation")) {
+            continue;
+          } else if (node.getNodeName().equalsIgnoreCase("listOfSpecies")) {
+            continue;
+          } else if (node.getNodeName().equalsIgnoreCase("listOfReactions")) {
+            reactionsNode = node;
+          } else if (node.getNodeName().equalsIgnoreCase("listOfCompartments")) {
+            // we already parsed compartments
+            continue;
+          } else if (node.getNodeName().equalsIgnoreCase("notes")) {
+            String notes = rap.getNotes(node);
+            if (notes != null) {
+              notes = StringEscapeUtils.unescapeHtml4(notes);
+            }
+            model.setNotes(notes);
+          } else if (node.getNodeName().equalsIgnoreCase("listOfUnitDefinitions")) {
+            continue; // we can ignore unit definitions
+          } else if (node.getNodeName().equalsIgnoreCase("listOfFunctionDefinitions")) {
+            functionsNode = node;
+          } else {
+            throw new InvalidInputDataExecption("Unknown element of model: " + node.getNodeName());
+          }
+        }
+      }
+
+      Node annotationNode = getNode("annotation", modelNode.getChildNodes());
+      if (annotationNode == null) {
+        throw new InvalidInputDataExecption("No annotation node in SBML/model");
+      }
+
+      parseAnnotation(model, annotationNode, modelData, elements);
+
+      if (speciesNode != null) {
+        List<Pair<String, ? extends CellDesignerSpecies<?>>> species = speciesSbmlParser
+            .parseSbmlSpeciesCollection(speciesNode);
+        modelData.updateSpecies(species);
+      }
+      if (functionsNode!=null) {
+        model.addFunctions(functionParser.parseXmlFunctionCollection(functionsNode));
+      }
+
+      if (reactionsNode != null) {
+        ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements,
+            params.isSbgnFormat());
+        List<Reaction> reactions = reactionCollectionXmlParser.parseXmlReactionCollection(reactionsNode);
+        model.addReactions(reactions);
+      }
+
+      if (params.isSizeAutoAdjust()) {
+        Rectangle2D bound = getModelBound(model);
+        double width = bound.getWidth() + 2 * (Math.max(0, bound.getX()));
+        double height = bound.getHeight() + 2 * (Math.max(0, bound.getY()));
+
+        model.setWidth(width);
+        model.setHeight(height);
+      }
+    } catch (InvalidXmlSchemaException e) {
+      throw new InvalidInputDataExecption(e);
+    } catch (UnknownReactionClassException e) {
+      String type = e.getReactionType();
+      String reactionId = e.getReactionId();
+      String newType = null;
+      if ("CATALYSIS".equalsIgnoreCase(type)) {
+        newType = "positive influence";
+      } else if ("INHIBITION".equalsIgnoreCase(type)) {
+        newType = "negative influence";
+      } else if ("UNKNOWN_CATALYSIS".equalsIgnoreCase(type)) {
+        newType = "unknown positive influence";
+      } else if ("UNKNOWN_INHIBITION".equalsIgnoreCase(type)) {
+        newType = "unknown negative influence";
+      } else if ("PHYSICAL_STIMULATION".equalsIgnoreCase(type)) {
+        newType = "reduced physical stimulation";
+      } else if ("MODULATION".equalsIgnoreCase(type)) {
+        newType = "reduced modulation";
+      } else if ("TRIGGER".equalsIgnoreCase(type)) {
+        newType = "reduced trigger";
+      } else {
+        throw new InvalidInputDataExecption(e);
+      }
+      throw new InvalidInputDataExecption("Reaction type \"" + type + "\" is inappropriate for reaction " + reactionId
+          + ". Suggested type: " + newType + " in the \"Reduced\" notation of CellDesigner.", e);
+    } catch (CellDesignerParserException e) {
+      throw new InvalidInputDataExecption(e);
+    }
+
+    return model;
+  }
+
+  /**
+   * Computes bound of the model.
+   * 
+   * @param model
+   *          object for which computaion is done
+   * @return bound of the model
+   */
+  Rectangle2D getModelBound(Model model) {
+    double maxX = 0;
+    double maxY = 0;
+    double minX = model.getWidth();
+    double minY = model.getHeight();
+    for (Element alias : model.getElements()) {
+      maxX = Math.max(maxX, alias.getWidth() + alias.getX());
+      maxY = Math.max(maxY, alias.getHeight() + alias.getY());
+
+      minX = Math.min(minX, alias.getX());
+      minY = Math.min(minY, alias.getY());
+
+    }
+
+    for (Reaction reaction : model.getReactions()) {
+      for (Line2D line : reaction.getLines()) {
+        maxX = Math.max(maxX, line.getX1());
+        maxX = Math.max(maxX, line.getX2());
+        maxY = Math.max(maxY, line.getY1());
+        maxY = Math.max(maxY, line.getY2());
+
+        minX = Math.min(minX, line.getX1());
+        minX = Math.min(minX, line.getX2());
+        minY = Math.min(minY, line.getY1());
+        minY = Math.min(minY, line.getY2());
+      }
+    }
+
+    for (Layer layer : model.getLayers()) {
+      for (PolylineData lline : layer.getLines()) {
+        for (Line2D line : lline.getLines()) {
+          maxX = Math.max(maxX, line.getX1());
+          maxX = Math.max(maxX, line.getX2());
+          maxY = Math.max(maxY, line.getY1());
+          maxY = Math.max(maxY, line.getY2());
+
+          minX = Math.min(minX, line.getX1());
+          minX = Math.min(minX, line.getX2());
+          minY = Math.min(minY, line.getY1());
+          minY = Math.min(minY, line.getY2());
+        }
+      }
+      for (LayerRect rect : layer.getRectangles()) {
+        maxX = Math.max(maxX, rect.getX());
+        maxX = Math.max(maxX, rect.getX() + rect.getWidth());
+        maxY = Math.max(maxY, rect.getY());
+        maxY = Math.max(maxY, rect.getY() + rect.getHeight());
+
+        minX = Math.min(minX, rect.getX());
+        minX = Math.min(minX, rect.getX() + rect.getWidth());
+        minY = Math.min(minY, rect.getY());
+        minY = Math.min(minY, rect.getY() + rect.getHeight());
+      }
+      for (LayerOval oval : layer.getOvals()) {
+        maxX = Math.max(maxX, oval.getX());
+        maxX = Math.max(maxX, oval.getX() + oval.getWidth());
+        maxY = Math.max(maxY, oval.getY());
+        maxY = Math.max(maxY, oval.getY() + oval.getHeight());
+
+        minX = Math.min(minX, oval.getX());
+        minX = Math.min(minX, oval.getX() + oval.getWidth());
+        minY = Math.min(minY, oval.getY());
+        minY = Math.min(minY, oval.getY() + oval.getHeight());
+      }
+      for (LayerText text : layer.getTexts()) {
+        maxX = Math.max(maxX, text.getX());
+        maxX = Math.max(maxX, text.getX() + text.getWidth());
+        maxY = Math.max(maxY, text.getY());
+        maxY = Math.max(maxY, text.getY() + text.getHeight());
+
+        minX = Math.min(minX, text.getX());
+        minX = Math.min(minX, text.getX() + text.getWidth());
+        minY = Math.min(minY, text.getY());
+        minY = Math.min(minY, text.getY() + text.getHeight());
+      }
+    }
+
+    Rectangle2D result = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
+    return result;
+  }
+
+  /**
+   * Parse annotation part of CellDesigner xml.
+   * 
+   * @param model
+   *          model that is parsed (and will be updated)
+   * @param modelData
+   *          object conmtaining infoirmation about species during CellDesigner
+   *          parsing
+   * @param elements
+   *          collection of {@link CellDesignerElement cell designer elements}
+   *          parsed from xml
+   * @param annotationNode
+   *          xml node to parse
+   * @throws InvalidXmlSchemaException
+   *           thrown when xmlString is invalid
+   */
+  private void parseAnnotation(Model model, Node annotationNode, InternalModelSpeciesData modelData,
+      CellDesignerElementCollection elements) throws InvalidXmlSchemaException {
+    SpeciesCollectionXmlParser parser = new SpeciesCollectionXmlParser(elements);
+
+    Node extensionNode = getNode("celldesigner:extension", annotationNode.getChildNodes());
+    if (extensionNode == null) {
+      throw new InvalidXmlSchemaException("No celldesigner:extension node in SBML/model/annotation");
+    }
+
+    NodeList nodes = extensionNode.getChildNodes();
+    Node includedSpecies = null;
+    Node listofSpeciesAlias = null;
+    Node listOfComplexSpeciesAlias = null;
+    Node listOfComparmentAlias = null;
+    Node listOfProteins = null;
+    Node listOfGenes = null;
+    Node listOfRnas = null;
+    Node listOfGroups = null;
+    Node listOfAntisenseRnas = null;
+    for (int x = 0; x < nodes.getLength(); x++) {
+      Node node = nodes.item(x);
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+        if (node.getNodeName().equalsIgnoreCase("celldesigner:modelVersion")) {
+          // we ignore map version (there is no use for us)
+          // model.setVersion(getNodeValue(node));
+          continue;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:modelDisplay")) {
+          model.setWidth(getNodeAttr("sizeX", node));
+          model.setHeight(getNodeAttr("sizeY", node));
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfSpeciesAliases")) {
+          listofSpeciesAlias = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfComplexSpeciesAliases")) {
+          listOfComplexSpeciesAlias = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfProteins")) {
+          listOfProteins = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfGenes")) {
+          listOfGenes = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfRNAs")) {
+          listOfRnas = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfAntisenseRNAs")) {
+          listOfAntisenseRnas = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfIncludedSpecies")) {
+          includedSpecies = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfCompartmentAliases")) {
+          listOfComparmentAlias = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfLayers")) {
+          model.addLayers(layerParser.parseLayers(node));
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfGroups")) {
+          listOfGroups = node;
+        } else if (node.getNodeName().equalsIgnoreCase("celldesigner:listOfBlockDiagrams")) {
+          layerParser.parseBlocks(model, node);
+        } else {
+          throw new InvalidXmlSchemaException("Unknown element of annotation: " + node.getNodeName());
+        }
+      }
+    }
+
+    if (listOfComparmentAlias != null) {
+      List<Compartment> aliases = aliasCollectionParser.parseXmlCompartmentAliasCollection(listOfComparmentAlias);
+      for (Element alias : aliases) {
+        rap.processNotes(alias);
+        model.addElement(alias);
+      }
+    }
+
+    if (includedSpecies != null) {
+      List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser
+          .parseIncludedSpeciesCollection(includedSpecies);
+      modelData.updateSpecies(species);
+    }
+
+    if (listOfProteins != null) {
+      List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlProteinCollection(listOfProteins);
+      modelData.updateSpecies(species);
+    }
+    if (listOfGenes != null) {
+      List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlGeneCollection(listOfGenes);
+      modelData.updateSpecies(species);
+    }
+    if (listOfRnas != null) {
+      List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser.parseXmlRnaCollection(listOfRnas);
+      modelData.updateSpecies(species);
+    }
+    if (listOfAntisenseRnas != null) {
+      List<Pair<String, ? extends CellDesignerSpecies<?>>> species = parser
+          .parseXmlAntisenseRnaCollection(listOfAntisenseRnas);
+      modelData.updateSpecies(species);
+    }
+
+    for (CellDesignerSpecies<?> species : modelData.getAll()) {
+      if (!species.getElementId().equals("")) {
+        elements.addElement(species);
+      } else {
+        logger.warn("Species (class: " + species.getClass().getName() + ", name: " + species.getName()
+            + ") exists in CD file, but is never instantiated. It's CellDesigner file problem.");
+      }
+    }
+
+    if (listOfComplexSpeciesAlias != null) {
+      List<Complex> aliases = aliasCollectionParser.parseXmlComplexAliasCollection(listOfComplexSpeciesAlias);
+      for (Element alias : aliases) {
+        rap.processNotes(alias);
+        model.addElement(alias);
+      }
+    }
+
+    if (listofSpeciesAlias != null) {
+      List<Species> aliases = aliasCollectionParser.parseXmlSpeciesAliasCollection(listofSpeciesAlias);
+      for (Element alias : aliases) {
+        rap.processNotes(alias);
+        model.addElement(alias);
+      }
+    }
+
+    if (includedSpecies != null) {
+      parseAnnotationAliasesConnections(model, getNode("celldesigner:listOfSpeciesAliases", nodes));
+      parseAnnotationComplexAliasesConnections(model, getNode("celldesigner:listOfComplexSpeciesAliases", nodes));
+    }
+
+    if (listOfGroups != null) {
+      layerParser.parseGroups(model, listOfGroups);
+    }
+
+  }
+
+  /**
+   * Parses celldesigner:listOfComplexSpeciesAliases node for annotation part of
+   * the CellDEsigner format.
+   * 
+   * @param model
+   *          model that is parsed
+   * @param aliasNode
+   *          node to parse
+   * @throws InvalidXmlSchemaException
+   *           thrown when xml node contains data that is not supported by xml
+   *           schema
+   */
+  void parseAnnotationComplexAliasesConnections(Model model, Node aliasNode) throws InvalidXmlSchemaException {
+    NodeList nodes = aliasNode.getChildNodes();
+    for (int x = 0; x < nodes.getLength(); x++) {
+      Node node = nodes.item(x);
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+        if (node.getNodeName().equalsIgnoreCase("celldesigner:complexSpeciesAlias")) {
+          String aliasId = getNodeAttr("id", node);
+          String complexId = getNodeAttr("complexSpeciesAlias", node);
+          Complex alias = model.getElementByElementId(aliasId);
+          if (alias == null) {
+            throw new InvalidXmlSchemaException("Alias does not exist " + aliasId);
+          }
+          Complex parentComplex = model.getElementByElementId(complexId);
+          if (parentComplex != null) {
+            parentComplex.addSpecies(alias);
+            alias.setComplex(parentComplex);
+          }
+        } else {
+          throw new InvalidXmlSchemaException(
+              "Unknown element of celldesigner:listOfComplexSpeciesAliases: " + node.getNodeName());
+        }
+      }
+    }
+
+  }
+
+  /**
+   * Parses celldesigner:listOfSpeciesAliases node for annotation part of the
+   * CellDesigner format.
+   * 
+   * @param model
+   *          model that is parsed
+   * @param aliasNode
+   *          node to parse
+   * @throws InvalidXmlSchemaException
+   *           thrown when xml node contains data that is not supported by xml
+   *           schema
+   */
+  void parseAnnotationAliasesConnections(Model model, Node aliasNode) throws InvalidXmlSchemaException {
+    NodeList nodes = aliasNode.getChildNodes();
+    for (int x = 0; x < nodes.getLength(); x++) {
+      Node node = nodes.item(x);
+      if (node.getNodeType() == Node.ELEMENT_NODE) {
+        if (node.getNodeName().equalsIgnoreCase("celldesigner:speciesAlias")) {
+          String aliasId = getNodeAttr("id", node);
+          String complexId = getNodeAttr("complexSpeciesAlias", node);
+          Element alias = model.getElementByElementId(aliasId);
+          if (alias == null) {
+            throw new InvalidXmlSchemaException("Alias does not exist " + aliasId);
+          } else if (alias instanceof Species) {
+            Species species = ((Species) alias);
+            Complex complex = model.getElementByElementId(complexId);
+            if (complex != null) {
+              species.setComplex(complex);
+              complex.addSpecies(species);
+            }
+          }
+        } else {
+          throw new InvalidXmlSchemaException(
+              "Unknown element of celldesigner:listOfSpeciesAliases: " + node.getNodeName());
+        }
+      }
+    }
+  }
+
+  /**
+   * Transforms model into CellDesigner xml.
+   * 
+   * @param model
+   *          model that should be transformed
+   * @return CellDesigner xml string for the model
+   * @throws InconsistentModelException
+   *           thrown when then model is invalid
+   */
+  public String toXml(Model model) throws InconsistentModelException {
+    CellDesignerElementCollection elements = new CellDesignerElementCollection();
+
+    SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
+    ReactionCollectionXmlParser reactionCollectionXmlParser = new ReactionCollectionXmlParser(model, elements, false);
+    aliasCollectionParser = new AliasCollectionXmlParser(elements, model);
+
+    StringBuilder result = new StringBuilder();
+    result.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    result.append("<sbml xmlns=\"http://www.sbml.org/sbml/level2/version4\" "
+        + "xmlns:celldesigner=\"http://www.sbml.org/2001/ns/celldesigner\" level=\"2\" version=\"4\">\n");
+    // metaid is a strang cell designer id, usually it's model id and as far as
+    // we can tell it's not used at all
+    result.append("<model metaid=\"" + model.getIdModel() + "\" id=\"" + model.getIdModel() + "\">\n");
+
+    if (model.getNotes() != null) {
+      result.append("<notes>");
+      result.append(escapeXml(model.getNotes()));
+      result.append("</notes>");
+    }
+
+    result.append(annotationToXml(model, elements));
+
+    compartmentCollectionXmlParser = new CompartmentCollectionXmlParser(elements);
+
+    result.append(compartmentCollectionXmlParser.toXml(model.getCompartments()));
+
+    result.append(speciesCollectionXmlParser.speciesCollectionToSbmlString(model.getSpeciesList()));
+
+    result.append(reactionCollectionXmlParser.reactionCollectionToXmlString(model.getReactions()));
+
+    result.append("</model>");
+    result.append("</sbml>");
+    return result.toString();
+  }
+
+  /**
+   * Generates xml node that should be in annotation part of the model.
+   * 
+   * @param model
+   *          model to transform
+   * @param elements
+   *          collection of {@link CellDesignerElement cell designer elements}
+   *          parsed from xml
+   * @return annotation xml string for the model
+   */
+  private String annotationToXml(Model model, CellDesignerElementCollection elements) {
+    SpeciesCollectionXmlParser speciesCollectionXmlParser = new SpeciesCollectionXmlParser(elements);
+
+    StringBuilder result = new StringBuilder();
+    result.append("<annotation>\n");
+    result.append("<celldesigner:extension>\n");
+    result.append("<celldesigner:modelVersion>4.0</celldesigner:modelVersion>\n");
+    result.append("<celldesigner:modelDisplay sizeX=\"" + model.getWidth().intValue() + "\" sizeY=\""
+        + model.getHeight().intValue() + "\"/>\n");
+
+    result.append(speciesCollectionXmlParser.speciesCollectionToXmlIncludedString(model.getSpeciesList()));
+
+    result.append(aliasCollectionParser.compartmentAliasCollectionToXmlString(model.getCompartments()));
+    result.append(aliasCollectionParser.complexAliasCollectionToXmlString(model.getComplexList()));
+    result.append(aliasCollectionParser.speciesAliasCollectionToXmlString(model.getNotComplexSpeciesList()));
+
+    List<Protein> proteins = new ArrayList<>();
+    List<Gene> genes = new ArrayList<>();
+    List<Rna> rnas = new ArrayList<>();
+    List<AntisenseRna> antisenseRnas = new ArrayList<>();
+    for (Element element : model.getElements()) {
+      if (element instanceof Protein) {
+        proteins.add((Protein) element);
+      } else if (element instanceof Gene) {
+        genes.add((Gene) element);
+      } else if (element instanceof AntisenseRna) {
+        antisenseRnas.add((AntisenseRna) element);
+      } else if (element instanceof Rna) {
+        rnas.add((Rna) element);
+      }
+    }
+
+    result.append(speciesCollectionXmlParser.proteinCollectionToXmlString(proteins));
+    result.append(speciesCollectionXmlParser.geneCollectionToXmlString(genes));
+    result.append(speciesCollectionXmlParser.rnaCollectionToXmlString(rnas));
+    result.append(speciesCollectionXmlParser.antisenseRnaCollectionToXmlString(antisenseRnas));
+    result.append(layerParser.layerCollectionToXml(model.getLayers()));
+
+    result.append("</celldesigner:extension>\n");
+    result.append("</annotation>\n");
+    return result.toString();
+  }
+
+  @Override
+  public InputStream exportModelToInputStream(Model model) throws InconsistentModelException {
+    String exportedString = toXml(model);
+    InputStream inputStream = new ByteArrayInputStream(exportedString.getBytes());
+    return inputStream;
+  }
+
+  @Override
+  public File exportModelToFile(Model model, String filePath) throws InconsistentModelException, IOException {
+    File file = new File(filePath);
+    String exportedString = toXml(model);
+    FileWriter fileWriter = new FileWriter(file);
+    fileWriter.write(exportedString);
+    fileWriter.flush();
+    fileWriter.close();
+
+    return file;
+  }
+
+  @Override
+  public String getCommonName() {
+    return "CellDesigner SBML";
+  }
+
+  @Override
+  public MimeType getMimeType() {
+    return MimeType.SBML;
+  }
+
+  @Override
+  public String getFileExtension() {
+    return "xml";
+  }
 }
diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionCollectionXmlParser.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionCollectionXmlParser.java
new file mode 100644
index 0000000000..ceeaa4963e
--- /dev/null
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionCollectionXmlParser.java
@@ -0,0 +1,24 @@
+package lcsb.mapviewer.converter.model.celldesigner.function;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.w3c.dom.Node;
+
+import lcsb.mapviewer.common.XmlParser;
+import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
+import lcsb.mapviewer.model.map.SbmlFunction;
+
+public class FunctionCollectionXmlParser extends XmlParser {
+
+  private FunctionXmlParser functionParser = new FunctionXmlParser();
+
+  public Set<SbmlFunction> parseXmlFunctionCollection(Node functionsNode) throws InvalidXmlSchemaException {
+    Set<SbmlFunction> result = new HashSet<>();
+    for (Node node : super.getNodes("functionDefinition", functionsNode.getChildNodes())) {
+      result.add(functionParser.parseFunction(node));
+    }
+    return result;
+  }
+
+}
diff --git a/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParser.java b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParser.java
new file mode 100644
index 0000000000..b25b2829d1
--- /dev/null
+++ b/converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParser.java
@@ -0,0 +1,44 @@
+package lcsb.mapviewer.converter.model.celldesigner.function;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Node;
+
+import lcsb.mapviewer.common.XmlParser;
+import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
+import lcsb.mapviewer.model.map.SbmlFunction;
+
+public class FunctionXmlParser extends XmlParser {
+  Logger logger = Logger.getLogger(FunctionXmlParser.class);
+
+  public SbmlFunction parseFunction(Node functionNode) throws InvalidXmlSchemaException {
+    String functionId = getNodeAttr("id", functionNode);
+
+    SbmlFunction result = new SbmlFunction(functionId);
+    result.setName(getNodeAttr("name", functionNode));
+    Node mathDefinition = getNode("math", functionNode);
+    if (mathDefinition == null) {
+      throw new InvalidXmlSchemaException("Function " + functionId + " doesn't contain MathML definition (math node)");
+    }
+    result.setDefinition(nodeToString(mathDefinition));
+    result.setArguments(parseArgumentsFromMath(mathDefinition, functionId));
+
+    return result;
+  }
+
+  private List<String> parseArgumentsFromMath(Node mathDefinition, String functionId) throws InvalidXmlSchemaException {
+    List<String> result = new ArrayList<>();
+    Node lambdaDefinition = getNode("lambda", mathDefinition);
+    if (lambdaDefinition == null) {
+      throw new InvalidXmlSchemaException("Function " + functionId + " doesn't contain valid MathML lambda node");
+    }
+    List<Node> variables = super.getNodes("bvar", lambdaDefinition.getChildNodes());
+    for (Node node : variables) {
+      result.add(getNodeValue(getNode("ci", node)).trim());
+    }
+    return result;
+  }
+
+}
diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ComplexParserTests.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ComplexParserTests.java
index ac9d237edf..6484b40970 100644
--- a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ComplexParserTests.java
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/ComplexParserTests.java
@@ -36,342 +36,358 @@ import lcsb.mapviewer.model.map.species.field.ModificationState;
 import lcsb.mapviewer.modelutils.map.ElementUtils;
 
 public class ComplexParserTests extends CellDesignerTestFunctions {
-	Logger			 logger	= Logger.getLogger(ComplexParserTests.class);
-
-	ElementUtils eu			= new ElementUtils();
-
-	@Before
-	public void setUp() throws Exception {
-	}
-
-	@After
-	public void tearDown() throws Exception {
-	}
-
-	@Test
-	public void testParseCompartmens() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/reactions/centeredAnchorInModifier.xml");
-
-			assertEquals(0, model.getCompartments().size());
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParseBubbles() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/bubbles.xml");
-
-			Element species = model.getElementByElementId("sa6");
-			assertNotNull(species);
-			Protein protein = (Protein) species;
-			assertTrue(protein.getModificationResidues().size() > 0);
-			assertNotNull(protein.getStructuralState());
-			assertFalse(protein.getStructuralState().equals(""));
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParseCompartmensRelation() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/problematic/elements_in_compartments.xml");
-
-			List<Element> aliases = new ArrayList<>();
-			aliases.add(model.getElementByElementId("csa3"));
-			aliases.add(model.getElementByElementId("csa4"));
-			aliases.add(model.getElementByElementId("csa5"));
-			aliases.add(model.getElementByElementId("csa6"));
-			aliases.add(model.getElementByElementId("sa3"));
-			aliases.add(model.getElementByElementId("sa5"));
-			aliases.add(model.getElementByElementId("sa6"));
-			aliases.add(model.getElementByElementId("sa7"));
-			aliases.add(model.getElementByElementId("sa8"));
-			aliases.add(model.getElementByElementId("sa9"));
-			aliases.add(model.getElementByElementId("sa10"));
-			aliases.add(model.getElementByElementId("sa11"));
-			aliases.add(model.getElementByElementId("sa12"));
-			aliases.add(model.getElementByElementId("sa13"));
-			aliases.add(model.getElementByElementId("sa14"));
-			aliases.add(model.getElementByElementId("sa15"));
-			aliases.add(model.getElementByElementId("sa16"));
-			aliases.add(model.getElementByElementId("sa17"));
-			aliases.add(model.getElementByElementId("sa18"));
-			aliases.add(model.getElementByElementId("sa19"));
-			aliases.add(model.getElementByElementId("sa20"));
-			for (Element alias : aliases) {
-				assertNotNull(eu.getElementTag(alias) + " does not contain info about compartment", alias.getCompartment());
-			}
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParseProteins() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/problematic/acetyled_protein.xml");
-
-			Set<ModificationState> residues = new HashSet<ModificationState>();
-			for (Element element : model.getElements()) {
-				if (element instanceof Species) {
-					Protein p = (Protein) element;
-					for (ModificationResidue mr : p.getModificationResidues()) {
-						residues.add(mr.getState());
-					}
-				}
-			}
-			// we have a protein which is acetylated and not acetylated so two types
-			// of residues
-			assertEquals(2, residues.size());
-
-			assertEquals(ModificationState.ACETYLATED, ((Protein) model.getElementByElementId("sa2")).getModificationResidues().get(0).getState());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testNotesComplexesInNested() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/includedSpecies.xml");
-			Element species = model.getElementByElementId("sa1");
-			assertNotNull(species);
-			assertNotNull(species.getNotes());
-			assertTrue("Wrong notes: " + species.getNotes(), species.getNotes().contains("hello world"));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testHypotheticalComplex() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/problematic/hypothetical_complex.xml");
-			Complex species = (Complex) model.getElementByElementId("csa1");
-			assertTrue(species.isHypothetical());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testProblematicAcetylation() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/problematic/problematic_acetylation.xml");
-			Protein p1 = (Protein) model.getElementByElementId("sa73");
-			Protein p2 = (Protein) model.getElementByElementId("sa27");
-			assertEquals(ModificationState.ACETYLATED, p1.getModificationResidues().get(0).getState());
-			assertFalse(ModificationState.ACETYLATED.equals(p2.getModificationResidues().get(0).getState()));
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testDuplicateMiriam() throws Exception {
-		Model model;
-		try {
-			model = getModelForFile("testFiles/problematic/duplicated_miriam.xml");
-			Protein p1 = (Protein) model.getElementByElementId("sa1");
-			Set<String> ids = new HashSet<>();
-
-			assertTrue(p1.getMiriamData().size() > 0);
-			for (MiriamData md : p1.getMiriamData()) {
-				if (md.getDataType().equals(MiriamType.PUBMED)) {
-					assertFalse("Protein contains double pubmed annotation for pubmed id: " + md.getResource(), ids.contains(md.getResource()));
-					ids.add(md.getResource());
-				}
-			}
-			assertTrue(ids.size() > 0);
-			assertEquals(2, getWarnings().size());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testKappaInDescription() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/problematic/kappa_example.xml");
-
-			Element species = model.getElementByElementId("sa1");
-
-			assertFalse(species.getName().toLowerCase().contains("kappa"));
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testMissingXmlNodes() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/missing_xml_nodes.xml");
-
-			assertTrue(((Species) model.getElementByElementId("sa3")).isHypothetical());
-			assertTrue(((Species) model.getElementByElementId("sa4")).isHypothetical());
-			assertTrue(((Species) model.getElementByElementId("sa5")).isHypothetical());
-
-			assertFalse(((Species) model.getElementByElementId("sa12")).isHypothetical());
-			assertFalse(((Species) model.getElementByElementId("sa13")).isHypothetical());
-			assertFalse(((Species) model.getElementByElementId("sa14")).isHypothetical());
-			assertFalse(((Species) model.getElementByElementId("sa15")).isHypothetical());
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParsingSpecialCharactersInName() throws Exception {
-		try {
-			Model model = new ModelFullIndexed(null);
-			model.setIdModel("as");
-			model.setWidth(100);
-			model.setHeight(100);
-			GenericProtein alias = new GenericProtein("aid");
-			alias.setName("name & no-name");
-			model.addElement(alias);
-
-			CellDesignerXmlParser parser = new CellDesignerXmlParser();
-			String string = parser.toXml(model);
-
-			InputStream stream = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
-
-			ConverterParams params = new ConverterParams().inputStream(stream).sizeAutoAdjust(false);
-
-			Model model2 = parser.createModel(params);
-
-			ModelComparator comparator = new ModelComparator();
-
-			assertEquals(0, comparator.compare(model, model2));
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testParsingEndLineInName() throws Exception {
-		try {
-			Model model = new ModelFullIndexed(null);
-			model.setIdModel("as");
-			model.setWidth(100);
-			model.setHeight(100);
-			GenericProtein alias = new GenericProtein("aid");
-			alias.setName("name\rno-name");
-			model.addElement(alias);
-
-			CellDesignerXmlParser parser = new CellDesignerXmlParser();
-			String string = parser.toXml(model);
-
-			InputStream stream = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
-
-			ConverterParams params = new ConverterParams().inputStream(stream).sizeAutoAdjust(false);
-
-			Model model2 = parser.createModel(params);
-
-			ModelComparator comparator = new ModelComparator();
-
-			assertEquals(0, comparator.compare(model, model2));
-
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testWarningInParser() throws Exception {
-		try {
-			getModelForFile("testFiles/problematic/invalid_elements_name.xml");
-			assertTrue(getWarnings().size() > 0);
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testRnaWithRegion() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/rnaWithRegion.xml");
-			for (Element species : model.getNotComplexSpeciesList()) {
-				Rna rna = (Rna) species;
-				assertEquals(1, rna.getRegions().size());
-			}
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testProtinWithModifications() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/proteinWithEverPossibleModification.xml");
-			Protein protein = (Protein) model.getElementByElementId("sa1");
-			assertEquals(14, protein.getModificationResidues().size());
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testAntisenseRnaWithRegion() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/antisenseRnaWithRegion.xml");
-			for (Species species : model.getNotComplexSpeciesList()) {
-				AntisenseRna rna = (AntisenseRna) species;
-				assertEquals(1, rna.getRegions().size());
-			}
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
-
-	@Test
-	public void testPhosporylatedProteinToXml() throws Exception {
-		try {
-			Model model = getModelForFile("testFiles/problematic/phosphorylated_protein.xml");
-			model.setName(null);
-			CellDesignerXmlParser p = new CellDesignerXmlParser();
-			String xml = p.toXml(model);
-			InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
-			Model model2 = p.createModel(new ConverterParams().inputStream(is).sizeAutoAdjust(false));
-			ModelComparator comparator = new ModelComparator();
-			assertEquals(0, comparator.compare(model, model2));
-		} catch (Exception e) {
-			e.printStackTrace();
-			throw e;
-		}
-	}
+  Logger logger = Logger.getLogger(ComplexParserTests.class);
+
+  ElementUtils eu = new ElementUtils();
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void testParseFunctions() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/function.xml");
+
+      assertEquals(1, model.getFunctions().size());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseCompartmens() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/reactions/centeredAnchorInModifier.xml");
+
+      assertEquals(0, model.getCompartments().size());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseBubbles() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/bubbles.xml");
+
+      Element species = model.getElementByElementId("sa6");
+      assertNotNull(species);
+      Protein protein = (Protein) species;
+      assertTrue(protein.getModificationResidues().size() > 0);
+      assertNotNull(protein.getStructuralState());
+      assertFalse(protein.getStructuralState().equals(""));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseCompartmensRelation() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/problematic/elements_in_compartments.xml");
+
+      List<Element> aliases = new ArrayList<>();
+      aliases.add(model.getElementByElementId("csa3"));
+      aliases.add(model.getElementByElementId("csa4"));
+      aliases.add(model.getElementByElementId("csa5"));
+      aliases.add(model.getElementByElementId("csa6"));
+      aliases.add(model.getElementByElementId("sa3"));
+      aliases.add(model.getElementByElementId("sa5"));
+      aliases.add(model.getElementByElementId("sa6"));
+      aliases.add(model.getElementByElementId("sa7"));
+      aliases.add(model.getElementByElementId("sa8"));
+      aliases.add(model.getElementByElementId("sa9"));
+      aliases.add(model.getElementByElementId("sa10"));
+      aliases.add(model.getElementByElementId("sa11"));
+      aliases.add(model.getElementByElementId("sa12"));
+      aliases.add(model.getElementByElementId("sa13"));
+      aliases.add(model.getElementByElementId("sa14"));
+      aliases.add(model.getElementByElementId("sa15"));
+      aliases.add(model.getElementByElementId("sa16"));
+      aliases.add(model.getElementByElementId("sa17"));
+      aliases.add(model.getElementByElementId("sa18"));
+      aliases.add(model.getElementByElementId("sa19"));
+      aliases.add(model.getElementByElementId("sa20"));
+      for (Element alias : aliases) {
+        assertNotNull(eu.getElementTag(alias) + " does not contain info about compartment", alias.getCompartment());
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParseProteins() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/problematic/acetyled_protein.xml");
+
+      Set<ModificationState> residues = new HashSet<ModificationState>();
+      for (Element element : model.getElements()) {
+        if (element instanceof Species) {
+          Protein p = (Protein) element;
+          for (ModificationResidue mr : p.getModificationResidues()) {
+            residues.add(mr.getState());
+          }
+        }
+      }
+      // we have a protein which is acetylated and not acetylated so two types
+      // of residues
+      assertEquals(2, residues.size());
+
+      assertEquals(ModificationState.ACETYLATED,
+          ((Protein) model.getElementByElementId("sa2")).getModificationResidues().get(0).getState());
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testNotesComplexesInNested() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/includedSpecies.xml");
+      Element species = model.getElementByElementId("sa1");
+      assertNotNull(species);
+      assertNotNull(species.getNotes());
+      assertTrue("Wrong notes: " + species.getNotes(), species.getNotes().contains("hello world"));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testHypotheticalComplex() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/problematic/hypothetical_complex.xml");
+      Complex species = (Complex) model.getElementByElementId("csa1");
+      assertTrue(species.isHypothetical());
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testProblematicAcetylation() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/problematic/problematic_acetylation.xml");
+      Protein p1 = (Protein) model.getElementByElementId("sa73");
+      Protein p2 = (Protein) model.getElementByElementId("sa27");
+      assertEquals(ModificationState.ACETYLATED, p1.getModificationResidues().get(0).getState());
+      assertFalse(ModificationState.ACETYLATED.equals(p2.getModificationResidues().get(0).getState()));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testDuplicateMiriam() throws Exception {
+    Model model;
+    try {
+      model = getModelForFile("testFiles/problematic/duplicated_miriam.xml");
+      Protein p1 = (Protein) model.getElementByElementId("sa1");
+      Set<String> ids = new HashSet<>();
+
+      assertTrue(p1.getMiriamData().size() > 0);
+      for (MiriamData md : p1.getMiriamData()) {
+        if (md.getDataType().equals(MiriamType.PUBMED)) {
+          assertFalse("Protein contains double pubmed annotation for pubmed id: " + md.getResource(),
+              ids.contains(md.getResource()));
+          ids.add(md.getResource());
+        }
+      }
+      assertTrue(ids.size() > 0);
+      assertEquals(2, getWarnings().size());
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testKappaInDescription() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/problematic/kappa_example.xml");
+
+      Element species = model.getElementByElementId("sa1");
+
+      assertFalse(species.getName().toLowerCase().contains("kappa"));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testMissingXmlNodes() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/missing_xml_nodes.xml");
+
+      assertTrue(((Species) model.getElementByElementId("sa3")).isHypothetical());
+      assertTrue(((Species) model.getElementByElementId("sa4")).isHypothetical());
+      assertTrue(((Species) model.getElementByElementId("sa5")).isHypothetical());
+
+      assertFalse(((Species) model.getElementByElementId("sa12")).isHypothetical());
+      assertFalse(((Species) model.getElementByElementId("sa13")).isHypothetical());
+      assertFalse(((Species) model.getElementByElementId("sa14")).isHypothetical());
+      assertFalse(((Species) model.getElementByElementId("sa15")).isHypothetical());
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParsingSpecialCharactersInName() throws Exception {
+    try {
+      Model model = new ModelFullIndexed(null);
+      model.setIdModel("as");
+      model.setWidth(100);
+      model.setHeight(100);
+      GenericProtein alias = new GenericProtein("aid");
+      alias.setName("name & no-name");
+      model.addElement(alias);
+
+      CellDesignerXmlParser parser = new CellDesignerXmlParser();
+      String string = parser.toXml(model);
+
+      InputStream stream = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
+
+      ConverterParams params = new ConverterParams().inputStream(stream).sizeAutoAdjust(false);
+
+      Model model2 = parser.createModel(params);
+
+      ModelComparator comparator = new ModelComparator();
+
+      assertEquals(0, comparator.compare(model, model2));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testParsingEndLineInName() throws Exception {
+    try {
+      Model model = new ModelFullIndexed(null);
+      model.setIdModel("as");
+      model.setWidth(100);
+      model.setHeight(100);
+      GenericProtein alias = new GenericProtein("aid");
+      alias.setName("name\rno-name");
+      model.addElement(alias);
+
+      CellDesignerXmlParser parser = new CellDesignerXmlParser();
+      String string = parser.toXml(model);
+
+      InputStream stream = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
+
+      ConverterParams params = new ConverterParams().inputStream(stream).sizeAutoAdjust(false);
+
+      Model model2 = parser.createModel(params);
+
+      ModelComparator comparator = new ModelComparator();
+
+      assertEquals(0, comparator.compare(model, model2));
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testWarningInParser() throws Exception {
+    try {
+      getModelForFile("testFiles/problematic/invalid_elements_name.xml");
+      assertTrue(getWarnings().size() > 0);
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testRnaWithRegion() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/rnaWithRegion.xml");
+      for (Element species : model.getNotComplexSpeciesList()) {
+        Rna rna = (Rna) species;
+        assertEquals(1, rna.getRegions().size());
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testProtinWithModifications() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/proteinWithEverPossibleModification.xml");
+      Protein protein = (Protein) model.getElementByElementId("sa1");
+      assertEquals(14, protein.getModificationResidues().size());
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testAntisenseRnaWithRegion() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/antisenseRnaWithRegion.xml");
+      for (Species species : model.getNotComplexSpeciesList()) {
+        AntisenseRna rna = (AntisenseRna) species;
+        assertEquals(1, rna.getRegions().size());
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
+
+  @Test
+  public void testPhosporylatedProteinToXml() throws Exception {
+    try {
+      Model model = getModelForFile("testFiles/problematic/phosphorylated_protein.xml");
+      model.setName(null);
+      CellDesignerXmlParser p = new CellDesignerXmlParser();
+      String xml = p.toXml(model);
+      InputStream is = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
+      Model model2 = p.createModel(new ConverterParams().inputStream(is).sizeAutoAdjust(false));
+      ModelComparator comparator = new ModelComparator();
+      assertEquals(0, comparator.compare(model, model2));
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw e;
+    }
+  }
 }
diff --git a/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParserTest.java b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParserTest.java
new file mode 100644
index 0000000000..71af481a35
--- /dev/null
+++ b/converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/function/FunctionXmlParserTest.java
@@ -0,0 +1,49 @@
+package lcsb.mapviewer.converter.model.celldesigner.function;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import lcsb.mapviewer.common.exception.InvalidXmlSchemaException;
+import lcsb.mapviewer.converter.model.celldesigner.CellDesignerTestFunctions;
+import lcsb.mapviewer.model.map.SbmlFunction;
+
+public class FunctionXmlParserTest extends CellDesignerTestFunctions {
+
+  FunctionXmlParser parser = new FunctionXmlParser();
+
+  @Test
+  public void testParseBasicData() throws InvalidXmlSchemaException, IOException {
+    SbmlFunction fun = parser
+        .parseFunction(super.getXmlDocumentFromFile("testFiles/function/simple.xml").getFirstChild());
+    assertNotNull(fun);
+    assertEquals(fun.getFunctionId(), "function_id");
+    assertEquals(fun.getName(), "Function name");
+    assertTrue(fun.getDefinition().indexOf("lambda") >= 0);
+    assertTrue(fun.getDefinition().indexOf("apply") >= 0);
+  }
+
+  @Test
+  public void testParseArgumentsData() throws InvalidXmlSchemaException, IOException {
+    SbmlFunction fun = parser
+        .parseFunction(super.getXmlDocumentFromFile("testFiles/function/simple.xml").getFirstChild());
+    assertNotNull(fun);
+    assertEquals(fun.getArguments().size(), 2);
+    assertEquals(fun.getArguments().get(0), "x");
+  }
+
+  @Test(expected = InvalidXmlSchemaException.class)
+  public void testParseInvalidDefinition() throws InvalidXmlSchemaException, IOException {
+    parser.parseFunction(super.getXmlDocumentFromFile("testFiles/function/invalid_definition.xml").getFirstChild());
+  }
+
+  @Test(expected = InvalidXmlSchemaException.class)
+  public void testParseInvalidMathMLNode() throws InvalidXmlSchemaException, IOException {
+    parser.parseFunction(super.getXmlDocumentFromFile("testFiles/function/invalid_math.xml").getFirstChild());
+  }
+
+}
diff --git a/converter-CellDesigner/testFiles/function.xml b/converter-CellDesigner/testFiles/function.xml
new file mode 100644
index 0000000000..acdb36ed9a
--- /dev/null
+++ b/converter-CellDesigner/testFiles/function.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<sbml xmlns="http://www.sbml.org/sbml/level2/version4" xmlns:celldesigner="http://www.sbml.org/2001/ns/celldesigner" level="2" version="4">
+<model metaid="untitled" id="untitled">
+<annotation>
+<celldesigner:extension>
+<celldesigner:modelVersion>4.0</celldesigner:modelVersion>
+<celldesigner:modelDisplay sizeX="600" sizeY="400"/>
+<celldesigner:listOfCompartmentAliases/>
+<celldesigner:listOfComplexSpeciesAliases/>
+<celldesigner:listOfSpeciesAliases/>
+<celldesigner:listOfGroups/>
+<celldesigner:listOfProteins/>
+<celldesigner:listOfGenes/>
+<celldesigner:listOfRNAs/>
+<celldesigner:listOfAntisenseRNAs/>
+<celldesigner:listOfLayers/>
+<celldesigner:listOfBlockDiagrams/>
+</celldesigner:extension>
+</annotation>
+<listOfFunctionDefinitions>
+<functionDefinition metaid="function_id" id="function_id" name="Function name">
+<math xmlns="http://www.w3.org/1998/Math/MathML">
+<lambda>
+<bvar>
+<ci> x </ci>
+</bvar>
+<bvar>
+<ci> y </ci>
+</bvar>
+<apply>
+<plus/>
+<ci> x </ci>
+<ci> y </ci>
+</apply>
+</lambda>
+</math>
+</functionDefinition>
+</listOfFunctionDefinitions>
+<listOfUnitDefinitions>
+<unitDefinition metaid="substance" id="substance" name="substance">
+<listOfUnits>
+<unit metaid="CDMT00004" kind="mole"/>
+</listOfUnits>
+</unitDefinition>
+<unitDefinition metaid="volume" id="volume" name="volume">
+<listOfUnits>
+<unit metaid="CDMT00005" kind="litre"/>
+</listOfUnits>
+</unitDefinition>
+<unitDefinition metaid="area" id="area" name="area">
+<listOfUnits>
+<unit metaid="CDMT00006" kind="metre" exponent="2"/>
+</listOfUnits>
+</unitDefinition>
+<unitDefinition metaid="length" id="length" name="length">
+<listOfUnits>
+<unit metaid="CDMT00007" kind="metre"/>
+</listOfUnits>
+</unitDefinition>
+<unitDefinition metaid="time" id="time" name="time">
+<listOfUnits>
+<unit metaid="CDMT00008" kind="second"/>
+</listOfUnits>
+</unitDefinition>
+</listOfUnitDefinitions>
+<listOfCompartments>
+<compartment metaid="default" id="default" size="1" units="volume"/>
+</listOfCompartments>
+</model>
+</sbml>
diff --git a/converter-CellDesigner/testFiles/function/invalid_definition.xml b/converter-CellDesigner/testFiles/function/invalid_definition.xml
new file mode 100644
index 0000000000..185faad8a7
--- /dev/null
+++ b/converter-CellDesigner/testFiles/function/invalid_definition.xml
@@ -0,0 +1,2 @@
+<functionDefinition metaid="function_id" id="function_id" name="Function name">
+</functionDefinition>
diff --git a/converter-CellDesigner/testFiles/function/invalid_math.xml b/converter-CellDesigner/testFiles/function/invalid_math.xml
new file mode 100644
index 0000000000..81df2b0f89
--- /dev/null
+++ b/converter-CellDesigner/testFiles/function/invalid_math.xml
@@ -0,0 +1,4 @@
+<functionDefinition metaid="function_id" id="function_id" name="Function name">
+<math xmlns="http://www.w3.org/1998/Math/MathML">
+</math>
+</functionDefinition>
diff --git a/converter-CellDesigner/testFiles/function/simple.xml b/converter-CellDesigner/testFiles/function/simple.xml
new file mode 100644
index 0000000000..8f43b43daa
--- /dev/null
+++ b/converter-CellDesigner/testFiles/function/simple.xml
@@ -0,0 +1,17 @@
+<functionDefinition metaid="function_id" id="function_id" name="Function name">
+<math xmlns="http://www.w3.org/1998/Math/MathML">
+<lambda>
+<bvar>
+<ci> x </ci>
+</bvar>
+<bvar>
+<ci> y </ci>
+</bvar>
+<apply>
+<plus/>
+<ci> x </ci>
+<ci> y </ci>
+</apply>
+</lambda>
+</math>
+</functionDefinition>
-- 
GitLab