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); + } +}