Home | History | Annotate | Download | only in binary
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.export.binary;
     34 
     35 import com.jme3.export.InputCapsule;
     36 import com.jme3.export.Savable;
     37 import com.jme3.export.SavableClassUtil;
     38 import com.jme3.util.BufferUtils;
     39 import com.jme3.util.IntMap;
     40 import java.io.IOException;
     41 import java.io.UnsupportedEncodingException;
     42 import java.nio.ByteBuffer;
     43 import java.nio.FloatBuffer;
     44 import java.nio.IntBuffer;
     45 import java.nio.ShortBuffer;
     46 import java.util.ArrayList;
     47 import java.util.BitSet;
     48 import java.util.HashMap;
     49 import java.util.Map;
     50 import java.util.logging.Level;
     51 import java.util.logging.Logger;
     52 
     53 /**
     54  * @author Joshua Slack
     55  */
     56 final class BinaryInputCapsule implements InputCapsule {
     57 
     58     private static final Logger logger = Logger
     59             .getLogger(BinaryInputCapsule.class.getName());
     60 
     61     protected BinaryImporter importer;
     62     protected BinaryClassObject cObj;
     63     protected Savable savable;
     64     protected HashMap<Byte, Object> fieldData;
     65 
     66     protected int index = 0;
     67 
     68     public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) {
     69         this.importer = importer;
     70         this.cObj = bco;
     71         this.savable = savable;
     72     }
     73 
     74     public void setContent(byte[] content, int start, int limit) {
     75         fieldData = new HashMap<Byte, Object>();
     76         for (index = start; index < limit;) {
     77             byte alias = content[index];
     78 
     79             index++;
     80 
     81             try {
     82                 byte type = cObj.aliasFields.get(alias).type;
     83                 Object value = null;
     84 
     85                 switch (type) {
     86                     case BinaryClassField.BITSET: {
     87                         value = readBitSet(content);
     88                         break;
     89                     }
     90                     case BinaryClassField.BOOLEAN: {
     91                         value = readBoolean(content);
     92                         break;
     93                     }
     94                     case BinaryClassField.BOOLEAN_1D: {
     95                         value = readBooleanArray(content);
     96                         break;
     97                     }
     98                     case BinaryClassField.BOOLEAN_2D: {
     99                         value = readBooleanArray2D(content);
    100                         break;
    101                     }
    102                     case BinaryClassField.BYTE: {
    103                         value = readByte(content);
    104                         break;
    105                     }
    106                     case BinaryClassField.BYTE_1D: {
    107                         value = readByteArray(content);
    108                         break;
    109                     }
    110                     case BinaryClassField.BYTE_2D: {
    111                         value = readByteArray2D(content);
    112                         break;
    113                     }
    114                     case BinaryClassField.BYTEBUFFER: {
    115                         value = readByteBuffer(content);
    116                         break;
    117                     }
    118                     case BinaryClassField.DOUBLE: {
    119                         value = readDouble(content);
    120                         break;
    121                     }
    122                     case BinaryClassField.DOUBLE_1D: {
    123                         value = readDoubleArray(content);
    124                         break;
    125                     }
    126                     case BinaryClassField.DOUBLE_2D: {
    127                         value = readDoubleArray2D(content);
    128                         break;
    129                     }
    130                     case BinaryClassField.FLOAT: {
    131                         value = readFloat(content);
    132                         break;
    133                     }
    134                     case BinaryClassField.FLOAT_1D: {
    135                         value = readFloatArray(content);
    136                         break;
    137                     }
    138                     case BinaryClassField.FLOAT_2D: {
    139                         value = readFloatArray2D(content);
    140                         break;
    141                     }
    142                     case BinaryClassField.FLOATBUFFER: {
    143                         value = readFloatBuffer(content);
    144                         break;
    145                     }
    146                     case BinaryClassField.FLOATBUFFER_ARRAYLIST: {
    147                         value = readFloatBufferArrayList(content);
    148                         break;
    149                     }
    150                     case BinaryClassField.BYTEBUFFER_ARRAYLIST: {
    151                         value = readByteBufferArrayList(content);
    152                         break;
    153                     }
    154                     case BinaryClassField.INT: {
    155                         value = readInt(content);
    156                         break;
    157                     }
    158                     case BinaryClassField.INT_1D: {
    159                         value = readIntArray(content);
    160                         break;
    161                     }
    162                     case BinaryClassField.INT_2D: {
    163                         value = readIntArray2D(content);
    164                         break;
    165                     }
    166                     case BinaryClassField.INTBUFFER: {
    167                         value = readIntBuffer(content);
    168                         break;
    169                     }
    170                     case BinaryClassField.LONG: {
    171                         value = readLong(content);
    172                         break;
    173                     }
    174                     case BinaryClassField.LONG_1D: {
    175                         value = readLongArray(content);
    176                         break;
    177                     }
    178                     case BinaryClassField.LONG_2D: {
    179                         value = readLongArray2D(content);
    180                         break;
    181                     }
    182                     case BinaryClassField.SAVABLE: {
    183                         value = readSavable(content);
    184                         break;
    185                     }
    186                     case BinaryClassField.SAVABLE_1D: {
    187                         value = readSavableArray(content);
    188                         break;
    189                     }
    190                     case BinaryClassField.SAVABLE_2D: {
    191                         value = readSavableArray2D(content);
    192                         break;
    193                     }
    194                     case BinaryClassField.SAVABLE_ARRAYLIST: {
    195                         value = readSavableArray(content);
    196                         break;
    197                     }
    198                     case BinaryClassField.SAVABLE_ARRAYLIST_1D: {
    199                         value = readSavableArray2D(content);
    200                         break;
    201                     }
    202                     case BinaryClassField.SAVABLE_ARRAYLIST_2D: {
    203                         value = readSavableArray3D(content);
    204                         break;
    205                     }
    206                     case BinaryClassField.SAVABLE_MAP: {
    207                         value = readSavableMap(content);
    208                         break;
    209                     }
    210                     case BinaryClassField.STRING_SAVABLE_MAP: {
    211                         value = readStringSavableMap(content);
    212                         break;
    213                     }
    214                     case BinaryClassField.INT_SAVABLE_MAP: {
    215                         value = readIntSavableMap(content);
    216                         break;
    217                     }
    218                     case BinaryClassField.SHORT: {
    219                         value = readShort(content);
    220                         break;
    221                     }
    222                     case BinaryClassField.SHORT_1D: {
    223                         value = readShortArray(content);
    224                         break;
    225                     }
    226                     case BinaryClassField.SHORT_2D: {
    227                         value = readShortArray2D(content);
    228                         break;
    229                     }
    230                     case BinaryClassField.SHORTBUFFER: {
    231                         value = readShortBuffer(content);
    232                         break;
    233                     }
    234                     case BinaryClassField.STRING: {
    235                         value = readString(content);
    236                         break;
    237                     }
    238                     case BinaryClassField.STRING_1D: {
    239                         value = readStringArray(content);
    240                         break;
    241                     }
    242                     case BinaryClassField.STRING_2D: {
    243                         value = readStringArray2D(content);
    244                         break;
    245                     }
    246 
    247                     default:
    248                         // skip put statement
    249                         continue;
    250                 }
    251 
    252                 fieldData.put(alias, value);
    253 
    254             } catch (IOException e) {
    255                 logger.logp(Level.SEVERE, this.getClass().toString(),
    256                         "setContent(byte[] content)", "Exception", e);
    257             }
    258         }
    259     }
    260 
    261     public int getSavableVersion(Class<? extends Savable> desiredClass){
    262         return SavableClassUtil.getSavedSavableVersion(savable, desiredClass,
    263                                             cObj.classHierarchyVersions, importer.getFormatVersion());
    264     }
    265 
    266     public BitSet readBitSet(String name, BitSet defVal) throws IOException {
    267         BinaryClassField field = cObj.nameFields.get(name);
    268         if (field == null || !fieldData.containsKey(field.alias))
    269             return defVal;
    270         return (BitSet) fieldData.get(field.alias);
    271     }
    272 
    273     public boolean readBoolean(String name, boolean defVal) throws IOException {
    274         BinaryClassField field = cObj.nameFields.get(name);
    275         if (field == null || !fieldData.containsKey(field.alias))
    276             return defVal;
    277         return ((Boolean) fieldData.get(field.alias)).booleanValue();
    278     }
    279 
    280     public boolean[] readBooleanArray(String name, boolean[] defVal)
    281             throws IOException {
    282         BinaryClassField field = cObj.nameFields.get(name);
    283         if (field == null || !fieldData.containsKey(field.alias))
    284             return defVal;
    285         return (boolean[]) fieldData.get(field.alias);
    286     }
    287 
    288     public boolean[][] readBooleanArray2D(String name, boolean[][] defVal)
    289             throws IOException {
    290         BinaryClassField field = cObj.nameFields.get(name);
    291         if (field == null || !fieldData.containsKey(field.alias))
    292             return defVal;
    293         return (boolean[][]) fieldData.get(field.alias);
    294     }
    295 
    296     public byte readByte(String name, byte defVal) throws IOException {
    297         BinaryClassField field = cObj.nameFields.get(name);
    298         if (field == null || !fieldData.containsKey(field.alias))
    299             return defVal;
    300         return ((Byte) fieldData.get(field.alias)).byteValue();
    301     }
    302 
    303     public byte[] readByteArray(String name, byte[] defVal) throws IOException {
    304         BinaryClassField field = cObj.nameFields.get(name);
    305         if (field == null || !fieldData.containsKey(field.alias))
    306             return defVal;
    307         return (byte[]) fieldData.get(field.alias);
    308     }
    309 
    310     public byte[][] readByteArray2D(String name, byte[][] defVal)
    311             throws IOException {
    312         BinaryClassField field = cObj.nameFields.get(name);
    313         if (field == null || !fieldData.containsKey(field.alias))
    314             return defVal;
    315         return (byte[][]) fieldData.get(field.alias);
    316     }
    317 
    318     public ByteBuffer readByteBuffer(String name, ByteBuffer defVal)
    319             throws IOException {
    320         BinaryClassField field = cObj.nameFields.get(name);
    321         if (field == null || !fieldData.containsKey(field.alias))
    322             return defVal;
    323         return (ByteBuffer) fieldData.get(field.alias);
    324     }
    325 
    326     @SuppressWarnings("unchecked")
    327     public ArrayList<ByteBuffer> readByteBufferArrayList(String name,
    328             ArrayList<ByteBuffer> defVal) throws IOException {
    329         BinaryClassField field = cObj.nameFields.get(name);
    330         if (field == null || !fieldData.containsKey(field.alias))
    331             return defVal;
    332         return (ArrayList<ByteBuffer>) fieldData.get(field.alias);
    333     }
    334 
    335     public double readDouble(String name, double defVal) throws IOException {
    336         BinaryClassField field = cObj.nameFields.get(name);
    337         if (field == null || !fieldData.containsKey(field.alias))
    338             return defVal;
    339         return ((Double) fieldData.get(field.alias)).doubleValue();
    340     }
    341 
    342     public double[] readDoubleArray(String name, double[] defVal)
    343             throws IOException {
    344         BinaryClassField field = cObj.nameFields.get(name);
    345         if (field == null || !fieldData.containsKey(field.alias))
    346             return defVal;
    347         return (double[]) fieldData.get(field.alias);
    348     }
    349 
    350     public double[][] readDoubleArray2D(String name, double[][] defVal)
    351             throws IOException {
    352         BinaryClassField field = cObj.nameFields.get(name);
    353         if (field == null || !fieldData.containsKey(field.alias))
    354             return defVal;
    355         return (double[][]) fieldData.get(field.alias);
    356     }
    357 
    358     public float readFloat(String name, float defVal) throws IOException {
    359         BinaryClassField field = cObj.nameFields.get(name);
    360         if (field == null || !fieldData.containsKey(field.alias))
    361             return defVal;
    362         return ((Float) fieldData.get(field.alias)).floatValue();
    363     }
    364 
    365     public float[] readFloatArray(String name, float[] defVal)
    366             throws IOException {
    367         BinaryClassField field = cObj.nameFields.get(name);
    368         if (field == null || !fieldData.containsKey(field.alias))
    369             return defVal;
    370         return (float[]) fieldData.get(field.alias);
    371     }
    372 
    373     public float[][] readFloatArray2D(String name, float[][] defVal)
    374             throws IOException {
    375         BinaryClassField field = cObj.nameFields.get(name);
    376         if (field == null || !fieldData.containsKey(field.alias))
    377             return defVal;
    378         return (float[][]) fieldData.get(field.alias);
    379     }
    380 
    381     public FloatBuffer readFloatBuffer(String name, FloatBuffer defVal)
    382             throws IOException {
    383         BinaryClassField field = cObj.nameFields.get(name);
    384         if (field == null || !fieldData.containsKey(field.alias))
    385             return defVal;
    386         return (FloatBuffer) fieldData.get(field.alias);
    387     }
    388 
    389     @SuppressWarnings("unchecked")
    390     public ArrayList<FloatBuffer> readFloatBufferArrayList(String name,
    391             ArrayList<FloatBuffer> defVal) throws IOException {
    392         BinaryClassField field = cObj.nameFields.get(name);
    393         if (field == null || !fieldData.containsKey(field.alias))
    394             return defVal;
    395         return (ArrayList<FloatBuffer>) fieldData.get(field.alias);
    396     }
    397 
    398     public int readInt(String name, int defVal) throws IOException {
    399         BinaryClassField field = cObj.nameFields.get(name);
    400         if (field == null || !fieldData.containsKey(field.alias))
    401             return defVal;
    402         return ((Integer) fieldData.get(field.alias)).intValue();
    403     }
    404 
    405     public int[] readIntArray(String name, int[] defVal) throws IOException {
    406         BinaryClassField field = cObj.nameFields.get(name);
    407         if (field == null || !fieldData.containsKey(field.alias))
    408             return defVal;
    409         return (int[]) fieldData.get(field.alias);
    410     }
    411 
    412     public int[][] readIntArray2D(String name, int[][] defVal)
    413             throws IOException {
    414         BinaryClassField field = cObj.nameFields.get(name);
    415         if (field == null || !fieldData.containsKey(field.alias))
    416             return defVal;
    417         return (int[][]) fieldData.get(field.alias);
    418     }
    419 
    420     public IntBuffer readIntBuffer(String name, IntBuffer defVal)
    421             throws IOException {
    422         BinaryClassField field = cObj.nameFields.get(name);
    423         if (field == null || !fieldData.containsKey(field.alias))
    424             return defVal;
    425         return (IntBuffer) fieldData.get(field.alias);
    426     }
    427 
    428     public long readLong(String name, long defVal) throws IOException {
    429         BinaryClassField field = cObj.nameFields.get(name);
    430         if (field == null || !fieldData.containsKey(field.alias))
    431             return defVal;
    432         return ((Long) fieldData.get(field.alias)).longValue();
    433     }
    434 
    435     public long[] readLongArray(String name, long[] defVal) throws IOException {
    436         BinaryClassField field = cObj.nameFields.get(name);
    437         if (field == null || !fieldData.containsKey(field.alias))
    438             return defVal;
    439         return (long[]) fieldData.get(field.alias);
    440     }
    441 
    442     public long[][] readLongArray2D(String name, long[][] defVal)
    443             throws IOException {
    444         BinaryClassField field = cObj.nameFields.get(name);
    445         if (field == null || !fieldData.containsKey(field.alias))
    446             return defVal;
    447         return (long[][]) fieldData.get(field.alias);
    448     }
    449 
    450     public Savable readSavable(String name, Savable defVal) throws IOException {
    451         BinaryClassField field = cObj.nameFields.get(name);
    452         if (field == null || !fieldData.containsKey(field.alias))
    453             return defVal;
    454         Object value = fieldData.get(field.alias);
    455         if (value == null)
    456             return null;
    457         else if (value instanceof ID) {
    458             value = importer.readObject(((ID) value).id);
    459             fieldData.put(field.alias, value);
    460             return (Savable) value;
    461         } else
    462             return defVal;
    463     }
    464 
    465     public Savable[] readSavableArray(String name, Savable[] defVal)
    466             throws IOException {
    467         BinaryClassField field = cObj.nameFields.get(name);
    468         if (field == null || !fieldData.containsKey(field.alias))
    469             return defVal;
    470         Object[] values = (Object[]) fieldData.get(field.alias);
    471         if (values instanceof ID[]) {
    472             values = resolveIDs(values);
    473             fieldData.put(field.alias, values);
    474             return (Savable[]) values;
    475         } else
    476             return defVal;
    477     }
    478 
    479     private Savable[] resolveIDs(Object[] values) {
    480         if (values != null) {
    481             Savable[] savables = new Savable[values.length];
    482             for (int i = 0; i < values.length; i++) {
    483                 final ID id = (ID) values[i];
    484                 savables[i] = id != null ? importer.readObject(id.id) : null;
    485             }
    486             return savables;
    487         } else {
    488             return null;
    489         }
    490     }
    491 
    492     public Savable[][] readSavableArray2D(String name, Savable[][] defVal)
    493             throws IOException {
    494         BinaryClassField field = cObj.nameFields.get(name);
    495         if (field == null ||!fieldData.containsKey(field.alias))
    496             return defVal;
    497         Object[][] values = (Object[][]) fieldData.get(field.alias);
    498         if (values instanceof ID[][]) {
    499             Savable[][] savables = new Savable[values.length][];
    500             for (int i = 0; i < values.length; i++) {
    501                 if (values[i] != null) {
    502                     savables[i] = resolveIDs(values[i]);
    503                 } else savables[i] = null;
    504             }
    505             values = savables;
    506             fieldData.put(field.alias, values);
    507         }
    508         return (Savable[][]) values;
    509     }
    510 
    511     public Savable[][][] readSavableArray3D(String name, Savable[][][] defVal)
    512             throws IOException {
    513         BinaryClassField field = cObj.nameFields.get(name);
    514         if (field == null || !fieldData.containsKey(field.alias))
    515             return defVal;
    516         Object[][][] values = (Object[][][]) fieldData.get(field.alias);
    517         if (values instanceof ID[][][]) {
    518             Savable[][][] savables = new Savable[values.length][][];
    519             for (int i = 0; i < values.length; i++) {
    520                 if (values[i] != null) {
    521                     savables[i] = new Savable[values[i].length][];
    522                     for (int j = 0; j < values[i].length; j++) {
    523                         savables[i][j] = resolveIDs(values[i][j]);
    524                     }
    525                 } else savables[i] = null;
    526             }
    527             fieldData.put(field.alias, savables);
    528             return savables;
    529         } else
    530             return defVal;
    531     }
    532 
    533     private ArrayList<Savable> savableArrayListFromArray(Savable[] savables) {
    534         if(savables == null) {
    535             return null;
    536         }
    537         ArrayList<Savable> arrayList = new ArrayList<Savable>(savables.length);
    538         for (int x = 0; x < savables.length; x++) {
    539             arrayList.add(savables[x]);
    540         }
    541         return arrayList;
    542     }
    543 
    544     // Assumes array of size 2 arrays where pos 0 is key and pos 1 is value.
    545     private Map<Savable, Savable> savableMapFrom2DArray(Savable[][] savables) {
    546         if(savables == null) {
    547             return null;
    548         }
    549         Map<Savable, Savable> map = new HashMap<Savable, Savable>(savables.length);
    550         for (int x = 0; x < savables.length; x++) {
    551             map.put(savables[x][0], savables[x][1]);
    552         }
    553         return map;
    554     }
    555 
    556     private Map<String, Savable> stringSavableMapFromKV(String[] keys, Savable[] values) {
    557         if(keys == null || values == null) {
    558             return null;
    559         }
    560 
    561         Map<String, Savable> map = new HashMap<String, Savable>(keys.length);
    562         for (int x = 0; x < keys.length; x++)
    563             map.put(keys[x], values[x]);
    564 
    565         return map;
    566     }
    567 
    568     private IntMap<Savable> intSavableMapFromKV(int[] keys, Savable[] values) {
    569         if(keys == null || values == null) {
    570             return null;
    571         }
    572 
    573         IntMap<Savable> map = new IntMap<Savable>(keys.length);
    574         for (int x = 0; x < keys.length; x++)
    575             map.put(keys[x], values[x]);
    576 
    577         return map;
    578     }
    579 
    580     public ArrayList readSavableArrayList(String name, ArrayList defVal)
    581             throws IOException {
    582         BinaryClassField field = cObj.nameFields.get(name);
    583         if (field == null || !fieldData.containsKey(field.alias))
    584             return defVal;
    585         Object value = fieldData.get(field.alias);
    586         if (value instanceof ID[]) {
    587             // read Savable array and convert to ArrayList
    588             Savable[] savables = readSavableArray(name, null);
    589             value = savableArrayListFromArray(savables);
    590             fieldData.put(field.alias, value);
    591         }
    592         return (ArrayList) value;
    593     }
    594 
    595     public ArrayList[] readSavableArrayListArray(String name, ArrayList[] defVal)
    596             throws IOException {
    597         BinaryClassField field = cObj.nameFields.get(name);
    598         if (field == null || !fieldData.containsKey(field.alias))
    599             return defVal;
    600         Object value = fieldData.get(field.alias);
    601         if (value instanceof ID[][]) {
    602             // read 2D Savable array and convert to ArrayList array
    603             Savable[][] savables = readSavableArray2D(name, null);
    604             if (savables != null) {
    605                 ArrayList[] arrayLists = new ArrayList[savables.length];
    606                 for (int i = 0; i < savables.length; i++) {
    607                     arrayLists[i] = savableArrayListFromArray(savables[i]);
    608                 }
    609                 value = arrayLists;
    610             } else
    611                 value = defVal;
    612             fieldData.put(field.alias, value);
    613         }
    614         return (ArrayList[]) value;
    615     }
    616 
    617     public ArrayList[][] readSavableArrayListArray2D(String name,
    618             ArrayList[][] defVal) throws IOException {
    619         BinaryClassField field = cObj.nameFields.get(name);
    620         if (field == null || !fieldData.containsKey(field.alias))
    621             return defVal;
    622         Object value = fieldData.get(field.alias);
    623         if (value instanceof ID[][][]) {
    624             // read 3D Savable array and convert to 2D ArrayList array
    625             Savable[][][] savables = readSavableArray3D(name, null);
    626             if (savables != null && savables.length > 0) {
    627                 ArrayList[][] arrayLists = new ArrayList[savables.length][];
    628                 for (int i = 0; i < savables.length; i++) {
    629                     arrayLists[i] = new ArrayList[savables[i].length];
    630                     for (int j = 0; j < savables[i].length; j++) {
    631                         arrayLists[i][j] = savableArrayListFromArray(savables[i][j]);
    632                     }
    633                 }
    634                 value = arrayLists;
    635             } else
    636                 value = defVal;
    637             fieldData.put(field.alias, value);
    638         }
    639         return (ArrayList[][]) value;
    640     }
    641 
    642     @SuppressWarnings("unchecked")
    643     public Map<? extends Savable, ? extends Savable> readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal)
    644             throws IOException {
    645         BinaryClassField field = cObj.nameFields.get(name);
    646         if (field == null || !fieldData.containsKey(field.alias))
    647             return defVal;
    648         Object value = fieldData.get(field.alias);
    649         if (value instanceof ID[][]) {
    650             // read Savable array and convert to Map
    651             Savable[][] savables = readSavableArray2D(name, null);
    652             value = savableMapFrom2DArray(savables);
    653             fieldData.put(field.alias, value);
    654         }
    655         return (Map<? extends Savable, ? extends Savable>) value;
    656     }
    657 
    658     @SuppressWarnings("unchecked")
    659     public Map<String, ? extends Savable> readStringSavableMap(String name, Map<String, ? extends Savable> defVal)
    660             throws IOException {
    661         BinaryClassField field = cObj.nameFields.get(name);
    662         if (field == null || !fieldData.containsKey(field.alias))
    663             return defVal;
    664         Object value = fieldData.get(field.alias);
    665         if (value instanceof StringIDMap) {
    666             // read Savable array and convert to Map values
    667             StringIDMap in = (StringIDMap) value;
    668             Savable[] values = resolveIDs(in.values);
    669             value = stringSavableMapFromKV(in.keys, values);
    670             fieldData.put(field.alias, value);
    671         }
    672         return (Map<String, Savable>) value;
    673     }
    674 
    675     @SuppressWarnings("unchecked")
    676     public IntMap<? extends Savable> readIntSavableMap(String name, IntMap<? extends Savable> defVal)
    677             throws IOException {
    678         BinaryClassField field = cObj.nameFields.get(name);
    679         if (field == null || !fieldData.containsKey(field.alias))
    680             return defVal;
    681         Object value = fieldData.get(field.alias);
    682         if (value instanceof IntIDMap) {
    683             // read Savable array and convert to Map values
    684             IntIDMap in = (IntIDMap) value;
    685             Savable[] values = resolveIDs(in.values);
    686             value = intSavableMapFromKV(in.keys, values);
    687             fieldData.put(field.alias, value);
    688         }
    689         return (IntMap<Savable>) value;
    690     }
    691 
    692     public short readShort(String name, short defVal) throws IOException {
    693         BinaryClassField field = cObj.nameFields.get(name);
    694         if (field == null || !fieldData.containsKey(field.alias))
    695             return defVal;
    696         return ((Short) fieldData.get(field.alias)).shortValue();
    697     }
    698 
    699     public short[] readShortArray(String name, short[] defVal)
    700             throws IOException {
    701         BinaryClassField field = cObj.nameFields.get(name);
    702         if (field == null || !fieldData.containsKey(field.alias))
    703             return defVal;
    704         return (short[]) fieldData.get(field.alias);
    705     }
    706 
    707     public short[][] readShortArray2D(String name, short[][] defVal)
    708             throws IOException {
    709         BinaryClassField field = cObj.nameFields.get(name);
    710         if (field == null || !fieldData.containsKey(field.alias))
    711             return defVal;
    712         return (short[][]) fieldData.get(field.alias);
    713     }
    714 
    715     public ShortBuffer readShortBuffer(String name, ShortBuffer defVal)
    716             throws IOException {
    717         BinaryClassField field = cObj.nameFields.get(name);
    718         if (field == null || !fieldData.containsKey(field.alias))
    719             return defVal;
    720         return (ShortBuffer) fieldData.get(field.alias);
    721     }
    722 
    723     public String readString(String name, String defVal) throws IOException {
    724         BinaryClassField field = cObj.nameFields.get(name);
    725         if (field == null || !fieldData.containsKey(field.alias))
    726             return defVal;
    727         return (String) fieldData.get(field.alias);
    728     }
    729 
    730     public String[] readStringArray(String name, String[] defVal)
    731             throws IOException {
    732         BinaryClassField field = cObj.nameFields.get(name);
    733         if (field == null || !fieldData.containsKey(field.alias))
    734             return defVal;
    735         return (String[]) fieldData.get(field.alias);
    736     }
    737 
    738     public String[][] readStringArray2D(String name, String[][] defVal)
    739             throws IOException {
    740         BinaryClassField field = cObj.nameFields.get(name);
    741         if (field == null || !fieldData.containsKey(field.alias))
    742             return defVal;
    743         return (String[][]) fieldData.get(field.alias);
    744     }
    745 
    746     // byte primitive
    747 
    748     protected byte readByte(byte[] content) throws IOException {
    749         byte value = content[index];
    750         index++;
    751         return value;
    752     }
    753 
    754     protected byte readByteForBuffer(byte[] content) throws IOException {
    755         byte value = content[index];
    756         index++;
    757         return value;
    758     }
    759 
    760     protected byte[] readByteArray(byte[] content) throws IOException {
    761         int length = readInt(content);
    762         if (length == BinaryOutputCapsule.NULL_OBJECT)
    763             return null;
    764         byte[] value = new byte[length];
    765         for (int x = 0; x < length; x++)
    766             value[x] = readByte(content);
    767         return value;
    768     }
    769 
    770     protected byte[][] readByteArray2D(byte[] content) throws IOException {
    771         int length = readInt(content);
    772         if (length == BinaryOutputCapsule.NULL_OBJECT)
    773             return null;
    774         byte[][] value = new byte[length][];
    775         for (int x = 0; x < length; x++)
    776             value[x] = readByteArray(content);
    777         return value;
    778     }
    779 
    780     // int primitive
    781 
    782     protected int readIntForBuffer(byte[] content){
    783         int number = ((content[index+3] & 0xFF) << 24)
    784                    + ((content[index+2] & 0xFF) << 16)
    785                    + ((content[index+1] & 0xFF) << 8)
    786                    +  (content[index]   & 0xFF);
    787         index += 4;
    788         return number;
    789     }
    790 
    791     protected int readInt(byte[] content) throws IOException {
    792         byte[] bytes = inflateFrom(content, index);
    793         index += 1 + bytes.length;
    794         bytes = ByteUtils.rightAlignBytes(bytes, 4);
    795         int value = ByteUtils.convertIntFromBytes(bytes);
    796         if (value == BinaryOutputCapsule.NULL_OBJECT
    797                 || value == BinaryOutputCapsule.DEFAULT_OBJECT)
    798             index -= 4;
    799         return value;
    800     }
    801 
    802     protected int[] readIntArray(byte[] content) throws IOException {
    803         int length = readInt(content);
    804         if (length == BinaryOutputCapsule.NULL_OBJECT)
    805             return null;
    806         int[] value = new int[length];
    807         for (int x = 0; x < length; x++)
    808             value[x] = readInt(content);
    809         return value;
    810     }
    811 
    812     protected int[][] readIntArray2D(byte[] content) throws IOException {
    813         int length = readInt(content);
    814         if (length == BinaryOutputCapsule.NULL_OBJECT)
    815             return null;
    816         int[][] value = new int[length][];
    817         for (int x = 0; x < length; x++)
    818             value[x] = readIntArray(content);
    819         return value;
    820     }
    821 
    822     // float primitive
    823 
    824     protected float readFloat(byte[] content) throws IOException {
    825         float value = ByteUtils.convertFloatFromBytes(content, index);
    826         index += 4;
    827         return value;
    828     }
    829 
    830     protected float readFloatForBuffer(byte[] content) throws IOException {
    831         int number = readIntForBuffer(content);
    832         return Float.intBitsToFloat(number);
    833     }
    834 
    835     protected float[] readFloatArray(byte[] content) throws IOException {
    836         int length = readInt(content);
    837         if (length == BinaryOutputCapsule.NULL_OBJECT)
    838             return null;
    839         float[] value = new float[length];
    840         for (int x = 0; x < length; x++)
    841             value[x] = readFloat(content);
    842         return value;
    843     }
    844 
    845     protected float[][] readFloatArray2D(byte[] content) throws IOException {
    846         int length = readInt(content);
    847         if (length == BinaryOutputCapsule.NULL_OBJECT)
    848             return null;
    849         float[][] value = new float[length][];
    850         for (int x = 0; x < length; x++)
    851             value[x] = readFloatArray(content);
    852         return value;
    853     }
    854 
    855     // double primitive
    856 
    857     protected double readDouble(byte[] content) throws IOException {
    858         double value = ByteUtils.convertDoubleFromBytes(content, index);
    859         index += 8;
    860         return value;
    861     }
    862 
    863     protected double[] readDoubleArray(byte[] content) throws IOException {
    864         int length = readInt(content);
    865         if (length == BinaryOutputCapsule.NULL_OBJECT)
    866             return null;
    867         double[] value = new double[length];
    868         for (int x = 0; x < length; x++)
    869             value[x] = readDouble(content);
    870         return value;
    871     }
    872 
    873     protected double[][] readDoubleArray2D(byte[] content) throws IOException {
    874         int length = readInt(content);
    875         if (length == BinaryOutputCapsule.NULL_OBJECT)
    876             return null;
    877         double[][] value = new double[length][];
    878         for (int x = 0; x < length; x++)
    879             value[x] = readDoubleArray(content);
    880         return value;
    881     }
    882 
    883     // long primitive
    884 
    885     protected long readLong(byte[] content) throws IOException {
    886         byte[] bytes = inflateFrom(content, index);
    887         index += 1 + bytes.length;
    888         bytes = ByteUtils.rightAlignBytes(bytes, 8);
    889         long value = ByteUtils.convertLongFromBytes(bytes);
    890         return value;
    891     }
    892 
    893     protected long[] readLongArray(byte[] content) throws IOException {
    894         int length = readInt(content);
    895         if (length == BinaryOutputCapsule.NULL_OBJECT)
    896             return null;
    897         long[] value = new long[length];
    898         for (int x = 0; x < length; x++)
    899             value[x] = readLong(content);
    900         return value;
    901     }
    902 
    903     protected long[][] readLongArray2D(byte[] content) throws IOException {
    904         int length = readInt(content);
    905         if (length == BinaryOutputCapsule.NULL_OBJECT)
    906             return null;
    907         long[][] value = new long[length][];
    908         for (int x = 0; x < length; x++)
    909             value[x] = readLongArray(content);
    910         return value;
    911     }
    912 
    913     // short primitive
    914 
    915     protected short readShort(byte[] content) throws IOException {
    916         short value = ByteUtils.convertShortFromBytes(content, index);
    917         index += 2;
    918         return value;
    919     }
    920 
    921     protected short readShortForBuffer(byte[] content) throws IOException {
    922         short number = (short) ((content[index+0] & 0xFF)
    923                              + ((content[index+1] & 0xFF) << 8));
    924         index += 2;
    925         return number;
    926     }
    927 
    928     protected short[] readShortArray(byte[] content) throws IOException {
    929         int length = readInt(content);
    930         if (length == BinaryOutputCapsule.NULL_OBJECT)
    931             return null;
    932         short[] value = new short[length];
    933         for (int x = 0; x < length; x++)
    934             value[x] = readShort(content);
    935         return value;
    936     }
    937 
    938     protected short[][] readShortArray2D(byte[] content) throws IOException {
    939         int length = readInt(content);
    940         if (length == BinaryOutputCapsule.NULL_OBJECT)
    941             return null;
    942         short[][] value = new short[length][];
    943         for (int x = 0; x < length; x++)
    944             value[x] = readShortArray(content);
    945         return value;
    946     }
    947 
    948     // boolean primitive
    949 
    950     protected boolean readBoolean(byte[] content) throws IOException {
    951         boolean value = ByteUtils.convertBooleanFromBytes(content, index);
    952         index += 1;
    953         return value;
    954     }
    955 
    956     protected boolean[] readBooleanArray(byte[] content) throws IOException {
    957         int length = readInt(content);
    958         if (length == BinaryOutputCapsule.NULL_OBJECT)
    959             return null;
    960         boolean[] value = new boolean[length];
    961         for (int x = 0; x < length; x++)
    962             value[x] = readBoolean(content);
    963         return value;
    964     }
    965 
    966     protected boolean[][] readBooleanArray2D(byte[] content) throws IOException {
    967         int length = readInt(content);
    968         if (length == BinaryOutputCapsule.NULL_OBJECT)
    969             return null;
    970         boolean[][] value = new boolean[length][];
    971         for (int x = 0; x < length; x++)
    972             value[x] = readBooleanArray(content);
    973         return value;
    974     }
    975 
    976     /*
    977      * UTF-8 crash course:
    978      *
    979      * UTF-8 codepoints map to UTF-16 codepoints and vv, which is what Java uses for it's Strings.
    980      * (so a UTF-8 codepoint can contain all possible values for a Java char)
    981      *
    982      * 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:
    983      * b < 0x80, 1 byte
    984      * (b & 0xC0) == 0xC0, 2 bytes
    985      * (b & 0xE0) == 0xE0, 3 bytes
    986      *
    987      * However there is an additional restriction to UTF-8, to enable you to find the start of a UTF-8 codepoint,
    988      * 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
    989      * a multibyte codepoint:
    990      * (b & 0x80) == 0x80  (in other words, first bit must be 1)
    991      */
    992     private final static int UTF8_START = 0; // next byte should be the start of a new
    993     private final static int UTF8_2BYTE = 2; // next byte should be the second byte of a 2 byte codepoint
    994     private final static int UTF8_3BYTE_1 = 3; // next byte should be the second byte of a 3 byte codepoint
    995     private final static int UTF8_3BYTE_2 = 4; // next byte should be the third byte of a 3 byte codepoint
    996     private final static int UTF8_ILLEGAL = 10; // not an UTF8 string
    997 
    998     // String
    999     protected String readString(byte[] content) throws IOException {
   1000         int length = readInt(content);
   1001         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1002             return null;
   1003 
   1004         /*
   1005          * @see ISSUE 276
   1006          *
   1007          * We'll transfer the bytes into a seperate byte array.
   1008          * While we do that we'll take the opportunity to check if the byte data is valid UTF-8.
   1009          *
   1010          * If it is not UTF-8 it is most likely saved with the BinaryOutputCapsule bug, that saves Strings using their native
   1011          * encoding. Unfortunatly there is no way to know what encoding was used, so we'll parse using the most common one in
   1012          * that case; latin-1 aka ISO8859_1
   1013          *
   1014          * Encoding of "low" ASCII codepoint (in plain speak: when no special characters are used) will usually look the same
   1015          * for UTF-8 and the other 1 byte codepoint encodings (espc true for numbers and regular letters of the alphabet). So these
   1016          * are valid UTF-8 and will give the same result (at most a few charakters will appear different, such as the euro sign).
   1017          *
   1018          * However, when "high" codepoints are used (any codepoint that over 0x7F, in other words where the first bit is a 1) it's
   1019          * a different matter and UTF-8 and the 1 byte encoding greatly will differ, as well as most 1 byte encodings relative to each
   1020          * other.
   1021          *
   1022          * It is impossible to detect which one-byte encoding is used. Since UTF8 and practically all 1-byte encodings share the most
   1023          * used characters (the "none-high" ones) parsing them will give the same result. However, not all byte sequences are legal in
   1024          * UTF-8 (see explantion above). If not UTF-8 encoded content is detected we therefor fallback on latin1. We also log a warning.
   1025          *
   1026          * By this method we detect all use of 1 byte encoding if they:
   1027          * - use a "high" codepoint after a "low" codepoint or a sequence of codepoints that is valid as UTF-8 bytes, that starts with 1000
   1028          * - use a "low" codepoint after a "high" codepoint
   1029          * - use a "low" codepoint after "high" codepoint, after a "high" codepoint that starts with 1110
   1030          *
   1031          *  In practise this means that unless 2 or 3 "high" codepoints are used after each other in proper order, we'll detect the string
   1032          *  was not originally UTF-8 encoded.
   1033          *
   1034          */
   1035         byte[] bytes = new byte[length];
   1036         int utf8State = UTF8_START;
   1037         int b;
   1038         for (int x = 0; x < length; x++) {
   1039             bytes[x] =  content[index++];
   1040             b = (int) bytes[x] & 0xFF; // unsign our byte
   1041 
   1042             switch (utf8State) {
   1043             case UTF8_START:
   1044                 if (b < 0x80) {
   1045                     // good
   1046                 }
   1047                 else if ((b & 0xC0) == 0xC0) {
   1048                     utf8State = UTF8_2BYTE;
   1049                 }
   1050                 else if ((b & 0xE0) == 0xE0) {
   1051                     utf8State = UTF8_3BYTE_1;
   1052                 }
   1053                 else {
   1054                     utf8State = UTF8_ILLEGAL;
   1055                 }
   1056                 break;
   1057             case UTF8_3BYTE_1:
   1058             case UTF8_3BYTE_2:
   1059             case UTF8_2BYTE:
   1060                  if ((b & 0x80) == 0x80)
   1061                     utf8State = utf8State == UTF8_3BYTE_1 ? UTF8_3BYTE_2 : UTF8_START;
   1062                  else
   1063                     utf8State = UTF8_ILLEGAL;
   1064                 break;
   1065             }
   1066         }
   1067 
   1068         try {
   1069             // 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
   1070             if (utf8State == UTF8_START) {
   1071                 // Java misspells UTF-8 as UTF8 for official use in java.lang
   1072                 return new String(bytes, "UTF8");
   1073             }
   1074             else {
   1075                 logger.log(
   1076                         Level.WARNING,
   1077                         "Your export has been saved with an incorrect encoding for it's String fields which means it might not load correctly " +
   1078                         "due to encoding issues. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
   1079                 );
   1080                 // We use ISO8859_1 to be consistent across platforms. We could default to native encoding, but this would lead to inconsistent
   1081                 // behaviour across platforms!
   1082                 // Developers that have previously saved their exports using the old exporter (wich uses native encoding), can temporarly
   1083                 // remove the ""ISO8859_1" parameter, and change the above if statement to "if (false)".
   1084                 // They should then import and re-export their models using the same enviroment they were orginally created in.
   1085                 return new String(bytes, "ISO8859_1");
   1086             }
   1087         } catch (UnsupportedEncodingException uee) {
   1088             // as a last resort fall back to platform native.
   1089             // JavaDoc is vague about what happens when a decoding a String that contains un undecodable sequence
   1090             // 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)
   1091             logger.log(
   1092                     Level.SEVERE,
   1093                     "Your export has been saved with an incorrect encoding or your version of Java is unable to decode the stored string. " +
   1094                     "While your export may load correctly by falling back, using it on different platforms or java versions might lead to "+
   1095                     "very strange inconsitenties. You should probably re-export your work. See ISSUE 276 in the jME issue tracker."
   1096             );
   1097             return new String(bytes);
   1098         }
   1099     }
   1100 
   1101     protected String[] readStringArray(byte[] content) throws IOException {
   1102         int length = readInt(content);
   1103         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1104             return null;
   1105         String[] value = new String[length];
   1106         for (int x = 0; x < length; x++)
   1107             value[x] = readString(content);
   1108         return value;
   1109     }
   1110 
   1111     protected String[][] readStringArray2D(byte[] content) throws IOException {
   1112         int length = readInt(content);
   1113         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1114             return null;
   1115         String[][] value = new String[length][];
   1116         for (int x = 0; x < length; x++)
   1117             value[x] = readStringArray(content);
   1118         return value;
   1119     }
   1120 
   1121     // BitSet
   1122 
   1123     protected BitSet readBitSet(byte[] content) throws IOException {
   1124         int length = readInt(content);
   1125         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1126             return null;
   1127         BitSet value = new BitSet(length);
   1128         for (int x = 0; x < length; x++)
   1129             value.set(x, readBoolean(content));
   1130         return value;
   1131     }
   1132 
   1133     // INFLATOR for int and long
   1134 
   1135     protected static byte[] inflateFrom(byte[] contents, int index) {
   1136         byte firstByte = contents[index];
   1137         if (firstByte == BinaryOutputCapsule.NULL_OBJECT)
   1138             return ByteUtils.convertToBytes(BinaryOutputCapsule.NULL_OBJECT);
   1139         else if (firstByte == BinaryOutputCapsule.DEFAULT_OBJECT)
   1140             return ByteUtils.convertToBytes(BinaryOutputCapsule.DEFAULT_OBJECT);
   1141         else if (firstByte == 0)
   1142             return new byte[0];
   1143         else {
   1144             byte[] rVal = new byte[firstByte];
   1145             for (int x = 0; x < rVal.length; x++)
   1146                 rVal[x] = contents[x + 1 + index];
   1147             return rVal;
   1148         }
   1149     }
   1150 
   1151     // BinarySavable
   1152 
   1153     protected ID readSavable(byte[] content) throws IOException {
   1154         int id = readInt(content);
   1155         if (id == BinaryOutputCapsule.NULL_OBJECT) {
   1156             return null;
   1157         }
   1158 
   1159         return new ID(id);
   1160     }
   1161 
   1162     // BinarySavable array
   1163 
   1164     protected ID[] readSavableArray(byte[] content) throws IOException {
   1165         int elements = readInt(content);
   1166         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1167             return null;
   1168         ID[] rVal = new ID[elements];
   1169         for (int x = 0; x < elements; x++) {
   1170             rVal[x] = readSavable(content);
   1171         }
   1172         return rVal;
   1173     }
   1174 
   1175     protected ID[][] readSavableArray2D(byte[] content) throws IOException {
   1176         int elements = readInt(content);
   1177         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1178             return null;
   1179         ID[][] rVal = new ID[elements][];
   1180         for (int x = 0; x < elements; x++) {
   1181             rVal[x] = readSavableArray(content);
   1182         }
   1183         return rVal;
   1184     }
   1185 
   1186     protected ID[][][] readSavableArray3D(byte[] content) throws IOException {
   1187         int elements = readInt(content);
   1188         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1189             return null;
   1190         ID[][][] rVal = new ID[elements][][];
   1191         for (int x = 0; x < elements; x++) {
   1192             rVal[x] = readSavableArray2D(content);
   1193         }
   1194         return rVal;
   1195     }
   1196 
   1197     // BinarySavable map
   1198 
   1199     protected ID[][] readSavableMap(byte[] content) throws IOException {
   1200         int elements = readInt(content);
   1201         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1202             return null;
   1203         ID[][] rVal = new ID[elements][];
   1204         for (int x = 0; x < elements; x++) {
   1205             rVal[x] = readSavableArray(content);
   1206         }
   1207         return rVal;
   1208     }
   1209 
   1210     protected StringIDMap readStringSavableMap(byte[] content) throws IOException {
   1211         int elements = readInt(content);
   1212         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1213             return null;
   1214         String[] keys = readStringArray(content);
   1215         ID[] values = readSavableArray(content);
   1216         StringIDMap rVal = new StringIDMap();
   1217         rVal.keys = keys;
   1218         rVal.values = values;
   1219         return rVal;
   1220     }
   1221 
   1222     protected IntIDMap readIntSavableMap(byte[] content) throws IOException {
   1223         int elements = readInt(content);
   1224         if (elements == BinaryOutputCapsule.NULL_OBJECT)
   1225             return null;
   1226         int[] keys = readIntArray(content);
   1227         ID[] values = readSavableArray(content);
   1228         IntIDMap rVal = new IntIDMap();
   1229         rVal.keys = keys;
   1230         rVal.values = values;
   1231         return rVal;
   1232     }
   1233 
   1234 
   1235     // ArrayList<FloatBuffer>
   1236 
   1237     protected ArrayList<FloatBuffer> readFloatBufferArrayList(byte[] content)
   1238             throws IOException {
   1239         int length = readInt(content);
   1240         if (length == BinaryOutputCapsule.NULL_OBJECT) {
   1241             return null;
   1242         }
   1243         ArrayList<FloatBuffer> rVal = new ArrayList<FloatBuffer>(length);
   1244         for (int x = 0; x < length; x++) {
   1245             rVal.add(readFloatBuffer(content));
   1246         }
   1247         return rVal;
   1248     }
   1249 
   1250     // ArrayList<ByteBuffer>
   1251 
   1252     protected ArrayList<ByteBuffer> readByteBufferArrayList(byte[] content)
   1253             throws IOException {
   1254         int length = readInt(content);
   1255         if (length == BinaryOutputCapsule.NULL_OBJECT) {
   1256             return null;
   1257         }
   1258         ArrayList<ByteBuffer> rVal = new ArrayList<ByteBuffer>(length);
   1259         for (int x = 0; x < length; x++) {
   1260             rVal.add(readByteBuffer(content));
   1261         }
   1262         return rVal;
   1263     }
   1264 
   1265     // NIO BUFFERS
   1266     // float buffer
   1267 
   1268     protected FloatBuffer readFloatBuffer(byte[] content) throws IOException {
   1269         int length = readInt(content);
   1270         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1271             return null;
   1272 
   1273         if (BinaryImporter.canUseFastBuffers()){
   1274             ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
   1275             value.put(content, index, length * 4).rewind();
   1276             index += length * 4;
   1277             return value.asFloatBuffer();
   1278         }else{
   1279             FloatBuffer value = BufferUtils.createFloatBuffer(length);
   1280             for (int x = 0; x < length; x++) {
   1281                 value.put(readFloatForBuffer(content));
   1282             }
   1283             value.rewind();
   1284             return value;
   1285         }
   1286     }
   1287 
   1288     // int buffer
   1289 
   1290     protected IntBuffer readIntBuffer(byte[] content) throws IOException {
   1291         int length = readInt(content);
   1292         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1293             return null;
   1294 
   1295         if (BinaryImporter.canUseFastBuffers()){
   1296             ByteBuffer value = BufferUtils.createByteBuffer(length * 4);
   1297             value.put(content, index, length * 4).rewind();
   1298             index += length * 4;
   1299             return value.asIntBuffer();
   1300         }else{
   1301             IntBuffer value = BufferUtils.createIntBuffer(length);
   1302             for (int x = 0; x < length; x++) {
   1303                 value.put(readIntForBuffer(content));
   1304             }
   1305             value.rewind();
   1306             return value;
   1307         }
   1308     }
   1309 
   1310     // byte buffer
   1311 
   1312     protected ByteBuffer readByteBuffer(byte[] content) throws IOException {
   1313         int length = readInt(content);
   1314         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1315             return null;
   1316 
   1317         if (BinaryImporter.canUseFastBuffers()){
   1318             ByteBuffer value = BufferUtils.createByteBuffer(length);
   1319             value.put(content, index, length).rewind();
   1320             index += length;
   1321             return value;
   1322         }else{
   1323             ByteBuffer value = BufferUtils.createByteBuffer(length);
   1324             for (int x = 0; x < length; x++) {
   1325                 value.put(readByteForBuffer(content));
   1326             }
   1327             value.rewind();
   1328             return value;
   1329         }
   1330     }
   1331 
   1332     // short buffer
   1333 
   1334     protected ShortBuffer readShortBuffer(byte[] content) throws IOException {
   1335         int length = readInt(content);
   1336         if (length == BinaryOutputCapsule.NULL_OBJECT)
   1337             return null;
   1338 
   1339         if (BinaryImporter.canUseFastBuffers()){
   1340             ByteBuffer value = BufferUtils.createByteBuffer(length * 2);
   1341             value.put(content, index, length * 2).rewind();
   1342             index += length * 2;
   1343             return value.asShortBuffer();
   1344         }else{
   1345             ShortBuffer value = BufferUtils.createShortBuffer(length);
   1346             for (int x = 0; x < length; x++) {
   1347                 value.put(readShortForBuffer(content));
   1348             }
   1349             value.rewind();
   1350             return value;
   1351         }
   1352     }
   1353 
   1354     static private class ID {
   1355         public int id;
   1356 
   1357         public ID(int id) {
   1358             this.id = id;
   1359         }
   1360     }
   1361 
   1362     static private class StringIDMap {
   1363         public String[] keys;
   1364         public ID[] values;
   1365     }
   1366 
   1367     static private class IntIDMap {
   1368         public int[] keys;
   1369         public ID[] values;
   1370     }
   1371 
   1372     public <T extends Enum<T>> T readEnum(String name, Class<T> enumType, T defVal) throws IOException {
   1373         String eVal = readString(name, defVal != null ? defVal.name() : null);
   1374         if (eVal != null) {
   1375             return Enum.valueOf(enumType, eVal);
   1376         } else {
   1377             return null;
   1378         }
   1379     }
   1380 }