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}