blob: d265a0d36c8d70ca13b362d9a6d6c7c0a4047f22 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2007-2010 IBM Corporation 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
*
* Contributors:
* IBM - Initial API and implementation
*
* </copyright>
*
* $Id: BinaryResourceImpl.java,v 1.3 2010/06/14 15:32:43 emerks Exp $
*/
package org.eclipse.emf.ecore.resource.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.InputStream;
import org.eclipse.emf.common.util.OutputStream;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import org.eclipse.emf.ecore.util.InternalEList;
/**
* PROVISIONAL API for efficiently producing and consuming a compact binary serialization.
* Absolutely any and all aspects of this design are likely to change arbitrarily.
* It might not be wise to use this format for long term persistence while the API is provisional.
* @since 2.4
*/
public class BinaryResourceImpl extends ResourceImpl
{
public BinaryResourceImpl()
{
super();
}
public BinaryResourceImpl(URI uri)
{
super(uri);
}
@Override
protected void doSave(OutputStream outputStream, Map<?, ?> options) throws IOException
{
EObjectOutputStream eObjectOutputStream = new EObjectOutputStream(outputStream, options);
eObjectOutputStream.saveResource(this);
}
@Override
protected void doLoad(InputStream inputStream, Map<?, ?> options) throws IOException
{
EObjectInputStream eObjectInputStream = new EObjectInputStream(inputStream, options);
eObjectInputStream.loadResource(this);
}
public static class BinaryIO
{
public enum Version
{
VERSION_1_0
}
protected Version version;
protected Resource resource;
protected URI baseURI;
protected Map<?, ?> options;
protected char[] characters;
protected InternalEObject[][] internalEObjectDataArrayBuffer = new InternalEObject[50][];
protected int internalEObjectDataArrayBufferCount = -1;
protected URI resolve(URI uri)
{
return baseURI != null && uri.isRelative() && uri.hasRelativePath() ? uri.resolve(baseURI) : uri;
}
protected URI deresolve(URI uri)
{
if (baseURI != null && !uri.isRelative())
{
URI deresolvedURI = uri.deresolve(baseURI, true, true, false);
if (deresolvedURI.hasRelativePath() && (!uri.isPlatform() || uri.segment(0).equals(baseURI.segment(0))))
{
uri = deresolvedURI;
}
}
return uri;
}
protected InternalEObject [] allocateInternalEObjectArray(int length)
{
if (internalEObjectDataArrayBufferCount == -1)
{
return new InternalEObject[length];
}
else
{
InternalEObject [] buffer = internalEObjectDataArrayBuffer[internalEObjectDataArrayBufferCount];
internalEObjectDataArrayBuffer[internalEObjectDataArrayBufferCount--] = null;
return buffer.length >= length ? buffer : new InternalEObject[length];
}
}
protected void recycle(InternalEObject[] values)
{
if (++internalEObjectDataArrayBufferCount >= internalEObjectDataArrayBuffer.length)
{
InternalEObject [][] newInternalEObjectDataArrayBuffer = new InternalEObject[internalEObjectDataArrayBufferCount * 2][];
System.arraycopy(internalEObjectDataArrayBuffer, 0, newInternalEObjectDataArrayBuffer, 0, internalEObjectDataArrayBufferCount);
internalEObjectDataArrayBuffer = newInternalEObjectDataArrayBuffer;
}
internalEObjectDataArrayBuffer[internalEObjectDataArrayBufferCount] = values;
}
protected FeatureMap.Entry.Internal[][] featureMapEntryDataArrayBuffer = new FeatureMap.Entry.Internal[50][];
protected int featureMapEntryDataArrayBufferCount = -1;
protected FeatureMap.Entry.Internal [] allocateFeatureMapEntryArray(int length)
{
if (featureMapEntryDataArrayBufferCount == -1)
{
return new FeatureMap.Entry.Internal[length];
}
else
{
FeatureMap.Entry.Internal [] buffer = featureMapEntryDataArrayBuffer[featureMapEntryDataArrayBufferCount];
featureMapEntryDataArrayBuffer[featureMapEntryDataArrayBufferCount--] = null;
return buffer.length >= length ? buffer : new FeatureMap.Entry.Internal[length];
}
}
protected void recycle(FeatureMap.Entry.Internal[] values)
{
if (++featureMapEntryDataArrayBufferCount >= featureMapEntryDataArrayBuffer.length)
{
FeatureMap.Entry.Internal [][] newFeatureMapEntryDataArrayBuffer = new FeatureMap.Entry.Internal[featureMapEntryDataArrayBufferCount * 2][];
System.arraycopy(featureMapEntryDataArrayBuffer, 0, newFeatureMapEntryDataArrayBuffer, 0, featureMapEntryDataArrayBufferCount);
featureMapEntryDataArrayBuffer = newFeatureMapEntryDataArrayBuffer;
}
featureMapEntryDataArrayBuffer[featureMapEntryDataArrayBufferCount] = values;
}
protected enum FeatureKind
{
EOBJECT_CONTAINER,
EOBJECT_CONTAINER_PROXY_RESOLVING,
EOBJECT,
EOBJECT_PROXY_RESOLVING,
EOBJECT_LIST,
EOBJECT_LIST_PROXY_RESOLVING,
EOBJECT_CONTAINMENT,
EOBJECT_CONTAINMENT_PROXY_RESOLVING,
EOBJECT_CONTAINMENT_LIST,
EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING,
BOOLEAN,
BYTE,
CHAR,
DOUBLE,
FLOAT,
INT,
LONG,
SHORT,
STRING,
DATA,
DATA_LIST,
FEATURE_MAP;
public static FeatureKind get(EStructuralFeature eStructuralFeature)
{
if (eStructuralFeature instanceof EReference)
{
EReference eReference = (EReference)eStructuralFeature;
if (eReference.isContainment())
{
if (eReference.isResolveProxies())
{
if (eReference.isMany())
{
return EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING;
}
else
{
return EOBJECT_CONTAINMENT_PROXY_RESOLVING;
}
}
else
{
if (eReference.isMany())
{
return EOBJECT_CONTAINMENT_LIST;
}
else
{
return EOBJECT_CONTAINMENT;
}
}
}
else if (eReference.isContainer())
{
if (eReference.isResolveProxies())
{
return EOBJECT_CONTAINER_PROXY_RESOLVING;
}
else
{
return EOBJECT_CONTAINER;
}
}
else if (eReference.isResolveProxies())
{
if (eReference.isMany())
{
return EOBJECT_LIST_PROXY_RESOLVING;
}
else
{
return EOBJECT_PROXY_RESOLVING;
}
}
else
{
if (eReference.isMany())
{
return EOBJECT_LIST;
}
else
{
return EOBJECT;
}
}
}
else
{
EAttribute eAttribute = (EAttribute)eStructuralFeature;
EDataType eDataType = eAttribute.getEAttributeType();
String instanceClassName = eDataType.getInstanceClassName();
if (instanceClassName == "org.eclipse.emf.ecore.util.FeatureMap$Entry")
{
return FEATURE_MAP;
}
else if (eAttribute.isMany())
{
return DATA_LIST;
}
else if (instanceClassName == "java.lang.String")
{
return STRING;
}
else if (instanceClassName == "boolean")
{
return BOOLEAN;
}
else if (instanceClassName == "byte")
{
return BYTE;
}
else if (instanceClassName == "char")
{
return CHAR;
}
else if (instanceClassName == "double")
{
return DOUBLE;
}
else if (instanceClassName == "float")
{
return FLOAT;
}
else if (instanceClassName == "int")
{
return INT;
}
else if (instanceClassName == "long")
{
return LONG;
}
else if (instanceClassName == "short")
{
return SHORT;
}
else
{
return DATA;
}
}
}
}
}
public static class EObjectOutputStream extends BinaryIO
{
public enum Check
{
NOTHING,
DIRECT_RESOURCE,
RESOURCE,
CONTAINER
}
protected static class EPackageData
{
public int id;
public EClassData[] eClassData;
public final int allocateEClassID()
{
for (int i = 0, length = eClassData.length; i < length; ++i)
{
EClassData eClassData = this.eClassData[i];
if (eClassData == null)
{
return i;
}
}
return -1;
}
}
protected static class EClassData
{
public int ePackageID;
public int id;
public EStructuralFeatureData[] eStructuralFeatureData;
}
protected static class EStructuralFeatureData
{
public String name;
public boolean isTransient;
public FeatureKind kind;
public EFactory eFactory;
public EDataType eDataType;
}
protected OutputStream outputStream;
protected Map<EPackage, EPackageData> ePackageDataMap = new HashMap<EPackage, EPackageData>();
protected Map<EClass, EClassData> eClassDataMap = new HashMap<EClass, EClassData>();
protected Map<EObject, Integer> eObjectIDMap = new HashMap<EObject, Integer>();
protected Map<URI, Integer> uriToIDMap = new HashMap<URI, Integer>();
public EObjectOutputStream(OutputStream outputStream, Map<?, ?> options) throws IOException
{
this(outputStream, options, Version.VERSION_1_0);
}
public EObjectOutputStream(OutputStream outputStream, Map<?, ?> options, Version version) throws IOException
{
this.outputStream = outputStream;
this.options = options;
this.version = version;
writeSignature();
writeVersion();
}
protected void writeSignature() throws IOException
{
// Write a signature that will be obviously corrupt
// if the binary contents end up being UTF-8 encoded
// or altered by line feed or carriage return changes.
//
writeByte('\211');
writeByte('e');
writeByte('m');
writeByte('f');
writeByte('\n');
writeByte('\r');
writeByte('\032');
writeByte('\n');
}
protected void writeVersion() throws IOException
{
writeByte(version.ordinal());
}
protected EPackageData writeEPackage(EPackage ePackage) throws IOException
{
EPackageData ePackageData = ePackageDataMap.get(ePackage);
if (ePackageData == null)
{
ePackageData = new EPackageData();
int id = ePackageDataMap.size();
ePackageData.id = id;
ePackageData.eClassData = new EClassData[ePackage.getEClassifiers().size()];
writeCompressedInt(id);
writeString(ePackage.getNsURI());
writeURI(EcoreUtil.getURI(ePackage));
ePackageDataMap.put(ePackage, ePackageData);
}
else
{
writeCompressedInt(ePackageData.id);
}
return ePackageData;
}
protected EClassData writeEClass(EClass eClass) throws IOException
{
EClassData eClassData = eClassDataMap.get(eClass);
if (eClassData == null)
{
eClassData = new EClassData();
EPackageData ePackageData = writeEPackage(eClass.getEPackage());
eClassData.ePackageID = ePackageData.id;
writeCompressedInt(eClassData.id = ePackageData.allocateEClassID());
writeString(eClass.getName());
int featureCount = eClass.getFeatureCount();
EStructuralFeatureData [] eStructuralFeaturesData = eClassData.eStructuralFeatureData = new EStructuralFeatureData[featureCount];
for (int i = 0; i < featureCount; ++i)
{
EStructuralFeatureData eStructuralFeatureData = eStructuralFeaturesData[i] = new EStructuralFeatureData();
EStructuralFeature.Internal eStructuralFeature = (EStructuralFeature.Internal)eClass.getEStructuralFeature(i);
eStructuralFeatureData.name = eStructuralFeature.getName();
eStructuralFeatureData.isTransient = eStructuralFeature.isTransient() || eStructuralFeature.isContainer() && !eStructuralFeature.isResolveProxies();
eStructuralFeatureData.kind = FeatureKind.get(eStructuralFeature);
if (eStructuralFeature instanceof EAttribute)
{
EAttribute eAttribute = (EAttribute)eStructuralFeature;
EDataType eDataType = eAttribute.getEAttributeType();
eStructuralFeatureData.eDataType = eDataType;
eStructuralFeatureData.eFactory = eDataType.getEPackage().getEFactoryInstance();
}
}
ePackageData.eClassData[eClassData.id] = eClassData;
eClassDataMap.put(eClass, eClassData);
}
else
{
writeCompressedInt(eClassData.ePackageID);
writeCompressedInt(eClassData.id);
}
return eClassData;
}
protected EStructuralFeatureData writeEStructuralFeature(EStructuralFeature eStructuralFeature) throws IOException
{
EClass eClass = eStructuralFeature.getEContainingClass();
EClassData eClassData = writeEClass(eClass);
int featureID = eClass.getFeatureID(eStructuralFeature);
EStructuralFeatureData eStructuralFeatureData = eClassData.eStructuralFeatureData[featureID];
writeCompressedInt(featureID);
if (eStructuralFeatureData.name != null)
{
writeString(eStructuralFeatureData.name);
eStructuralFeatureData.name = null;
}
return eStructuralFeatureData;
}
public void saveResource(Resource resource) throws IOException
{
this.resource = resource;
URI uri = resource.getURI();
if (uri != null && uri.isHierarchical() && !uri.isRelative())
{
baseURI = uri;
}
@SuppressWarnings("unchecked")
InternalEList<? extends InternalEObject> internalEList = (InternalEList<? extends InternalEObject>)(InternalEList<?>)resource.getContents();
saveEObjects(internalEList, Check.CONTAINER);
}
public void saveEObjects(InternalEList<? extends InternalEObject> internalEObjects, Check check) throws IOException
{
int size = internalEObjects.size();
InternalEObject [] values = allocateInternalEObjectArray(size);
internalEObjects.basicToArray(values);
writeCompressedInt(size);
for (int i = 0; i < size; ++i)
{
InternalEObject internalEObject = values[i];
saveEObject(internalEObject, check);
}
recycle(values);
}
public void saveFeatureMap(FeatureMap.Internal featureMap) throws IOException
{
int size = featureMap.size();
FeatureMap.Entry.Internal [] values = allocateFeatureMapEntryArray(size);
featureMap.toArray(values);
writeCompressedInt(size);
for (int i = 0; i < size; ++i)
{
FeatureMap.Entry.Internal entry = values[i];
saveFeatureMapEntry(entry);
}
recycle(values);
}
public void saveFeatureMapEntry(FeatureMap.Entry.Internal entry) throws IOException
{
EStructuralFeatureData eStructuralFeatureData = writeEStructuralFeature(entry.getEStructuralFeature());
Object value = entry.getValue();
switch (eStructuralFeatureData.kind)
{
case EOBJECT:
case EOBJECT_LIST:
case EOBJECT_CONTAINMENT:
case EOBJECT_CONTAINMENT_LIST:
{
saveEObject((InternalEObject)value, Check.NOTHING);
break;
}
case EOBJECT_CONTAINMENT_PROXY_RESOLVING:
case EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING:
{
saveEObject((InternalEObject)value, Check.DIRECT_RESOURCE);
break;
}
case EOBJECT_PROXY_RESOLVING:
case EOBJECT_LIST_PROXY_RESOLVING:
{
saveEObject((InternalEObject)value, Check.RESOURCE);
break;
}
case BOOLEAN:
{
writeBoolean((Boolean)value);
break;
}
case BYTE:
{
writeByte((Byte)value);
break;
}
case CHAR:
{
writeChar((Character)value);
break;
}
case DOUBLE:
{
writeDouble((Double)value);
break;
}
case FLOAT:
{
writeFloat((Float)value);
break;
}
case INT:
{
writeInt((Integer)value);
break;
}
case LONG:
{
writeLong((Long)value);
break;
}
case SHORT:
{
writeShort((Short)value);
break;
}
case STRING:
{
writeString((String)value);
break;
}
case DATA:
case DATA_LIST:
{
String literal = eStructuralFeatureData.eFactory.convertToString(eStructuralFeatureData.eDataType, value);
writeString(literal);
break;
}
default:
{
throw new IOException("Unhandled case " + eStructuralFeatureData.kind);
}
}
}
public void saveEObject(InternalEObject internalEObject, Check check) throws IOException
{
if (internalEObject == null)
{
writeCompressedInt(-1);
}
else
{
Integer id = eObjectIDMap.get(internalEObject);
if (id == null)
{
int idValue = eObjectIDMap.size();
writeCompressedInt(idValue);
eObjectIDMap.put(internalEObject, idValue);
EClass eClass = internalEObject.eClass();
EClassData eClassData = writeEClass(eClass);
switch (check)
{
case DIRECT_RESOURCE:
{
Internal resource = internalEObject.eDirectResource();
if (resource != null)
{
writeCompressedInt(-1);
writeURI(resource.getURI(), resource.getURIFragment(internalEObject));
return;
}
else if (internalEObject.eIsProxy())
{
writeCompressedInt(-1);
writeURI(internalEObject.eProxyURI());
return;
}
break;
}
case RESOURCE:
{
Resource resource = internalEObject.eResource();
if (resource != this.resource && resource != null)
{
writeCompressedInt(-1);
writeURI(resource.getURI(), resource.getURIFragment(internalEObject));
return;
}
else if (internalEObject.eIsProxy())
{
writeCompressedInt(-1);
writeURI(internalEObject.eProxyURI());
return;
}
break;
}
case NOTHING:
case CONTAINER:
{
break;
}
}
EStructuralFeatureData [] eStructuralFeatureData = eClassData.eStructuralFeatureData;
for (int i = 0, length = eStructuralFeatureData.length; i < length; ++i)
{
EStructuralFeatureData structuralFeatureData = eStructuralFeatureData[i];
if (!structuralFeatureData.isTransient &&
(structuralFeatureData.kind != FeatureKind.EOBJECT_CONTAINER_PROXY_RESOLVING || check == Check.CONTAINER))
{
saveFeatureValue(internalEObject, i, structuralFeatureData);
}
}
writeCompressedInt(0);
}
else
{
writeCompressedInt(id);
}
}
}
protected void saveFeatureValue(InternalEObject internalEObject, int featureID, EStructuralFeatureData eStructuralFeatureData) throws IOException
{
if (internalEObject.eIsSet(featureID))
{
writeCompressedInt(featureID + 1);
if (eStructuralFeatureData.name != null)
{
writeString(eStructuralFeatureData.name);
eStructuralFeatureData.name = null;
}
Object value = internalEObject.eGet(featureID, false, true);
switch (eStructuralFeatureData.kind)
{
case EOBJECT:
case EOBJECT_CONTAINMENT:
{
saveEObject((InternalEObject)value, Check.NOTHING);
break;
}
case EOBJECT_CONTAINER_PROXY_RESOLVING:
{
saveEObject((InternalEObject)value, Check.DIRECT_RESOURCE);
break;
}
case EOBJECT_CONTAINMENT_PROXY_RESOLVING:
{
saveEObject((InternalEObject)value, Check.DIRECT_RESOURCE);
break;
}
case EOBJECT_PROXY_RESOLVING:
{
saveEObject((InternalEObject)value, Check.RESOURCE);
break;
}
case EOBJECT_LIST:
case EOBJECT_CONTAINMENT_LIST:
{
@SuppressWarnings("unchecked")
InternalEList<? extends InternalEObject> internalEList = (InternalEList<? extends InternalEObject>)value;
saveEObjects(internalEList, Check.NOTHING);
break;
}
case EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING:
{
@SuppressWarnings("unchecked")
InternalEList<? extends InternalEObject> internalEList = (InternalEList<? extends InternalEObject>)value;
saveEObjects(internalEList, Check.DIRECT_RESOURCE);
break;
}
case EOBJECT_LIST_PROXY_RESOLVING:
{
@SuppressWarnings("unchecked")
InternalEList<? extends InternalEObject> internalEList = (InternalEList<? extends InternalEObject>)value;
saveEObjects(internalEList, Check.RESOURCE);
break;
}
case BOOLEAN:
{
writeBoolean((Boolean)value);
break;
}
case BYTE:
{
writeByte((Byte)value);
break;
}
case CHAR:
{
writeChar((Character)value);
break;
}
case DOUBLE:
{
writeDouble((Double)value);
break;
}
case FLOAT:
{
writeFloat((Float)value);
break;
}
case INT:
{
writeInt((Integer)value);
break;
}
case LONG:
{
writeLong((Long)value);
break;
}
case SHORT:
{
writeShort((Short)value);
break;
}
case STRING:
{
writeString((String)value);
break;
}
case FEATURE_MAP:
{
FeatureMap.Internal featureMap = (FeatureMap.Internal)value;
saveFeatureMap(featureMap);
break;
}
case DATA:
{
String literal = eStructuralFeatureData.eFactory.convertToString(eStructuralFeatureData.eDataType, value);
writeString(literal);
break;
}
case DATA_LIST:
{
List<?> dataValues = (List<?>)value;
int length = dataValues.size();
writeCompressedInt(length);
for (int j = 0; j < length; ++j)
{
String literal = eStructuralFeatureData.eFactory.convertToString(eStructuralFeatureData.eDataType, dataValues.get(j));
writeString(literal);
}
break;
}
default:
{
throw new IOException("Unhandled case " + eStructuralFeatureData.kind);
}
}
}
}
public void writeByte(int value) throws IOException
{
outputStream.write(value);
}
public void writeBoolean(boolean value) throws IOException
{
writeByte(value ? 1 : 0);
}
public void writeChar(int value) throws IOException
{
writeByte((byte)(value >> 8 & 0xFF));
writeByte((byte)(value & 0xFF));
}
public void writeShort(int value) throws IOException
{
writeByte((byte)(value >> 8 & 0xFF));
writeByte((byte)(value & 0xFF));
}
public void writeInt(int value) throws IOException
{
writeByte((byte)(value >> 24 & 0xFF));
writeByte((byte)(value >> 16 & 0xFF));
writeByte((byte)(value >> 8 & 0xFF));
writeByte((byte)(value & 0xFF));
}
public void writeLong(long value) throws IOException
{
writeInt((int)(value >> 32));
writeInt((int)value);
}
public void writeFloat(float value) throws IOException
{
// TODO
// writeInt(Float.floatToIntBits(value));
throw new UnsupportedOperationException();
}
public void writeDouble(double value) throws IOException
{
// TODO
// writeLong(Double.doubleToLongBits(value));
throw new UnsupportedOperationException();
}
public void writeCompressedInt(int value) throws IOException
{
++value;
int firstByte = value >> 24 & 0xFF;
int secondByte = value >> 16 & 0xFF;
int thirdByte = value >> 8 & 0xFF;
int fourthBtye = value & 0xFF;
if (firstByte > 0x3F)
{
handleInvalidValue(value);
}
else if (firstByte != 0 || secondByte > 0x3F)
{
writeByte(firstByte | 0xC0);
writeByte(secondByte);
writeByte(thirdByte);
writeByte(fourthBtye);
}
else if (secondByte != 0 || thirdByte > 0x3F)
{
writeByte(secondByte | 0x80);
writeByte(thirdByte);
writeByte(fourthBtye);
}
else if (thirdByte != 0 || fourthBtye > 0x3F)
{
writeByte(thirdByte | 0x40);
writeByte(fourthBtye);
}
else
{
writeByte(fourthBtye);
}
}
private final void handleInvalidValue(int value) throws IOException
{
throw new IOException("Invalid value " + value);
}
public void writeString(String value) throws IOException
{
if (value == null)
{
writeCompressedInt(-1);
}
else
{
int length = value.length();
writeCompressedInt(length);
if (characters == null || characters.length < length)
{
characters = new char[length];
}
value.getChars(0, length, characters, 0);
LOOP:
for (int i = 0; i < length; ++i)
{
char character = characters[i];
if (character == 0 || character > 0xFF)
{
writeByte((byte)0);
writeChar(character);
while (++i < length)
{
writeChar(characters[i]);
}
break LOOP;
}
else
{
writeByte((byte)character);
}
}
}
}
public void writeURI(URI uri) throws IOException
{
writeURI(uri.trimFragment(), uri.fragment());
}
public void writeURI(URI uri, String fragment) throws IOException
{
if (uri == null)
{
writeCompressedInt(-1);
}
else
{
assert uri.fragment() == null;
Integer id = uriToIDMap.get(uri);
if (id == null)
{
int idValue = uriToIDMap.size();
uriToIDMap.put(uri, idValue);
writeCompressedInt(idValue);
writeString(deresolve(uri).toString());
}
else
{
writeCompressedInt(id);
}
writeString(fragment);
}
}
}
public static class EObjectInputStream extends BinaryIO
{
protected static class EPackageData
{
public EPackage ePackage;
public EClassData[] eClassData;
public final int allocateEClassID()
{
for (int i = 0, length = eClassData.length; i < length; ++i)
{
EClassData eClassData = this.eClassData[i];
if (eClassData == null)
{
return i;
}
}
return -1;
}
}
protected static class EClassData
{
public EClass eClass;
public EFactory eFactory;
public EStructuralFeatureData[] eStructuralFeatureData;
}
protected static class EStructuralFeatureData
{
public int featureID;
public EStructuralFeature eStructuralFeature;
public FeatureKind kind;
public EFactory eFactory;
public EDataType eDataType;
}
protected ResourceSet resourceSet;
protected InputStream inputStream;
protected List<EPackageData> ePackageDataList = new ArrayList<EPackageData>();
protected List<EClassData> eClassDataList = new ArrayList<EClassData>();
protected List<InternalEObject> eObjectList = new ArrayList<InternalEObject>();
protected List<URI> uriList = new ArrayList<URI>();
protected BasicEList<InternalEObject> internalEObjectList = new BasicEList<InternalEObject>();
protected BasicEList<Object> dataValueList = new BasicEList<Object>();
public EObjectInputStream(InputStream inputStream, Map<?, ?> options) throws IOException
{
this.inputStream = inputStream;
this.options = options;
readSignature();
readVersion();
}
protected void readSignature() throws IOException
{
if (readByte() != (byte)'\211' ||
readByte() != 'e' ||
readByte() != 'm' ||
readByte() != 'f' ||
readByte() != '\n' ||
readByte() != '\r' ||
readByte() != '\032' ||
readByte() != '\n')
{
throw new IOException("Invalid signature for a binary EMF serialization");
}
}
protected void readVersion() throws IOException
{
version = Version.values()[readByte()];
}
protected int[][] intDataArrayBuffer = new int[50][];
protected int intDataArrayBufferCount = -1;
protected int [] allocateIntArray(int length)
{
if (intDataArrayBufferCount == -1)
{
return new int[length];
}
else
{
int[] buffer = intDataArrayBuffer[intDataArrayBufferCount];
intDataArrayBuffer[intDataArrayBufferCount--] = null;
return buffer.length >= length ? buffer : new int[length];
}
}
protected void recycle(int[] values)
{
if (++intDataArrayBufferCount >= intDataArrayBuffer.length)
{
int [][] newIntDataArrayBuffer = new int[intDataArrayBufferCount * 2][];
System.arraycopy(intDataArrayBuffer, 0, newIntDataArrayBuffer, 0, intDataArrayBufferCount);
intDataArrayBuffer = newIntDataArrayBuffer;
}
intDataArrayBuffer[intDataArrayBufferCount] = values;
}
protected EPackageData readEPackage() throws IOException
{
int id = readCompressedInt();
if (ePackageDataList.size() <= id)
{
EPackageData ePackageData = new EPackageData();
String nsURI = readString();
URI uri = readURI();
if (resourceSet != null)
{
ePackageData.ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
if (ePackageData.ePackage == null)
{
ePackageData.ePackage = (EPackage)resourceSet.getEObject(uri, true);
}
}
else
{
ePackageData.ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
}
ePackageData.eClassData = new EClassData [ePackageData.ePackage.getEClassifiers().size()];
ePackageDataList.add(ePackageData);
return ePackageData;
}
else
{
return ePackageDataList.get(id);
}
}
protected EClassData readEClass() throws IOException
{
EPackageData ePackageData = readEPackage();
int id = readCompressedInt();
EClassData eClassData = ePackageData.eClassData[id];
if (eClassData == null)
{
eClassData = ePackageData.eClassData[id] = new EClassData();
String name = readString();
eClassData.eClass = (EClass)ePackageData.ePackage.getEClassifier(name);
eClassData.eFactory = ePackageData.ePackage.getEFactoryInstance();
eClassData.eStructuralFeatureData = new EStructuralFeatureData [eClassData.eClass.getFeatureCount()];
}
return eClassData;
}
protected EStructuralFeatureData readEStructuralFeature() throws IOException
{
EClassData eClassData = readEClass();
int featureID = readCompressedInt();
return getEStructuralFeatureData(eClassData, featureID);
}
protected EStructuralFeatureData getEStructuralFeatureData(EClassData eClassData, int featureID) throws IOException
{
EStructuralFeatureData eStructuralFeatureData = eClassData.eStructuralFeatureData[featureID];
if (eStructuralFeatureData == null)
{
eStructuralFeatureData = eClassData.eStructuralFeatureData[featureID] = new EStructuralFeatureData();
String name = readString();
eStructuralFeatureData.eStructuralFeature = eClassData.eClass.getEStructuralFeature(name);
eStructuralFeatureData.featureID = eClassData.eClass.getFeatureID(eStructuralFeatureData.eStructuralFeature);
eStructuralFeatureData.kind = FeatureKind.get(eStructuralFeatureData.eStructuralFeature);
if (eStructuralFeatureData.eStructuralFeature instanceof EAttribute)
{
EAttribute eAttribute = (EAttribute)eStructuralFeatureData.eStructuralFeature;
eStructuralFeatureData.eDataType = eAttribute.getEAttributeType();
eStructuralFeatureData.eFactory = eStructuralFeatureData.eDataType.getEPackage().getEFactoryInstance();
}
}
return eStructuralFeatureData;
}
public void loadResource(Resource resource) throws IOException
{
this.resource = resource;
this.resourceSet = resource.getResourceSet();
URI uri = resource.getURI();
if (uri != null && uri.isHierarchical() && !uri.isRelative())
{
baseURI = uri;
}
int size = readCompressedInt();
InternalEObject[] values = allocateInternalEObjectArray(size);
for (int i = 0; i < size; ++i)
{
values[i] = loadEObject();
}
internalEObjectList.setData(size, values);
@SuppressWarnings("unchecked")
InternalEList<InternalEObject> internalEObjects = (InternalEList<InternalEObject>)(InternalEList<?>)resource.getContents();
internalEObjects.addAllUnique(internalEObjectList);
recycle(values);
}
public void loadEObjects(InternalEList<InternalEObject> internalEObjects) throws IOException
{
// Read all the values into an array.
//
int size = readCompressedInt();
InternalEObject[] values = allocateInternalEObjectArray(size);
for (int i = 0; i < size; ++i)
{
values[i] = loadEObject();
}
int existingSize = internalEObjects.size();
// If the list is empty, we need to add all the objects,
// otherwise, the reference is bidirectional and the list is at least partially populated.
//
if (existingSize == 0)
{
internalEObjectList.setData(size, values);
internalEObjects.addAllUnique(internalEObjectList);
}
else
{
InternalEObject [] existingValues = allocateInternalEObjectArray(existingSize);
internalEObjects.basicToArray(existingValues);
int [] indices = allocateIntArray(existingSize);
int duplicateCount = 0;
LOOP:
for (int i = 0; i < size; ++i)
{
InternalEObject internalEObject = values[i];
for (int j = 0, count = 0; j < existingSize; ++j)
{
InternalEObject existingInternalEObject = existingValues[j];
if (existingInternalEObject == internalEObject)
{
if (duplicateCount != count)
{
internalEObjects.move(duplicateCount, count);
}
indices[duplicateCount] = i;
++count;
++duplicateCount;
existingValues[j] = null;
continue LOOP;
}
else if (existingInternalEObject != null)
{
++count;
}
}
values[i - duplicateCount] = internalEObject;
}
size -= existingSize;
internalEObjectList.setData(size, values);
internalEObjects.addAllUnique(0, internalEObjectList);
for (int i = 0; i < existingSize; ++i)
{
int newPosition = indices[i];
int oldPosition = size + i;
if (newPosition != oldPosition)
{
internalEObjects.move(newPosition, oldPosition);
}
}
recycle(existingValues);
recycle(indices);
}
recycle(values);
}
public void loadFeatureMap(FeatureMap.Internal featureMap) throws IOException
{
// Read all the values into an array.
//
int size = readCompressedInt();
FeatureMap.Entry.Internal[] values = allocateFeatureMapEntryArray(size);
for (int i = 0; i < size; ++i)
{
values[i] = loadFeatureMapEntry();
}
int existingSize = featureMap.size();
// If the list is empty, we need to add all the objects,
// otherwise, the reference is bidirectional and the list is at least partially populated.
//
if (existingSize == 0)
{
featureMap.addAllUnique(values, 0, size);
}
else
{
FeatureMap.Entry.Internal [] existingValues = allocateFeatureMapEntryArray(existingSize);
featureMap.basicToArray(existingValues);
int [] indices = allocateIntArray(existingSize);
int duplicateCount = 0;
LOOP:
for (int i = 0; i < size; ++i)
{
FeatureMap.Entry.Internal entry = values[i];
for (int j = 0, count = 0; j < existingSize; ++j)
{
FeatureMap.Entry.Internal existingEntry = existingValues[j];
if (entry.equals(existingEntry))
{
if (duplicateCount != count)
{
featureMap.move(duplicateCount, count);
}
indices[duplicateCount] = i;
++count;
++duplicateCount;
existingValues[j] = null;
continue LOOP;
}
else if (existingEntry != null)
{
++count;
}
}
values[i - duplicateCount] = entry;
}
size -= existingSize;
internalEObjectList.setData(size, values);
featureMap.addAllUnique(0, values, 0, size);
for (int i = 0; i < existingSize; ++i)
{
int newPosition = indices[i];
int oldPosition = size + i;
if (newPosition != oldPosition)
{
featureMap.move(newPosition, oldPosition);
}
}
recycle(existingValues);
recycle(indices);
}
recycle(values);
}
public FeatureMap.Entry.Internal loadFeatureMapEntry() throws IOException
{
EStructuralFeatureData eStructuralFeatureData = readEStructuralFeature();
Object value;
switch (eStructuralFeatureData.kind)
{
case EOBJECT_CONTAINER:
case EOBJECT_CONTAINER_PROXY_RESOLVING:
case EOBJECT:
case EOBJECT_LIST:
case EOBJECT_PROXY_RESOLVING:
case EOBJECT_LIST_PROXY_RESOLVING:
case EOBJECT_CONTAINMENT:
case EOBJECT_CONTAINMENT_LIST:
case EOBJECT_CONTAINMENT_PROXY_RESOLVING:
case EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING:
{
value = loadEObject();
break;
}
case STRING:
{
value = readString();
break;
}
case DATA:
{
String literal = readString();
value = eStructuralFeatureData.eFactory.createFromString(eStructuralFeatureData.eDataType, literal);
break;
}
case BOOLEAN:
{
value = readBoolean();
break;
}
case BYTE:
{
value = readByte();
break;
}
case CHAR:
{
value = readChar();
break;
}
case DOUBLE:
{
value = readDouble();
break;
}
case FLOAT:
{
value = readFloat();
break;
}
case INT:
{
value = readInt();
break;
}
case LONG:
{
value = readLong();
break;
}
case SHORT:
{
value = readShort();
break;
}
default:
{
throw new IOException("Unhandled case " + eStructuralFeatureData.kind);
}
}
return FeatureMapUtil.createRawEntry(eStructuralFeatureData.eStructuralFeature, value);
}
public InternalEObject loadEObject() throws IOException
{
int id = readCompressedInt();
if (id == -1)
{
return null;
}
else
{
if (eObjectList.size() <= id)
{
EClassData eClassData = readEClass();
InternalEObject internalEObject = (InternalEObject)eClassData.eFactory.create(eClassData.eClass);
eObjectList.add(internalEObject);
for (;;)
{
int featureID = readCompressedInt() - 1;
if (featureID == -1)
{
break;
}
else if (featureID == -2)
{
internalEObject.eSetProxyURI(readURI());
break;
}
else
{
EStructuralFeatureData eStructuralFeatureData = getEStructuralFeatureData(eClassData, featureID);
loadFeatureValue(internalEObject, eStructuralFeatureData);
}
}
return internalEObject;
}
else
{
return eObjectList.get(id);
}
}
}
protected void loadFeatureValue(InternalEObject internalEObject, EStructuralFeatureData eStructuralFeatureData) throws IOException
{
switch (eStructuralFeatureData.kind)
{
case EOBJECT_CONTAINER:
case EOBJECT_CONTAINER_PROXY_RESOLVING:
case EOBJECT:
case EOBJECT_PROXY_RESOLVING:
case EOBJECT_CONTAINMENT:
case EOBJECT_CONTAINMENT_PROXY_RESOLVING:
{
internalEObject.eSet(eStructuralFeatureData.featureID, loadEObject());
break;
}
case EOBJECT_LIST:
case EOBJECT_LIST_PROXY_RESOLVING:
case EOBJECT_CONTAINMENT_LIST:
case EOBJECT_CONTAINMENT_LIST_PROXY_RESOLVING:
{
@SuppressWarnings("unchecked")
InternalEList<InternalEObject> internalEList = (InternalEList<InternalEObject>)internalEObject.eGet(eStructuralFeatureData.featureID, false, true);
loadEObjects(internalEList);
break;
}
case STRING:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readString());
break;
}
case FEATURE_MAP:
{
FeatureMap.Internal featureMap = (FeatureMap.Internal)internalEObject.eGet(eStructuralFeatureData.featureID, false, true);
loadFeatureMap(featureMap);
break;
}
case DATA:
{
String literal = readString();
internalEObject.eSet(eStructuralFeatureData.featureID, eStructuralFeatureData.eFactory.createFromString(eStructuralFeatureData.eDataType, literal));
break;
}
case DATA_LIST:
{
int size = readCompressedInt();
dataValueList.grow(size);
Object[] dataValues = dataValueList.data();
for (int i = 0; i < size; ++i)
{
String literal = readString();
dataValues[i] = eStructuralFeatureData.eFactory.createFromString(eStructuralFeatureData.eDataType, literal);
}
dataValueList.setData(size, dataValues);
@SuppressWarnings("unchecked")
List<Object> values = (List<Object>)internalEObject.eGet(eStructuralFeatureData.featureID, false, true);
values.addAll(dataValueList);
break;
}
case BOOLEAN:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readBoolean());
break;
}
case BYTE:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readByte());
break;
}
case CHAR:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readChar());
break;
}
case DOUBLE:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readDouble());
break;
}
case FLOAT:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readFloat());
break;
}
case INT:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readInt());
break;
}
case LONG:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readLong());
break;
}
case SHORT:
{
internalEObject.eSet(eStructuralFeatureData.featureID, readShort());
break;
}
default:
{
throw new IOException("Unhandled case " + eStructuralFeatureData.kind);
}
}
}
public byte readByte() throws IOException
{
int result = inputStream.read();
if (result == -1)
{
throw new IOException("Unexpected end of stream");
}
return (byte)result;
}
public boolean readBoolean() throws IOException
{
return readByte() != 0;
}
public char readChar() throws IOException
{
return (char)((readByte() << 8) & 0xFF00 | readByte() & 0xFF);
}
public short readShort() throws IOException
{
return (short)((readByte()<< 8) & 0xFF00 | readByte() & 0xFF);
}
public int readInt() throws IOException
{
return (readByte() << 24) | (readByte() << 16) & 0xFF0000 | (readByte() << 8) & 0xFF00 | readByte() & 0xFF;
}
public long readLong() throws IOException
{
return (long)readInt() << 32 | readInt() & 0xFFFFFFFFL;
}
public float readFloat() throws IOException
{
// TODO
// return Float.intBitsToFloat(readInt());
throw new UnsupportedOperationException();
}
public double readDouble() throws IOException
{
// TODO
// return Double.longBitsToDouble(readLong());
throw new UnsupportedOperationException();
}
public int readCompressedInt() throws IOException
{
byte initialByte = readByte();
int code = (initialByte >> 6) & 0x3;
switch (code)
{
case 0:
{
return initialByte - 1;
}
case 1:
{
return (initialByte << 8 & 0x3F00 | readByte() & 0xFF) - 1;
}
case 2:
{
return ((initialByte << 16) & 0x3F0000 | (readByte() << 8) & 0xFF00 | readByte() & 0xFF) - 1;
}
default:
{
return ((initialByte << 24) & 0x3F000000 | (readByte() << 16) & 0xFF0000 | (readByte() << 8) & 0xFF00 | readByte() & 0xFF) - 1;
}
}
}
public String readString() throws IOException
{
int length = readCompressedInt();
if (length == -1)
{
return null;
}
else
{
if (characters == null || characters.length < length)
{
characters = new char[length];
}
LOOP:
for (int i = 0; i < length; ++i)
{
byte value = readByte();
if (value == 0)
{
do
{
characters[i] = readChar();
}
while (++i < length);
break LOOP;
}
else
{
characters[i] = (char)(value & 0xFF);
}
}
return new String(characters, 0, length);
}
}
public URI readURI() throws IOException
{
int id = readCompressedInt();
if (id == -1)
{
return null;
}
else
{
URI uri;
if (uriList.size() <= id)
{
String value = readString();
uri = resolve(URI.createURI(value));
uriList.add(uri);
}
else
{
uri = uriList.get(id);
}
String fragment = readString();
if (fragment != null)
{
uri = uri.appendFragment(fragment);
}
return uri;
}
}
}
}