| /******************************************************************************* |
| * Copyright (c) 2009, 2019 Xored Software Inc and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Xored Software Inc - initial API and implementation and/or initial documentation |
| *******************************************************************************/ |
| package org.eclipse.rcptt.ecl.core.util; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| 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.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.resource.impl.ResourceImpl; |
| 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; |
| |
| public class ECLBinaryResourceImpl extends ResourceImpl { |
| /** |
| * Specify the capacity of the buffered stream used when |
| * {@link #doSave(OutputStream, Map) saving} or |
| * {@link #doLoad(InputStream, Map) loading} the resource content. The value |
| * must be an integer. If not specified, {@link #DEFAULT_BUFFER_CAPACITY} is |
| * used. A value less than one disables the cache. |
| * |
| * @since 2.6 |
| */ |
| public static final String OPTION_BUFFER_CAPACITY = "BUFFER_CAPACITY"; |
| |
| /** |
| * The default {@link #OPTION_BUFFER_CAPACITY} capacity of the buffered |
| * stream used when {@link #doSave(OutputStream, Map) saving} or |
| * {@link #doLoad(InputStream, Map) loading} the resource content. |
| * |
| * @since 2.6 |
| */ |
| public static final int DEFAULT_BUFFER_CAPACITY = 1024; |
| |
| /** |
| * Extract the {@link #OPTION_BUFFER_CAPACITY} from the options. |
| * |
| * @param options |
| * a map of options. |
| * @return the value associated with the {@link #OPTION_BUFFER_CAPACITY} key |
| * in the options map. |
| * @since 2.6 |
| */ |
| protected static int getBufferCapacity(Map<?, ?> options) { |
| if (options != null) { |
| Integer capacity = (Integer) options.get(OPTION_BUFFER_CAPACITY); |
| if (capacity != null) { |
| return capacity; |
| } |
| } |
| return DEFAULT_BUFFER_CAPACITY; |
| } |
| |
| public ECLBinaryResourceImpl() { |
| super(); |
| } |
| |
| public ECLBinaryResourceImpl(URI uri) { |
| super(uri); |
| } |
| |
| @Override |
| protected void doSave(OutputStream outputStream, Map<?, ?> options) |
| throws IOException { |
| boolean buffer = !(outputStream instanceof BufferedOutputStream); |
| if (buffer) { |
| int bufferCapacity = getBufferCapacity(options); |
| if (bufferCapacity > 0) { |
| outputStream = new BufferedOutputStream(outputStream, |
| bufferCapacity); |
| } else { |
| buffer = false; |
| } |
| } |
| |
| try { |
| EObjectOutputStream eObjectOutputStream = new EObjectOutputStream( |
| outputStream, options); |
| eObjectOutputStream.saveResource(this); |
| } finally { |
| if (buffer) { |
| outputStream.flush(); |
| } |
| } |
| } |
| |
| @Override |
| protected void doLoad(InputStream inputStream, Map<?, ?> options) |
| throws IOException { |
| if (!(inputStream instanceof BufferedInputStream)) { |
| int bufferCapacity = getBufferCapacity(options); |
| if (bufferCapacity > 0) { |
| inputStream = new BufferedInputStream(inputStream, |
| bufferCapacity); |
| } |
| } |
| |
| 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 { |
| writeInt(Float.floatToIntBits(value)); |
| } |
| |
| public void writeDouble(double value) throws IOException { |
| writeLong(Double.doubleToLongBits(value)); |
| } |
| |
| 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 || id < 0) { |
| throw new RuntimeException("Package id is not incremented by 1"); |
| } else 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); |
| } |
| if (ePackageData.ePackage == null) |
| throw new NullPointerException("Can load package " + nsURI |
| + ", " + uri.toString()); |
| 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 { |
| return Float.intBitsToFloat(readInt()); |
| } |
| |
| public double readDouble() throws IOException { |
| return Double.longBitsToDouble(readLong()); |
| } |
| |
| 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; |
| } |
| } |
| } |
| } |