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 
BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco)68     public BinaryInputCapsule(BinaryImporter importer, Savable savable, BinaryClassObject bco) {
69         this.importer = importer;
70         this.cObj = bco;
71         this.savable = savable;
72     }
73 
setContent(byte[] content, int start, int limit)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 
getSavableVersion(Class<? extends Savable> desiredClass)261     public int getSavableVersion(Class<? extends Savable> desiredClass){
262         return SavableClassUtil.getSavedSavableVersion(savable, desiredClass,
263                                             cObj.classHierarchyVersions, importer.getFormatVersion());
264     }
265 
readBitSet(String name, BitSet defVal)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 
readBoolean(String name, boolean defVal)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 
readBooleanArray(String name, boolean[] defVal)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 
readBooleanArray2D(String name, boolean[][] defVal)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 
readByte(String name, byte defVal)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 
readByteArray(String name, byte[] defVal)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 
readByteArray2D(String name, byte[][] defVal)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 
readByteBuffer(String name, ByteBuffer defVal)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")
readByteBufferArrayList(String name, ArrayList<ByteBuffer> defVal)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 
readDouble(String name, double defVal)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 
readDoubleArray(String name, double[] defVal)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 
readDoubleArray2D(String name, double[][] defVal)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 
readFloat(String name, float defVal)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 
readFloatArray(String name, float[] defVal)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 
readFloatArray2D(String name, float[][] defVal)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 
readFloatBuffer(String name, FloatBuffer defVal)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")
readFloatBufferArrayList(String name, ArrayList<FloatBuffer> defVal)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 
readInt(String name, int defVal)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 
readIntArray(String name, int[] defVal)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 
readIntArray2D(String name, int[][] defVal)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 
readIntBuffer(String name, IntBuffer defVal)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 
readLong(String name, long defVal)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 
readLongArray(String name, long[] defVal)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 
readLongArray2D(String name, long[][] defVal)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 
readSavable(String name, Savable defVal)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 
readSavableArray(String name, Savable[] defVal)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 
resolveIDs(Object[] values)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 
readSavableArray2D(String name, Savable[][] defVal)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 
readSavableArray3D(String name, Savable[][][] defVal)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 
savableArrayListFromArray(Savable[] savables)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.
savableMapFrom2DArray(Savable[][] savables)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 
stringSavableMapFromKV(String[] keys, Savable[] values)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 
intSavableMapFromKV(int[] keys, Savable[] values)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 
readSavableArrayList(String name, ArrayList defVal)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 
readSavableArrayListArray(String name, ArrayList[] defVal)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 
readSavableArrayListArray2D(String name, ArrayList[][] defVal)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")
readSavableMap(String name, Map<? extends Savable, ? extends Savable> defVal)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")
readStringSavableMap(String name, Map<String, ? extends Savable> defVal)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")
readIntSavableMap(String name, IntMap<? extends Savable> defVal)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 
readShort(String name, short defVal)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 
readShortArray(String name, short[] defVal)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 
readShortArray2D(String name, short[][] defVal)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 
readShortBuffer(String name, ShortBuffer defVal)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 
readString(String name, String defVal)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 
readStringArray(String name, String[] defVal)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 
readStringArray2D(String name, String[][] defVal)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 
readByte(byte[] content)748     protected byte readByte(byte[] content) throws IOException {
749         byte value = content[index];
750         index++;
751         return value;
752     }
753 
readByteForBuffer(byte[] content)754     protected byte readByteForBuffer(byte[] content) throws IOException {
755         byte value = content[index];
756         index++;
757         return value;
758     }
759 
readByteArray(byte[] content)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 
readByteArray2D(byte[] content)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 
readIntForBuffer(byte[] content)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 
readInt(byte[] content)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 
readIntArray(byte[] content)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 
readIntArray2D(byte[] content)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 
readFloat(byte[] content)824     protected float readFloat(byte[] content) throws IOException {
825         float value = ByteUtils.convertFloatFromBytes(content, index);
826         index += 4;
827         return value;
828     }
829 
readFloatForBuffer(byte[] content)830     protected float readFloatForBuffer(byte[] content) throws IOException {
831         int number = readIntForBuffer(content);
832         return Float.intBitsToFloat(number);
833     }
834 
readFloatArray(byte[] content)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 
readFloatArray2D(byte[] content)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 
readDouble(byte[] content)857     protected double readDouble(byte[] content) throws IOException {
858         double value = ByteUtils.convertDoubleFromBytes(content, index);
859         index += 8;
860         return value;
861     }
862 
readDoubleArray(byte[] content)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 
readDoubleArray2D(byte[] content)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 
readLong(byte[] content)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 
readLongArray(byte[] content)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 
readLongArray2D(byte[] content)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 
readShort(byte[] content)915     protected short readShort(byte[] content) throws IOException {
916         short value = ByteUtils.convertShortFromBytes(content, index);
917         index += 2;
918         return value;
919     }
920 
readShortForBuffer(byte[] content)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 
readShortArray(byte[] content)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 
readShortArray2D(byte[] content)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 
readBoolean(byte[] content)950     protected boolean readBoolean(byte[] content) throws IOException {
951         boolean value = ByteUtils.convertBooleanFromBytes(content, index);
952         index += 1;
953         return value;
954     }
955 
readBooleanArray(byte[] content)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 
readBooleanArray2D(byte[] content)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
readString(byte[] content)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 
readStringArray(byte[] content)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 
readStringArray2D(byte[] content)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 
readBitSet(byte[] content)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 
inflateFrom(byte[] contents, int index)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 
readSavable(byte[] content)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 
readSavableArray(byte[] content)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 
readSavableArray2D(byte[] content)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 
readSavableArray3D(byte[] content)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 
readSavableMap(byte[] content)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 
readStringSavableMap(byte[] content)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 
readIntSavableMap(byte[] content)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 
readFloatBufferArrayList(byte[] content)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 
readByteBufferArrayList(byte[] content)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 
readFloatBuffer(byte[] content)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 
readIntBuffer(byte[] content)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 
readByteBuffer(byte[] content)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 
readShortBuffer(byte[] content)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 
ID(int id)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 
readEnum(String name, Class<T> enumType, T defVal)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 }