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

net.razorvine.pickle.objects.ArrayConstructor Maven / Gradle / Ivy

package net.razorvine.pickle.objects;

import java.util.ArrayList;
import net.razorvine.pickle.IObjectConstructor;
import net.razorvine.pickle.PickleException;
import net.razorvine.pickle.PickleUtils;

/**
 * Creates arrays of objects. Returns a primitive type array such as int[] if
 * the objects are ints, etc. Returns an ArrayList if it needs to
 * contain arbitrary objects (such as lists).
 * 
 * @author Irmen de Jong ([email protected])
 */
public class ArrayConstructor implements IObjectConstructor {

	public Object construct(Object[] args) throws PickleException {
		// args for array constructor: [ String typecode, ArrayList values ]
		// or: [ constructor_class, typecode, machinecode_type, byte[] ] 
		if (args.length == 4) {
			ArrayConstructor constructor = (ArrayConstructor) args[0];
			char typecode = ((String) args[1]).charAt(0);
			int machinecodeType = (Integer) args[2];
			byte[] data = (byte[]) args[3];
			return constructor.construct(typecode, machinecodeType, data);
		}
		if (args.length != 2) {
			throw new PickleException("invalid pickle data for array; expected 2 args, got " + args.length);
		}

		String typecode = (String) args[0];
		@SuppressWarnings("unchecked")
		ArrayList values = (ArrayList) args[1];

		switch (typecode.charAt(0)) {
		case 'c':// character 1 -> char[]
		case 'u':// Unicode character 2 -> char[]
		{
			char[] result = new char[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((String) c).charAt(0);
			}
			return result;
		}
		case 'b':// signed integer 1 -> byte[]
		{
			byte[] result = new byte[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).byteValue();
			}
			return result;
		}
		case 'B':// unsigned integer 1 -> short[]
		case 'h':// signed integer 2 -> short[]
		{
			short[] result = new short[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).shortValue();
			}
			return result;
		}
		case 'H':// unsigned integer 2 -> int[]
		case 'i':// signed integer 2 -> int[]
		case 'l':// signed integer 4 -> int[]
		{
			int[] result = new int[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).intValue();
			}
			return result;
		}
		case 'I':// unsigned integer 4 -> long[]
		case 'L':// unsigned integer 4 -> long[]
		{
			long[] result = new long[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).longValue();
			}
			return result;
		}
		case 'f':// floating point 4 -> float[]
		{
			float[] result = new float[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).floatValue();
			}
			return result;
		}
		case 'd':// floating point 8 -> double[]
		{
			double[] result = new double[values.size()];
			int i = 0;
			for (Object c : values) {
				result[i++] = ((Number) c).doubleValue();
			}
			return result;
		}
		default:
			throw new PickleException("invalid array typecode: " + typecode);
		}
	}

	/**
	 * Create an object based on machine code type
	 */
	public Object construct(char typecode, int machinecode, byte[] data) throws PickleException {
		// Machine format codes.
		// Search for "enum machine_format_code" in Modules/arraymodule.c to get
		// the authoritative values.
		// UNKNOWN_FORMAT = -1
		// UNSIGNED_INT8 = 0
		// SIGNED_INT8 = 1
		// UNSIGNED_INT16_LE = 2
		// UNSIGNED_INT16_BE = 3
		// SIGNED_INT16_LE = 4
		// SIGNED_INT16_BE = 5
		// UNSIGNED_INT32_LE = 6
		// UNSIGNED_INT32_BE = 7
		// SIGNED_INT32_LE = 8
		// SIGNED_INT32_BE = 9
		// UNSIGNED_INT64_LE = 10
		// UNSIGNED_INT64_BE = 11
		// SIGNED_INT64_LE = 12
		// SIGNED_INT64_BE = 13
		// IEEE_754_FLOAT_LE = 14
		// IEEE_754_FLOAT_BE = 15
		// IEEE_754_DOUBLE_LE = 16
		// IEEE_754_DOUBLE_BE = 17
		// UTF16_LE = 18
		// UTF16_BE = 19
		// UTF32_LE = 20
		// UTF32_BE = 21

		if (machinecode < 0)
			throw new PickleException("unknown machine type format");

		switch (typecode) {
		case 'c':// character 1 -> char[]
		case 'u':// Unicode character 2 -> char[]
		{
			if (machinecode != 18 && machinecode != 19 && machinecode != 20 && machinecode != 21)
				throw new PickleException("for c/u type must be 18/19/20/21");
			if (machinecode == 18 || machinecode == 19) {
				// utf-16 , 2 bytes
				if (data.length % 2 != 0)
					throw new PickleException("data size alignment error");
				return constructCharArrayUTF16(machinecode, data);
			} else {
				// utf-32, 4 bytes
				if (data.length % 4 != 0)
					throw new PickleException("data size alignment error");
				return constructCharArrayUTF32(machinecode, data);
			}
		}
		case 'b':// signed integer 1 -> byte[]
		{
			if (machinecode != 1)
				throw new PickleException("for b type must be 1");
			return data;
		}
		case 'B':// unsigned integer 1 -> short[]
		{
			if (machinecode != 0)
				throw new PickleException("for B type must be 0");
			return constructShortArrayFromUByte(data);
		}
		case 'h':// signed integer 2 -> short[]
		{
			if (machinecode != 4 && machinecode != 5)
				throw new PickleException("for h type must be 4/5");
			if (data.length % 2 != 0)
				throw new PickleException("data size alignment error");
			return constructShortArraySigned(machinecode, data);
		}
		case 'H':// unsigned integer 2 -> int[]
		{
			if (machinecode != 2 && machinecode != 3)
				throw new PickleException("for H type must be 2/3");
			if (data.length % 2 != 0)
				throw new PickleException("data size alignment error");
			return constructIntArrayFromUShort(machinecode, data);
		}
		case 'i':// signed integer 4 -> int[]
		{
			if (machinecode != 8 && machinecode != 9)
				throw new PickleException("for i type must be 8/9");
			if (data.length % 4 != 0)
				throw new PickleException("data size alignment error");
			return constructIntArrayFromInt32(machinecode, data);
		}
		case 'l':// signed integer 4/8 -> int[]
		{
			if (machinecode != 8 && machinecode != 9 && machinecode != 12 && machinecode != 13)
				throw new PickleException("for l type must be 8/9/12/13");
			if ((machinecode==8 || machinecode==9) && (data.length % 4 != 0))
				throw new PickleException("data size alignment error");
			if ((machinecode==12 || machinecode==13) && (data.length % 8 != 0))
				throw new PickleException("data size alignment error");
			if(machinecode==8 || machinecode==9) {
				//32 bits
				return constructIntArrayFromInt32(machinecode, data);
			} else {
				//64 bits
				return constructLongArrayFromInt64(machinecode, data);
			}
		}
		case 'I':// unsigned integer 4 -> long[]
		{
			if (machinecode != 6 && machinecode != 7)
				throw new PickleException("for I type must be 6/7");
			if (data.length % 4 != 0)
				throw new PickleException("data size alignment error");
			return constructLongArrayFromUInt32(machinecode, data);
		}
		case 'L':// unsigned integer 4/8 -> long[]
		{
			if (machinecode != 6 && machinecode != 7 && machinecode != 10 && machinecode != 11)
				throw new PickleException("for L type must be 6/7/10/11");
			if ((machinecode==6 || machinecode==7) && (data.length % 4 != 0))
				throw new PickleException("data size alignment error");
			if ((machinecode==10 || machinecode==11) && (data.length % 8 != 0))
				throw new PickleException("data size alignment error");
			if(machinecode==6 || machinecode==7) {
				// 32 bits
				return constructLongArrayFromUInt32(machinecode, data);
			} else {
				// 64 bits
				return constructLongArrayFromUInt64(machinecode, data);
			}
		}
		case 'f':// floating point 4 -> float[]
		{
			if (machinecode != 14 && machinecode != 15)
				throw new PickleException("for f type must be 14/15");
			if (data.length % 4 != 0)
				throw new PickleException("data size alignment error");
			return constructFloatArray(machinecode, data);
		}
		case 'd':// floating point 8 -> double[]
		{
			if (machinecode != 16 && machinecode != 17)
				throw new PickleException("for d type must be 16/17");
			if (data.length % 8 != 0)
				throw new PickleException("data size alignment error");
			return constructDoubleArray(machinecode, data);
		}
		default:
			throw new PickleException("invalid array typecode: " + typecode);
		}
	}

	protected int[] constructIntArrayFromInt32(int machinecode, byte[] data) {
		int[] result=new int[data.length/4];
		byte[] bigendian=new byte[4];
		for(int i=0; i1)
					throw new PickleException("cannot process UTF-32 character codepoint "+codepoint);
				result[index] = cc[0];
			}
			else {
				// big endian, swap
				bigendian[0]=data[3+index*4];
				bigendian[1]=data[2+index*4];
				bigendian[2]=data[1+index*4];
				bigendian[3]=data[index*4];
				int codepoint=PickleUtils.bytes_to_integer(bigendian);
				char[] cc=Character.toChars(codepoint);
				if(cc.length>1)
					throw new PickleException("cannot process UTF-32 character codepoint "+codepoint);
				result[index] = cc[0];
			}
		}
		return result;
	}

	protected char[] constructCharArrayUTF16(int machinecode, byte[] data) {
		char[] result = new char[data.length / 2];
		byte[] bigendian=new byte[2];
		for (int index = 0; index < data.length / 2; ++index) {
			if (machinecode == 18) { 
				result[index] = (char) PickleUtils.bytes_to_integer(data, index*2, 2);
			}
			else {
				// big endian, swap
				bigendian[0]=data[1+index*2];
				bigendian[1]=data[0+index*2];
				result[index] = (char) PickleUtils.bytes_to_integer(bigendian);
			}
		}
		return result;
	}
}