diff options
Diffstat (limited to 'engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java')
-rw-r--r-- | engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java | 1380 |
1 files changed, 1380 insertions, 0 deletions
diff --git a/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java new file mode 100644 index 0000000..cebd764 --- /dev/null +++ b/engine/src/core-plugins/com/jme3/export/binary/BinaryInputCapsule.java @@ -0,0 +1,1380 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.export.binary; + +import com.jme3.export.InputCapsule; +import com.jme3.export.Savable; +import com.jme3.export.SavableClassUtil; +import com.jme3.util.BufferUtils; +import com.jme3.util.IntMap; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Joshua Slack + */ +final class BinaryInputCapsule implements InputCapsule { + + private static final Logger logger = Logger + .getLogger(BinaryInputCapsule.class.getName()); + + protected BinaryImporter importer; + protected BinaryClassObject cObj; + protected Savable savable; + protected HashMap<Byte, Object> fieldData; + + protected int index = 0; + + public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) { + this.importer = importer; + this.cObj = bco; + this.savable = savable; + } + + public void setContent(byte[] content, int start, int limit) { + fieldData = new HashMap<Byte, Object>(); + for (index = start; index < limit;) { + byte alias = content[index]; + + index++; + + try { + byte type = cObj.aliasFields.get(alias).type; + Object value = null; + + switch (type) { + case BinaryClassField.BITSET: { + value = readBitSet(content); + break; + } + case BinaryClassField.BOOLEAN: { + value = readBoolean(content); + break; + } + case BinaryClassField.BOOLEAN_1D: { + value = readBooleanArray(content); + break; + } + case BinaryClassField.BOOLEAN_2D: { + value = readBooleanArray2D(content); + break; + } + case BinaryClassField.BYTE: { + value = readByte(content); + break; + } + case BinaryClassField.BYTE_1D: { + value = readByteArray(content); + break; + } + case BinaryClassField.BYTE_2D: { + value = readByteArray2D(content); + break; + } + case BinaryClassField.BYTEBUFFER: { + value = readByteBuffer(content); + break; + } + case BinaryClassField.DOUBLE: { + value = readDouble(content); + break; + } + case BinaryClassField.DOUBLE_1D: { + value = readDoubleArray(content); + break; + } + case BinaryClassField.DOUBLE_2D: { + value = readDoubleArray2D(content); + break; + } + case BinaryClassField.FLOAT: { + value = readFloat(content); + break; + } + case BinaryClassField.FLOAT_1D: { + value = readFloatArray(content); + break; + } + case BinaryClassField.FLOAT_2D: { + value = readFloatArray2D(content); + break; + } + case BinaryClassField.FLOATBUFFER: { + value = readFloatBuffer(content); + break; + } + case BinaryClassField.FLOATBUFFER_ARRAYLIST: { + value = readFloatBufferArrayList(content); + break; + } + case BinaryClassField.BYTEBUFFER_ARRAYLIST: { + value = readByteBufferArrayList(content); + break; + } + case BinaryClassField.INT: { + value = readInt(content); + break; + } + case BinaryClassField.INT_1D: { + value = readIntArray(content); + break; + } + case BinaryClassField.INT_2D: { + value = readIntArray2D(content); + break; + } + case BinaryClassField.INTBUFFER: { + value = readIntBuffer(content); + break; + } + case BinaryClassField.LONG: { + value = readLong(content); + break; + } + case BinaryClassField.LONG_1D: { + value = readLongArray(content); + break; + } + case BinaryClassField.LONG_2D: { + value = readLongArray2D(content); + break; + } + case BinaryClassField.SAVABLE: { + value = readSavable(content); + break; + } + case BinaryClassField.SAVABLE_1D: { + value = readSavableArray(content); + break; + } + case BinaryClassField.SAVABLE_2D: { + value = readSavableArray2D(content); + break; + } + case BinaryClassField.SAVABLE_ARRAYLIST: { + value = readSavableArray(content); + break; + } + case BinaryClassField.SAVABLE_ARRAYLIST_1D: { + value = readSavableArray2D(content); + break; + } + case BinaryClassField.SAVABLE_ARRAYLIST_2D: { + value = readSavableArray3D(content); + break; + } + case BinaryClassField.SAVABLE_MAP: { + value = readSavableMap(content); + break; + } + case BinaryClassField.STRING_SAVABLE_MAP: { + value = readStringSavableMap(content); + break; + } + case BinaryClassField.INT_SAVABLE_MAP: { + value = readIntSavableMap(content); + break; + } + case BinaryClassField.SHORT: { + value = readShort(content); + break; + } + case BinaryClassField.SHORT_1D: { + value = readShortArray(content); + break; + } + case BinaryClassField.SHORT_2D: { + value = readShortArray2D(content); + break; + } + case BinaryClassField.SHORTBUFFER: { + value = readShortBuffer(content); + break; + } + case BinaryClassField.STRING: { + value = readString(content); + break; + } + case BinaryClassField.STRING_1D: { + value = readStringArray(content); + break; + } + case BinaryClassField.STRING_2D: { + value = readStringArray2D(content); + break; + } + + default: + // skip put statement + continue; + } + + fieldData.put(alias, value); + + } catch (IOException e) { + logger.logp(Level.SEVERE, this.getClass().toString(), + "setContent(byte[] content)", "Exception", e); + } + } + } + + public int getSavableVersion(Class<? extends Savable> desiredClass){ + return SavableClassUtil.getSavedSavableVersion(savable, desiredClass, + cObj.classHierarchyVersions, importer.getFormatVersion()); + } + + public BitSet readBitSet(String name, BitSet defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (BitSet) fieldData.get(field.alias); + } + + public boolean readBoolean(String name, boolean defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Boolean) fieldData.get(field.alias)).booleanValue(); + } + + public boolean[] readBooleanArray(String name, boolean[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (boolean[]) fieldData.get(field.alias); + } + + public boolean[][] readBooleanArray2D(String name, boolean[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (boolean[][]) fieldData.get(field.alias); + } + + public byte readByte(String name, byte defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Byte) fieldData.get(field.alias)).byteValue(); + } + + public byte[] readByteArray(String name, byte[] defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (byte[]) fieldData.get(field.alias); + } + + public byte[][] readByteArray2D(String name, byte[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (byte[][]) fieldData.get(field.alias); + } + + public ByteBuffer readByteBuffer(String name, ByteBuffer defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (ByteBuffer) fieldData.get(field.alias); + } + + @SuppressWarnings("unchecked") + public ArrayList<ByteBuffer> readByteBufferArrayList(String name, + ArrayList<ByteBuffer> defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (ArrayList<ByteBuffer>) fieldData.get(field.alias); + } + + public double readDouble(String name, double defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Double) fieldData.get(field.alias)).doubleValue(); + } + + public double[] readDoubleArray(String name, double[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (double[]) fieldData.get(field.alias); + } + + public double[][] readDoubleArray2D(String name, double[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (double[][]) fieldData.get(field.alias); + } + + public float readFloat(String name, float defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Float) fieldData.get(field.alias)).floatValue(); + } + + public float[] readFloatArray(String name, float[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (float[]) fieldData.get(field.alias); + } + + public float[][] readFloatArray2D(String name, float[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (float[][]) fieldData.get(field.alias); + } + + public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (FloatBuffer) fieldData.get(field.alias); + } + + @SuppressWarnings("unchecked") + public ArrayList<FloatBuffer> readFloatBufferArrayList(String name, + ArrayList<FloatBuffer> defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (ArrayList<FloatBuffer>) fieldData.get(field.alias); + } + + public int readInt(String name, int defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Integer) fieldData.get(field.alias)).intValue(); + } + + public int[] readIntArray(String name, int[] defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (int[]) fieldData.get(field.alias); + } + + public int[][] readIntArray2D(String name, int[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (int[][]) fieldData.get(field.alias); + } + + public IntBuffer readIntBuffer(String name, IntBuffer defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (IntBuffer) fieldData.get(field.alias); + } + + public long readLong(String name, long defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Long) fieldData.get(field.alias)).longValue(); + } + + public long[] readLongArray(String name, long[] defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (long[]) fieldData.get(field.alias); + } + + public long[][] readLongArray2D(String name, long[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (long[][]) fieldData.get(field.alias); + } + + public Savable readSavable(String name, Savable defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value == null) + return null; + else if (value instanceof ID) { + value = importer.readObject(((ID) value).id); + fieldData.put(field.alias, value); + return (Savable) value; + } else + return defVal; + } + + public Savable[] readSavableArray(String name, Savable[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object[] values = (Object[]) fieldData.get(field.alias); + if (values instanceof ID[]) { + values = resolveIDs(values); + fieldData.put(field.alias, values); + return (Savable[]) values; + } else + return defVal; + } + + private Savable[] resolveIDs(Object[] values) { + if (values != null) { + Savable[] savables = new Savable[values.length]; + for (int i = 0; i < values.length; i++) { + final ID id = (ID) values[i]; + savables[i] = id != null ? importer.readObject(id.id) : null; + } + return savables; + } else { + return null; + } + } + + public Savable[][] readSavableArray2D(String name, Savable[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null ||!fieldData.containsKey(field.alias)) + return defVal; + Object[][] values = (Object[][]) fieldData.get(field.alias); + if (values instanceof ID[][]) { + Savable[][] savables = new Savable[values.length][]; + for (int i = 0; i < values.length; i++) { + if (values[i] != null) { + savables[i] = resolveIDs(values[i]); + } else savables[i] = null; + } + values = savables; + fieldData.put(field.alias, values); + } + return (Savable[][]) values; + } + + public Savable[][][] readSavableArray3D(String name, Savable[][][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object[][][] values = (Object[][][]) fieldData.get(field.alias); + if (values instanceof ID[][][]) { + Savable[][][] savables = new Savable[values.length][][]; + for (int i = 0; i < values.length; i++) { + if (values[i] != null) { + savables[i] = new Savable[values[i].length][]; + for (int j = 0; j < values[i].length; j++) { + savables[i][j] = resolveIDs(values[i][j]); + } + } else savables[i] = null; + } + fieldData.put(field.alias, savables); + return savables; + } else + return defVal; + } + + private ArrayList<Savable> savableArrayListFromArray(Savable[] savables) { + if(savables == null) { + return null; + } + ArrayList<Savable> arrayList = new ArrayList<Savable>(savables.length); + for (int x = 0; x < savables.length; x++) { + arrayList.add(savables[x]); + } + return arrayList; + } + + // Assumes array of size 2 arrays where pos 0 is key and pos 1 is value. + private Map<Savable, Savable> savableMapFrom2DArray(Savable[][] savables) { + if(savables == null) { + return null; + } + Map<Savable, Savable> map = new HashMap<Savable, Savable>(savables.length); + for (int x = 0; x < savables.length; x++) { + map.put(savables[x][0], savables[x][1]); + } + return map; + } + + private Map<String, Savable> stringSavableMapFromKV(String[] keys, Savable[] values) { + if(keys == null || values == null) { + return null; + } + + Map<String, Savable> map = new HashMap<String, Savable>(keys.length); + for (int x = 0; x < keys.length; x++) + map.put(keys[x], values[x]); + + return map; + } + + private IntMap<Savable> intSavableMapFromKV(int[] keys, Savable[] values) { + if(keys == null || values == null) { + return null; + } + + IntMap<Savable> map = new IntMap<Savable>(keys.length); + for (int x = 0; x < keys.length; x++) + map.put(keys[x], values[x]); + + return map; + } + + public ArrayList readSavableArrayList(String name, ArrayList defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof ID[]) { + // read Savable array and convert to ArrayList + Savable[] savables = readSavableArray(name, null); + value = savableArrayListFromArray(savables); + fieldData.put(field.alias, value); + } + return (ArrayList) value; + } + + public ArrayList[] readSavableArrayListArray(String name, ArrayList[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof ID[][]) { + // read 2D Savable array and convert to ArrayList array + Savable[][] savables = readSavableArray2D(name, null); + if (savables != null) { + ArrayList[] arrayLists = new ArrayList[savables.length]; + for (int i = 0; i < savables.length; i++) { + arrayLists[i] = savableArrayListFromArray(savables[i]); + } + value = arrayLists; + } else + value = defVal; + fieldData.put(field.alias, value); + } + return (ArrayList[]) value; + } + + public ArrayList[][] readSavableArrayListArray2D(String name, + ArrayList[][] defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof ID[][][]) { + // read 3D Savable array and convert to 2D ArrayList array + Savable[][][] savables = readSavableArray3D(name, null); + if (savables != null && savables.length > 0) { + ArrayList[][] arrayLists = new ArrayList[savables.length][]; + for (int i = 0; i < savables.length; i++) { + arrayLists[i] = new ArrayList[savables[i].length]; + for (int j = 0; j < savables[i].length; j++) { + arrayLists[i][j] = savableArrayListFromArray(savables[i][j]); + } + } + value = arrayLists; + } else + value = defVal; + fieldData.put(field.alias, value); + } + return (ArrayList[][]) value; + } + + @SuppressWarnings("unchecked") + public Map<? extends Savable, ? extends Savable> readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof ID[][]) { + // read Savable array and convert to Map + Savable[][] savables = readSavableArray2D(name, null); + value = savableMapFrom2DArray(savables); + fieldData.put(field.alias, value); + } + return (Map<? extends Savable, ? extends Savable>) value; + } + + @SuppressWarnings("unchecked") + public Map<String, ? extends Savable> readStringSavableMap(String name, Map<String, ? extends Savable> defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof StringIDMap) { + // read Savable array and convert to Map values + StringIDMap in = (StringIDMap) value; + Savable[] values = resolveIDs(in.values); + value = stringSavableMapFromKV(in.keys, values); + fieldData.put(field.alias, value); + } + return (Map<String, Savable>) value; + } + + @SuppressWarnings("unchecked") + public IntMap<? extends Savable> readIntSavableMap(String name, IntMap<? extends Savable> defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + Object value = fieldData.get(field.alias); + if (value instanceof IntIDMap) { + // read Savable array and convert to Map values + IntIDMap in = (IntIDMap) value; + Savable[] values = resolveIDs(in.values); + value = intSavableMapFromKV(in.keys, values); + fieldData.put(field.alias, value); + } + return (IntMap<Savable>) value; + } + + public short readShort(String name, short defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return ((Short) fieldData.get(field.alias)).shortValue(); + } + + public short[] readShortArray(String name, short[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (short[]) fieldData.get(field.alias); + } + + public short[][] readShortArray2D(String name, short[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (short[][]) fieldData.get(field.alias); + } + + public ShortBuffer readShortBuffer(String name, ShortBuffer defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (ShortBuffer) fieldData.get(field.alias); + } + + public String readString(String name, String defVal) throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (String) fieldData.get(field.alias); + } + + public String[] readStringArray(String name, String[] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (String[]) fieldData.get(field.alias); + } + + public String[][] readStringArray2D(String name, String[][] defVal) + throws IOException { + BinaryClassField field = cObj.nameFields.get(name); + if (field == null || !fieldData.containsKey(field.alias)) + return defVal; + return (String[][]) fieldData.get(field.alias); + } + + // byte primitive + + protected byte readByte(byte[] content) throws IOException { + byte value = content[index]; + index++; + return value; + } + + protected byte readByteForBuffer(byte[] content) throws IOException { + byte value = content[index]; + index++; + return value; + } + + protected byte[] readByteArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + byte[] value = new byte[length]; + for (int x = 0; x < length; x++) + value[x] = readByte(content); + return value; + } + + protected byte[][] readByteArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + byte[][] value = new byte[length][]; + for (int x = 0; x < length; x++) + value[x] = readByteArray(content); + return value; + } + + // int primitive + + protected int readIntForBuffer(byte[] content){ + int number = ((content[index+3] & 0xFF) << 24) + + ((content[index+2] & 0xFF) << 16) + + ((content[index+1] & 0xFF) << 8) + + (content[index] & 0xFF); + index += 4; + return number; + } + + protected int readInt(byte[] content) throws IOException { + byte[] bytes = inflateFrom(content, index); + index += 1 + bytes.length; + bytes = ByteUtils.rightAlignBytes(bytes, 4); + int value = ByteUtils.convertIntFromBytes(bytes); + if (value == BinaryOutputCapsule.NULL_OBJECT + || value == BinaryOutputCapsule.DEFAULT_OBJECT) + index -= 4; + return value; + } + + protected int[] readIntArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + int[] value = new int[length]; + for (int x = 0; x < length; x++) + value[x] = readInt(content); + return value; + } + + protected int[][] readIntArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + int[][] value = new int[length][]; + for (int x = 0; x < length; x++) + value[x] = readIntArray(content); + return value; + } + + // float primitive + + protected float readFloat(byte[] content) throws IOException { + float value = ByteUtils.convertFloatFromBytes(content, index); + index += 4; + return value; + } + + protected float readFloatForBuffer(byte[] content) throws IOException { + int number = readIntForBuffer(content); + return Float.intBitsToFloat(number); + } + + protected float[] readFloatArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + float[] value = new float[length]; + for (int x = 0; x < length; x++) + value[x] = readFloat(content); + return value; + } + + protected float[][] readFloatArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + float[][] value = new float[length][]; + for (int x = 0; x < length; x++) + value[x] = readFloatArray(content); + return value; + } + + // double primitive + + protected double readDouble(byte[] content) throws IOException { + double value = ByteUtils.convertDoubleFromBytes(content, index); + index += 8; + return value; + } + + protected double[] readDoubleArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + double[] value = new double[length]; + for (int x = 0; x < length; x++) + value[x] = readDouble(content); + return value; + } + + protected double[][] readDoubleArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + double[][] value = new double[length][]; + for (int x = 0; x < length; x++) + value[x] = readDoubleArray(content); + return value; + } + + // long primitive + + protected long readLong(byte[] content) throws IOException { + byte[] bytes = inflateFrom(content, index); + index += 1 + bytes.length; + bytes = ByteUtils.rightAlignBytes(bytes, 8); + long value = ByteUtils.convertLongFromBytes(bytes); + return value; + } + + protected long[] readLongArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + long[] value = new long[length]; + for (int x = 0; x < length; x++) + value[x] = readLong(content); + return value; + } + + protected long[][] readLongArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + long[][] value = new long[length][]; + for (int x = 0; x < length; x++) + value[x] = readLongArray(content); + return value; + } + + // short primitive + + protected short readShort(byte[] content) throws IOException { + short value = ByteUtils.convertShortFromBytes(content, index); + index += 2; + return value; + } + + protected short readShortForBuffer(byte[] content) throws IOException { + short number = (short) ((content[index+0] & 0xFF) + + ((content[index+1] & 0xFF) << 8)); + index += 2; + return number; + } + + protected short[] readShortArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + short[] value = new short[length]; + for (int x = 0; x < length; x++) + value[x] = readShort(content); + return value; + } + + protected short[][] readShortArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + short[][] value = new short[length][]; + for (int x = 0; x < length; x++) + value[x] = readShortArray(content); + return value; + } + + // boolean primitive + + protected boolean readBoolean(byte[] content) throws IOException { + boolean value = ByteUtils.convertBooleanFromBytes(content, index); + index += 1; + return value; + } + + protected boolean[] readBooleanArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + boolean[] value = new boolean[length]; + for (int x = 0; x < length; x++) + value[x] = readBoolean(content); + return value; + } + + protected boolean[][] readBooleanArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + boolean[][] value = new boolean[length][]; + for (int x = 0; x < length; x++) + value[x] = readBooleanArray(content); + return value; + } + + /* + * UTF-8 crash course: + * + * UTF-8 codepoints map to UTF-16 codepoints and vv, which is what Java uses for it's Strings. + * (so a UTF-8 codepoint can contain all possible values for a Java char) + * + * A UTF-8 codepoint can be 1, 2 or 3 bytes long. How long a codepint is can be told by reading the first byte: + * b < 0x80, 1 byte + * (b & 0xC0) == 0xC0, 2 bytes + * (b & 0xE0) == 0xE0, 3 bytes + * + * However there is an additional restriction to UTF-8, to enable you to find the start of a UTF-8 codepoint, + * if you start reading at a random point in a UTF-8 byte stream. That's why UTF-8 requires for the second and third byte of + * a multibyte codepoint: + * (b & 0x80) == 0x80 (in other words, first bit must be 1) + */ + private final static int UTF8_START = 0; // next byte should be the start of a new + private final static int UTF8_2BYTE = 2; // next byte should be the second byte of a 2 byte codepoint + private final static int UTF8_3BYTE_1 = 3; // next byte should be the second byte of a 3 byte codepoint + private final static int UTF8_3BYTE_2 = 4; // next byte should be the third byte of a 3 byte codepoint + private final static int UTF8_ILLEGAL = 10; // not an UTF8 string + + // String + protected String readString(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + + /* + * @see ISSUE 276 + * + * We'll transfer the bytes into a seperate byte array. + * While we do that we'll take the opportunity to check if the byte data is valid UTF-8. + * + * If it is not UTF-8 it is most likely saved with the BinaryOutputCapsule bug, that saves Strings using their native + * encoding. Unfortunatly there is no way to know what encoding was used, so we'll parse using the most common one in + * that case; latin-1 aka ISO8859_1 + * + * Encoding of "low" ASCII codepoint (in plain speak: when no special characters are used) will usually look the same + * for UTF-8 and the other 1 byte codepoint encodings (espc true for numbers and regular letters of the alphabet). So these + * are valid UTF-8 and will give the same result (at most a few charakters will appear different, such as the euro sign). + * + * However, when "high" codepoints are used (any codepoint that over 0x7F, in other words where the first bit is a 1) it's + * a different matter and UTF-8 and the 1 byte encoding greatly will differ, as well as most 1 byte encodings relative to each + * other. + * + * It is impossible to detect which one-byte encoding is used. Since UTF8 and practically all 1-byte encodings share the most + * used characters (the "none-high" ones) parsing them will give the same result. However, not all byte sequences are legal in + * UTF-8 (see explantion above). If not UTF-8 encoded content is detected we therefor fallback on latin1. We also log a warning. + * + * By this method we detect all use of 1 byte encoding if they: + * - use a "high" codepoint after a "low" codepoint or a sequence of codepoints that is valid as UTF-8 bytes, that starts with 1000 + * - use a "low" codepoint after a "high" codepoint + * - use a "low" codepoint after "high" codepoint, after a "high" codepoint that starts with 1110 + * + * In practise this means that unless 2 or 3 "high" codepoints are used after each other in proper order, we'll detect the string + * was not originally UTF-8 encoded. + * + */ + byte[] bytes = new byte[length]; + int utf8State = UTF8_START; + int b; + for (int x = 0; x < length; x++) { + bytes[x] = content[index++]; + b = (int) bytes[x] & 0xFF; // unsign our byte + + switch (utf8State) { + case UTF8_START: + if (b < 0x80) { + // good + } + else if ((b & 0xC0) == 0xC0) { + utf8State = UTF8_2BYTE; + } + else if ((b & 0xE0) == 0xE0) { + utf8State = UTF8_3BYTE_1; + } + else { + utf8State = UTF8_ILLEGAL; + } + break; + case UTF8_3BYTE_1: + case UTF8_3BYTE_2: + case UTF8_2BYTE: + if ((b & 0x80) == 0x80) + utf8State = utf8State == UTF8_3BYTE_1 ? UTF8_3BYTE_2 : UTF8_START; + else + utf8State = UTF8_ILLEGAL; + break; + } + } + + try { + // even though so far the parsing might have been a legal UTF-8 sequence, only if a codepoint is fully given is it correct UTF-8 + if (utf8State == UTF8_START) { + // Java misspells UTF-8 as UTF8 for official use in java.lang + return new String(bytes, "UTF8"); + } + else { + logger.log( + Level.WARNING, + "Your export has been saved with an incorrect encoding for it's String fields which means it might not load correctly " + + "due to encoding issues. You should probably re-export your work. See ISSUE 276 in the jME issue tracker." + ); + // We use ISO8859_1 to be consistent across platforms. We could default to native encoding, but this would lead to inconsistent + // behaviour across platforms! + // Developers that have previously saved their exports using the old exporter (wich uses native encoding), can temporarly + // remove the ""ISO8859_1" parameter, and change the above if statement to "if (false)". + // They should then import and re-export their models using the same enviroment they were orginally created in. + return new String(bytes, "ISO8859_1"); + } + } catch (UnsupportedEncodingException uee) { + // as a last resort fall back to platform native. + // JavaDoc is vague about what happens when a decoding a String that contains un undecodable sequence + // it also doesn't specify which encodings have to be supported (though UTF-8 and ISO8859 have been in the SUN JRE since at least 1.1) + logger.log( + Level.SEVERE, + "Your export has been saved with an incorrect encoding or your version of Java is unable to decode the stored string. " + + "While your export may load correctly by falling back, using it on different platforms or java versions might lead to "+ + "very strange inconsitenties. You should probably re-export your work. See ISSUE 276 in the jME issue tracker." + ); + return new String(bytes); + } + } + + protected String[] readStringArray(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + String[] value = new String[length]; + for (int x = 0; x < length; x++) + value[x] = readString(content); + return value; + } + + protected String[][] readStringArray2D(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + String[][] value = new String[length][]; + for (int x = 0; x < length; x++) + value[x] = readStringArray(content); + return value; + } + + // BitSet + + protected BitSet readBitSet(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + BitSet value = new BitSet(length); + for (int x = 0; x < length; x++) + value.set(x, readBoolean(content)); + return value; + } + + // INFLATOR for int and long + + protected static byte[] inflateFrom(byte[] contents, int index) { + byte firstByte = contents[index]; + if (firstByte == BinaryOutputCapsule.NULL_OBJECT) + return ByteUtils.convertToBytes(BinaryOutputCapsule.NULL_OBJECT); + else if (firstByte == BinaryOutputCapsule.DEFAULT_OBJECT) + return ByteUtils.convertToBytes(BinaryOutputCapsule.DEFAULT_OBJECT); + else if (firstByte == 0) + return new byte[0]; + else { + byte[] rVal = new byte[firstByte]; + for (int x = 0; x < rVal.length; x++) + rVal[x] = contents[x + 1 + index]; + return rVal; + } + } + + // BinarySavable + + protected ID readSavable(byte[] content) throws IOException { + int id = readInt(content); + if (id == BinaryOutputCapsule.NULL_OBJECT) { + return null; + } + + return new ID(id); + } + + // BinarySavable array + + protected ID[] readSavableArray(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + ID[] rVal = new ID[elements]; + for (int x = 0; x < elements; x++) { + rVal[x] = readSavable(content); + } + return rVal; + } + + protected ID[][] readSavableArray2D(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + ID[][] rVal = new ID[elements][]; + for (int x = 0; x < elements; x++) { + rVal[x] = readSavableArray(content); + } + return rVal; + } + + protected ID[][][] readSavableArray3D(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + ID[][][] rVal = new ID[elements][][]; + for (int x = 0; x < elements; x++) { + rVal[x] = readSavableArray2D(content); + } + return rVal; + } + + // BinarySavable map + + protected ID[][] readSavableMap(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + ID[][] rVal = new ID[elements][]; + for (int x = 0; x < elements; x++) { + rVal[x] = readSavableArray(content); + } + return rVal; + } + + protected StringIDMap readStringSavableMap(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + String[] keys = readStringArray(content); + ID[] values = readSavableArray(content); + StringIDMap rVal = new StringIDMap(); + rVal.keys = keys; + rVal.values = values; + return rVal; + } + + protected IntIDMap readIntSavableMap(byte[] content) throws IOException { + int elements = readInt(content); + if (elements == BinaryOutputCapsule.NULL_OBJECT) + return null; + int[] keys = readIntArray(content); + ID[] values = readSavableArray(content); + IntIDMap rVal = new IntIDMap(); + rVal.keys = keys; + rVal.values = values; + return rVal; + } + + + // ArrayList<FloatBuffer> + + protected ArrayList<FloatBuffer> readFloatBufferArrayList(byte[] content) + throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) { + return null; + } + ArrayList<FloatBuffer> rVal = new ArrayList<FloatBuffer>(length); + for (int x = 0; x < length; x++) { + rVal.add(readFloatBuffer(content)); + } + return rVal; + } + + // ArrayList<ByteBuffer> + + protected ArrayList<ByteBuffer> readByteBufferArrayList(byte[] content) + throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) { + return null; + } + ArrayList<ByteBuffer> rVal = new ArrayList<ByteBuffer>(length); + for (int x = 0; x < length; x++) { + rVal.add(readByteBuffer(content)); + } + return rVal; + } + + // NIO BUFFERS + // float buffer + + protected FloatBuffer readFloatBuffer(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + + if (BinaryImporter.canUseFastBuffers()){ + ByteBuffer value = BufferUtils.createByteBuffer(length * 4); + value.put(content, index, length * 4).rewind(); + index += length * 4; + return value.asFloatBuffer(); + }else{ + FloatBuffer value = BufferUtils.createFloatBuffer(length); + for (int x = 0; x < length; x++) { + value.put(readFloatForBuffer(content)); + } + value.rewind(); + return value; + } + } + + // int buffer + + protected IntBuffer readIntBuffer(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + + if (BinaryImporter.canUseFastBuffers()){ + ByteBuffer value = BufferUtils.createByteBuffer(length * 4); + value.put(content, index, length * 4).rewind(); + index += length * 4; + return value.asIntBuffer(); + }else{ + IntBuffer value = BufferUtils.createIntBuffer(length); + for (int x = 0; x < length; x++) { + value.put(readIntForBuffer(content)); + } + value.rewind(); + return value; + } + } + + // byte buffer + + protected ByteBuffer readByteBuffer(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + + if (BinaryImporter.canUseFastBuffers()){ + ByteBuffer value = BufferUtils.createByteBuffer(length); + value.put(content, index, length).rewind(); + index += length; + return value; + }else{ + ByteBuffer value = BufferUtils.createByteBuffer(length); + for (int x = 0; x < length; x++) { + value.put(readByteForBuffer(content)); + } + value.rewind(); + return value; + } + } + + // short buffer + + protected ShortBuffer readShortBuffer(byte[] content) throws IOException { + int length = readInt(content); + if (length == BinaryOutputCapsule.NULL_OBJECT) + return null; + + if (BinaryImporter.canUseFastBuffers()){ + ByteBuffer value = BufferUtils.createByteBuffer(length * 2); + value.put(content, index, length * 2).rewind(); + index += length * 2; + return value.asShortBuffer(); + }else{ + ShortBuffer value = BufferUtils.createShortBuffer(length); + for (int x = 0; x < length; x++) { + value.put(readShortForBuffer(content)); + } + value.rewind(); + return value; + } + } + + static private class ID { + public int id; + + public ID(int id) { + this.id = id; + } + } + + static private class StringIDMap { + public String[] keys; + public ID[] values; + } + + static private class IntIDMap { + public int[] keys; + public ID[] values; + } + + public <T extends Enum<T>> T readEnum(String name, Class<T> enumType, T defVal) throws IOException { + String eVal = readString(name, defVal != null ? defVal.name() : null); + if (eVal != null) { + return Enum.valueOf(enumType, eVal); + } else { + return null; + } + } +}
\ No newline at end of file |