All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.joda.beans.ser.bin.AbstractBinReader Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2001-present Stephen Colebourne
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.joda.beans.ser.bin;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.MetaBean;
import org.joda.beans.MetaProperty;
import org.joda.beans.ser.JodaBeanSer;
import org.joda.beans.ser.SerCategory;
import org.joda.beans.ser.SerDeserializer;
import org.joda.beans.ser.SerIterable;
import org.joda.beans.ser.SerOptional;
import org.joda.beans.ser.SerTypeMapper;

/**
 * Provides the ability for a Joda-Bean to read from both the standard and referencing binary formats.
 * 

* This class contains mutable state and cannot be used from multiple threads. * A new instance must be created for each message. */ abstract class AbstractBinReader extends MsgPack { /** * Settings. */ final JodaBeanSer settings; // CSIGNORE /** * The reader. */ final DataInputStream input; // CSIGNORE /** * The base package including the trailing dot. */ private String basePackage; /** * The known types. */ private Map> knownTypes = new HashMap<>(); //----------------------------------------------------------------------- // creates an instance AbstractBinReader(JodaBeanSer settings, DataInputStream input) { this.settings = settings; this.input = input; } //----------------------------------------------------------------------- // reads the input stream where the array and version bytes have been read already T read(Class rootType) { try { try { return parseRemaining(rootType); } finally { input.close(); } } catch (RuntimeException ex) { throw ex; } catch (Exception ex) { throw new RuntimeException(ex); } } T parseRemaining(Class declaredType) throws Exception { // the array and version has already been read Object parsed = parseObject(declaredType, null, null, null, true); return declaredType.cast(parsed); } Object parseBean(int propertyCount, Class beanType) { String propName = ""; try { SerDeserializer deser = settings.getDeserializers().findDeserializer(beanType); MetaBean metaBean = deser.findMetaBean(beanType); BeanBuilder builder = deser.createBuilder(beanType, metaBean); for (int i = 0; i < propertyCount; i++) { // property name propName = acceptPropertyName(); MetaProperty metaProp = deser.findMetaProperty(beanType, metaBean, propName); if (metaProp == null || metaProp.style().isDerived()) { MsgPackInput.skipObject(input); } else { Object value = parseObject(SerOptional.extractType(metaProp, beanType), metaProp, beanType, null, false); deser.setValue(builder, metaProp, SerOptional.wrapValue(metaProp, beanType, value)); } propName = ""; } return deser.build(beanType, builder); } catch (Exception ex) { throw new RuntimeException("Error parsing bean: " + beanType.getName() + "::" + propName + ", " + ex.getMessage(), ex); } } String acceptPropertyName() throws IOException { byte typeByte = input.readByte(); return acceptString(typeByte); } Object parseObject(Class declaredType, MetaProperty metaProp, Class beanType, SerIterable parentIterable, boolean rootType) throws Exception { // establish type Class effectiveType = declaredType; String metaType = null; int typeByte = input.readByte(); if (isMap(typeByte)) { input.mark(8); int mapSize = acceptMap(typeByte); if (mapSize > 0) { int typeByteTemp = input.readByte(); if (typeByteTemp == EXT_8) { int size = input.readUnsignedByte(); typeByteTemp = input.readByte(); if (typeByteTemp == JODA_TYPE_BEAN) { String typeStr = acceptStringBytes(size); effectiveType = SerTypeMapper.decodeType(typeStr, settings, basePackage, knownTypes); return parseBean(declaredType, rootType, effectiveType, mapSize); } else if (typeByteTemp == JODA_TYPE_DATA) { if (mapSize != 1) { throw new IllegalArgumentException("Invalid binary data: Expected map size 1, but was: " + mapSize); } String typeStr = acceptStringBytes(size); effectiveType = settings.getDeserializers().decodeType(typeStr, settings, basePackage, knownTypes, declaredType); if (declaredType.isAssignableFrom(effectiveType) == false) { throw new IllegalArgumentException("Specified type is incompatible with declared type: " + declaredType.getName() + " and " + effectiveType.getName()); } typeByte = input.readByte(); } else if (typeByteTemp == JODA_TYPE_META) { if (mapSize != 1) { throw new IllegalArgumentException("Invalid binary data: Expected map size 1, but was: " + mapSize); } metaType = acceptStringBytes(size); typeByte = input.readByte(); } else { input.reset(); } } else { input.reset(); } } else { input.reset(); } } // parse based on type if (typeByte == NIL) { return null; } if (Bean.class.isAssignableFrom(effectiveType)) { if (isMap(typeByte)) { int mapSize = acceptMap(typeByte); return parseBean(mapSize, effectiveType); } else { return parseSimple(typeByte, effectiveType); } } else { if (isMap(typeByte) || isArray(typeByte)) { SerIterable childIterable = null; if (metaType != null) { childIterable = settings.getIteratorFactory().createIterable(metaType, settings, knownTypes); } else if (metaProp != null) { childIterable = settings.getIteratorFactory().createIterable(metaProp, beanType); } else if (parentIterable != null) { childIterable = settings.getIteratorFactory().createIterable(parentIterable); } if (childIterable == null) { throw new IllegalArgumentException("Invalid binary data: Invalid metaType: " + metaType); } return parseIterable(typeByte, childIterable); } else { return parseSimple(typeByte, effectiveType); } } } Object parseBean(Class declaredType, boolean rootType, Class effectiveType, int mapSize) throws Exception { if (rootType) { if (Bean.class.isAssignableFrom(effectiveType) == false) { throw new IllegalArgumentException("Root type is not a Joda-Bean: " + effectiveType.getName()); } basePackage = effectiveType.getPackage().getName() + "."; } if (declaredType.isAssignableFrom(effectiveType) == false) { throw new IllegalArgumentException("Specified type is incompatible with declared type: " + declaredType.getName() + " and " + effectiveType.getName()); } if (input.readByte() != NIL) { throw new IllegalArgumentException("Invalid binary data: Expected null after bean type"); } return parseBean(mapSize - 1, effectiveType); } Object parseIterable(int typeByte, SerIterable iterable) throws Exception { if (iterable.category() == SerCategory.MAP) { return parseIterableMap(typeByte, iterable); } else if (iterable.category() == SerCategory.COUNTED) { return parseIterableCounted(typeByte, iterable); } else if (iterable.category() == SerCategory.TABLE) { return parseIterableTable(typeByte, iterable); } else if (iterable.category() == SerCategory.GRID) { return parseIterableGrid(typeByte, iterable); } else { return parseIterableArray(typeByte, iterable); } } Object parseIterableMap(int typeByte, SerIterable iterable) throws Exception { int size = acceptMap(typeByte); for (int i = 0; i < size; i++) { Object key = parseObject(iterable.keyType(), null, null, null, false); Object value = parseObject(iterable.valueType(), null, null, iterable, false); iterable.add(key, null, value, 1); } return iterable.build(); } Object parseIterableTable(int typeByte, SerIterable iterable) throws Exception { int size = acceptArray(typeByte); for (int i = 0; i < size; i++) { if (acceptArray(input.readByte()) != 3) { throw new IllegalArgumentException("Table must have cell array size 3"); } Object key = parseObject(iterable.keyType(), null, null, null, false); Object column = parseObject(iterable.columnType(), null, null, null, false); Object value = parseObject(iterable.valueType(), null, null, iterable, false); iterable.add(key, column, value, 1); } return iterable.build(); } Object parseIterableGrid(int typeByte, SerIterable iterable) throws Exception { int size = acceptArray(typeByte); int rows = acceptInteger(input.readByte()); int columns = acceptInteger(input.readByte()); iterable.dimensions(new int[]{rows, columns}); if ((rows * columns) != (size - 2)) { // sparse for (int i = 0; i < (size - 2); i++) { if (acceptArray(input.readByte()) != 3) { throw new IllegalArgumentException("Grid must have cell array size 3"); } int row = acceptInteger(input.readByte()); int column = acceptInteger(input.readByte()); Object value = parseObject(iterable.valueType(), null, null, iterable, false); iterable.add(row, column, value, 1); } } else { // dense for (int row = 0; row < rows; row++) { for (int column = 0; column < columns; column++) { Object value = parseObject(iterable.valueType(), null, null, iterable, false); iterable.add(row, column, value, 1); } } } return iterable.build(); } Object parseIterableCounted(int typeByte, SerIterable iterable) throws Exception { int size = acceptMap(typeByte); for (int i = 0; i < size; i++) { Object value = parseObject(iterable.valueType(), null, null, iterable, false); int count = acceptInteger(input.readByte()); iterable.add(null, null, value, count); } return iterable.build(); } Object parseIterableArray(int typeByte, SerIterable iterable) throws Exception { int size = acceptArray(typeByte); for (int i = 0; i < size; i++) { iterable.add(null, null, parseObject(iterable.valueType(), null, null, iterable, false), 1); } return iterable.build(); } Object parseSimple(int typeByte, Class type) throws Exception { if (isString(typeByte)) { String text = acceptString(typeByte); if (type == String.class || type == Object.class) { return text; } return settings.getConverter().convertFromString(type, text); } if (isIntegral(typeByte)) { long value = acceptLong(typeByte); if (type == Long.class || type == long.class) { return Long.valueOf(value); } else if (type == Short.class || type == short.class) { if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { throw new IllegalArgumentException("Invalid binary data: Expected byte, but was " + value); } return Short.valueOf((short) value); } else if (type == Byte.class || type == byte.class) { if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { throw new IllegalArgumentException("Invalid binary data: Expected byte, but was " + value); } return Byte.valueOf((byte) value); } else if (type == Double.class || type == double.class) { // handle case where property type has changed from integral to double return Double.valueOf((double) value); } else if (type == Float.class || type == float.class) { // handle case where property type has changed from integral to float return Float.valueOf((float) value); } else if (type == Integer.class || type == int.class) { if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { throw new IllegalArgumentException("Invalid binary data: Expected int, but was " + value); } return Integer.valueOf((int) value); } else { if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) { return Long.valueOf(value); } return Integer.valueOf((int) value); } } switch (typeByte) { case TRUE: return Boolean.TRUE; case FALSE: return Boolean.FALSE; case FLOAT_32: return Float.valueOf(input.readFloat()); case FLOAT_64: return Double.valueOf(input.readDouble()); case BIN_8: case BIN_16: case BIN_32: return acceptBinary(typeByte); } throw new IllegalArgumentException("Invalid binary data: Expected " + type.getName() + ", but was: 0x" + toHex(typeByte)); } //----------------------------------------------------------------------- int acceptMap(int typeByte) throws IOException { int size; if (typeByte >= MIN_FIX_MAP && typeByte <= MAX_FIX_MAP) { size = (typeByte - MIN_FIX_MAP); } else if (typeByte == MAP_16) { size = input.readUnsignedShort(); } else if (typeByte == MAP_32) { size = input.readInt(); if (size < 0) { throw new IllegalArgumentException("Invalid binary data: Map too large"); } } else { throw new IllegalArgumentException("Invalid binary data: Expected map, but was: 0x" + toHex(typeByte)); } return size; } int acceptArray(int typeByte) throws IOException { int size; if (typeByte >= MIN_FIX_ARRAY && typeByte <= MAX_FIX_ARRAY) { size = (typeByte - MIN_FIX_ARRAY); } else if (typeByte == ARRAY_16) { size = input.readUnsignedShort(); } else if (typeByte == ARRAY_32) { size = input.readInt(); if (size < 0) { throw new IllegalArgumentException("Invalid binary data: Array too large"); } } else { throw new IllegalArgumentException("Invalid binary data: Expected array, but was: 0x" + toHex(typeByte)); } return size; } String acceptString(int typeByte) throws IOException { int size; if (typeByte >= MIN_FIX_STR && typeByte <= MAX_FIX_STR) { size = (typeByte - MIN_FIX_STR); } else if (typeByte == STR_8) { size = input.readUnsignedByte(); } else if (typeByte == STR_16) { size = input.readUnsignedShort(); } else if (typeByte == STR_32) { size = input.readInt(); if (size < 0) { throw new IllegalArgumentException("Invalid binary data: String too large"); } } else { throw new IllegalArgumentException("Invalid binary data: Expected string, but was: 0x" + toHex(typeByte)); } return acceptStringBytes(size); } String acceptStringBytes(int size) throws IOException { byte[] bytes = new byte[size]; input.readFully(bytes); // inline common ASCII case for much better performance char[] chars = new char[size]; for (int i = 0; i < size; i++) { byte b = bytes[i]; if (b >= 0) { chars[i] = (char) b; } else { return new String(bytes, UTF_8); } } return new String(chars); } byte[] acceptBinary(int typeByte) throws IOException { int size; if (typeByte == BIN_8) { size = input.readUnsignedByte(); } else if (typeByte == BIN_16) { size = input.readUnsignedShort(); } else if (typeByte == BIN_32) { size = input.readInt(); if (size < 0) { throw new IllegalArgumentException("Invalid binary data: Binary too large"); } } else { throw new IllegalArgumentException("Invalid binary data: Expected binary, but was: 0x" + toHex(typeByte)); } byte[] bytes = new byte[size]; input.readFully(bytes); return bytes; } int acceptInteger(int typeByte) throws IOException { if (typeByte >= MIN_FIX_INT && typeByte <= MAX_FIX_INT) { return typeByte; } switch (typeByte) { case UINT_8: return input.readUnsignedByte(); case UINT_16: return input.readUnsignedShort(); case UINT_32: { int val = input.readInt(); if (val < 0) { throw new IllegalArgumentException("Invalid binary data: Expected int, but was large unsigned int"); } return val; } case UINT_64: { long val = input.readLong(); if (val < 0 || val > Integer.MAX_VALUE) { throw new IllegalArgumentException("Invalid binary data: Expected int, but was large unsigned int"); } return (int) val; } case SINT_8: return input.readByte(); case SINT_16: return input.readShort(); case SINT_32: return input.readInt(); case SINT_64: { long val = input.readLong(); if (val < Integer.MIN_VALUE || val > Integer.MAX_VALUE) { throw new IllegalArgumentException("Invalid binary data: Expected int, but was large signed int"); } return (int) val; } } throw new IllegalArgumentException("Invalid binary data: Expected int, but was: 0x" + toHex(typeByte)); } long acceptLong(int typeByte) throws IOException { if (typeByte >= MIN_FIX_INT && typeByte <= MAX_FIX_INT) { return typeByte; } switch (typeByte) { case UINT_8: return input.readUnsignedByte(); case UINT_16: return input.readUnsignedShort(); case UINT_32: { return ((long) input.readInt()) & 0xFFFFFFFFL; } case UINT_64: { long val = input.readLong(); if (val < 0) { throw new IllegalArgumentException("Invalid binary data: Expected long, but was large unsigned int"); } return val; } case SINT_8: return input.readByte(); case SINT_16: return input.readShort(); case SINT_32: return input.readInt(); case SINT_64: { return input.readLong(); } } throw new IllegalArgumentException("Invalid binary data: Expected long, but was: 0x" + toHex(typeByte)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy