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.OutputCapsule;
     36 import com.jme3.export.Savable;
     37 import com.jme3.util.IntMap;
     38 import com.jme3.util.IntMap.Entry;
     39 import java.io.ByteArrayOutputStream;
     40 import java.io.IOException;
     41 import java.nio.ByteBuffer;
     42 import java.nio.FloatBuffer;
     43 import java.nio.IntBuffer;
     44 import java.nio.ShortBuffer;
     45 import java.util.ArrayList;
     46 import java.util.Arrays;
     47 import java.util.BitSet;
     48 import java.util.Map;
     49 
     50 /**
     51  * @author Joshua Slack
     52  */
     53 final class BinaryOutputCapsule implements OutputCapsule {
     54 
     55     public static final int NULL_OBJECT = -1;
     56     public static final int DEFAULT_OBJECT = -2;
     57 
     58     public static byte[] NULL_BYTES = new byte[] { (byte) -1 };
     59     public static byte[] DEFAULT_BYTES = new byte[] { (byte) -2 };
     60 
     61     protected ByteArrayOutputStream baos;
     62     protected byte[] bytes;
     63     protected BinaryExporter exporter;
     64     protected BinaryClassObject cObj;
     65 
     66     public BinaryOutputCapsule(BinaryExporter exporter, BinaryClassObject bco) {
     67         this.baos = new ByteArrayOutputStream();
     68         this.exporter = exporter;
     69         this.cObj = bco;
     70     }
     71 
     72     public void write(byte value, String name, byte defVal) throws IOException {
     73         if (value == defVal)
     74             return;
     75         writeAlias(name, BinaryClassField.BYTE);
     76         write(value);
     77     }
     78 
     79     public void write(byte[] value, String name, byte[] defVal)
     80             throws IOException {
     81         if (value == defVal)
     82             return;
     83         writeAlias(name, BinaryClassField.BYTE_1D);
     84         write(value);
     85     }
     86 
     87     public void write(byte[][] value, String name, byte[][] defVal)
     88             throws IOException {
     89         if (value == defVal)
     90             return;
     91         writeAlias(name, BinaryClassField.BYTE_2D);
     92         write(value);
     93     }
     94 
     95     public void write(int value, String name, int defVal) throws IOException {
     96         if (value == defVal)
     97             return;
     98         writeAlias(name, BinaryClassField.INT);
     99         write(value);
    100     }
    101 
    102     public void write(int[] value, String name, int[] defVal)
    103             throws IOException {
    104         if (value == defVal)
    105             return;
    106         writeAlias(name, BinaryClassField.INT_1D);
    107         write(value);
    108     }
    109 
    110     public void write(int[][] value, String name, int[][] defVal)
    111             throws IOException {
    112         if (value == defVal)
    113             return;
    114         writeAlias(name, BinaryClassField.INT_2D);
    115         write(value);
    116     }
    117 
    118     public void write(float value, String name, float defVal)
    119             throws IOException {
    120         if (value == defVal)
    121             return;
    122         writeAlias(name, BinaryClassField.FLOAT);
    123         write(value);
    124     }
    125 
    126     public void write(float[] value, String name, float[] defVal)
    127             throws IOException {
    128         if (value == defVal)
    129             return;
    130         writeAlias(name, BinaryClassField.FLOAT_1D);
    131         write(value);
    132     }
    133 
    134     public void write(float[][] value, String name, float[][] defVal)
    135             throws IOException {
    136         if (value == defVal)
    137             return;
    138         writeAlias(name, BinaryClassField.FLOAT_2D);
    139         write(value);
    140     }
    141 
    142     public void write(double value, String name, double defVal)
    143             throws IOException {
    144         if (value == defVal)
    145             return;
    146         writeAlias(name, BinaryClassField.DOUBLE);
    147         write(value);
    148     }
    149 
    150     public void write(double[] value, String name, double[] defVal)
    151             throws IOException {
    152         if (value == defVal)
    153             return;
    154         writeAlias(name, BinaryClassField.DOUBLE_1D);
    155         write(value);
    156     }
    157 
    158     public void write(double[][] value, String name, double[][] defVal)
    159             throws IOException {
    160         if (value == defVal)
    161             return;
    162         writeAlias(name, BinaryClassField.DOUBLE_2D);
    163         write(value);
    164     }
    165 
    166     public void write(long value, String name, long defVal) throws IOException {
    167         if (value == defVal)
    168             return;
    169         writeAlias(name, BinaryClassField.LONG);
    170         write(value);
    171     }
    172 
    173     public void write(long[] value, String name, long[] defVal)
    174             throws IOException {
    175         if (value == defVal)
    176             return;
    177         writeAlias(name, BinaryClassField.LONG_1D);
    178         write(value);
    179     }
    180 
    181     public void write(long[][] value, String name, long[][] defVal)
    182             throws IOException {
    183         if (value == defVal)
    184             return;
    185         writeAlias(name, BinaryClassField.LONG_2D);
    186         write(value);
    187     }
    188 
    189     public void write(short value, String name, short defVal)
    190             throws IOException {
    191         if (value == defVal)
    192             return;
    193         writeAlias(name, BinaryClassField.SHORT);
    194         write(value);
    195     }
    196 
    197     public void write(short[] value, String name, short[] defVal)
    198             throws IOException {
    199         if (value == defVal)
    200             return;
    201         writeAlias(name, BinaryClassField.SHORT_1D);
    202         write(value);
    203     }
    204 
    205     public void write(short[][] value, String name, short[][] defVal)
    206             throws IOException {
    207         if (value == defVal)
    208             return;
    209         writeAlias(name, BinaryClassField.SHORT_2D);
    210         write(value);
    211     }
    212 
    213     public void write(boolean value, String name, boolean defVal)
    214             throws IOException {
    215         if (value == defVal)
    216             return;
    217         writeAlias(name, BinaryClassField.BOOLEAN);
    218         write(value);
    219     }
    220 
    221     public void write(boolean[] value, String name, boolean[] defVal)
    222             throws IOException {
    223         if (value == defVal)
    224             return;
    225         writeAlias(name, BinaryClassField.BOOLEAN_1D);
    226         write(value);
    227     }
    228 
    229     public void write(boolean[][] value, String name, boolean[][] defVal)
    230             throws IOException {
    231         if (value == defVal)
    232             return;
    233         writeAlias(name, BinaryClassField.BOOLEAN_2D);
    234         write(value);
    235     }
    236 
    237     public void write(String value, String name, String defVal)
    238             throws IOException {
    239         if (value == null ? defVal == null : value.equals(defVal))
    240             return;
    241         writeAlias(name, BinaryClassField.STRING);
    242         write(value);
    243     }
    244 
    245     public void write(String[] value, String name, String[] defVal)
    246             throws IOException {
    247         if (value == defVal)
    248             return;
    249         writeAlias(name, BinaryClassField.STRING_1D);
    250         write(value);
    251     }
    252 
    253     public void write(String[][] value, String name, String[][] defVal)
    254             throws IOException {
    255         if (value == defVal)
    256             return;
    257         writeAlias(name, BinaryClassField.STRING_2D);
    258         write(value);
    259     }
    260 
    261     public void write(BitSet value, String name, BitSet defVal)
    262             throws IOException {
    263         if (value == defVal)
    264             return;
    265         writeAlias(name, BinaryClassField.BITSET);
    266         write(value);
    267     }
    268 
    269     public void write(Savable object, String name, Savable defVal)
    270             throws IOException {
    271         if (object == defVal)
    272             return;
    273         writeAlias(name, BinaryClassField.SAVABLE);
    274         write(object);
    275     }
    276 
    277     public void write(Savable[] objects, String name, Savable[] defVal)
    278             throws IOException {
    279         if (objects == defVal)
    280             return;
    281         writeAlias(name, BinaryClassField.SAVABLE_1D);
    282         write(objects);
    283     }
    284 
    285     public void write(Savable[][] objects, String name, Savable[][] defVal)
    286             throws IOException {
    287         if (objects == defVal)
    288             return;
    289         writeAlias(name, BinaryClassField.SAVABLE_2D);
    290         write(objects);
    291     }
    292 
    293     public void write(FloatBuffer value, String name, FloatBuffer defVal)
    294             throws IOException {
    295         if (value == defVal)
    296             return;
    297         writeAlias(name, BinaryClassField.FLOATBUFFER);
    298         write(value);
    299     }
    300 
    301     public void write(IntBuffer value, String name, IntBuffer defVal)
    302             throws IOException {
    303         if (value == defVal)
    304             return;
    305         writeAlias(name, BinaryClassField.INTBUFFER);
    306         write(value);
    307     }
    308 
    309     public void write(ByteBuffer value, String name, ByteBuffer defVal)
    310             throws IOException {
    311         if (value == defVal)
    312             return;
    313         writeAlias(name, BinaryClassField.BYTEBUFFER);
    314         write(value);
    315     }
    316 
    317     public void write(ShortBuffer value, String name, ShortBuffer defVal)
    318             throws IOException {
    319         if (value == defVal)
    320             return;
    321         writeAlias(name, BinaryClassField.SHORTBUFFER);
    322         write(value);
    323     }
    324 
    325     public void writeFloatBufferArrayList(ArrayList<FloatBuffer> array,
    326             String name, ArrayList<FloatBuffer> defVal) throws IOException {
    327         if (array == defVal)
    328             return;
    329         writeAlias(name, BinaryClassField.FLOATBUFFER_ARRAYLIST);
    330         writeFloatBufferArrayList(array);
    331     }
    332 
    333     public void writeByteBufferArrayList(ArrayList<ByteBuffer> array,
    334             String name, ArrayList<ByteBuffer> defVal) throws IOException {
    335         if (array == defVal)
    336             return;
    337         writeAlias(name, BinaryClassField.BYTEBUFFER_ARRAYLIST);
    338         writeByteBufferArrayList(array);
    339     }
    340 
    341     public void writeSavableArrayList(ArrayList array, String name,
    342             ArrayList defVal) throws IOException {
    343         if (array == defVal)
    344             return;
    345         writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST);
    346         writeSavableArrayList(array);
    347     }
    348 
    349     public void writeSavableArrayListArray(ArrayList[] array, String name,
    350             ArrayList[] defVal) throws IOException {
    351         if (array == defVal)
    352             return;
    353         writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_1D);
    354         writeSavableArrayListArray(array);
    355     }
    356 
    357     public void writeSavableArrayListArray2D(ArrayList[][] array, String name,
    358             ArrayList[][] defVal) throws IOException {
    359         if (array == defVal)
    360             return;
    361         writeAlias(name, BinaryClassField.SAVABLE_ARRAYLIST_2D);
    362         writeSavableArrayListArray2D(array);
    363     }
    364 
    365     public void writeSavableMap(Map<? extends Savable, ? extends Savable> map,
    366             String name, Map<? extends Savable, ? extends Savable> defVal)
    367             throws IOException {
    368         if (map == defVal)
    369             return;
    370         writeAlias(name, BinaryClassField.SAVABLE_MAP);
    371         writeSavableMap(map);
    372     }
    373 
    374     public void writeStringSavableMap(Map<String, ? extends Savable> map,
    375             String name, Map<String, ? extends Savable> defVal)
    376             throws IOException {
    377         if (map == defVal)
    378             return;
    379         writeAlias(name, BinaryClassField.STRING_SAVABLE_MAP);
    380         writeStringSavableMap(map);
    381     }
    382 
    383     public void writeIntSavableMap(IntMap<? extends Savable> map,
    384             String name, IntMap<? extends Savable> defVal)
    385             throws IOException {
    386         if (map == defVal)
    387             return;
    388         writeAlias(name, BinaryClassField.INT_SAVABLE_MAP);
    389         writeIntSavableMap(map);
    390     }
    391 
    392     protected void writeAlias(String name, byte fieldType) throws IOException {
    393         if (cObj.nameFields.get(name) == null)
    394             generateAlias(name, fieldType);
    395 
    396         byte alias = cObj.nameFields.get(name).alias;
    397         write(alias);
    398     }
    399 
    400     // XXX: The generation of aliases is limited to 256 possible values.
    401     // If we run into classes with more than 256 fields, we need to expand this.
    402     // But I mean, come on...
    403     protected void generateAlias(String name, byte type) {
    404         byte alias = (byte) cObj.nameFields.size();
    405         cObj.nameFields.put(name, new BinaryClassField(name, alias, type));
    406     }
    407 
    408     @Override
    409     public boolean equals(Object arg0) {
    410         if (!(arg0 instanceof BinaryOutputCapsule))
    411             return false;
    412 
    413         byte[] other = ((BinaryOutputCapsule) arg0).bytes;
    414         if (bytes.length != other.length)
    415             return false;
    416         return Arrays.equals(bytes, other);
    417     }
    418 
    419     @Override
    420     public int hashCode() {
    421         int hash = 7;
    422         hash = 23 * hash + Arrays.hashCode(this.bytes);
    423         return hash;
    424     }
    425 
    426     public void finish() {
    427         // renamed to finish as 'finalize' in java.lang.Object should not be
    428         // overridden like this
    429         // - finalize should not be called directly but is called by garbage
    430         // collection!!!
    431         bytes = baos.toByteArray();
    432         baos = null;
    433     }
    434 
    435     // byte primitive
    436 
    437     protected void write(byte value) throws IOException {
    438         baos.write(value);
    439     }
    440 
    441     protected void writeForBuffer(byte value) throws IOException {
    442         baos.write(value);
    443     }
    444 
    445     protected void write(byte[] value) throws IOException {
    446         if (value == null) {
    447             write(NULL_OBJECT);
    448             return;
    449         }
    450         write(value.length);
    451         baos.write(value);
    452     }
    453 
    454     protected void write(byte[][] value) throws IOException {
    455         if (value == null) {
    456             write(NULL_OBJECT);
    457             return;
    458         }
    459         write(value.length);
    460         for (int x = 0; x < value.length; x++)
    461             write(value[x]);
    462     }
    463 
    464     // int primitive
    465 
    466     protected void write(int value) throws IOException {
    467         baos.write(deflate(ByteUtils.convertToBytes(value)));
    468     }
    469 
    470     protected void writeForBuffer(int value) throws IOException {
    471         byte[] byteArray = new byte[4];
    472         byteArray[0] = (byte) value;
    473         byteArray[1] = (byte) (value >> 8);
    474         byteArray[2] = (byte) (value >> 16);
    475         byteArray[3] = (byte) (value >> 24);
    476         baos.write(byteArray);
    477     }
    478 
    479     protected void write(int[] value) throws IOException {
    480         if (value == null) {
    481             write(NULL_OBJECT);
    482             return;
    483         }
    484         write(value.length);
    485         for (int x = 0; x < value.length; x++)
    486             write(value[x]);
    487     }
    488 
    489     protected void write(int[][] value) throws IOException {
    490         if (value == null) {
    491             write(NULL_OBJECT);
    492             return;
    493         }
    494         write(value.length);
    495         for (int x = 0; x < value.length; x++)
    496             write(value[x]);
    497     }
    498 
    499     // float primitive
    500 
    501     protected void write(float value) throws IOException {
    502         baos.write(ByteUtils.convertToBytes(value));
    503     }
    504 
    505     protected void writeForBuffer(float value) throws IOException {
    506         int integer = Float.floatToIntBits(value);
    507         writeForBuffer(integer);
    508     }
    509 
    510     protected void write(float[] value) throws IOException {
    511         if (value == null) {
    512             write(NULL_OBJECT);
    513             return;
    514         }
    515         write(value.length);
    516         for (int x = 0; x < value.length; x++)
    517             write(value[x]);
    518     }
    519 
    520     protected void write(float[][] value) throws IOException {
    521         if (value == null) {
    522             write(NULL_OBJECT);
    523             return;
    524         }
    525         write(value.length);
    526         for (int x = 0; x < value.length; x++)
    527             write(value[x]);
    528     }
    529 
    530     // double primitive
    531 
    532     protected void write(double value) throws IOException {
    533         baos.write(ByteUtils.convertToBytes(value));
    534     }
    535 
    536     protected void write(double[] value) throws IOException {
    537         if (value == null) {
    538             write(NULL_OBJECT);
    539             return;
    540         }
    541         write(value.length);
    542         for (int x = 0; x < value.length; x++)
    543             write(value[x]);
    544     }
    545 
    546     protected void write(double[][] value) throws IOException {
    547         if (value == null) {
    548             write(NULL_OBJECT);
    549             return;
    550         }
    551         write(value.length);
    552         for (int x = 0; x < value.length; x++)
    553             write(value[x]);
    554     }
    555 
    556     // long primitive
    557 
    558     protected void write(long value) throws IOException {
    559         baos.write(deflate(ByteUtils.convertToBytes(value)));
    560     }
    561 
    562     protected void write(long[] value) throws IOException {
    563         if (value == null) {
    564             write(NULL_OBJECT);
    565             return;
    566         }
    567         write(value.length);
    568         for (int x = 0; x < value.length; x++)
    569             write(value[x]);
    570     }
    571 
    572     protected void write(long[][] value) throws IOException {
    573         if (value == null) {
    574             write(NULL_OBJECT);
    575             return;
    576         }
    577         write(value.length);
    578         for (int x = 0; x < value.length; x++)
    579             write(value[x]);
    580     }
    581 
    582     // short primitive
    583 
    584     protected void write(short value) throws IOException {
    585         baos.write(ByteUtils.convertToBytes(value));
    586     }
    587 
    588     protected void writeForBuffer(short value) throws IOException {
    589         byte[] byteArray = new byte[2];
    590         byteArray[0] = (byte) value;
    591         byteArray[1] = (byte) (value >> 8);
    592         baos.write(byteArray);
    593     }
    594 
    595     protected void write(short[] value) throws IOException {
    596         if (value == null) {
    597             write(NULL_OBJECT);
    598             return;
    599         }
    600         write(value.length);
    601         for (int x = 0; x < value.length; x++)
    602             write(value[x]);
    603     }
    604 
    605     protected void write(short[][] value) throws IOException {
    606         if (value == null) {
    607             write(NULL_OBJECT);
    608             return;
    609         }
    610         write(value.length);
    611         for (int x = 0; x < value.length; x++)
    612             write(value[x]);
    613     }
    614 
    615     // boolean primitive
    616 
    617     protected void write(boolean value) throws IOException {
    618         baos.write(ByteUtils.convertToBytes(value));
    619     }
    620 
    621     protected void write(boolean[] value) throws IOException {
    622         if (value == null) {
    623             write(NULL_OBJECT);
    624             return;
    625         }
    626         write(value.length);
    627         for (int x = 0; x < value.length; x++)
    628             write(value[x]);
    629     }
    630 
    631     protected void write(boolean[][] value) throws IOException {
    632         if (value == null) {
    633             write(NULL_OBJECT);
    634             return;
    635         }
    636         write(value.length);
    637         for (int x = 0; x < value.length; x++)
    638             write(value[x]);
    639     }
    640 
    641     // String
    642 
    643     protected void write(String value) throws IOException {
    644         if (value == null) {
    645             write(NULL_OBJECT);
    646             return;
    647         }
    648         // write our output as UTF-8. Java misspells UTF-8 as UTF8 for official use in java.lang
    649         byte[] bytes = value.getBytes("UTF8");
    650         write(bytes.length);
    651         baos.write(bytes);
    652     }
    653 
    654     protected void write(String[] value) throws IOException {
    655         if (value == null) {
    656             write(NULL_OBJECT);
    657             return;
    658         }
    659         write(value.length);
    660         for (int x = 0; x < value.length; x++)
    661             write(value[x]);
    662     }
    663 
    664     protected void write(String[][] value) throws IOException {
    665         if (value == null) {
    666             write(NULL_OBJECT);
    667             return;
    668         }
    669         write(value.length);
    670         for (int x = 0; x < value.length; x++)
    671             write(value[x]);
    672     }
    673 
    674     // BitSet
    675 
    676     protected void write(BitSet value) throws IOException {
    677         if (value == null) {
    678             write(NULL_OBJECT);
    679             return;
    680         }
    681         write(value.size());
    682         // TODO: MAKE THIS SMALLER
    683         for (int x = 0, max = value.size(); x < max; x++)
    684             write(value.get(x));
    685     }
    686 
    687     // DEFLATOR for int and long
    688 
    689     protected static byte[] deflate(byte[] bytes) {
    690         int size = bytes.length;
    691         if (size == 4) {
    692             int possibleMagic = ByteUtils.convertIntFromBytes(bytes);
    693             if (possibleMagic == NULL_OBJECT)
    694                 return NULL_BYTES;
    695             else if (possibleMagic == DEFAULT_OBJECT)
    696                 return DEFAULT_BYTES;
    697         }
    698         for (int x = 0; x < bytes.length; x++) {
    699             if (bytes[x] != 0)
    700                 break;
    701             size--;
    702         }
    703         if (size == 0)
    704             return new byte[1];
    705 
    706         byte[] rVal = new byte[1 + size];
    707         rVal[0] = (byte) size;
    708         for (int x = 1; x < rVal.length; x++)
    709             rVal[x] = bytes[bytes.length - size - 1 + x];
    710 
    711         return rVal;
    712     }
    713 
    714     // BinarySavable
    715 
    716     protected void write(Savable object) throws IOException {
    717         if (object == null) {
    718             write(NULL_OBJECT);
    719             return;
    720         }
    721         int id = exporter.processBinarySavable(object);
    722         write(id);
    723     }
    724 
    725     // BinarySavable array
    726 
    727     protected void write(Savable[] objects) throws IOException {
    728         if (objects == null) {
    729             write(NULL_OBJECT);
    730             return;
    731         }
    732         write(objects.length);
    733         for (int x = 0; x < objects.length; x++) {
    734             write(objects[x]);
    735         }
    736     }
    737 
    738     protected void write(Savable[][] objects) throws IOException {
    739         if (objects == null) {
    740             write(NULL_OBJECT);
    741             return;
    742         }
    743         write(objects.length);
    744         for (int x = 0; x < objects.length; x++) {
    745             write(objects[x]);
    746         }
    747     }
    748 
    749     // ArrayList<BinarySavable>
    750 
    751     protected void writeSavableArrayList(ArrayList array) throws IOException {
    752         if (array == null) {
    753             write(NULL_OBJECT);
    754             return;
    755         }
    756         write(array.size());
    757         for (Object bs : array) {
    758             write((Savable) bs);
    759         }
    760     }
    761 
    762     protected void writeSavableArrayListArray(ArrayList[] array)
    763             throws IOException {
    764         if (array == null) {
    765             write(NULL_OBJECT);
    766             return;
    767         }
    768         write(array.length);
    769         for (ArrayList bs : array) {
    770             writeSavableArrayList(bs);
    771         }
    772     }
    773 
    774     protected void writeSavableArrayListArray2D(ArrayList[][] array)
    775             throws IOException {
    776         if (array == null) {
    777             write(NULL_OBJECT);
    778             return;
    779         }
    780         write(array.length);
    781         for (ArrayList[] bs : array) {
    782             writeSavableArrayListArray(bs);
    783         }
    784     }
    785 
    786     // Map<BinarySavable, BinarySavable>
    787 
    788     protected void writeSavableMap(
    789             Map<? extends Savable, ? extends Savable> array) throws IOException {
    790         if (array == null) {
    791             write(NULL_OBJECT);
    792             return;
    793         }
    794         write(array.size());
    795         for (Savable key : array.keySet()) {
    796             write(new Savable[] { key, array.get(key) });
    797         }
    798     }
    799 
    800     protected void writeStringSavableMap(Map<String, ? extends Savable> array)
    801             throws IOException {
    802         if (array == null) {
    803             write(NULL_OBJECT);
    804             return;
    805         }
    806         write(array.size());
    807 
    808         // write String array for keys
    809         String[] keys = array.keySet().toArray(new String[] {});
    810         write(keys);
    811 
    812         // write Savable array for values
    813         Savable[] values = array.values().toArray(new Savable[] {});
    814         write(values);
    815     }
    816 
    817     protected void writeIntSavableMap(IntMap<? extends Savable> array)
    818             throws IOException {
    819         if (array == null) {
    820             write(NULL_OBJECT);
    821             return;
    822         }
    823         write(array.size());
    824 
    825         int[] keys = new int[array.size()];
    826         Savable[] values = new Savable[keys.length];
    827         int i = 0;
    828         for (Entry<? extends Savable> entry : array){
    829             keys[i] = entry.getKey();
    830             values[i] = entry.getValue();
    831             i++;
    832         }
    833 
    834         // write String array for keys
    835         write(keys);
    836 
    837         // write Savable array for values
    838         write(values);
    839     }
    840 
    841     // ArrayList<FloatBuffer>
    842 
    843     protected void writeFloatBufferArrayList(ArrayList<FloatBuffer> array)
    844             throws IOException {
    845         if (array == null) {
    846             write(NULL_OBJECT);
    847             return;
    848         }
    849         write(array.size());
    850         for (FloatBuffer buf : array) {
    851             write(buf);
    852         }
    853     }
    854 
    855     // ArrayList<FloatBuffer>
    856 
    857     protected void writeByteBufferArrayList(ArrayList<ByteBuffer> array)
    858             throws IOException {
    859         if (array == null) {
    860             write(NULL_OBJECT);
    861             return;
    862         }
    863         write(array.size());
    864         for (ByteBuffer buf : array) {
    865             write(buf);
    866         }
    867     }
    868 
    869     // NIO BUFFERS
    870     // float buffer
    871 
    872     protected void write(FloatBuffer value) throws IOException {
    873         if (value == null) {
    874             write(NULL_OBJECT);
    875             return;
    876         }
    877         value.rewind();
    878         int length = value.limit();
    879         write(length);
    880         for (int x = 0; x < length; x++) {
    881             writeForBuffer(value.get());
    882         }
    883         value.rewind();
    884     }
    885 
    886     // int buffer
    887 
    888     protected void write(IntBuffer value) throws IOException {
    889         if (value == null) {
    890             write(NULL_OBJECT);
    891             return;
    892         }
    893         value.rewind();
    894         int length = value.limit();
    895         write(length);
    896 
    897         for (int x = 0; x < length; x++) {
    898             writeForBuffer(value.get());
    899         }
    900         value.rewind();
    901     }
    902 
    903     // byte buffer
    904 
    905     protected void write(ByteBuffer value) throws IOException {
    906         if (value == null) {
    907             write(NULL_OBJECT);
    908             return;
    909         }
    910         value.rewind();
    911         int length = value.limit();
    912         write(length);
    913         for (int x = 0; x < length; x++) {
    914             writeForBuffer(value.get());
    915         }
    916         value.rewind();
    917     }
    918 
    919     // short buffer
    920 
    921     protected void write(ShortBuffer value) throws IOException {
    922         if (value == null) {
    923             write(NULL_OBJECT);
    924             return;
    925         }
    926         value.rewind();
    927         int length = value.limit();
    928         write(length);
    929         for (int x = 0; x < length; x++) {
    930             writeForBuffer(value.get());
    931         }
    932         value.rewind();
    933     }
    934 
    935     public void write(Enum value, String name, Enum defVal) throws IOException {
    936         if (value == defVal)
    937             return;
    938         if (value == null) {
    939             return;
    940         } else {
    941             write(value.name(), name, null);
    942         }
    943     }
    944 }