From f56b26ff2de513f83179a6b8cfd574ecc5727167 Mon Sep 17 00:00:00 2001
From: Jan Koehnlein <jan.koehnlein@itemis.de>
Date: Fri, 20 Apr 2012 12:09:28 +0200
Subject: [PATCH] [nodemodel] Make node model serializable applied patch
 https://bugs.eclipse.org/bugs/attachment.cgi?id=212979 see
 https://bugs.eclipse.org/bugs/show_bug.cgi?id=359824

---
 .../org.eclipse.xtext/META-INF/MANIFEST.MF    | 180 ++++++-------
 .../xtext/nodemodel/impl/AbstractNode.java    | 118 ++++++++-
 .../xtext/nodemodel/impl/CompositeNode.java   | 138 +++++++++-
 .../CompositeNodeWithSemanticElement.java     |  42 ++-
 ...NodeWithSemanticElementAndSyntaxError.java |  26 ++
 .../impl/CompositeNodeWithSyntaxError.java    |  28 ++
 .../xtext/nodemodel/impl/HiddenLeafNode.java  |   5 +
 .../impl/HiddenLeafNodeWithSyntaxError.java   |  29 +-
 .../xtext/nodemodel/impl/LeafNode.java        |  25 ++
 .../impl/LeafNodeWithSyntaxError.java         |  25 ++
 .../xtext/nodemodel/impl/RootNode.java        |  62 +++++
 .../nodemodel/impl/SerializableNodeModel.java |  78 ++++++
 .../DeserializationConversionContext.java     | 102 +++++++
 .../SerializationConversionContext.java       |  80 ++++++
 .../serialization/SerializationUtil.java      | 248 ++++++++++++++++++
 .../serialization/SerializationUtilTest.java  |  55 ++++
 16 files changed, 1147 insertions(+), 94 deletions(-)
 create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java
 create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java
 create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java
 create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java
 create mode 100644 tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java

diff --git a/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF b/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF
index 42a7480e9..d5f84a922 100644
--- a/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF
@@ -1,90 +1,90 @@
-Manifest-Version: 1.0
-Bundle-ManifestVersion: 2
-Bundle-Name: %pluginName
-Bundle-SymbolicName: org.eclipse.xtext;singleton:=true
-Bundle-Version: 2.3.0.qualifier
-Bundle-ClassPath: .
-Bundle-Vendor: %providerName
-Bundle-Localization: plugin
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
-Export-Package: org.eclipse.xtext,
- org.eclipse.xtext.common,
- org.eclipse.xtext.common.parseTreeConstruction,
- org.eclipse.xtext.common.parser.packrat;x-internal:=true,
- org.eclipse.xtext.common.parser.packrat.consumers;x-internal:=true,
- org.eclipse.xtext.common.services,
- org.eclipse.xtext.conversion,
- org.eclipse.xtext.conversion.impl,
- org.eclipse.xtext.debug;x-internal:=true,
- org.eclipse.xtext.diagnostics,
- org.eclipse.xtext.documentation,
- org.eclipse.xtext.documentation.impl,
- org.eclipse.xtext.formatting,
- org.eclipse.xtext.formatting.impl,
- org.eclipse.xtext.generator,
- org.eclipse.xtext.generator.trace;x-internal:=true,
- org.eclipse.xtext.grammaranalysis;x-internal:=true,
- org.eclipse.xtext.grammaranalysis.impl;x-internal:=true,
- org.eclipse.xtext.impl,
- org.eclipse.xtext.internal;x-internal:=true,
- org.eclipse.xtext.linking,
- org.eclipse.xtext.linking.impl,
- org.eclipse.xtext.linking.lazy,
- org.eclipse.xtext.mwe,
- org.eclipse.xtext.naming,
- org.eclipse.xtext.nodemodel,
- org.eclipse.xtext.nodemodel.impl,
- org.eclipse.xtext.nodemodel.util,
- org.eclipse.xtext.parseTreeConstruction;x-internal:=true,
- org.eclipse.xtext.parser,
- org.eclipse.xtext.parser.antlr,
- org.eclipse.xtext.parser.antlr.internal;x-internal:=true,
- org.eclipse.xtext.parser.impl,
- org.eclipse.xtext.parser.packrat;x-internal:=true,
- org.eclipse.xtext.parser.packrat.consumers;x-internal:=true,
- org.eclipse.xtext.parser.packrat.debug;x-internal:=true,
- org.eclipse.xtext.parser.packrat.internal;x-internal:=true,
- org.eclipse.xtext.parser.packrat.matching;x-internal:=true,
- org.eclipse.xtext.parser.packrat.tokens;x-internal:=true,
- org.eclipse.xtext.parsetree.reconstr,
- org.eclipse.xtext.parsetree.reconstr.impl,
- org.eclipse.xtext.resource,
- org.eclipse.xtext.resource.containers,
- org.eclipse.xtext.resource.generic,
- org.eclipse.xtext.resource.impl,
- org.eclipse.xtext.scoping,
- org.eclipse.xtext.scoping.impl,
- org.eclipse.xtext.serializer,
- org.eclipse.xtext.serializer.acceptor;x-internal:=true,
- org.eclipse.xtext.serializer.analysis;x-internal:=true,
- org.eclipse.xtext.serializer.diagnostic;x-internal:=true,
- org.eclipse.xtext.serializer.impl;x-internal:=true,
- org.eclipse.xtext.serializer.sequencer;x-internal:=true,
- org.eclipse.xtext.serializer.tokens;x-internal:=true,
- org.eclipse.xtext.service,
- org.eclipse.xtext.services;x-internal:=true,
- org.eclipse.xtext.util,
- org.eclipse.xtext.validation,
- org.eclipse.xtext.validation.impl,
- org.eclipse.xtext.xtext;x-internal:=true,
- org.eclipse.xtext.xtext.ecoreInference;x-internal:=true
-Require-Bundle: org.eclipse.emf.ecore.xmi;visibility:=reexport,
- org.eclipse.emf.ecore;bundle-version="2.5.0";visibility:=reexport,
- org.eclipse.xtext.util;bundle-version="2.0.0";visibility:=reexport,
- org.antlr.runtime;bundle-version="[3.2.0,3.2.1)";visibility:=reexport,
- com.google.inject;bundle-version="2.0.0";resolution:=optional;visibility:=reexport,
- org.eclipse.emf.codegen;bundle-version="2.5.0";resolution:=optional,
- org.eclipse.emf.codegen.ecore;bundle-version="2.5.0";resolution:=optional,
- org.eclipse.emf.mwe.core;bundle-version="1.2.0";resolution:=optional;visibility:=reexport,
- org.eclipse.emf.mwe.utils;bundle-version="1.2.0";resolution:=optional;visibility:=reexport,
- org.eclipse.emf.common,
- org.eclipse.xtend;bundle-version="1.1.0";resolution:=optional,
- org.eclipse.xtend.typesystem.emf;bundle-version="1.0.1";resolution:=optional,
- org.eclipse.core.runtime;bundle-version="3.5.0";resolution:=optional,
- org.eclipse.core.resources;bundle-version="3.5.0";resolution:=optional,
- org.eclipse.jdt.annotation;bundle-version="1.0.0";resolution:=optional,
- org.eclipse.xtext.smap;bundle-version="2.3.0";resolution:=optional
-Import-Package: org.apache.log4j;version="1.2.15"
-Bundle-ActivationPolicy: lazy
-Bundle-Activator: org.eclipse.xtext.internal.Activator
-
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.xtext;singleton:=true
+Bundle-Version: 2.3.0.qualifier
+Bundle-ClassPath: .
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.eclipse.xtext,
+ org.eclipse.xtext.common,
+ org.eclipse.xtext.common.parseTreeConstruction,
+ org.eclipse.xtext.common.parser.packrat;x-internal:=true,
+ org.eclipse.xtext.common.parser.packrat.consumers;x-internal:=true,
+ org.eclipse.xtext.common.services,
+ org.eclipse.xtext.conversion,
+ org.eclipse.xtext.conversion.impl,
+ org.eclipse.xtext.debug;x-internal:=true,
+ org.eclipse.xtext.diagnostics,
+ org.eclipse.xtext.documentation,
+ org.eclipse.xtext.documentation.impl,
+ org.eclipse.xtext.formatting,
+ org.eclipse.xtext.formatting.impl,
+ org.eclipse.xtext.generator,
+ org.eclipse.xtext.generator.trace;x-internal:=true,
+ org.eclipse.xtext.grammaranalysis;x-internal:=true,
+ org.eclipse.xtext.grammaranalysis.impl;x-internal:=true,
+ org.eclipse.xtext.impl,
+ org.eclipse.xtext.internal;x-internal:=true,
+ org.eclipse.xtext.linking,
+ org.eclipse.xtext.linking.impl,
+ org.eclipse.xtext.linking.lazy,
+ org.eclipse.xtext.mwe,
+ org.eclipse.xtext.naming,
+ org.eclipse.xtext.nodemodel,
+ org.eclipse.xtext.nodemodel.impl,
+ org.eclipse.xtext.nodemodel.serialization;x-friends:="org.eclipse.xtext.builder,org.eclipse.xtext.tests",
+ org.eclipse.xtext.nodemodel.util,
+ org.eclipse.xtext.parseTreeConstruction;x-internal:=true,
+ org.eclipse.xtext.parser,
+ org.eclipse.xtext.parser.antlr,
+ org.eclipse.xtext.parser.antlr.internal;x-internal:=true,
+ org.eclipse.xtext.parser.impl,
+ org.eclipse.xtext.parser.packrat;x-internal:=true,
+ org.eclipse.xtext.parser.packrat.consumers;x-internal:=true,
+ org.eclipse.xtext.parser.packrat.debug;x-internal:=true,
+ org.eclipse.xtext.parser.packrat.internal;x-internal:=true,
+ org.eclipse.xtext.parser.packrat.matching;x-internal:=true,
+ org.eclipse.xtext.parser.packrat.tokens;x-internal:=true,
+ org.eclipse.xtext.parsetree.reconstr,
+ org.eclipse.xtext.parsetree.reconstr.impl,
+ org.eclipse.xtext.resource,
+ org.eclipse.xtext.resource.containers,
+ org.eclipse.xtext.resource.generic,
+ org.eclipse.xtext.resource.impl,
+ org.eclipse.xtext.scoping,
+ org.eclipse.xtext.scoping.impl,
+ org.eclipse.xtext.serializer,
+ org.eclipse.xtext.serializer.acceptor;x-internal:=true,
+ org.eclipse.xtext.serializer.analysis;x-internal:=true,
+ org.eclipse.xtext.serializer.diagnostic;x-internal:=true,
+ org.eclipse.xtext.serializer.impl;x-internal:=true,
+ org.eclipse.xtext.serializer.sequencer;x-internal:=true,
+ org.eclipse.xtext.serializer.tokens;x-internal:=true,
+ org.eclipse.xtext.service,
+ org.eclipse.xtext.services;x-internal:=true,
+ org.eclipse.xtext.util,
+ org.eclipse.xtext.validation,
+ org.eclipse.xtext.validation.impl,
+ org.eclipse.xtext.xtext;x-internal:=true,
+ org.eclipse.xtext.xtext.ecoreInference;x-internal:=true
+Require-Bundle: org.eclipse.emf.ecore.xmi;visibility:=reexport,
+ org.eclipse.emf.ecore;bundle-version="2.5.0";visibility:=reexport,
+ org.eclipse.xtext.util;bundle-version="2.0.0";visibility:=reexport,
+ org.antlr.runtime;bundle-version="[3.2.0,3.2.1)";visibility:=reexport,
+ com.google.inject;bundle-version="2.0.0";resolution:=optional;visibility:=reexport,
+ org.eclipse.emf.codegen;bundle-version="2.5.0";resolution:=optional,
+ org.eclipse.emf.codegen.ecore;bundle-version="2.5.0";resolution:=optional,
+ org.eclipse.emf.mwe.core;bundle-version="1.2.0";resolution:=optional;visibility:=reexport,
+ org.eclipse.emf.mwe.utils;bundle-version="1.2.0";resolution:=optional;visibility:=reexport,
+ org.eclipse.emf.common,
+ org.eclipse.xtend;bundle-version="1.1.0";resolution:=optional,
+ org.eclipse.xtend.typesystem.emf;bundle-version="1.0.1";resolution:=optional,
+ org.eclipse.core.runtime;bundle-version="3.5.0";resolution:=optional,
+ org.eclipse.core.resources;bundle-version="3.5.0";resolution:=optional,
+ org.eclipse.jdt.annotation;bundle-version="1.0.0";resolution:=optional,
+ org.eclipse.xtext.smap;bundle-version="2.3.0";resolution:=optional
+Import-Package: org.apache.log4j;version="1.2.15"
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.eclipse.xtext.internal.Activator
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/AbstractNode.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/AbstractNode.java
index 96fc1a915..2b6bb2d6c 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/AbstractNode.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/AbstractNode.java
@@ -7,10 +7,17 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
+import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
 import org.eclipse.xtext.nodemodel.BidiIterator;
 import org.eclipse.xtext.nodemodel.BidiTreeIterable;
 import org.eclipse.xtext.nodemodel.BidiTreeIterator;
@@ -18,6 +25,9 @@ import org.eclipse.xtext.nodemodel.ICompositeNode;
 import org.eclipse.xtext.nodemodel.ILeafNode;
 import org.eclipse.xtext.nodemodel.INode;
 import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 import org.eclipse.xtext.nodemodel.util.NodeTreeIterator;
 import org.eclipse.xtext.nodemodel.util.ReversedBidiTreeIterable;
 import org.eclipse.xtext.util.Strings;
@@ -26,6 +36,7 @@ import com.google.common.collect.Iterators;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public abstract class AbstractNode implements INode, BidiTreeIterable<INode> {
@@ -174,7 +185,7 @@ public abstract class AbstractNode implements INode, BidiTreeIterable<INode> {
 	public ICompositeNode getRootNode() {
 		if (parent == null)
 			return null;
-		CompositeNode candidate = parent;
+		AbstractNode candidate = parent;
 		while(candidate.basicGetParent() != null)
 			candidate = candidate.basicGetParent();
 		return candidate.getRootNode();
@@ -266,4 +277,109 @@ public abstract class AbstractNode implements INode, BidiTreeIterable<INode> {
 		return prev != this;
 	}
 
+	enum NodeType {
+		CompositeNode, LeafNode, CompositeNodeWithSemanticElement, CompositeNodeWithSyntaxError, CompositeNodeWithSemanticElementAndSyntaxError, RootNode, HiddenLeafNode, HiddenLeafNodeWithSyntaxError, LeafNodeWithSyntaxError
+	}
+
+	abstract NodeType getNodeId();
+	
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		int length = SerializationUtil.readInt(in, true);
+
+		if (length == 1) {
+			int grammarId = SerializationUtil.readInt(in, true);
+			grammarElementOrArray = context.getGrammarElement(grammarId);
+		} else {
+			if (length > 0) {
+				EObject[] grammarElements = new EObject[length];
+				for (int i = 0; i < length; ++i) {
+					int grammarId = SerializationUtil.readInt(in, true);
+					EObject grammarElement = context.getGrammarElement(grammarId);
+					grammarElements[i] = grammarElement;
+				}
+				grammarElementOrArray = grammarElements;
+			} else {
+				if (length != -1) {
+					throw new IllegalStateException("Read unexpected length of grammar element array from stream: "
+							+ length);
+				}
+
+				grammarElementOrArray = null;
+			}
+		}
+	}
+
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		if (grammarElementOrArray instanceof EObject) {
+			EObject eObject = (EObject) grammarElementOrArray;
+			SerializationUtil.writeInt(out, 1, true);
+			writeGrammarId(out, scc, eObject);
+		} else {
+			if (grammarElementOrArray instanceof EObject[]) {
+				EObject[] eObjects = (EObject[]) grammarElementOrArray;
+				SerializationUtil.writeInt(out, eObjects.length, true);
+
+				for (EObject eObject : eObjects) {
+					writeGrammarId(out, scc, eObject);
+				}
+			} else {
+				SerializationUtil.writeInt(out, -1, true);
+			}
+		}
+	}
+
+	private void writeGrammarId(DataOutputStream out, SerializationConversionContext scc, EObject eObject)
+			throws IOException {
+		Integer grammarId = scc.getGrammarElementId(eObject);
+		if (grammarId == null) {
+			throw new IllegalStateException("Must write a grammar element but got an unknown EMF object of class "
+					+ eObject.getClass().getName());
+		}
+
+		SerializationUtil.writeInt(out, grammarId.intValue(), true);
+	}
+
+	int fillGrammarElementToIdMap(int currentId, Map<EObject, Integer> grammarElementToIdMap,
+			List<String> grammarIdToURIMap) {
+		if (grammarElementOrArray != null) {
+			if (grammarElementOrArray instanceof EObject) {
+				EObject grammarElement = (EObject) grammarElementOrArray;
+				currentId = updateMapping(currentId, grammarElementToIdMap, grammarIdToURIMap, grammarElement);
+			}
+
+			if (grammarElementOrArray instanceof EObject[]) {
+				EObject[] grammarElements = (EObject[]) grammarElementOrArray;
+				for (EObject grammarElement : grammarElements) {
+					currentId = updateMapping(currentId, grammarElementToIdMap, grammarIdToURIMap, grammarElement);
+				}
+			}
+		}
+
+		return currentId;
+	}
+
+	private int updateMapping(int currentId, Map<EObject, Integer> grammarElementToIdMap,
+			List<String> grammarIdToURIMap, EObject grammarElement) {
+		if (!grammarElementToIdMap.containsKey(grammarElement)) {
+			URI uri = EcoreUtil.getURI(grammarElement);
+			if (uri == null) {
+				throw new IllegalStateException("While building the map of grammar elements to an ID, "
+						+ "got a grammar element that does not have an URI.  The " + "grammar element has class "
+						+ grammarElement.eClass().getName());
+			}
+			grammarElementToIdMap.put(grammarElement, currentId);
+			grammarIdToURIMap.add(uri.toString());
+			++currentId;
+		}
+
+		if (currentId != grammarIdToURIMap.size()) {
+			throw new IllegalStateException("The next id for a grammar element will be " + currentId
+					+ " but the number of elements in "
+					+ "the map of grammar elements to IDs contains a different number of elements: "
+					+ grammarIdToURIMap.size());
+		}
+
+		return currentId;
+	}
+	
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNode.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNode.java
index d643395ee..edf31e998 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNode.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNode.java
@@ -7,18 +7,28 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.nodemodel.BidiIterable;
 import org.eclipse.xtext.nodemodel.BidiTreeIterator;
 import org.eclipse.xtext.nodemodel.ICompositeNode;
 import org.eclipse.xtext.nodemodel.ILeafNode;
 import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 import org.eclipse.xtext.nodemodel.util.EmptyBidiIterable;
 import org.eclipse.xtext.nodemodel.util.NodeIterable;
 import org.eclipse.xtext.nodemodel.util.SingletonBidiIterable;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class CompositeNode extends AbstractNode implements ICompositeNode {
@@ -70,7 +80,7 @@ public class CompositeNode extends AbstractNode implements ICompositeNode {
 	public int getTotalOffset() {
 		if (firstChild != null)
 			return firstChild.getTotalOffset();
-		CompositeNode compositeWithSiblings = this;
+		AbstractNode compositeWithSiblings = this;
 		while(!compositeWithSiblings.basicHasNextSibling() && compositeWithSiblings.basicGetParent() != null) {
 			compositeWithSiblings = compositeWithSiblings.basicGetParent();
 		}
@@ -153,5 +163,131 @@ public class CompositeNode extends AbstractNode implements ICompositeNode {
 		EObject[] grammarElements = (EObject[]) grammarElementOrArray;
 		return grammarElements[0];
 	}
+
+	private static final NodeType[] NODE_TYPE_VALUES = NodeType.values();
 	
+	@Override 
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+
+		int childNodeCount = SerializationUtil.readInt(in, true);
+
+		if (childNodeCount > 0) {
+			AbstractNode child = null;
+			AbstractNode prevChild = null;
+			for (int i = 0; i < childNodeCount; ++i) {
+				int nodeId = SerializationUtil.readInt(in, true);
+				NodeType nodeType = NODE_TYPE_VALUES[nodeId];
+				child = createChildNode(nodeType);
+				child.readData(in, context);
+
+				if (firstChild == null) {
+					firstChild = child;
+				}
+
+				child.basicSetParent(this);
+				child.basicSetPreviousSibling(prevChild);
+
+				prevChild = child;
+			}
+
+			firstChild.basicSetPreviousSibling(child);
+
+			// All left links are fine, now the right ones
+
+			child = firstChild.basicGetPreviousSibling();
+			prevChild = firstChild;
+
+			while (child != firstChild) {
+				child.basicSetNextSibling(prevChild);
+				prevChild = child;
+				child = child.basicGetPreviousSibling();
+			}
+
+			firstChild.basicSetNextSibling(prevChild);
+		}
+
+		lookAhead = SerializationUtil.readInt(in, true);
+	}
+	
+	private AbstractNode createChildNode(AbstractNode.NodeType type) {
+		switch (type) {
+			case CompositeNode:
+				return new CompositeNode();
+			case CompositeNodeWithSemanticElement:
+				return new CompositeNodeWithSemanticElement();
+			case CompositeNodeWithSemanticElementAndSyntaxError:
+				return new CompositeNodeWithSemanticElementAndSyntaxError();
+			case CompositeNodeWithSyntaxError:
+				return new CompositeNodeWithSyntaxError();
+			case HiddenLeafNode:
+				return new HiddenLeafNode();
+			case HiddenLeafNodeWithSyntaxError:
+				return new HiddenLeafNodeWithSyntaxError();
+			case LeafNode:
+				return new LeafNode();
+			case LeafNodeWithSyntaxError:
+				return new LeafNodeWithSyntaxError();
+			case RootNode:
+				return new RootNode();
+			default:
+				throw new IllegalArgumentException("Trying to construct a non-existing INode");
+		}
+	}
+	
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+
+		int childNodeCount = getChildCount();
+		SerializationUtil.writeInt(out, childNodeCount, true);
+
+		AbstractNode it = firstChild;
+
+		for (int i = 0; i < childNodeCount; ++i) {
+			SerializationUtil.writeInt(out, it.getNodeId().ordinal(), true);
+			it.write(out, scc);
+			it = it.basicGetNextSibling();
+		}
+
+		SerializationUtil.writeInt(out, lookAhead, true);
+	}
+
+	private int getChildCount() {
+		if (firstChild == null) {
+			return 0;
+		}
+
+		AbstractNode it = firstChild;
+		int count = 0;
+
+		do {
+			++count;
+			it = it.basicGetNextSibling();
+		} while (it != firstChild);
+
+		return count;
+	}
+
+	@Override
+	int fillGrammarElementToIdMap(int currentId, Map<EObject, Integer> grammarElementToIdMap,
+			List<String> grammarIdToURIMap) {
+		currentId = super.fillGrammarElementToIdMap(currentId, grammarElementToIdMap, grammarIdToURIMap);
+
+		if (firstChild != null) {
+			AbstractNode it = firstChild;
+
+			do {
+				currentId = it.fillGrammarElementToIdMap(currentId, grammarElementToIdMap, grammarIdToURIMap);
+				it = it.basicGetNextSibling();
+			} while (it != firstChild);
+		}
+
+		return currentId;
+	}
+
+	@Override
+	NodeType getNodeId() {
+		return NodeType.CompositeNode;
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElement.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElement.java
index 20dd4d276..4c178e32d 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElement.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElement.java
@@ -7,14 +7,22 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 import org.eclipse.emf.common.notify.Adapter;
 import org.eclipse.emf.common.notify.Notification;
 import org.eclipse.emf.common.notify.Notifier;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class CompositeNodeWithSemanticElement extends CompositeNode implements Adapter {
@@ -55,5 +63,37 @@ public class CompositeNodeWithSemanticElement extends CompositeNode implements A
 	public boolean isAdapterForType(Object type) {
 		return type instanceof Class<?> && INode.class.isAssignableFrom((Class<?>)type);
 	}
-	
+
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+
+		boolean isNull = in.readBoolean();
+
+		if (!isNull) {
+			int id = SerializationUtil.readInt(in, true);
+
+			semanticElement = context.getSemanticObject(id);
+			semanticElement.eAdapters().add(this);
+		}
+	}
+
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+
+		boolean isNull = semanticElement == null;
+
+		out.writeBoolean(isNull);
+
+		if (!isNull) {
+			Integer id = scc.getEObjectId(semanticElement);
+			SerializationUtil.writeInt(out, id, true);
+		}
+	}
+
+	@Override
+	NodeType getNodeId() {
+		return NodeType.CompositeNodeWithSemanticElement;
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElementAndSyntaxError.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElementAndSyntaxError.java
index f3a9ad7fb..6f6667a42 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElementAndSyntaxError.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSemanticElementAndSyntaxError.java
@@ -7,10 +7,18 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class CompositeNodeWithSemanticElementAndSyntaxError extends CompositeNodeWithSemanticElement {
@@ -25,4 +33,22 @@ public class CompositeNodeWithSemanticElementAndSyntaxError extends CompositeNod
 	protected void basicSetSyntaxErrorMessage(SyntaxErrorMessage syntaxErrorMessage) {
 		this.syntaxErrorMessage = syntaxErrorMessage;
 	}
+
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+		syntaxErrorMessage = SerializationUtil.readSyntaxErrorMessage(in, context);
+		context.setHasErrors(true); 
+	}
+
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+		SerializationUtil.writeSyntaxErrorMessage(out, scc, syntaxErrorMessage);
+	}
+
+	@Override
+	NodeType getNodeId() {
+		return NodeType.CompositeNodeWithSemanticElementAndSyntaxError; 
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSyntaxError.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSyntaxError.java
index 2f4485518..a3ce272a8 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSyntaxError.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/CompositeNodeWithSyntaxError.java
@@ -7,10 +7,19 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class CompositeNodeWithSyntaxError extends CompositeNode {
@@ -25,5 +34,24 @@ public class CompositeNodeWithSyntaxError extends CompositeNode {
 	protected void basicSetSyntaxErrorMessage(SyntaxErrorMessage syntaxErrorMessage) {
 		this.syntaxErrorMessage = syntaxErrorMessage;
 	}
+
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+		
+		syntaxErrorMessage = SerializationUtil.readSyntaxErrorMessage(in, context); 
+		context.setHasErrors(true);
+	}
 	
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+
+		SerializationUtil.writeSyntaxErrorMessage(out, scc, syntaxErrorMessage); 
+	}
+	
+	@Override
+	NodeType getNodeId() {
+		return NodeType.CompositeNodeWithSyntaxError;
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNode.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNode.java
index 9af677c55..cdd53ea31 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNode.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNode.java
@@ -9,6 +9,7 @@ package org.eclipse.xtext.nodemodel.impl;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class HiddenLeafNode extends LeafNode {
@@ -18,4 +19,8 @@ public class HiddenLeafNode extends LeafNode {
 		return true;
 	}
 	
+	@Override
+	NodeType getNodeId() {
+		return NodeType.HiddenLeafNode; 
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNodeWithSyntaxError.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNodeWithSyntaxError.java
index 04ee0b3e3..abc19ba0a 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNodeWithSyntaxError.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/HiddenLeafNodeWithSyntaxError.java
@@ -7,10 +7,18 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class HiddenLeafNodeWithSyntaxError extends HiddenLeafNode {
@@ -25,5 +33,24 @@ public class HiddenLeafNodeWithSyntaxError extends HiddenLeafNode {
 	protected void basicSetSyntaxErrorMessage(SyntaxErrorMessage syntaxErrorMessage) {
 		this.syntaxErrorMessage = syntaxErrorMessage;
 	}
+
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+		
+		syntaxErrorMessage = SerializationUtil.readSyntaxErrorMessage(in, context); 
+		context.setHasErrors(true);
+	}
+
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+
+		SerializationUtil.writeSyntaxErrorMessage(out, scc, syntaxErrorMessage); 
+	}
 	
-}
+	@Override
+	NodeType getNodeId() {
+		return NodeType.HiddenLeafNodeWithSyntaxError;  
+	}
+}
\ No newline at end of file
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNode.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNode.java
index e8fc29b91..15d98cba8 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNode.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNode.java
@@ -7,12 +7,19 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
 import java.util.Collections;
 
 import org.eclipse.xtext.nodemodel.ILeafNode;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class LeafNode extends AbstractNode implements ILeafNode {
@@ -56,4 +63,22 @@ public class LeafNode extends AbstractNode implements ILeafNode {
 		return Collections.<ILeafNode>singletonList(this);
 	}
 
+	@Override 
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+		
+		totalLength = SerializationUtil.readInt(in, true);   
+	}
+	
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+		
+		SerializationUtil.writeInt (out, totalLength, true); 
+	}
+
+	@Override
+	NodeType getNodeId() {
+		return NodeType.LeafNode;
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNodeWithSyntaxError.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNodeWithSyntaxError.java
index f8813f5b3..32f721d92 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNodeWithSyntaxError.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/LeafNodeWithSyntaxError.java
@@ -7,10 +7,18 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
 import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class LeafNodeWithSyntaxError extends LeafNode {
@@ -25,5 +33,22 @@ public class LeafNodeWithSyntaxError extends LeafNode {
 	protected void basicSetSyntaxErrorMessage(SyntaxErrorMessage syntaxErrorMessage) {
 		this.syntaxErrorMessage = syntaxErrorMessage;
 	}
+
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
+		syntaxErrorMessage = SerializationUtil.readSyntaxErrorMessage(in, context);
+		context.setHasErrors(true);
+	}
+
+	@Override
+	void write(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		super.write(out, scc);
+		SerializationUtil.writeSyntaxErrorMessage(out, scc, syntaxErrorMessage);
+	}
 	
+	@Override
+	NodeType getNodeId() {
+		return NodeType.LeafNodeWithSyntaxError;
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/RootNode.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/RootNode.java
index 1d6677cd9..5747aebd2 100644
--- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/RootNode.java
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/RootNode.java
@@ -7,15 +7,21 @@
  *******************************************************************************/
 package org.eclipse.xtext.nodemodel.impl;
 
+import java.io.DataInputStream;
+import java.io.IOException;
 import java.util.List;
+import java.util.Map;
 
+import org.eclipse.emf.ecore.EObject;
 import org.eclipse.xtext.nodemodel.ICompositeNode;
 import org.eclipse.xtext.nodemodel.INode;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
 
 import com.google.common.collect.Lists;
 
 /**
  * @author Sebastian Zarnekow - Initial contribution and API
+ * @author Mark Christiaens - Serialization support
  * @noextend This class is not intended to be subclassed by clients.
  */
 public class RootNode extends CompositeNodeWithSemanticElementAndSyntaxError {
@@ -168,5 +174,61 @@ public class RootNode extends CompositeNodeWithSemanticElementAndSyntaxError {
 		}
 		return result;
 	}
+	
+	@Override
+	void readData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		super.readData(in, context);
 
+		basicSetCompleteContent(context.getCompleteContent());
+
+		int totalLength = fixupOffsets(this, 0);
+
+		if (totalLength != getCompleteContent().length()) {
+			throw new IllegalStateException("The length of the resource's content was " + getCompleteContent().length()
+					+ " but the length calculated based upon the serialized form of the RootNode was " + totalLength);
+		}
+	}
+	
+	private int fixupOffsets(INode node, int nodeOffset) {
+		if (node instanceof LeafNode) {
+			LeafNode leafNode = (LeafNode) node;
+			leafNode.basicSetTotalOffset(nodeOffset);
+			return leafNode.getTotalLength() + nodeOffset;
+		}
+
+		if (node instanceof CompositeNode) {
+			CompositeNode compositeNode = (CompositeNode) node;
+
+			int currentOffset = nodeOffset;
+
+			AbstractNode firstChild = compositeNode.basicGetFirstChild();
+
+			if (firstChild != null) {
+				AbstractNode it = firstChild;
+
+				do {
+					currentOffset = fixupOffsets(it, currentOffset);
+					it = it.basicGetNextSibling();
+				} while (it != firstChild);
+			}
+
+			return currentOffset;
+		}
+
+		return 0;
+	}
+
+	@Override
+	NodeType getNodeId() {
+		return NodeType.RootNode;
+	}
+
+	/**
+	 * @since 2.3
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public void fillGrammarElementToIdMap(Map<EObject, Integer> grammarElementToIdMap,
+			List<String> grammarIdToURIMap) {
+		fillGrammarElementToIdMap(0, grammarElementToIdMap, grammarIdToURIMap);
+	}
 }
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java
new file mode 100644
index 000000000..1967baf8d
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/impl/SerializableNodeModel.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi N.V. (http://www.sigasi.com) and others. All rights reserved. This program and the
+ * accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.xtext.nodemodel.impl;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.xtext.nodemodel.ICompositeNode;
+import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
+import org.eclipse.xtext.nodemodel.serialization.SerializationConversionContext;
+import org.eclipse.xtext.resource.XtextResource;
+
+/**
+ * @author Mark Christiaens - Initial contribution
+ * 
+ * @since 2.3
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class SerializableNodeModel {
+	public int formatVersion;
+	public Date date;
+	public RootNode root;
+
+	public SerializableNodeModel(XtextResource resource) {
+		ICompositeNode rootNode = resource.getParseResult().getRootNode();
+
+		if (rootNode != null) {
+			root = (RootNode) rootNode;
+		}
+
+		formatVersion = 1;
+		date = new Date();
+	}
+
+	public SerializableNodeModel() {
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public void writeObjectData(DataOutputStream out, SerializationConversionContext scc) throws IOException {
+		String[] grammarIdToURIMap = scc.getGrammarIdToURIMap();
+
+		out.writeInt(grammarIdToURIMap.length);
+		for (String string : grammarIdToURIMap) {
+			out.writeUTF(string);
+		}
+
+		root.write(out, scc);
+	}
+
+	/**
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public void readObjectData(DataInputStream in, DeserializationConversionContext context) throws IOException {
+		int grammarIdToURIMapLength = in.readInt();
+
+		String[] grammarIdToURIMap = new String[grammarIdToURIMapLength];
+		for (int i = 0; i < grammarIdToURIMapLength; ++i) {
+			grammarIdToURIMap[i] = in.readUTF();
+			if (grammarIdToURIMap[i] == null) {
+				throw new IllegalStateException("During deserialzing the grammar id to URI map got a null reference. ");
+			}
+		}
+
+		context.setGrammarIdToURIMap(grammarIdToURIMap);
+
+		root = new RootNode();
+		root.readData(in, context);
+	}
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java
new file mode 100644
index 000000000..053276709
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/DeserializationConversionContext.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi N.V. (http://www.sigasi.com) and others. All rights reserved. This program and the
+ * accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.xtext.nodemodel.serialization;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.xtext.IGrammarAccess;
+import org.eclipse.xtext.resource.XtextResource;
+
+import com.google.common.collect.Lists;
+
+/**
+ * @author mark.christiaens - Initial contribution and API
+ * 
+ * @since 2.3
+ */
+public class DeserializationConversionContext {
+	private EObject[] grammarIdToGrammarElementMap;
+
+	final private List<EObject> idToEObjectMap;
+
+	final private IGrammarAccess grammarAccess;
+
+	final private String completeContent;
+
+	private boolean hasErrors;
+
+	public DeserializationConversionContext(XtextResource xr, String completeContent) throws IOException {
+		this.grammarAccess = xr.getResourceServiceProvider().get(IGrammarAccess.class);
+		this.idToEObjectMap = Lists.newArrayList();
+		this.completeContent = completeContent;
+		this.hasErrors = false;
+		fillIdToEObjectMap(xr);
+	}
+
+	public void setGrammarIdToURIMap(String[] grammarIdToURIMap) {
+		grammarIdToGrammarElementMap = new EObject[grammarIdToURIMap.length];
+
+		ResourceSet grammarResourceSet = grammarAccess.getGrammar().eResource().getResourceSet();
+		for (int grammarId = 0; grammarId < grammarIdToURIMap.length; ++grammarId) {
+			URI uri = URI.createURI(grammarIdToURIMap[grammarId], true);
+			EObject grammarElement = grammarResourceSet.getEObject(uri, true);
+
+			if (grammarElement == null) {
+				throw new IllegalStateException(
+						"Apparently the grammar has changed so that it's no longer possible to identify the "
+								+ "serialized grammar elements.  The following grammar element URI is no longer valid: "
+								+ uri.toString());
+			}
+
+			grammarIdToGrammarElementMap[grammarId] = grammarElement;
+		}
+	}
+
+	public EObject getGrammarElement(int grammarId) {
+		if (grammarId >= grammarIdToGrammarElementMap.length) {
+			throw new IllegalStateException(
+					"Trying to obtain a grammar element that does not (or no longer) exists with id: " + grammarId);
+		}
+
+		EObject result = grammarIdToGrammarElementMap[grammarId];
+
+		return result;
+	}
+
+	public void fillIdToEObjectMap(Resource resource) {
+		SerializationUtil.fillIdToEObjectMap(resource, idToEObjectMap);
+	}
+
+	public EObject getSemanticObject(int id) {
+		EObject eObject = idToEObjectMap.get(id);
+
+		if (eObject == null) {
+			throw new IllegalStateException(
+					"Trying to get an EMF object in the EMF resource that does not exist.  We are looking for id: "
+							+ id);
+		}
+
+		return eObject;
+	}
+
+	public void setHasErrors(boolean hasErrors) {
+		this.hasErrors = hasErrors;
+	}
+
+	boolean hasErrors() {
+		return hasErrors;
+	}
+
+	public String getCompleteContent() {
+		return completeContent;
+	}
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java
new file mode 100644
index 000000000..c7fa4e835
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationConversionContext.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi N.V. (http://www.sigasi.com) and others. All rights reserved. This program and the
+ * accompanying materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this
+ * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.xtext.nodemodel.serialization;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.nodemodel.impl.RootNode;
+import org.eclipse.xtext.resource.XtextResource;
+
+/**
+ * @author Mark Christiaens - Initial contribution
+ * 
+ * @since 2.3
+ */
+
+public class SerializationConversionContext {
+	final private Map<EObject, Integer> grammarElementToIdMap;
+	final private ArrayList<String> grammarIdToURIMap;
+	final private Map<EObject, Integer> eObjectToIdMap;
+
+	public SerializationConversionContext(XtextResource resource) {
+		grammarElementToIdMap = new IdentityHashMap<EObject, Integer>();
+		grammarIdToURIMap = new ArrayList<String>();
+		eObjectToIdMap = new IdentityHashMap<EObject, Integer>();
+
+		fillEObjectToIdMap(resource);
+		fillGrammarElementToIdMap(resource);
+	}
+
+	public Integer getGrammarElementId(EObject grammarElement) {
+		final Integer id = grammarElementToIdMap.get(grammarElement);
+
+		if (id == null) {
+			throw new IllegalArgumentException(
+					"Trying to fetch a grammar element that does not (no longer) exists with id: " + id);
+		}
+
+		return id;
+	}
+
+	protected void fillEObjectToIdMap(Resource resource) {
+		ArrayList<EObject> idToEObjectMap = new ArrayList<EObject>();
+
+		SerializationUtil.fillIdToEObjectMap(resource, idToEObjectMap);
+
+		for (int id = 0; id < idToEObjectMap.size(); ++id) {
+			eObjectToIdMap.put(idToEObjectMap.get(id), id);
+		}
+	}
+
+	protected void fillGrammarElementToIdMap(XtextResource r) {
+		RootNode it = (RootNode) r.getParseResult().getRootNode();
+		it.fillGrammarElementToIdMap(grammarElementToIdMap, grammarIdToURIMap);
+	}
+
+	public Integer getEObjectId(EObject eObject) {
+		Integer id = eObjectToIdMap.get(eObject);
+
+		if (id == null) {
+			throw new IllegalArgumentException("Tryin to fetch an EMF object that does not exist (no longer) with id: "
+					+ id);
+		}
+
+		return id;
+	}
+
+	public String[] getGrammarIdToURIMap() {
+		String[] map = grammarIdToURIMap.toArray(new String[0]);
+
+		return map;
+	}
+}
diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java
new file mode 100644
index 000000000..bd40cefc4
--- /dev/null
+++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtil.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.xtext.nodemodel.serialization;
+
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.URIConverter;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.resource.XtextResource;
+import org.eclipse.xtext.resource.XtextResourceSet;
+
+/**
+ * @author mark.christiaens - Initial contribution and API
+ * 
+ * @since 2.3
+ * @noextend This class is not intended to be subclassed by clients.
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ */
+public class SerializationUtil {
+	public static final int KIB = 1024;
+	
+	public static void fillIdToEObjectMap(Resource resource, List<EObject> map) {
+		TreeIterator<EObject> allContents = EcoreUtil.getAllContents(resource, false);  
+				
+		if (allContents.hasNext()) {
+			EObject root = allContents.next();
+			fillIdToEObjectMap(root, map);
+		}
+	}
+
+	public static void fillIdToEObjectMap(EObject eObject, List<EObject> map) {
+		map.add(eObject);
+
+		EList<EObject> eContents = eObject.eContents();
+
+		for (EObject child : eContents) {
+			fillIdToEObjectMap(child, map);
+		}
+	}
+
+	public static long milliDiff(long startLoad, long doneLoad) {
+		return (doneLoad - startLoad) / 1000000;
+	}
+
+	public static String getCompleteContent(XtextResource xr) throws IOException, UnsupportedEncodingException {
+		XtextResourceSet resourceSet = (XtextResourceSet) xr.getResourceSet();
+		URIConverter uriConverter = resourceSet.getURIConverter();
+		URI uri = xr.getURI();
+		String encoding = xr.getEncoding();
+
+		InputStream inputStream = null;
+
+		try {
+			inputStream = uriConverter.createInputStream(uri);
+
+			return getCompleteContent(encoding, inputStream);
+		} finally {
+			tryClose(inputStream, null);
+		}
+	}
+
+	public static String getCompleteContent(String encoding, InputStream inputStream)
+			throws UnsupportedEncodingException, IOException {
+		InputStreamReader inputStreamReader = new InputStreamReader(inputStream, encoding);
+
+		StringBuilder sb = new StringBuilder (); 
+		char[] buffer = new char[128 * KIB];
+
+		int n = inputStreamReader.read(buffer);
+
+		while (n != -1) {
+			sb.append (buffer, 0, n);
+			n = inputStreamReader.read(buffer);
+		}
+
+		return sb.toString();
+	}
+	
+	public static byte [] getCompleteContent(InputStream inputStream)
+			throws IOException {
+		byte[] buffer = new byte[128 * KIB];
+		int nextFreePos = 0; 
+		int n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
+		while (n != -1) {
+			nextFreePos += n; 
+			if (nextFreePos >= buffer.length) {
+				buffer = copyOf(buffer, buffer.length*2); 
+			}
+			n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
+		}
+		
+		return copyOf(buffer, nextFreePos);  
+	}
+	
+	static byte[] copyOf(byte[] original, int newLength) {
+        byte[] copy = new byte[newLength];
+        System.arraycopy(original, 0, copy, 0,
+                         Math.min(original.length, newLength));
+        return copy;
+    }
+
+	public static void writeSyntaxErrorMessage(DataOutputStream out, SerializationConversionContext scc,
+			SyntaxErrorMessage syntaxErrorMessage) throws IOException {
+		if (syntaxErrorMessage == null) {
+			out.writeBoolean(true);
+		} else {
+			out.writeBoolean(false);
+			SerializationUtil.writeString(out, syntaxErrorMessage.getMessage());
+			SerializationUtil.writeString(out, syntaxErrorMessage.getIssueCode());
+			SerializationUtil.writeStringArray(out, syntaxErrorMessage.getIssueData());
+		}
+	}
+	
+	public static SyntaxErrorMessage readSyntaxErrorMessage(DataInputStream in, DeserializationConversionContext context)
+			throws IOException {
+		boolean isNull = in.readBoolean();
+		if (isNull)
+			return null;
+		String message = SerializationUtil.readString(in);
+		String issueCode = SerializationUtil.readString(in);
+		String[] issueData = SerializationUtil.readStringArray(in);
+		SyntaxErrorMessage result = new SyntaxErrorMessage(message, issueCode, issueData);
+		return result;
+	}
+
+	static public int writeInt(DataOutputStream out, int value, boolean optimizePositive) throws IOException {
+		if (!optimizePositive)
+			value = (value << 1) ^ (value >> 31);
+		if ((value & ~0x7F) == 0) {
+			out.writeByte((byte) value);
+			return 1;
+		}
+		out.writeByte((byte) ((value & 0x7F) | 0x80));
+		value >>>= 7;
+		if ((value & ~0x7F) == 0) {
+			out.writeByte((byte) value);
+			return 2;
+		}
+		out.writeByte((byte) ((value & 0x7F) | 0x80));
+		value >>>= 7;
+		if ((value & ~0x7F) == 0) {
+			out.writeByte((byte) value);
+			return 3;
+		}
+		out.writeByte((byte) ((value & 0x7F) | 0x80));
+		value >>>= 7;
+		if ((value & ~0x7F) == 0) {
+			out.writeByte((byte) value);
+			return 4;
+		}
+		out.writeByte((byte) ((value & 0x7F) | 0x80));
+		value >>>= 7;
+		out.writeByte((byte) value);
+		return 5;
+	}
+
+	static public int readInt(DataInputStream in, boolean optimizePositive) throws IOException {
+		for (int offset = 0, result = 0; offset < 32; offset += 7) {
+			int b = in.readByte();
+			result |= (b & 0x7F) << offset;
+			if ((b & 0x80) == 0) {
+				if (!optimizePositive)
+					result = (result >>> 1) ^ -(result & 1);
+				return result;
+			}
+		}
+		throw new IOException("Malformed integer");
+	}
+
+	public static final void writeString(DataOutputStream out, String s) throws IOException {
+		boolean isNull = s == null; 
+		
+		out.writeBoolean(isNull); 
+		
+		if (!isNull) {
+			out.writeUTF(s);
+		}
+	}
+
+	public static final String readString(DataInputStream in) throws IOException {
+		boolean isNull = in.readBoolean();
+	
+		if (isNull) {
+			return null;
+		}
+	
+		String string = in.readUTF();
+	
+		return string;
+	}
+
+	public static void writeStringArray(DataOutputStream out, String[] ss) throws IOException {
+		out.writeBoolean(ss == null);
+		if (ss != null) {
+			writeInt(out, ss.length, true); 
+			for (String data : ss) {
+				writeString(out, data);
+			}
+		}
+	}
+
+	public static String[] readStringArray(DataInputStream in) throws IOException {
+		boolean isIssueDataNull = in.readBoolean();
+		String[] result = null;
+	
+		if (!isIssueDataNull) {
+			int issueDataLength = readInt(in, true); 
+			result = new String[issueDataLength];
+	
+			for (int i = 0; i < issueDataLength; ++i) {
+				result[i] = readString(in);
+			}
+		}
+	
+		return result;
+	}
+	
+	public static void tryClose(Closeable stream, Logger logger) throws IOException {
+		if (stream != null) {
+			try {
+				stream.close();
+			} catch (Exception e) {
+				if (logger != null) {
+					logger.error("Could not close an stream for a cache entry: " + e, e);
+				}
+			}
+		}
+	}
+}
diff --git a/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java
new file mode 100644
index 000000000..8517b2d20
--- /dev/null
+++ b/tests/org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization/SerializationUtilTest.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Sigasi N.V. (http://www.sigasi.com) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.xtext.nodemodel.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;
+import org.eclipse.xtext.nodemodel.serialization.SerializationUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+/** @author Mark Christiaens */
+
+public class SerializationUtilTest extends Assert {
+	
+	@Test
+	public void testSyntaxErrorMessage() throws IOException {
+		final String message = "hi";
+		String [] issueCodes = { null, "issue" };
+		String [][] issueDatas = { null, {null}, {"issue data"}};
+		
+		for (String[] issueData : issueDatas) {
+			for (String issueCode : issueCodes) {
+				SyntaxErrorMessage sem = new SyntaxErrorMessage(message, issueCode, issueData);
+				ByteArrayOutputStream out = new ByteArrayOutputStream ();
+				DataOutputStream dout = new DataOutputStream(out);
+				SerializationUtil.writeSyntaxErrorMessage(dout, null, sem);
+				dout.close();
+				byte[] array = out.toByteArray();
+				ByteArrayInputStream in = new ByteArrayInputStream(array); 
+				DataInputStream din = new DataInputStream(in);
+				SyntaxErrorMessage sem2 = SerializationUtil.readSyntaxErrorMessage(din, null);
+				assertEquals(sem, sem2); 
+			}
+		}
+		ByteArrayOutputStream out = new ByteArrayOutputStream ();
+		DataOutputStream dout = new DataOutputStream(out);
+		SerializationUtil.writeSyntaxErrorMessage(dout, null, null);
+		dout.close();
+		byte[] array = out.toByteArray();
+		ByteArrayInputStream in = new ByteArrayInputStream(array); 
+		DataInputStream din = new DataInputStream(in);
+		SyntaxErrorMessage readMessage = SerializationUtil.readSyntaxErrorMessage(din, null);
+		assertNull(readMessage);
+	}
+}