001/**
002 * Copyright (c) 2008-2014 Ardor Labs, Inc.
003 *
004 * This file is part of Ardor3D.
005 *
006 * Ardor3D is free software: you can redistribute it and/or modify it 
007 * under the terms of its license which may be found in the accompanying
008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
009 */
010
011package com.ardor3d.util.export.binary;
012
013import java.io.IOException;
014import java.io.UnsupportedEncodingException;
015import java.lang.reflect.Array;
016import java.nio.ByteBuffer;
017import java.nio.ByteOrder;
018import java.nio.FloatBuffer;
019import java.nio.IntBuffer;
020import java.nio.ShortBuffer;
021import java.util.ArrayList;
022import java.util.BitSet;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.logging.Level;
027import java.util.logging.Logger;
028
029import com.ardor3d.util.export.ByteUtils;
030import com.ardor3d.util.export.InputCapsule;
031import com.ardor3d.util.export.Savable;
032import com.ardor3d.util.geom.BufferUtils;
033
034public class BinaryInputCapsule implements InputCapsule {
035    private static final Logger logger = Logger.getLogger(BinaryInputCapsule.class.getName());
036
037    protected BinaryImporter _importer;
038    protected BinaryClassObject _cObj;
039    protected HashMap<Byte, Object> _fieldData;
040
041    protected int _index = 0;
042
043    public BinaryInputCapsule(final BinaryImporter importer, final BinaryClassObject bco) {
044        _importer = importer;
045        _cObj = bco;
046    }
047
048    public void setContent(final byte[] content, final int start, final int limit) {
049        _fieldData = new HashMap<>();
050        for (_index = start; _index < limit;) {
051            final byte alias = content[_index];
052
053            _index++;
054
055            try {
056                final byte type = _cObj._aliasFields.get(alias)._type;
057                Object value = null;
058
059                switch (type) {
060                    case BinaryClassField.BITSET: {
061                        value = readBitSet(content);
062                        break;
063                    }
064                    case BinaryClassField.BOOLEAN: {
065                        value = readBoolean(content);
066                        break;
067                    }
068                    case BinaryClassField.BOOLEAN_1D: {
069                        value = readBooleanArray(content);
070                        break;
071                    }
072                    case BinaryClassField.BOOLEAN_2D: {
073                        value = readBooleanArray2D(content);
074                        break;
075                    }
076                    case BinaryClassField.BYTE: {
077                        value = readByte(content);
078                        break;
079                    }
080                    case BinaryClassField.BYTE_1D: {
081                        value = readByteArray(content);
082                        break;
083                    }
084                    case BinaryClassField.BYTE_2D: {
085                        value = readByteArray2D(content);
086                        break;
087                    }
088                    case BinaryClassField.BYTEBUFFER: {
089                        value = readByteBuffer(content);
090                        break;
091                    }
092                    case BinaryClassField.DOUBLE: {
093                        value = readDouble(content);
094                        break;
095                    }
096                    case BinaryClassField.DOUBLE_1D: {
097                        value = readDoubleArray(content);
098                        break;
099                    }
100                    case BinaryClassField.DOUBLE_2D: {
101                        value = readDoubleArray2D(content);
102                        break;
103                    }
104                    case BinaryClassField.FLOAT: {
105                        value = readFloat(content);
106                        break;
107                    }
108                    case BinaryClassField.FLOAT_1D: {
109                        value = readFloatArray(content);
110                        break;
111                    }
112                    case BinaryClassField.FLOAT_2D: {
113                        value = readFloatArray2D(content);
114                        break;
115                    }
116                    case BinaryClassField.FLOATBUFFER: {
117                        value = readFloatBuffer(content);
118                        break;
119                    }
120                    case BinaryClassField.FLOATBUFFER_ARRAYLIST: {
121                        value = readFloatBufferArrayList(content);
122                        break;
123                    }
124                    case BinaryClassField.BYTEBUFFER_ARRAYLIST: {
125                        value = readByteBufferArrayList(content);
126                        break;
127                    }
128                    case BinaryClassField.INT: {
129                        value = readInt(content);
130                        break;
131                    }
132                    case BinaryClassField.INT_1D: {
133                        value = readIntArray(content);
134                        break;
135                    }
136                    case BinaryClassField.INT_2D: {
137                        value = readIntArray2D(content);
138                        break;
139                    }
140                    case BinaryClassField.INTBUFFER: {
141                        value = readIntBuffer(content);
142                        break;
143                    }
144                    case BinaryClassField.LONG: {
145                        value = readLong(content);
146                        break;
147                    }
148                    case BinaryClassField.LONG_1D: {
149                        value = readLongArray(content);
150                        break;
151                    }
152                    case BinaryClassField.LONG_2D: {
153                        value = readLongArray2D(content);
154                        break;
155                    }
156                    case BinaryClassField.SAVABLE: {
157                        value = readSavable(content);
158                        break;
159                    }
160                    case BinaryClassField.SAVABLE_1D: {
161                        value = readSavableArray(content);
162                        break;
163                    }
164                    case BinaryClassField.SAVABLE_2D: {
165                        value = readSavableArray2D(content);
166                        break;
167                    }
168                    case BinaryClassField.SAVABLE_ARRAYLIST: {
169                        value = readSavableArray(content);
170                        break;
171                    }
172                    case BinaryClassField.SAVABLE_ARRAYLIST_1D: {
173                        value = readSavableArray2D(content);
174                        break;
175                    }
176                    case BinaryClassField.SAVABLE_ARRAYLIST_2D: {
177                        value = readSavableArray3D(content);
178                        break;
179                    }
180                    case BinaryClassField.SAVABLE_MAP: {
181                        value = readSavableMap(content);
182                        break;
183                    }
184                    case BinaryClassField.STRING_SAVABLE_MAP: {
185                        value = readStringSavableMap(content);
186                        break;
187                    }
188                    case BinaryClassField.SHORT: {
189                        value = readShort(content);
190                        break;
191                    }
192                    case BinaryClassField.SHORT_1D: {
193                        value = readShortArray(content);
194                        break;
195                    }
196                    case BinaryClassField.SHORT_2D: {
197                        value = readShortArray2D(content);
198                        break;
199                    }
200                    case BinaryClassField.SHORTBUFFER: {
201                        value = readShortBuffer(content);
202                        break;
203                    }
204                    case BinaryClassField.STRING: {
205                        value = readString(content);
206                        break;
207                    }
208                    case BinaryClassField.STRING_1D: {
209                        value = readStringArray(content);
210                        break;
211                    }
212                    case BinaryClassField.STRING_2D: {
213                        value = readStringArray2D(content);
214                        break;
215                    }
216
217                    default:
218                        // skip put statement
219                        continue;
220                }
221
222                _fieldData.put(alias, value);
223
224            } catch (final IOException e) {
225                logger.logp(Level.SEVERE, this.getClass().toString(), "setContent(byte[] content)", "Exception", e);
226            }
227        }
228    }
229
230    @Override
231    public BitSet readBitSet(final String name, final BitSet defVal) throws IOException {
232        final BinaryClassField field = _cObj._nameFields.get(name);
233        if (field == null || !_fieldData.containsKey(field._alias)) {
234            return defVal;
235        }
236        return (BitSet) _fieldData.get(field._alias);
237    }
238
239    @Override
240    public boolean readBoolean(final String name, final boolean defVal) throws IOException {
241        final BinaryClassField field = _cObj._nameFields.get(name);
242        if (field == null || !_fieldData.containsKey(field._alias)) {
243            return defVal;
244        }
245        return ((Boolean) _fieldData.get(field._alias)).booleanValue();
246    }
247
248    @Override
249    public boolean[] readBooleanArray(final String name, final boolean[] defVal) throws IOException {
250        final BinaryClassField field = _cObj._nameFields.get(name);
251        if (field == null || !_fieldData.containsKey(field._alias)) {
252            return defVal;
253        }
254        return (boolean[]) _fieldData.get(field._alias);
255    }
256
257    @Override
258    public boolean[][] readBooleanArray2D(final String name, final boolean[][] defVal) throws IOException {
259        final BinaryClassField field = _cObj._nameFields.get(name);
260        if (field == null || !_fieldData.containsKey(field._alias)) {
261            return defVal;
262        }
263        return (boolean[][]) _fieldData.get(field._alias);
264    }
265
266    @Override
267    public byte readByte(final String name, final byte defVal) throws IOException {
268        final BinaryClassField field = _cObj._nameFields.get(name);
269        if (field == null || !_fieldData.containsKey(field._alias)) {
270            return defVal;
271        }
272        return ((Byte) _fieldData.get(field._alias)).byteValue();
273    }
274
275    @Override
276    public byte[] readByteArray(final String name, final byte[] defVal) throws IOException {
277        final BinaryClassField field = _cObj._nameFields.get(name);
278        if (field == null || !_fieldData.containsKey(field._alias)) {
279            return defVal;
280        }
281        return (byte[]) _fieldData.get(field._alias);
282    }
283
284    @Override
285    public byte[][] readByteArray2D(final String name, final byte[][] defVal) throws IOException {
286        final BinaryClassField field = _cObj._nameFields.get(name);
287        if (field == null || !_fieldData.containsKey(field._alias)) {
288            return defVal;
289        }
290        return (byte[][]) _fieldData.get(field._alias);
291    }
292
293    @Override
294    public ByteBuffer readByteBuffer(final String name, final ByteBuffer defVal) throws IOException {
295        final BinaryClassField field = _cObj._nameFields.get(name);
296        if (field == null || !_fieldData.containsKey(field._alias)) {
297            return defVal;
298        }
299        return (ByteBuffer) _fieldData.get(field._alias);
300    }
301
302    @Override
303    @SuppressWarnings("unchecked")
304    public List<ByteBuffer> readByteBufferList(final String name, final List<ByteBuffer> defVal) throws IOException {
305        final BinaryClassField field = _cObj._nameFields.get(name);
306        if (field == null || !_fieldData.containsKey(field._alias)) {
307            return defVal;
308        }
309        return (List<ByteBuffer>) _fieldData.get(field._alias);
310    }
311
312    @Override
313    public double readDouble(final String name, final double defVal) throws IOException {
314        final BinaryClassField field = _cObj._nameFields.get(name);
315        if (field == null || !_fieldData.containsKey(field._alias)) {
316            return defVal;
317        }
318        return ((Double) _fieldData.get(field._alias)).doubleValue();
319    }
320
321    @Override
322    public double[] readDoubleArray(final String name, final double[] defVal) throws IOException {
323        final BinaryClassField field = _cObj._nameFields.get(name);
324        if (field == null || !_fieldData.containsKey(field._alias)) {
325            return defVal;
326        }
327        return (double[]) _fieldData.get(field._alias);
328    }
329
330    @Override
331    public double[][] readDoubleArray2D(final String name, final double[][] defVal) throws IOException {
332        final BinaryClassField field = _cObj._nameFields.get(name);
333        if (field == null || !_fieldData.containsKey(field._alias)) {
334            return defVal;
335        }
336        return (double[][]) _fieldData.get(field._alias);
337    }
338
339    @Override
340    public float readFloat(final String name, final float defVal) throws IOException {
341        final BinaryClassField field = _cObj._nameFields.get(name);
342        if (field == null || !_fieldData.containsKey(field._alias)) {
343            return defVal;
344        }
345        return ((Float) _fieldData.get(field._alias)).floatValue();
346    }
347
348    @Override
349    public float[] readFloatArray(final String name, final float[] defVal) throws IOException {
350        final BinaryClassField field = _cObj._nameFields.get(name);
351        if (field == null || !_fieldData.containsKey(field._alias)) {
352            return defVal;
353        }
354        return (float[]) _fieldData.get(field._alias);
355    }
356
357    @Override
358    public float[][] readFloatArray2D(final String name, final float[][] defVal) throws IOException {
359        final BinaryClassField field = _cObj._nameFields.get(name);
360        if (field == null || !_fieldData.containsKey(field._alias)) {
361            return defVal;
362        }
363        return (float[][]) _fieldData.get(field._alias);
364    }
365
366    @Override
367    public FloatBuffer readFloatBuffer(final String name, final FloatBuffer defVal) throws IOException {
368        final BinaryClassField field = _cObj._nameFields.get(name);
369        if (field == null || !_fieldData.containsKey(field._alias)) {
370            return defVal;
371        }
372        return (FloatBuffer) _fieldData.get(field._alias);
373    }
374
375    @Override
376    @SuppressWarnings("unchecked")
377    public List<FloatBuffer> readFloatBufferList(final String name, final List<FloatBuffer> defVal) throws IOException {
378        final BinaryClassField field = _cObj._nameFields.get(name);
379        if (field == null || !_fieldData.containsKey(field._alias)) {
380            return defVal;
381        }
382        return (List<FloatBuffer>) _fieldData.get(field._alias);
383    }
384
385    @Override
386    public int readInt(final String name, final int defVal) throws IOException {
387        final BinaryClassField field = _cObj._nameFields.get(name);
388        if (field == null || !_fieldData.containsKey(field._alias)) {
389            return defVal;
390        }
391        return ((Integer) _fieldData.get(field._alias)).intValue();
392    }
393
394    @Override
395    public int[] readIntArray(final String name, final int[] defVal) throws IOException {
396        final BinaryClassField field = _cObj._nameFields.get(name);
397        if (field == null || !_fieldData.containsKey(field._alias)) {
398            return defVal;
399        }
400        return (int[]) _fieldData.get(field._alias);
401    }
402
403    @Override
404    public int[][] readIntArray2D(final String name, final int[][] defVal) throws IOException {
405        final BinaryClassField field = _cObj._nameFields.get(name);
406        if (field == null || !_fieldData.containsKey(field._alias)) {
407            return defVal;
408        }
409        return (int[][]) _fieldData.get(field._alias);
410    }
411
412    @Override
413    public IntBuffer readIntBuffer(final String name, final IntBuffer defVal) throws IOException {
414        final BinaryClassField field = _cObj._nameFields.get(name);
415        if (field == null || !_fieldData.containsKey(field._alias)) {
416            return defVal;
417        }
418        return (IntBuffer) _fieldData.get(field._alias);
419    }
420
421    @Override
422    public long readLong(final String name, final long defVal) throws IOException {
423        final BinaryClassField field = _cObj._nameFields.get(name);
424        if (field == null || !_fieldData.containsKey(field._alias)) {
425            return defVal;
426        }
427        return ((Long) _fieldData.get(field._alias)).longValue();
428    }
429
430    @Override
431    public long[] readLongArray(final String name, final long[] defVal) throws IOException {
432        final BinaryClassField field = _cObj._nameFields.get(name);
433        if (field == null || !_fieldData.containsKey(field._alias)) {
434            return defVal;
435        }
436        return (long[]) _fieldData.get(field._alias);
437    }
438
439    @Override
440    public long[][] readLongArray2D(final String name, final long[][] defVal) throws IOException {
441        final BinaryClassField field = _cObj._nameFields.get(name);
442        if (field == null || !_fieldData.containsKey(field._alias)) {
443            return defVal;
444        }
445        return (long[][]) _fieldData.get(field._alias);
446    }
447
448    @Override
449    public Savable readSavable(final String name, final Savable defVal) throws IOException {
450        final BinaryClassField field = _cObj._nameFields.get(name);
451        if (field == null || !_fieldData.containsKey(field._alias)) {
452            return defVal;
453        }
454        Object value = _fieldData.get(field._alias);
455        if (value == null) {
456            return null;
457        } else if (value instanceof ID) {
458            value = _importer.readObject(((ID) value).id);
459            _fieldData.put(field._alias, value);
460            return (Savable) value;
461        } else {
462            return defVal;
463        }
464    }
465
466    @Override
467    public Savable[] readSavableArray(final String name, final Savable[] defVal) throws IOException {
468        final BinaryClassField field = _cObj._nameFields.get(name);
469        if (field == null || !_fieldData.containsKey(field._alias)) {
470            return defVal;
471        }
472        Object[] values = (Object[]) _fieldData.get(field._alias);
473        if (values instanceof ID[]) {
474            values = resolveIDs(values);
475            _fieldData.put(field._alias, values);
476            return (Savable[]) values;
477        } else {
478            return defVal;
479        }
480    }
481
482    private Savable[] resolveIDs(final Object[] values) {
483        if (values != null) {
484            final Savable[] savables = new Savable[values.length];
485            for (int i = 0; i < values.length; i++) {
486                final ID id = (ID) values[i];
487                savables[i] = id != null ? _importer.readObject(id.id) : null;
488            }
489            return savables;
490        } else {
491            return null;
492        }
493    }
494
495    @Override
496    public Savable[][] readSavableArray2D(final String name, final Savable[][] defVal) throws IOException {
497        final BinaryClassField field = _cObj._nameFields.get(name);
498        if (field == null || !_fieldData.containsKey(field._alias)) {
499            return defVal;
500        }
501        Object[][] values = (Object[][]) _fieldData.get(field._alias);
502        if (values instanceof ID[][]) {
503            final Savable[][] savables = new Savable[values.length][];
504            for (int i = 0; i < values.length; i++) {
505                if (values[i] != null) {
506                    savables[i] = resolveIDs(values[i]);
507                } else {
508                    savables[i] = null;
509                }
510            }
511            values = savables;
512            _fieldData.put(field._alias, values);
513        }
514        return (Savable[][]) values;
515    }
516
517    public Savable[][][] readSavableArray3D(final String name, final Savable[][][] defVal) throws IOException {
518        final BinaryClassField field = _cObj._nameFields.get(name);
519        if (field == null || !_fieldData.containsKey(field._alias)) {
520            return defVal;
521        }
522        final Object[][][] values = (Object[][][]) _fieldData.get(field._alias);
523        if (values instanceof ID[][][]) {
524            final Savable[][][] savables = new Savable[values.length][][];
525            for (int i = 0; i < values.length; i++) {
526                if (values[i] != null) {
527                    savables[i] = new Savable[values[i].length][];
528                    for (int j = 0; j < values[i].length; j++) {
529                        savables[i][j] = resolveIDs(values[i][j]);
530                    }
531                } else {
532                    savables[i] = null;
533                }
534            }
535            _fieldData.put(field._alias, savables);
536            return savables;
537        } else {
538            return defVal;
539        }
540    }
541
542    private List<Savable> savableArrayListFromArray(final Savable[] savables) {
543        if (savables == null) {
544            return null;
545        }
546        final List<Savable> list = new ArrayList<>(savables.length);
547        for (int x = 0; x < savables.length; x++) {
548            list.add(savables[x]);
549        }
550        return list;
551    }
552
553    // Assumes array of size 2 arrays where pos 0 is key and pos 1 is value.
554    private Map<Savable, Savable> savableMapFrom2DArray(final Savable[][] savables) {
555        if (savables == null) {
556            return null;
557        }
558        final Map<Savable, Savable> map = new HashMap<>(savables.length);
559        for (int x = 0; x < savables.length; x++) {
560            map.put(savables[x][0], savables[x][1]);
561        }
562        return map;
563    }
564
565    private Map<String, Savable> stringSavableMapFromKV(final String[] keys, final Savable[] values) {
566        if (keys == null || values == null) {
567            return null;
568        }
569
570        final Map<String, Savable> map = new HashMap<>(keys.length);
571        for (int x = 0; x < keys.length; x++) {
572            map.put(keys[x], values[x]);
573        }
574
575        return map;
576    }
577
578    @Override
579    @SuppressWarnings("unchecked")
580    public <E extends Savable> List<E> readSavableList(final String name, final List<E> defVal) throws IOException {
581        final BinaryClassField field = _cObj._nameFields.get(name);
582        if (field == null || !_fieldData.containsKey(field._alias)) {
583            return defVal;
584        }
585        Object value = _fieldData.get(field._alias);
586        if (value instanceof ID[]) {
587            // read Savable array and convert to ArrayList
588            final Savable[] savables = readSavableArray(name, null);
589            value = savableArrayListFromArray(savables);
590            _fieldData.put(field._alias, value);
591        }
592        return (List<E>) value;
593    }
594
595    @Override
596    @SuppressWarnings("unchecked")
597    public <E extends Savable> List<E>[] readSavableListArray(final String name, final List<E>[] defVal)
598            throws IOException {
599        final BinaryClassField field = _cObj._nameFields.get(name);
600        if (field == null || !_fieldData.containsKey(field._alias)) {
601            return defVal;
602        }
603        Object value = _fieldData.get(field._alias);
604        if (value instanceof ID[][]) {
605            // read 2D Savable array and convert to ArrayList array
606            final Savable[][] savables = readSavableArray2D(name, null);
607            if (savables != null) {
608                final List<Savable>[] arrayLists = new ArrayList[savables.length];
609                for (int i = 0; i < savables.length; i++) {
610                    arrayLists[i] = savableArrayListFromArray(savables[i]);
611                }
612                value = arrayLists;
613            } else {
614                value = defVal;
615            }
616            _fieldData.put(field._alias, value);
617        }
618        return (List<E>[]) value;
619    }
620
621    @Override
622    @SuppressWarnings("unchecked")
623    public <E extends Savable> List<E>[][] readSavableListArray2D(final String name, final List<E>[][] defVal)
624            throws IOException {
625        final BinaryClassField field = _cObj._nameFields.get(name);
626        if (field == null || !_fieldData.containsKey(field._alias)) {
627            return defVal;
628        }
629        Object value = _fieldData.get(field._alias);
630        if (value instanceof ID[][][]) {
631            // read 3D Savable array and convert to 2D ArrayList array
632            final Savable[][][] savables = readSavableArray3D(name, null);
633            if (savables != null && savables.length > 0) {
634                final List<Savable>[][] arrayLists = new ArrayList[savables.length][];
635                for (int i = 0; i < savables.length; i++) {
636                    arrayLists[i] = new ArrayList[savables[i].length];
637                    for (int j = 0; j < savables[i].length; j++) {
638                        arrayLists[i][j] = savableArrayListFromArray(savables[i][j]);
639                    }
640                }
641                value = arrayLists;
642            } else {
643                value = defVal;
644            }
645            _fieldData.put(field._alias, value);
646        }
647        return (List<E>[][]) value;
648    }
649
650    @Override
651    @SuppressWarnings("unchecked")
652    public <K extends Savable, V extends Savable> Map<K, V> readSavableMap(final String name, final Map<K, V> defVal)
653            throws IOException {
654        final BinaryClassField field = _cObj._nameFields.get(name);
655        if (field == null || !_fieldData.containsKey(field._alias)) {
656            return defVal;
657        }
658        Object value = _fieldData.get(field._alias);
659        if (value instanceof ID[][]) {
660            // read Savable array and convert to Map
661            final Savable[][] savables = readSavableArray2D(name, null);
662            value = savableMapFrom2DArray(savables);
663            _fieldData.put(field._alias, value);
664        }
665        return (Map<K, V>) value;
666    }
667
668    @Override
669    @SuppressWarnings("unchecked")
670    public <V extends Savable> Map<String, V> readStringSavableMap(final String name, final Map<String, V> defVal)
671            throws IOException {
672        final BinaryClassField field = _cObj._nameFields.get(name);
673        if (field == null || !_fieldData.containsKey(field._alias)) {
674            return defVal;
675        }
676        Object value = _fieldData.get(field._alias);
677        if (value instanceof StringIDMap) {
678            // read Savable array and convert to Map values
679            final StringIDMap in = (StringIDMap) value;
680            final Savable[] values = resolveIDs(in.values);
681            value = stringSavableMapFromKV(in.keys, values);
682            _fieldData.put(field._alias, value);
683        }
684        return (Map<String, V>) value;
685    }
686
687    @Override
688    public short readShort(final String name, final short defVal) throws IOException {
689        final BinaryClassField field = _cObj._nameFields.get(name);
690        if (field == null || !_fieldData.containsKey(field._alias)) {
691            return defVal;
692        }
693        return ((Short) _fieldData.get(field._alias)).shortValue();
694    }
695
696    @Override
697    public short[] readShortArray(final String name, final short[] defVal) throws IOException {
698        final BinaryClassField field = _cObj._nameFields.get(name);
699        if (field == null || !_fieldData.containsKey(field._alias)) {
700            return defVal;
701        }
702        return (short[]) _fieldData.get(field._alias);
703    }
704
705    @Override
706    public short[][] readShortArray2D(final String name, final short[][] defVal) throws IOException {
707        final BinaryClassField field = _cObj._nameFields.get(name);
708        if (field == null || !_fieldData.containsKey(field._alias)) {
709            return defVal;
710        }
711        return (short[][]) _fieldData.get(field._alias);
712    }
713
714    @Override
715    public ShortBuffer readShortBuffer(final String name, final ShortBuffer defVal) throws IOException {
716        final BinaryClassField field = _cObj._nameFields.get(name);
717        if (field == null || !_fieldData.containsKey(field._alias)) {
718            return defVal;
719        }
720        return (ShortBuffer) _fieldData.get(field._alias);
721    }
722
723    @Override
724    public String readString(final String name, final String defVal) throws IOException {
725        final BinaryClassField field = _cObj._nameFields.get(name);
726        if (field == null || !_fieldData.containsKey(field._alias)) {
727            return defVal;
728        }
729        return (String) _fieldData.get(field._alias);
730    }
731
732    @Override
733    public String[] readStringArray(final String name, final String[] defVal) throws IOException {
734        final BinaryClassField field = _cObj._nameFields.get(name);
735        if (field == null || !_fieldData.containsKey(field._alias)) {
736            return defVal;
737        }
738        return (String[]) _fieldData.get(field._alias);
739    }
740
741    @Override
742    public String[][] readStringArray2D(final String name, final String[][] defVal) throws IOException {
743        final BinaryClassField field = _cObj._nameFields.get(name);
744        if (field == null || !_fieldData.containsKey(field._alias)) {
745            return defVal;
746        }
747        return (String[][]) _fieldData.get(field._alias);
748    }
749
750    // byte primitive
751
752    protected byte readByte(final byte[] content) throws IOException {
753        final byte value = content[_index];
754        _index++;
755        return value;
756    }
757
758    protected byte[] readByteArray(final byte[] content) throws IOException {
759        final int length = readInt(content);
760        if (length == BinaryOutputCapsule.NULL_OBJECT) {
761            return null;
762        }
763        final byte[] value = new byte[length];
764        for (int x = 0; x < length; x++) {
765            value[x] = readByte(content);
766        }
767        return value;
768    }
769
770    protected byte[][] readByteArray2D(final byte[] content) throws IOException {
771        final int length = readInt(content);
772        if (length == BinaryOutputCapsule.NULL_OBJECT) {
773            return null;
774        }
775        final byte[][] value = new byte[length][];
776        for (int x = 0; x < length; x++) {
777            value[x] = readByteArray(content);
778        }
779        return value;
780    }
781
782    // int primitive
783
784    protected int readInt(final byte[] content) throws IOException {
785        byte[] bytes = inflateFrom(content, _index);
786        _index += 1 + bytes.length;
787        bytes = ByteUtils.rightAlignBytes(bytes, 4);
788        final int value = ByteUtils.convertIntFromBytes(bytes);
789        if (value == BinaryOutputCapsule.NULL_OBJECT || value == BinaryOutputCapsule.DEFAULT_OBJECT) {
790            _index -= 4;
791        }
792        return value;
793    }
794
795    protected int[] readIntArray(final byte[] content) throws IOException {
796        final int length = readInt(content);
797        if (length == BinaryOutputCapsule.NULL_OBJECT) {
798            return null;
799        }
800        final int[] value = new int[length];
801        for (int x = 0; x < length; x++) {
802            value[x] = readInt(content);
803        }
804        return value;
805    }
806
807    protected int[][] readIntArray2D(final byte[] content) throws IOException {
808        final int length = readInt(content);
809        if (length == BinaryOutputCapsule.NULL_OBJECT) {
810            return null;
811        }
812        final int[][] value = new int[length][];
813        for (int x = 0; x < length; x++) {
814            value[x] = readIntArray(content);
815        }
816        return value;
817    }
818
819    // float primitive
820
821    protected float readFloat(final byte[] content) throws IOException {
822        final float value = ByteUtils.convertFloatFromBytes(content, _index);
823        _index += 4;
824        return value;
825    }
826
827    protected float[] readFloatArray(final byte[] content) throws IOException {
828        final int length = readInt(content);
829        if (length == BinaryOutputCapsule.NULL_OBJECT) {
830            return null;
831        }
832        final float[] value = new float[length];
833        for (int x = 0; x < length; x++) {
834            value[x] = readFloat(content);
835        }
836        return value;
837    }
838
839    protected float[][] readFloatArray2D(final byte[] content) throws IOException {
840        final int length = readInt(content);
841        if (length == BinaryOutputCapsule.NULL_OBJECT) {
842            return null;
843        }
844        final float[][] value = new float[length][];
845        for (int x = 0; x < length; x++) {
846            value[x] = readFloatArray(content);
847        }
848        return value;
849    }
850
851    // double primitive
852
853    protected double readDouble(final byte[] content) throws IOException {
854        final double value = ByteUtils.convertDoubleFromBytes(content, _index);
855        _index += 8;
856        return value;
857    }
858
859    protected double[] readDoubleArray(final byte[] content) throws IOException {
860        final int length = readInt(content);
861        if (length == BinaryOutputCapsule.NULL_OBJECT) {
862            return null;
863        }
864        final double[] value = new double[length];
865        for (int x = 0; x < length; x++) {
866            value[x] = readDouble(content);
867        }
868        return value;
869    }
870
871    protected double[][] readDoubleArray2D(final byte[] content) throws IOException {
872        final int length = readInt(content);
873        if (length == BinaryOutputCapsule.NULL_OBJECT) {
874            return null;
875        }
876        final double[][] value = new double[length][];
877        for (int x = 0; x < length; x++) {
878            value[x] = readDoubleArray(content);
879        }
880        return value;
881    }
882
883    // long primitive
884
885    protected long readLong(final byte[] content) throws IOException {
886        byte[] bytes = inflateFrom(content, _index);
887        _index += 1 + bytes.length;
888        bytes = ByteUtils.rightAlignBytes(bytes, 8);
889        final long value = ByteUtils.convertLongFromBytes(bytes);
890        return value;
891    }
892
893    protected long[] readLongArray(final byte[] content) throws IOException {
894        final int length = readInt(content);
895        if (length == BinaryOutputCapsule.NULL_OBJECT) {
896            return null;
897        }
898        final long[] value = new long[length];
899        for (int x = 0; x < length; x++) {
900            value[x] = readLong(content);
901        }
902        return value;
903    }
904
905    protected long[][] readLongArray2D(final byte[] content) throws IOException {
906        final int length = readInt(content);
907        if (length == BinaryOutputCapsule.NULL_OBJECT) {
908            return null;
909        }
910        final long[][] value = new long[length][];
911        for (int x = 0; x < length; x++) {
912            value[x] = readLongArray(content);
913        }
914        return value;
915    }
916
917    // short primitive
918
919    protected short readShort(final byte[] content) throws IOException {
920        final short value = ByteUtils.convertShortFromBytes(content, _index);
921        _index += 2;
922        return value;
923    }
924
925    protected short[] readShortArray(final byte[] content) throws IOException {
926        final int length = readInt(content);
927        if (length == BinaryOutputCapsule.NULL_OBJECT) {
928            return null;
929        }
930        final short[] value = new short[length];
931        for (int x = 0; x < length; x++) {
932            value[x] = readShort(content);
933        }
934        return value;
935    }
936
937    protected short[][] readShortArray2D(final byte[] content) throws IOException {
938        final int length = readInt(content);
939        if (length == BinaryOutputCapsule.NULL_OBJECT) {
940            return null;
941        }
942        final short[][] value = new short[length][];
943        for (int x = 0; x < length; x++) {
944            value[x] = readShortArray(content);
945        }
946        return value;
947    }
948
949    // boolean primitive
950
951    protected boolean readBoolean(final byte[] content) throws IOException {
952        final boolean value = ByteUtils.convertBooleanFromBytes(content, _index);
953        _index += 1;
954        return value;
955    }
956
957    protected boolean[] readBooleanArray(final byte[] content) throws IOException {
958        final int length = readInt(content);
959        if (length == BinaryOutputCapsule.NULL_OBJECT) {
960            return null;
961        }
962        final boolean[] value = new boolean[length];
963        for (int x = 0; x < length; x++) {
964            value[x] = readBoolean(content);
965        }
966        return value;
967    }
968
969    protected boolean[][] readBooleanArray2D(final byte[] content) throws IOException {
970        final int length = readInt(content);
971        if (length == BinaryOutputCapsule.NULL_OBJECT) {
972            return null;
973        }
974        final boolean[][] value = new boolean[length][];
975        for (int x = 0; x < length; x++) {
976            value[x] = readBooleanArray(content);
977        }
978        return value;
979    }
980
981    /*
982     * UTF-8 crash course:
983     * 
984     * UTF-8 codepoints map to UTF-16 codepoints and vv, which is what Java uses for it's Strings. (so a UTF-8 codepoint
985     * can contain all possible values for a Java char)
986     * 
987     * A UTF-8 codepoint can be 1, 2 or 3 bytes long. How long a codepoint is can be told by reading the first byte: b <
988     * 0x80, 1 byte (b & 0xC0) == 0xC0, 2 bytes (b & 0xE0) == 0xE0, 3 bytes
989     * 
990     * However there is an additional restriction to UTF-8, to enable you to find the start of a UTF-8 codepoint, if you
991     * start reading at a random point in a UTF-8 byte stream. That's why UTF-8 requires for the second and third byte
992     * of a multibyte codepoint: (b & 0x80) == 0x80 (in other words, first bit must be 1)
993     */
994    private final static int UTF8_START = 0; // next byte should be the start of a new
995    private final static int UTF8_2BYTE = 2; // next byte should be the second byte of a 2 byte codepoint
996    private final static int UTF8_3BYTE_1 = 3; // next byte should be the second byte of a 3 byte codepoint
997    private final static int UTF8_3BYTE_2 = 4; // next byte should be the third byte of a 3 byte codepoint
998    private final static int UTF8_ILLEGAL = 10; // not an UTF8 string
999
1000    // String
1001    protected String readString(final byte[] content) throws IOException {
1002        final int length = readInt(content);
1003        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1004            return null;
1005        }
1006
1007        /*
1008         * We'll transfer the bytes into a separate byte array. While we do that we'll take the opportunity to check if
1009         * the byte data is valid UTF-8.
1010         * 
1011         * If it is not UTF-8 it is most likely saved with the BinaryOutputCapsule bug, that saves Strings using their
1012         * native encoding. Unfortunatly there is no way to know what encoding was used, so we'll parse using the most
1013         * common one in that case; latin-1 aka ISO8859_1
1014         * 
1015         * Encoding of "low" ASCII codepoint (in plain speak: when no special characters are used) will usually look the
1016         * same for UTF-8 and the other 1 byte codepoint encodings (espc true for numbers and regular letters of the
1017         * alphabet). So these are valid UTF-8 and will give the same result (at most a few charakters will appear
1018         * different, such as the euro sign).
1019         * 
1020         * However, when "high" codepoints are used (any codepoint that over 0x7F, in other words where the first bit is
1021         * a 1) it's a different matter and UTF-8 and the 1 byte encoding greatly will differ, as well as most 1 byte
1022         * encodings relative to each other.
1023         * 
1024         * It is impossible to detect which one-byte encoding is used. Since UTF8 and practically all 1-byte encodings
1025         * share the most used characters (the "none-high" ones) parsing them will give the same result. However, not
1026         * all byte sequences are legal in UTF-8 (see explantion above). If not UTF-8 encoded content is detected we
1027         * therefor fallback on latin1. We also log a warning.
1028         * 
1029         * By this method we detect all use of 1 byte encoding if they: - use a "high" codepoint after a "low" codepoint
1030         * or a sequence of codepoints that is valid as UTF-8 bytes, that starts with 1000 - use a "low" codepoint after
1031         * a "high" codepoint - use a "low" codepoint after "high" codepoint, after a "high" codepoint that starts with
1032         * 1110
1033         * 
1034         * In practice this means that unless 2 or 3 "high" codepoints are used after each other in proper order, we'll
1035         * detect the string was not originally UTF-8 encoded.
1036         */
1037        final byte[] bytes = new byte[length];
1038        int utf8State = UTF8_START;
1039        int b;
1040        for (int x = 0; x < length; x++) {
1041            bytes[x] = content[_index++];
1042            b = bytes[x] & 0xFF; // unsign our byte
1043
1044            switch (utf8State) {
1045                case UTF8_START:
1046                    if (b < 0x80) {
1047                        // good
1048                    } else if ((b & 0xC0) == 0xC0) {
1049                        utf8State = UTF8_2BYTE;
1050                    } else if ((b & 0xE0) == 0xE0) {
1051                        utf8State = UTF8_3BYTE_1;
1052                    } else {
1053                        utf8State = UTF8_ILLEGAL;
1054                    }
1055                    break;
1056                case UTF8_3BYTE_1:
1057                case UTF8_3BYTE_2:
1058                case UTF8_2BYTE:
1059                    if ((b & 0x80) == 0x80) {
1060                        utf8State = utf8State == UTF8_3BYTE_1 ? UTF8_3BYTE_2 : UTF8_START;
1061                    } else {
1062                        utf8State = UTF8_ILLEGAL;
1063                    }
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
1070            // is it correct UTF-8
1071            if (utf8State == UTF8_START) {
1072                // Java misspells UTF-8 as UTF8 for official use in java.lang
1073                return new String(bytes, "UTF8");
1074            } else {
1075                logger.log(Level.WARNING,
1076                        "Your export has been saved with an incorrect encoding for it's String fields which means it might not load correctly "
1077                                + "due to encoding issues.");
1078                // We use ISO8859_1 to be consistent across platforms. We could default to native encoding, but this
1079                // would lead to inconsistent
1080                // behaviour across platforms!
1081                // Developers that have previously saved their exports using the old exporter (wich uses native
1082                // 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
1085                // created in.
1086                return new String(bytes, "ISO8859_1");
1087            }
1088        } catch (final UnsupportedEncodingException uee) {
1089            // as a last resort fall back to platform native.
1090            // JavaDoc is vague about what happens when a decoding a String that contains un undecodable sequence
1091            // it also doesn't specify which encodings have to be supported (though UTF-8 and ISO8859 have been in the
1092            // SUN JRE since at least 1.1)
1093            logger.log(
1094                    Level.SEVERE,
1095                    "Your export has been saved with an incorrect encoding or your version of Java is unable to decode the stored string. "
1096                            + "While your export may load correctly by falling back, using it on different platforms or java versions might lead to "
1097                            + "very strange inconsitenties. You should probably re-export your work.");
1098            return new String(bytes);
1099        }
1100    }
1101
1102    protected String[] readStringArray(final byte[] content) throws IOException {
1103        final int length = readInt(content);
1104        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1105            return null;
1106        }
1107        final String[] value = new String[length];
1108        for (int x = 0; x < length; x++) {
1109            value[x] = readString(content);
1110        }
1111        return value;
1112    }
1113
1114    protected String[][] readStringArray2D(final byte[] content) throws IOException {
1115        final int length = readInt(content);
1116        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1117            return null;
1118        }
1119        final String[][] value = new String[length][];
1120        for (int x = 0; x < length; x++) {
1121            value[x] = readStringArray(content);
1122        }
1123        return value;
1124    }
1125
1126    // BitSet
1127
1128    protected BitSet readBitSet(final byte[] content) throws IOException {
1129        final int length = readInt(content);
1130        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1131            return null;
1132        }
1133        final BitSet value = new BitSet(length);
1134        for (int x = 0; x < length; x++) {
1135            value.set(x, readBoolean(content));
1136        }
1137        return value;
1138    }
1139
1140    // INFLATOR for int and long
1141
1142    protected static byte[] inflateFrom(final byte[] contents, final int index) {
1143        final byte firstByte = contents[index];
1144        if (firstByte == BinaryOutputCapsule.NULL_OBJECT) {
1145            return ByteUtils.convertToBytes(BinaryOutputCapsule.NULL_OBJECT);
1146        } else if (firstByte == BinaryOutputCapsule.DEFAULT_OBJECT) {
1147            return ByteUtils.convertToBytes(BinaryOutputCapsule.DEFAULT_OBJECT);
1148        } else if (firstByte == 0) {
1149            return new byte[0];
1150        } else {
1151            final byte[] rVal = new byte[firstByte];
1152            for (int x = 0; x < rVal.length; x++) {
1153                rVal[x] = contents[x + 1 + index];
1154            }
1155            return rVal;
1156        }
1157    }
1158
1159    // BinarySavable
1160
1161    protected ID readSavable(final byte[] content) throws IOException {
1162        final int id = readInt(content);
1163        if (id == BinaryOutputCapsule.NULL_OBJECT) {
1164            return null;
1165        }
1166
1167        return new ID(id);
1168    }
1169
1170    // BinarySavable array
1171
1172    protected ID[] readSavableArray(final byte[] content) throws IOException {
1173        final int elements = readInt(content);
1174        if (elements == BinaryOutputCapsule.NULL_OBJECT) {
1175            return null;
1176        }
1177        final ID[] rVal = new ID[elements];
1178        for (int x = 0; x < elements; x++) {
1179            rVal[x] = readSavable(content);
1180        }
1181        return rVal;
1182    }
1183
1184    protected ID[][] readSavableArray2D(final byte[] content) throws IOException {
1185        final int elements = readInt(content);
1186        if (elements == BinaryOutputCapsule.NULL_OBJECT) {
1187            return null;
1188        }
1189        final ID[][] rVal = new ID[elements][];
1190        for (int x = 0; x < elements; x++) {
1191            rVal[x] = readSavableArray(content);
1192        }
1193        return rVal;
1194    }
1195
1196    protected ID[][][] readSavableArray3D(final byte[] content) throws IOException {
1197        final int elements = readInt(content);
1198        if (elements == BinaryOutputCapsule.NULL_OBJECT) {
1199            return null;
1200        }
1201        final ID[][][] rVal = new ID[elements][][];
1202        for (int x = 0; x < elements; x++) {
1203            rVal[x] = readSavableArray2D(content);
1204        }
1205        return rVal;
1206    }
1207
1208    // BinarySavable map
1209
1210    protected ID[][] readSavableMap(final byte[] content) throws IOException {
1211        final int elements = readInt(content);
1212        if (elements == BinaryOutputCapsule.NULL_OBJECT) {
1213            return null;
1214        }
1215        final ID[][] rVal = new ID[elements][];
1216        for (int x = 0; x < elements; x++) {
1217            rVal[x] = readSavableArray(content);
1218        }
1219        return rVal;
1220    }
1221
1222    protected StringIDMap readStringSavableMap(final byte[] content) throws IOException {
1223        final int elements = readInt(content);
1224        if (elements == BinaryOutputCapsule.NULL_OBJECT) {
1225            return null;
1226        }
1227        final String[] keys = readStringArray(content);
1228        final ID[] values = readSavableArray(content);
1229        final StringIDMap rVal = new StringIDMap();
1230        rVal.keys = keys;
1231        rVal.values = values;
1232        return rVal;
1233    }
1234
1235    // ArrayList<FloatBuffer>
1236
1237    protected List<FloatBuffer> readFloatBufferArrayList(final byte[] content) throws IOException {
1238        final int length = readInt(content);
1239        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1240            return null;
1241        }
1242        final List<FloatBuffer> rVal = new ArrayList<>(length);
1243        for (int x = 0; x < length; x++) {
1244            rVal.add(readFloatBuffer(content));
1245        }
1246        return rVal;
1247    }
1248
1249    // ArrayList<ByteBuffer>
1250
1251    protected List<ByteBuffer> readByteBufferArrayList(final byte[] content) throws IOException {
1252        final int length = readInt(content);
1253        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1254            return null;
1255        }
1256        final List<ByteBuffer> rVal = new ArrayList<>(length);
1257        for (int x = 0; x < length; x++) {
1258            rVal.add(readByteBuffer(content));
1259        }
1260        return rVal;
1261    }
1262
1263    // NIO BUFFERS
1264
1265    // float buffer
1266    protected FloatBuffer readFloatBuffer(final byte[] content) throws IOException {
1267        final int length = readInt(content);
1268        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1269            return null;
1270        }
1271
1272        final boolean direct = readBoolean(content);
1273
1274        // Pull data in as a little endian byte buffer.
1275        final ByteBuffer buf = ByteBuffer.allocateDirect(length * 4).order(ByteOrder.LITTLE_ENDIAN);
1276        buf.put(content, _index, length * 4).rewind();
1277
1278        // increment index
1279        _index += length * 4;
1280
1281        // Convert to float buffer.
1282        final FloatBuffer value;
1283        final boolean contentCopyRequired;
1284        if (direct) {
1285            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
1286                value = buf.asFloatBuffer();
1287                contentCopyRequired = false;
1288            } else {
1289                value = BufferUtils.createFloatBuffer(length);
1290                contentCopyRequired = true;
1291            }
1292        } else {
1293            value = BufferUtils.createFloatBufferOnHeap(length);
1294            contentCopyRequired = true;
1295        }
1296        if (contentCopyRequired) {
1297            value.put(buf.asFloatBuffer());
1298            value.rewind();
1299        }
1300
1301        return value;
1302    }
1303
1304    // int buffer
1305    protected IntBuffer readIntBuffer(final byte[] content) throws IOException {
1306        final int length = readInt(content);
1307        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1308            return null;
1309        }
1310
1311        final boolean direct = readBoolean(content);
1312
1313        // Pull data in as a little endian byte buffer.
1314        final ByteBuffer buf = ByteBuffer.allocateDirect(length * 4).order(ByteOrder.LITTLE_ENDIAN);
1315        buf.put(content, _index, length * 4).rewind();
1316
1317        // increment index
1318        _index += length * 4;
1319
1320        // Convert to int buffer.
1321        final IntBuffer value;
1322        final boolean contentCopyRequired;
1323        if (direct) {
1324            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
1325                value = buf.asIntBuffer();
1326                contentCopyRequired = false;
1327            } else {
1328                value = BufferUtils.createIntBuffer(length);
1329                contentCopyRequired = true;
1330            }
1331        } else {
1332            value = BufferUtils.createIntBufferOnHeap(length);
1333            contentCopyRequired = true;
1334        }
1335        if (contentCopyRequired) {
1336            value.put(buf.asIntBuffer());
1337            value.rewind();
1338        }
1339        return value;
1340    }
1341
1342    // short buffer
1343    protected ShortBuffer readShortBuffer(final byte[] content) throws IOException {
1344        final int length = readInt(content);
1345        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1346            return null;
1347        }
1348
1349        final boolean direct = readBoolean(content);
1350
1351        // Pull data in as a little endian byte buffer.
1352        final ByteBuffer buf = ByteBuffer.allocateDirect(length * 2).order(ByteOrder.LITTLE_ENDIAN);
1353        buf.put(content, _index, length * 2).rewind();
1354
1355        // increment index
1356        _index += length * 2;
1357
1358        // Convert to short buffer.
1359        final ShortBuffer value;
1360        final boolean contentCopyRequired;
1361        if (direct) {
1362            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
1363                value = buf.asShortBuffer();
1364                contentCopyRequired = false;
1365            } else {
1366                value = BufferUtils.createShortBuffer(length);
1367                contentCopyRequired = true;
1368            }
1369        } else {
1370            value = BufferUtils.createShortBufferOnHeap(length);
1371            contentCopyRequired = true;
1372        }
1373        if (contentCopyRequired) {
1374            value.put(buf.asShortBuffer());
1375            value.rewind();
1376        }
1377        return value;
1378    }
1379
1380    // byte buffer
1381    protected ByteBuffer readByteBuffer(final byte[] content) throws IOException {
1382        final int length = readInt(content);
1383        if (length == BinaryOutputCapsule.NULL_OBJECT) {
1384            return null;
1385        }
1386
1387        final boolean direct = readBoolean(content);
1388
1389        // Pull data in as a little endian byte buffer.
1390        final ByteBuffer buf = ByteBuffer.allocateDirect(length).order(ByteOrder.LITTLE_ENDIAN);
1391        buf.put(content, _index, length).rewind();
1392
1393        // increment index
1394        _index += length;
1395
1396        // Convert to platform endian buffer.
1397        final ByteBuffer value;
1398        final boolean contentCopyRequired;
1399        if (direct) {
1400            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
1401                value = buf;
1402                contentCopyRequired = false;
1403            } else {
1404                value = BufferUtils.createByteBuffer(length);
1405                contentCopyRequired = true;
1406            }
1407        } else {
1408            value = BufferUtils.createByteBufferOnHeap(length);
1409            contentCopyRequired = true;
1410        }
1411        if (contentCopyRequired) {
1412            value.put(buf);
1413            value.rewind();
1414        }
1415        return value;
1416    }
1417
1418    static private class ID {
1419        public int id;
1420
1421        public ID(final int id) {
1422            this.id = id;
1423        }
1424    }
1425
1426    static private class StringIDMap {
1427        public String[] keys;
1428        public ID[] values;
1429    }
1430
1431    @Override
1432    public <T extends Enum<T>> T readEnum(final String name, final Class<T> enumType, final T defVal)
1433            throws IOException {
1434        final String eVal = readString(name, defVal != null ? defVal.name() : null);
1435        if (eVal != null) {
1436            return Enum.valueOf(enumType, eVal);
1437        } else {
1438            return null;
1439        }
1440    }
1441
1442    @Override
1443    @SuppressWarnings("unchecked")
1444    public <T extends Enum<T>> T[] readEnumArray(final String name, final Class<T> enumType, final T[] defVal)
1445            throws IOException {
1446        final String[] eVals = readStringArray(name, null);
1447        if (eVals != null) {
1448            final T[] rVal = (T[]) Array.newInstance(enumType, eVals.length);
1449            int i = 0;
1450            for (final String eVal : eVals) {
1451                rVal[i++] = Enum.valueOf(enumType, eVal);
1452            }
1453            return rVal;
1454        } else {
1455            return defVal;
1456        }
1457    }
1458}