mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 00:38:56 +00:00
Optimized (de)serialization code
* Added cache to grammarIdToURIMap for deserialization * Prevent conversion from list to map in serialization * Prevent conversion from map to string array in serialization Signed-off-by: Titouan Vervack <titouan.vervack@sigasi.com>
This commit is contained in:
parent
09c9adf666
commit
e1075671f1
5 changed files with 112 additions and 55 deletions
org.eclipse.xtext.tests/src/org/eclipse/xtext/nodemodel/serialization
org.eclipse.xtext/src/org/eclipse/xtext/nodemodel
|
@ -13,7 +13,10 @@ import java.io.DataInputStream;
|
|||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
import org.eclipse.emf.ecore.EFactory;
|
||||
|
@ -48,11 +51,19 @@ public class SerializationUtilTest extends Assert {
|
|||
rootObject.eSet(ref1, someTypeObject1);
|
||||
rootObject.eSet(ref2, someTypeObject2);
|
||||
|
||||
List<EObject> map = new ArrayList<>();
|
||||
List<EObject> list = new ArrayList<>();
|
||||
SerializationUtil.fillListWithAllEObjects(rootObject, list);
|
||||
assertTrue(list.contains(rootObject));
|
||||
assertTrue(list.contains(someTypeObject1));
|
||||
assertFalse(list.contains(someTypeObject2));
|
||||
assertEquals(2, list.size());
|
||||
|
||||
Map<EObject, Integer> map = new HashMap<>();
|
||||
SerializationUtil.fillIdToEObjectMap(rootObject, map);
|
||||
assertTrue(map.contains(rootObject));
|
||||
assertTrue(map.contains(someTypeObject1));
|
||||
assertFalse(map.contains(someTypeObject2));
|
||||
Set<EObject> keys = map.keySet();
|
||||
assertTrue(keys.contains(rootObject));
|
||||
assertTrue(keys.contains(someTypeObject1));
|
||||
assertFalse(keys.contains(someTypeObject2));
|
||||
assertEquals(2, map.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.io.DataInputStream;
|
|||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.ICompositeNode;
|
||||
import org.eclipse.xtext.nodemodel.serialization.DeserializationConversionContext;
|
||||
|
@ -46,9 +47,9 @@ public class 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();
|
||||
List<String> grammarIdToURIMap = scc.getGrammarIdToURIMap();
|
||||
|
||||
out.writeInt(grammarIdToURIMap.length);
|
||||
out.writeInt(grammarIdToURIMap.size());
|
||||
for (String string : grammarIdToURIMap) {
|
||||
out.writeUTF(string);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
package org.eclipse.xtext.nodemodel.serialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.emf.common.util.URI;
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
|
@ -17,13 +19,17 @@ import org.eclipse.xtext.IGrammarAccess;
|
|||
import org.eclipse.xtext.resource.XtextResource;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
/**
|
||||
* @author mark.christiaens - Initial contribution and API
|
||||
*
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public class DeserializationConversionContext {
|
||||
|
||||
private static final Map<URI, Map<URI, EObject>> CACHE = Maps.newHashMap();
|
||||
|
||||
private EObject[] grammarIdToGrammarElementMap;
|
||||
|
||||
final private List<EObject> idToEObjectMap;
|
||||
|
@ -43,12 +49,23 @@ public class DeserializationConversionContext {
|
|||
}
|
||||
|
||||
public void setGrammarIdToURIMap(String[] grammarIdToURIMap) {
|
||||
grammarIdToGrammarElementMap = new EObject[grammarIdToURIMap.length];
|
||||
URI grammarUri = grammarAccess.getGrammar().eResource().getURI();
|
||||
|
||||
Map<URI, EObject> grammarCache = CACHE.get(grammarUri);
|
||||
if (grammarCache == null) {
|
||||
grammarCache = new HashMap<>();
|
||||
CACHE.put(grammarUri, grammarCache);
|
||||
}
|
||||
|
||||
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);
|
||||
EObject grammarElement = grammarCache.get(uri);
|
||||
if (grammarElement == null) {
|
||||
grammarElement = grammarResourceSet.getEObject(uri, true);
|
||||
grammarCache.put(uri, grammarElement);
|
||||
}
|
||||
|
||||
if (grammarElement == null) {
|
||||
throw new IllegalStateException(
|
||||
|
@ -73,7 +90,7 @@ public class DeserializationConversionContext {
|
|||
}
|
||||
|
||||
public void fillIdToEObjectMap(Resource resource) {
|
||||
SerializationUtil.fillIdToEObjectMap(resource, idToEObjectMap);
|
||||
SerializationUtil.fillListWithAllEObjects(resource, idToEObjectMap);
|
||||
}
|
||||
|
||||
public EObject getSemanticObject(int id) {
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
package org.eclipse.xtext.nodemodel.serialization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
|
@ -27,9 +29,9 @@ public class SerializationConversionContext {
|
|||
final private Map<EObject, Integer> eObjectToIdMap;
|
||||
|
||||
public SerializationConversionContext(XtextResource resource) {
|
||||
grammarElementToIdMap = new IdentityHashMap<EObject, Integer>();
|
||||
grammarIdToURIMap = new ArrayList<String>();
|
||||
eObjectToIdMap = new IdentityHashMap<EObject, Integer>();
|
||||
grammarElementToIdMap = new IdentityHashMap<EObject, Integer>(100);
|
||||
grammarIdToURIMap = new ArrayList<String>(100);
|
||||
eObjectToIdMap = new IdentityHashMap<EObject, Integer>(1000);
|
||||
|
||||
fillEObjectToIdMap(resource);
|
||||
fillGrammarElementToIdMap(resource);
|
||||
|
@ -47,13 +49,7 @@ public class SerializationConversionContext {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
SerializationUtil.fillIdToEObjectMap(resource, eObjectToIdMap);
|
||||
}
|
||||
|
||||
protected void fillGrammarElementToIdMap(XtextResource r) {
|
||||
|
@ -68,16 +64,17 @@ public class SerializationConversionContext {
|
|||
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: "
|
||||
throw new IllegalArgumentException("Trying 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[grammarIdToURIMap.size()]);
|
||||
|
||||
return map;
|
||||
/**
|
||||
* Returns an unmodifiable view on the grammarIdToURIMap
|
||||
*/
|
||||
public List<String> getGrammarIdToURIMap() {
|
||||
return Collections.unmodifiableList(grammarIdToURIMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,14 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.EStructuralFeature;
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
import org.eclipse.emf.ecore.resource.URIConverter;
|
||||
import org.eclipse.emf.ecore.util.EcoreUtil;
|
||||
|
@ -30,26 +32,55 @@ 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);
|
||||
|
||||
|
||||
public static void fillListWithAllEObjects(Resource resource, List<EObject> map) {
|
||||
TreeIterator<EObject> allContents = EcoreUtil.getAllContents(resource, false);
|
||||
|
||||
if (allContents.hasNext()) {
|
||||
EObject root = allContents.next();
|
||||
fillListWithAllEObjects(root, map);
|
||||
}
|
||||
}
|
||||
|
||||
public static void fillListWithAllEObjects(EObject eObject, List<EObject> map) {
|
||||
if (eObject.eContainingFeature() == null || !eObject.eContainingFeature().isTransient()) {
|
||||
map.add(eObject);
|
||||
EList<EObject> eContents = eObject.eContents();
|
||||
|
||||
for (EObject child : eContents) {
|
||||
fillListWithAllEObjects(child, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basically a copy of {@link #fillListWithAllEObjects(Resource, List)}, but for efficiency we need an
|
||||
* implementation for Map and List
|
||||
*/
|
||||
public static void fillIdToEObjectMap(Resource resource, Map<EObject, Integer> 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) {
|
||||
if (eObject.eContainingFeature() == null || !eObject.eContainingFeature().isTransient()) {
|
||||
map.add(eObject);
|
||||
/**
|
||||
* Basically a copy of {@link #fillListWithAllEObjects(EObject, List)}, but for efficiency we need an implementation
|
||||
* for Map and List
|
||||
*/
|
||||
public static void fillIdToEObjectMap(EObject eObject, Map<EObject, Integer> map) {
|
||||
EStructuralFeature containingFeature = eObject.eContainingFeature();
|
||||
if (containingFeature == null || !containingFeature.isTransient()) {
|
||||
map.put(eObject, map.size());
|
||||
EList<EObject> eContents = eObject.eContents();
|
||||
|
||||
for (EObject child : eContents) {
|
||||
|
@ -83,7 +114,7 @@ public class SerializationUtil {
|
|||
throws UnsupportedEncodingException, IOException {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, encoding);
|
||||
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
char[] buffer = new char[128 * KIB];
|
||||
|
||||
int n = inputStreamReader.read(buffer);
|
||||
|
@ -95,23 +126,23 @@ public class SerializationUtil {
|
|||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static byte [] getCompleteContent(InputStream inputStream)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[128 * KIB];
|
||||
int nextFreePos = 0;
|
||||
int nextFreePos = 0;
|
||||
int n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
|
||||
while (n != -1) {
|
||||
nextFreePos += n;
|
||||
nextFreePos += n;
|
||||
if (nextFreePos >= buffer.length) {
|
||||
buffer = copyOf(buffer, buffer.length*2);
|
||||
buffer = copyOf(buffer, buffer.length*2);
|
||||
}
|
||||
n = inputStream.read(buffer, nextFreePos, buffer.length - nextFreePos);
|
||||
}
|
||||
|
||||
return copyOf(buffer, nextFreePos);
|
||||
|
||||
return copyOf(buffer, nextFreePos);
|
||||
}
|
||||
|
||||
|
||||
static byte[] copyOf(byte[] original, int newLength) {
|
||||
byte[] copy = new byte[newLength];
|
||||
System.arraycopy(original, 0, copy, 0,
|
||||
|
@ -130,7 +161,7 @@ public class SerializationUtil {
|
|||
SerializationUtil.writeStringArray(out, syntaxErrorMessage.getIssueData());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static SyntaxErrorMessage readSyntaxErrorMessage(DataInputStream in, DeserializationConversionContext context)
|
||||
throws IOException {
|
||||
boolean isNull = in.readBoolean();
|
||||
|
@ -188,10 +219,10 @@ public class SerializationUtil {
|
|||
}
|
||||
|
||||
public static final void writeString(DataOutputStream out, String s) throws IOException {
|
||||
boolean isNull = s == null;
|
||||
|
||||
out.writeBoolean(isNull);
|
||||
|
||||
boolean isNull = s == null;
|
||||
|
||||
out.writeBoolean(isNull);
|
||||
|
||||
if (!isNull) {
|
||||
out.writeUTF(s);
|
||||
}
|
||||
|
@ -199,20 +230,20 @@ public class SerializationUtil {
|
|||
|
||||
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);
|
||||
writeInt(out, ss.length, true);
|
||||
for (String data : ss) {
|
||||
writeString(out, data);
|
||||
}
|
||||
|
@ -222,19 +253,19 @@ public class SerializationUtil {
|
|||
public static String[] readStringArray(DataInputStream in) throws IOException {
|
||||
boolean isIssueDataNull = in.readBoolean();
|
||||
String[] result = null;
|
||||
|
||||
|
||||
if (!isIssueDataNull) {
|
||||
int issueDataLength = readInt(in, true);
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue