com.datatorrent.lib.appdata.gpo.GPOUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.datatorrent.lib.appdata.gpo.GPOUtils.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.datatorrent.lib.appdata.gpo;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

import org.apache.commons.lang3.mutable.MutableInt;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import com.datatorrent.lib.appdata.schemas.Fields;
import com.datatorrent.lib.appdata.schemas.FieldsDescriptor;
import com.datatorrent.lib.appdata.schemas.ResultFormatter;
import com.datatorrent.lib.appdata.schemas.Type;
import com.datatorrent.lib.util.PojoUtils;
import com.datatorrent.lib.util.PojoUtils.Getter;
import com.datatorrent.lib.util.PojoUtils.GetterBoolean;
import com.datatorrent.lib.util.PojoUtils.GetterByte;
import com.datatorrent.lib.util.PojoUtils.GetterChar;
import com.datatorrent.lib.util.PojoUtils.GetterDouble;
import com.datatorrent.lib.util.PojoUtils.GetterFloat;
import com.datatorrent.lib.util.PojoUtils.GetterInt;
import com.datatorrent.lib.util.PojoUtils.GetterLong;
import com.datatorrent.lib.util.PojoUtils.GetterShort;

/**
 * This class holds utility methods for serializing and deserializing {@link GPOMutable} objects to/from bytes and JSON.
 * There are also utility methods for converting POJOs into GPOMutable objects.
 * @since 3.0.0
 */
public class GPOUtils {
    /**
     * This class should not be instantiated
     */
    private GPOUtils() {
        //Do nothing
    }

    /**
     * This utility method converts a field to type map specified in JSON into
     * a java map from field to type. An example of a JSON field to type map
     * is the following:
     * <br/>
     * <br/>
     * <pre>
     * {@code
     * {
     *  "fieldName1":"integer",
     *  "fieldName2":"string",
     *  "fieldName3":"byte",
     *  ...
     * }
     * }
     * </pre>
     * @param jo The {@link JSONObject} containing the JSON to convert.
     * @return A java Map from field name to the corresponding field type.
     * @throws JSONException
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Type> buildTypeMap(JSONObject jo) throws JSONException {
        Map<String, Type> fieldToType = Maps.newHashMap();
        for (Iterator<String> keys = (Iterator<String>) jo.keys(); keys.hasNext();) {
            String key = keys.next();
            String val = jo.getString(key);
            Type type = Type.getTypeEx(val);
            fieldToType.put(key, type);
        }

        return fieldToType;
    }

    /**
     * Converts the provided JSON into a GPOMutable object with the provided {@link FieldsDescriptor}
     * @param fieldsDescriptor The {@link FieldsDescriptor} to initialize the {@link GPOMutable} object with.
     * @param dpou The JSONObject to deserialize from.
     * @return The deserialized GPOMutable object.
     */
    public static GPOMutable deserialize(FieldsDescriptor fieldsDescriptor, JSONObject dpou) {
        GPOMutable gpo = new GPOMutable(fieldsDescriptor);
        @SuppressWarnings("unchecked")
        Iterator<String> itr = (Iterator<String>) dpou.keys();

        while (itr.hasNext()) {
            String field = itr.next();
            setFieldFromJSON(gpo, field, dpou);
        }

        return gpo;
    }

    /**
     * This method deserializes the fields in the given {@link FieldsDescriptor} into a map.
     * @param fieldsDescriptor The {@link FieldsDescriptor} to fetch fields from.
     * @param dpou The {@link JSONObject} which contains the fields whose values need to be fetched.
     * @return A {@link Map} whose keys are field names, and whose values are possible values for those fields.
     */
    public static Map<String, Set<Object>> deserializeToMap(FieldsDescriptor fieldsDescriptor, JSONObject dpou) {
        Map<String, Set<Object>> keyToValues = Maps.newHashMap();

        for (String key : fieldsDescriptor.getFields().getFields()) {
            if (!dpou.has(key)) {
                throw new IllegalArgumentException("The given key " + key + " is not contained in the given JSON");
            }

            Set<Object> keyValues;
            Object keyValue;

            try {
                keyValue = dpou.get(key);
            } catch (JSONException ex) {
                throw new IllegalStateException("This should never happen", ex);
            }

            if (keyValue instanceof JSONArray) {

                JSONArray ja = (JSONArray) keyValue;
                keyValues = Sets.newHashSetWithExpectedSize(ja.length());

                Type type = fieldsDescriptor.getType(key);

                for (int index = 0; index < ja.length(); index++) {
                    keyValues.add(getFieldFromJSON(type, ja, index));
                }

            } else if (keyValue instanceof JSONObject) {
                throw new UnsupportedOperationException("Cannot extract objects from JSONObjects");
            } else {
                keyValues = Sets.newHashSetWithExpectedSize(1);
                keyValues.add(getFieldFromJSON(fieldsDescriptor, key, dpou));
            }

            keyToValues.put(key, keyValues);
        }

        return keyToValues;
    }

    /**
     * This is a helper method for deserialization of a GPOMutable from JSON. It allows you to select a field from
     * a JSONObject in a json array and set it on the provided GPOMutable object. The format of the JSONArray should
     * be the following:
     * <br/>
     * <br/>
     * <pre>
     * {@code
     * [
     *  {
     *    "fieldName1":"val1",
     *    "fieldName2":"val2",
     *    ...
     *  },
     *  {
     *    "fieldName1":"valx",
     *    ...
     *  }
     *  ...
     * ]
     * }
     * </pre>
     * <br/>
     * <br/>
     * @param gpo The {@link GPOMutable} to set fields on.
     * @param type The type of the field that will be set.
     * @param field The name of the field that will be set.
     * The name of the field must be the same in both the {@link GPOMutable}'s {@link FieldsDescriptor} object and in the JSONObject.
     * @param jo The JSONOArray holding JSONObjects to deserialize from.
     * @param index The index of the JSONArray
     */
    public static void setFieldFromJSON(GPOMutable gpo, Type type, String field, JSONArray jo, int index) {
        GPOType gpoType = GPOType.GPO_TYPE_ARRAY[type.ordinal()];
        gpoType.setFieldFromJSON(gpo, field, jo, index);
    }

    /**
     * This is a utility method to deserialize data from the given JSONObject into a {@link GPOMutable} object.
     * The format of the JSON to deserialize from should look like this.
     * <pre>
     *  {@code
     *  {
     *    "fieldName1":"val1",
     *    "fieldName2":"val2",
     *    ...
     *  }
     *  }
     * </pre>
     * @param gpo The {@link GPOMutable} object to deserialize into.
     * @param field The name of the field to take from the JSON and place into the {@link GPOMutable} object.
     * The name of the field must be the same in the {@link FieldsDescriptor} and the {@link GPOMutable} object.
     * @param jo The JSONObject to deserialize from.
     */
    public static void setFieldFromJSON(GPOMutable gpo, String field, JSONObject jo) {
        Object val = getFieldFromJSON(gpo.getFieldDescriptor(), field, jo);
        gpo.setFieldGeneric(field, val);
    }

    /**
     * This method gets the given field from the given {@link JSONObject} and converts the field to an object
     * of the type specified in the given {@link FieldsDescriptor}.
     * @param fd The {@link FieldsDescriptor} describing the type of each field.
     * @param field The field to retrieve from the given {@link JSONObject}.
     * @param jo The {@link JSONObject} to retrieve a field from.
     * @return The value of the given field converted to an object of the correct type.
     */
    public static Object getFieldFromJSON(FieldsDescriptor fd, String field, JSONObject jo) {
        Type type = fd.getType(field);
        int intVal = 0;

        if (numericTypeIntOrSmaller(type)) {
            try {
                intVal = jo.getInt(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException(
                        "The key " + field + " does not have a valid " + type + " value.", ex);
            }

            if (type != Type.INTEGER && !insideRange(type, intVal)) {
                throw new IllegalArgumentException("The key " + field + " has a value " + intVal
                        + " which is out of range for a " + type + ".");
            }
        }

        if (type == Type.BOOLEAN) {
            try {
                return jo.getBoolean(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid bool value.", ex);
            }
        } else if (type == Type.BYTE) {
            return ((byte) intVal);
        } else if (type == Type.SHORT) {
            return ((short) intVal);
        } else if (type == Type.INTEGER) {
            return intVal;
        } else if (type == Type.LONG) {
            try {
                return jo.getLong(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid long value.", ex);
            }
        } else if (type == Type.CHAR) {
            String val;

            try {
                val = jo.getString(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid character value.",
                        ex);
            }

            if (val.length() != 1) {
                throw new IllegalArgumentException(
                        "The key " + field + " has a value " + val + " that is not one character long.");
            }

            return val.charAt(0);
        } else if (type == Type.STRING) {
            try {
                return jo.getString(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid string value.", ex);
            }
        } else if (type == Type.DOUBLE) {
            try {
                return jo.getDouble(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid double value.", ex);
            }
        } else if (type == Type.FLOAT) {
            try {
                return (float) jo.getDouble(field);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The key " + field + " does not have a valid double value.", ex);
            }
        } else {
            throw new UnsupportedOperationException("The type " + type + " is not supported.");
        }
    }

    /**
     * This method gets an object of the given {@link Type} from the given {@link JSONArray} at the
     * given index.
     * @param type The {@link Type} of the object to retrieve from the {@link JSONArray}.
     * @param ja The {@link JSONArray} to retrieve objects from.
     * @param index The index of the object in the {@link JSONArray} to retrieve.
     * @return The object retrieved from the {@link JSONArray}.
     */
    public static Object getFieldFromJSON(Type type, JSONArray ja, int index) {
        int intVal = 0;

        if (numericTypeIntOrSmaller(type)) {
            try {
                intVal = ja.getInt(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException(
                        "The index " + index + " does not have a valid " + type + " value.", ex);
            }

            if (type != Type.INTEGER && !insideRange(type, intVal)) {
                throw new IllegalArgumentException("The index " + index + " has a value " + intVal
                        + " which is out of range for a " + type + ".");
            }
        }

        if (type == Type.BOOLEAN) {
            try {
                return ja.getBoolean(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid bool value.", ex);
            }
        } else if (type == Type.BYTE) {
            return ((byte) intVal);
        } else if (type == Type.SHORT) {
            return ((short) intVal);
        } else if (type == Type.INTEGER) {
            return intVal;
        } else if (type == Type.LONG) {
            try {
                return ja.getLong(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid long value.", ex);
            }
        } else if (type == Type.CHAR) {
            String val;

            try {
                val = ja.getString(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid character value.",
                        ex);
            }

            if (val.length() != 1) {
                throw new IllegalArgumentException(
                        "The index " + index + " has a value " + val + " that is not one character long.");
            }

            return val.charAt(0);
        } else if (type == Type.STRING) {
            try {
                return ja.getString(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid string value.",
                        ex);
            }
        } else if (type == Type.DOUBLE) {
            try {
                return ja.getDouble(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid double value.",
                        ex);
            }
        } else if (type == Type.FLOAT) {
            try {
                return (float) ja.getDouble(index);
            } catch (JSONException ex) {
                throw new IllegalArgumentException("The index " + index + " does not have a valid double value.",
                        ex);
            }
        } else {
            throw new UnsupportedOperationException("The type " + type + " is not supported.");
        }
    }

    /**
     * This utility method serializes the given fields from the given {@link GPOMutable} object into JSON using the
     * given resultFormatter.
     * @param gpo The {@link GPOMutable} to serialize.
     * @param fields The fields from the given {@link GPOMutable} object to serialize.
     * @param resultFormatter The result formatter to use when formatting the output data during serialization.
     * @return The serialized {@link GPOMutable} object.
     * @throws JSONException
     */
    public static JSONObject serializeJSONObject(GPOMutable gpo, Fields fields, ResultFormatter resultFormatter)
            throws JSONException {
        JSONObject jo = new JSONObject();
        FieldsDescriptor fd = gpo.getFieldDescriptor();

        for (String field : fields.getFields()) {
            Type fieldType = fd.getType(field);
            GPOType gpoType = GPOType.GPO_TYPE_ARRAY[fieldType.ordinal()];
            gpoType.serializeJSONObject(jo, gpo, field, resultFormatter);
        }

        return jo;
    }

    /**
     * Serializes the given {@link GPOMutable} object with the given resultFormatter.
     * @param gpo The {@link GPOMutable} to serialize.
     * @param resultFormatter The result formatter to use when serializing data.
     * @return The serialized {@link GPOMutable} object.
     * @throws JSONException
     */
    public static JSONObject serializeJSONObject(GPOMutable gpo, ResultFormatter resultFormatter)
            throws JSONException {
        return serializeJSONObject(gpo, gpo.getFieldDescriptor().getFields(), resultFormatter);
    }

    /**
     * Computes the number of bytes required to serialize the given {@link GPOMutable} object. Excluding the
     * object fields in the {@link GPOMutable}.
     * @param gpo The {@link GPOMutable} object to compute a serialized size for.
     * @return The serialized size for the given {@link GPOMutable} object.
     */
    public static int serializedLength(GPOMutable gpo) {
        int arrayLength = 0;
        FieldsDescriptor fd = gpo.getFieldDescriptor();

        List<Type> types = fd.getTypesList();

        for (int typeIndex = 0; typeIndex < types.size(); typeIndex++) {
            Type type = types.get(typeIndex);

            switch (type) {
            case STRING: {
                for (String val : gpo.getFieldsString()) {
                    arrayLength += Type.INTEGER.getByteSize();
                    arrayLength += val.getBytes().length;
                }
                break;
            }
            case OBJECT: {
                //Don't include objects.
                break;
            }
            default: {
                arrayLength += fd.getTypeToFields().get(type).size() * type.getByteSize();
            }
            }
        }

        return arrayLength;
    }

    /**
     * Serializes the given {@link GPOMutable} object to an array of bytes.
     * @param gpo The {@link GPOMutable} object to serialize.
     * @param byteArrayList A byte array list to pack serialized data into. Note that
     * it is assumed that the byteArrayList is empty when passed to this method.
     * @return The serialized {@link GPOMutable} object.
     */
    public static byte[] serialize(GPOMutable gpo, GPOByteArrayList byteArrayList) {
        int slength = serializedLength(gpo);
        byte[] sbytes = new byte[slength];
        MutableInt offset = new MutableInt(0);

        boolean[] fieldsBoolean = gpo.getFieldsBoolean();
        if (fieldsBoolean != null) {
            for (int index = 0; index < fieldsBoolean.length; index++) {
                serializeBoolean(fieldsBoolean[index], sbytes, offset);
            }
        }

        char[] fieldsCharacter = gpo.getFieldsCharacter();
        if (fieldsCharacter != null) {
            for (int index = 0; index < fieldsCharacter.length; index++) {
                serializeChar(fieldsCharacter[index], sbytes, offset);
            }
        }

        byte[] fieldsByte = gpo.getFieldsByte();
        if (fieldsByte != null) {
            for (int index = 0; index < fieldsByte.length; index++) {
                serializeByte(fieldsByte[index], sbytes, offset);
            }
        }

        short[] fieldsShort = gpo.getFieldsShort();
        if (fieldsShort != null) {
            for (int index = 0; index < fieldsShort.length; index++) {
                serializeShort(fieldsShort[index], sbytes, offset);
            }
        }

        int[] fieldsInteger = gpo.getFieldsInteger();
        if (fieldsInteger != null) {
            for (int index = 0; index < fieldsInteger.length; index++) {
                serializeInt(fieldsInteger[index], sbytes, offset);
            }
        }

        long[] fieldsLong = gpo.getFieldsLong();
        if (fieldsLong != null) {
            for (int index = 0; index < fieldsLong.length; index++) {
                serializeLong(fieldsLong[index], sbytes, offset);
            }
        }

        float[] fieldsFloat = gpo.getFieldsFloat();
        if (fieldsFloat != null) {
            for (int index = 0; index < fieldsFloat.length; index++) {
                serializeFloat(fieldsFloat[index], sbytes, offset);
            }
        }

        double[] fieldsDouble = gpo.getFieldsDouble();
        if (fieldsDouble != null) {
            for (int index = 0; index < fieldsDouble.length; index++) {
                serializeDouble(fieldsDouble[index], sbytes, offset);
            }
        }

        String[] fieldsString = gpo.getFieldsString();
        if (fieldsString != null) {
            for (int index = 0; index < fieldsString.length; index++) {
                serializeString(fieldsString[index], sbytes, offset);
            }
        }

        if (sbytes.length > 0) {
            byteArrayList.add(sbytes);
        }

        Object[] fieldsObject = gpo.getFieldsObject();
        Serde[] serdes = gpo.getFieldDescriptor().getSerdes();
        if (fieldsObject != null) {
            for (int index = 0; index < fieldsObject.length; index++) {
                byteArrayList.add(serdes[index].serializeObject(fieldsObject[index]));
            }
        }

        byte[] bytes = byteArrayList.toByteArray();
        byteArrayList.clear();
        return bytes;
    }

    /**
     * Serializes the given {@link GPOMutable} object while excluding the provided fields from the serialization.
     * @param gpo The {@link GPOMutable} to serialize.
     * @param excludedFields The fields from the {@link GPOMutable} object to exclude.
     * @return A byte array containing the serialized {@link GPOMutable}.
     */
    public static byte[] serialize(GPOMutable gpo, Fields excludedFields) {
        int slength = serializedLength(gpo);
        byte[] sbytes = new byte[slength];
        MutableInt offset = new MutableInt(0);

        Set<String> fields = gpo.getFieldDescriptor().getFields().getFields();
        Set<String> exFieldsSet = excludedFields.getFields();

        for (String field : fields) {
            if (exFieldsSet.contains(field)) {
                continue;
            }

            Type type = gpo.getFieldDescriptor().getType(field);
            GPOType gpoType = GPOType.GPO_TYPE_ARRAY[type.ordinal()];
            gpoType.serialize(gpo, field, sbytes, offset);
        }

        return sbytes;
    }

    /**
     * Deserializes a {@link GPOMutable} object from the given byte array at the given offset with the given
     * {@link FieldsDescriptor} object.
     * @param fd The {@link FieldsDescriptor} object which describes the schema of the {@link GPOMutable} object
     * to deserialize.
     * @param serializedGPO A byte array containing the serialized {@link GPOMutable} object.
     * @param offset An offset in the byte array to start deserializing from.
     * @return The deserialized GPOMutable.
     */
    public static GPOMutable deserialize(FieldsDescriptor fd, byte[] serializedGPO, MutableInt offset) {
        GPOMutable gpo = new GPOMutable(fd);

        boolean[] fieldsBoolean = gpo.getFieldsBoolean();
        if (fieldsBoolean != null) {
            for (int index = 0; index < fieldsBoolean.length; index++) {
                fieldsBoolean[index] = deserializeBoolean(serializedGPO, offset);
            }
        }

        char[] fieldsCharacter = gpo.getFieldsCharacter();
        if (fieldsCharacter != null) {
            for (int index = 0; index < fieldsCharacter.length; index++) {
                fieldsCharacter[index] = deserializeChar(serializedGPO, offset);
            }
        }

        byte[] fieldsByte = gpo.getFieldsByte();
        if (fieldsByte != null) {
            for (int index = 0; index < fieldsByte.length; index++) {
                fieldsByte[index] = deserializeByte(serializedGPO, offset);
            }
        }

        short[] fieldsShort = gpo.getFieldsShort();
        if (fieldsShort != null) {
            for (int index = 0; index < fieldsShort.length; index++) {
                fieldsShort[index] = deserializeShort(serializedGPO, offset);
            }
        }

        int[] fieldsInteger = gpo.getFieldsInteger();
        if (fieldsInteger != null) {
            for (int index = 0; index < fieldsInteger.length; index++) {
                fieldsInteger[index] = deserializeInt(serializedGPO, offset);
            }
        }

        long[] fieldsLong = gpo.getFieldsLong();
        if (fieldsLong != null) {
            for (int index = 0; index < fieldsLong.length; index++) {
                fieldsLong[index] = deserializeLong(serializedGPO, offset);
            }
        }

        float[] fieldsFloat = gpo.getFieldsFloat();
        if (fieldsFloat != null) {
            for (int index = 0; index < fieldsFloat.length; index++) {
                fieldsFloat[index] = deserializeFloat(serializedGPO, offset);
            }
        }

        double[] fieldsDouble = gpo.getFieldsDouble();
        if (fieldsDouble != null) {
            for (int index = 0; index < fieldsDouble.length; index++) {
                fieldsDouble[index] = deserializeDouble(serializedGPO, offset);
            }
        }

        String[] fieldsString = gpo.getFieldsString();
        if (fieldsString != null) {
            for (int index = 0; index < fieldsString.length; index++) {
                fieldsString[index] = deserializeString(serializedGPO, offset);
            }
        }

        Object[] fieldsObject = gpo.getFieldsObject();
        Serde[] serdes = fd.getSerdes();
        if (fieldsObject != null) {
            for (int index = 0; index < fieldsObject.length; index++) {
                fieldsObject[index] = serdes[index].deserializeObject(serializedGPO, offset);
            }
        }

        return gpo;
    }

    /**
     * Deserializes a {@link GPOMutable} object from the given byte array at the given offset, which was
     * serialized with the given {@link FieldsDescriptor} with the given fields excluded.
     * @param fieldsDescriptor The {@link FieldsDescriptor} object corresponding to the {@link GPOMutable}.
     * @param excludedFields The fields to exclude from serializing the {@link GPOMutable}.
     * @param serializedGPO The array containing the serialized {@link GPOMutable}.
     * @param offset The offset in the provided array to start deserializing from.
     * @return The deserialized {@link GPOMutable}.
     */
    public static GPOMutable deserialize(FieldsDescriptor fieldsDescriptor, Fields excludedFields,
            byte[] serializedGPO, int offset) {
        GPOMutable gpo = new GPOMutable(fieldsDescriptor);
        MutableInt offsetM = new MutableInt(offset);

        Set<String> exFieldsSet = excludedFields.getFields();

        for (String field : fieldsDescriptor.getFields().getFields()) {
            if (exFieldsSet.contains(field)) {
                continue;
            }

            Type type = fieldsDescriptor.getType(field);
            GPOType gpoType = GPOType.GPO_TYPE_ARRAY[type.ordinal()];
            gpoType.deserialize(gpo, field, serializedGPO, offsetM);
        }

        return gpo;
    }

    /**
     * This method deserializes a string from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized string.
     */
    public static String deserializeString(byte[] buffer, MutableInt offset) {
        int length = deserializeInt(buffer, offset);

        String val = new String(buffer, offset.intValue(), length);
        offset.add(length);
        return val;
    }

    /**
     * This method serializes the given string to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeString(String val, byte[] buffer, MutableInt offset) {
        byte[] stringBytes = val.getBytes();
        int length = stringBytes.length;

        serializeInt(length, buffer, offset);

        for (int index = 0; index < length; index++) {
            buffer[offset.intValue() + index] = stringBytes[index];
        }

        offset.add(length);
    }

    public static byte[] serializeString(String val) {
        byte[] stringBytes = val.getBytes();
        byte[] length = GPOUtils.serializeInt(stringBytes.length);

        byte[] serialized = new byte[stringBytes.length + length.length];

        System.arraycopy(length, 0, serialized, 0, length.length);
        System.arraycopy(stringBytes, 0, serialized, length.length, stringBytes.length);

        return serialized;
    }

    /**
     * This method deserializes a long from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized long.
     */
    public static long deserializeLong(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        long val = ((((long) buffer[0 + offsetInt]) & 0xFFL) << 56)
                | ((((long) buffer[1 + offsetInt]) & 0xFFL) << 48)
                | ((((long) buffer[2 + offsetInt]) & 0xFFL) << 40)
                | ((((long) buffer[3 + offsetInt]) & 0xFFL) << 32)
                | ((((long) buffer[4 + offsetInt]) & 0xFFL) << 24)
                | ((((long) buffer[5 + offsetInt]) & 0xFFL) << 16) | ((((long) buffer[6 + offsetInt]) & 0xFFL) << 8)
                | (((long) buffer[7 + offsetInt]) & 0xFFL);

        offset.add(Type.LONG.getByteSize());
        return val;
    }

    /**
     * This method serializes the given long to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeLong(long val, byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        buffer[0 + offsetInt] = (byte) ((val >> 56) & 0xFFL);
        buffer[1 + offsetInt] = (byte) ((val >> 48) & 0xFFL);
        buffer[2 + offsetInt] = (byte) ((val >> 40) & 0xFFL);
        buffer[3 + offsetInt] = (byte) ((val >> 32) & 0xFFL);
        buffer[4 + offsetInt] = (byte) ((val >> 24) & 0xFFL);
        buffer[5 + offsetInt] = (byte) ((val >> 16) & 0xFFL);
        buffer[6 + offsetInt] = (byte) ((val >> 8) & 0xFFL);
        buffer[7 + offsetInt] = (byte) (val & 0xFFL);

        offset.add(Type.LONG.getByteSize());
    }

    /**
     * Serializes the given long value to an array of bytes.
     * @param val The long value to serialize.
     * @return The serialized long value.
     */
    public static byte[] serializeLong(long val) {
        byte[] buffer = new byte[Type.LONG.getByteSize()];

        buffer[0] = (byte) ((val >> 56) & 0xFFL);
        buffer[1] = (byte) ((val >> 48) & 0xFFL);
        buffer[2] = (byte) ((val >> 40) & 0xFFL);
        buffer[3] = (byte) ((val >> 32) & 0xFFL);
        buffer[4] = (byte) ((val >> 24) & 0xFFL);
        buffer[5] = (byte) ((val >> 16) & 0xFFL);
        buffer[6] = (byte) ((val >> 8) & 0xFFL);
        buffer[7] = (byte) (val & 0xFFL);

        return buffer;
    }

    /**
     * Deserializes the given long value.
     * @param buffer A serialized long value.
     * @return The deserialized long value.
     */
    public static long deserializeLong(byte[] buffer) {
        Preconditions.checkArgument(buffer.length == Type.LONG.getByteSize());
        return deserializeLong(buffer, new MutableInt(0));
    }

    /**
     * This method deserializes a double from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized double.
     */
    public static double deserializeDouble(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        long val = (((long) buffer[0 + offsetInt]) & 0xFFL) << 56 | ((((long) buffer[1 + offsetInt]) & 0xFFL) << 48)
                | ((((long) buffer[2 + offsetInt]) & 0xFFL) << 40)
                | ((((long) buffer[3 + offsetInt]) & 0xFFL) << 32)
                | ((((long) buffer[4 + offsetInt]) & 0xFFL) << 24)
                | ((((long) buffer[5 + offsetInt]) & 0xFFL) << 16) | ((((long) buffer[6 + offsetInt]) & 0xFFL) << 8)
                | (((long) buffer[7 + offsetInt]) & 0xFFL);

        offset.add(Type.DOUBLE.getByteSize());
        return Double.longBitsToDouble(val);
    }

    /**
     * This method serializes the given double to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param valD The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeDouble(double valD, byte[] buffer, MutableInt offset) {
        long val = Double.doubleToLongBits(valD);

        int offsetInt = offset.intValue();
        buffer[0 + offsetInt] = (byte) ((val >> 56) & 0xFFL);
        buffer[1 + offsetInt] = (byte) ((val >> 48) & 0xFFL);
        buffer[2 + offsetInt] = (byte) ((val >> 40) & 0xFFL);
        buffer[3 + offsetInt] = (byte) ((val >> 32) & 0xFFL);
        buffer[4 + offsetInt] = (byte) ((val >> 24) & 0xFFL);
        buffer[5 + offsetInt] = (byte) ((val >> 16) & 0xFFL);
        buffer[6 + offsetInt] = (byte) ((val >> 8) & 0xFFL);
        buffer[7 + offsetInt] = (byte) (val & 0xFFL);

        offset.add(Type.DOUBLE.getByteSize());
    }

    public static byte[] serializeDouble(double valD) {
        byte[] buffer = new byte[Type.DOUBLE.getByteSize()];
        long val = Double.doubleToLongBits(valD);

        buffer[0] = (byte) ((val >> 56) & 0xFFL);
        buffer[1] = (byte) ((val >> 48) & 0xFFL);
        buffer[2] = (byte) ((val >> 40) & 0xFFL);
        buffer[3] = (byte) ((val >> 32) & 0xFFL);
        buffer[4] = (byte) ((val >> 24) & 0xFFL);
        buffer[5] = (byte) ((val >> 16) & 0xFFL);
        buffer[6] = (byte) ((val >> 8) & 0xFFL);
        buffer[7] = (byte) (val & 0xFFL);

        return buffer;
    }

    /**
     * This method deserializes an integer from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized integer.
     */
    public static int deserializeInt(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        int val = ((((int) buffer[0 + offsetInt]) & 0xFF) << 24) | ((((int) buffer[1 + offsetInt]) & 0xFF) << 16)
                | ((((int) buffer[2 + offsetInt]) & 0xFF) << 8) | (((int) buffer[3 + offsetInt]) & 0xFF);

        offset.add(Type.INTEGER.getByteSize());
        return val;
    }

    /**
     * Deserializes the given serialized integer.
     * @param buffer The integer value to deserialized.
     * @return The deserialized integer value.
     */
    public static int deserializeInt(byte[] buffer) {
        Preconditions.checkArgument(buffer.length == Type.INTEGER.getByteSize());
        return deserializeInt(buffer, new MutableInt(0));
    }

    /**
     * This method serializes the given integer to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeInt(int val, byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        buffer[0 + offsetInt] = (byte) ((val >> 24) & 0xFF);
        buffer[1 + offsetInt] = (byte) ((val >> 16) & 0xFF);
        buffer[2 + offsetInt] = (byte) ((val >> 8) & 0xFF);
        buffer[3 + offsetInt] = (byte) (val & 0xFF);

        offset.add(Type.INTEGER.getByteSize());
    }

    /**
     * Serializes the given integer value.
     * @param val The value to serialize.
     * @return The serialized integer value.
     */
    public static byte[] serializeInt(int val) {
        byte[] buffer = new byte[Type.INTEGER.getByteSize()];

        buffer[0] = (byte) ((val >> 24) & 0xFF);
        buffer[1] = (byte) ((val >> 16) & 0xFF);
        buffer[2] = (byte) ((val >> 8) & 0xFF);
        buffer[3] = (byte) (val & 0xFF);

        return buffer;
    }

    /**
     * This method deserializes a float from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized float.
     */
    public static float deserializeFloat(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        int val = ((((int) buffer[0 + offsetInt]) & 0xFF) << 24) | ((((int) buffer[1 + offsetInt]) & 0xFF) << 16)
                | ((((int) buffer[2 + offsetInt]) & 0xFF) << 8) | (((int) buffer[3 + offsetInt]) & 0xFF);

        offset.add(Type.FLOAT.getByteSize());
        return Float.intBitsToFloat(val);
    }

    /**
     * This method serializes the given float to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param valf The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeFloat(float valf, byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        int val = Float.floatToIntBits(valf);

        buffer[0 + offsetInt] = (byte) ((val >> 24) & 0xFF);
        buffer[1 + offsetInt] = (byte) ((val >> 16) & 0xFF);
        buffer[2 + offsetInt] = (byte) ((val >> 8) & 0xFF);
        buffer[3 + offsetInt] = (byte) (val & 0xFF);

        offset.add(Type.FLOAT.getByteSize());
    }

    public static byte[] serializeFloat(float valf) {
        byte[] buffer = new byte[Type.FLOAT.getByteSize()];
        int val = Float.floatToIntBits(valf);

        buffer[0] = (byte) ((val >> 24) & 0xFF);
        buffer[1] = (byte) ((val >> 16) & 0xFF);
        buffer[2] = (byte) ((val >> 8) & 0xFF);
        buffer[3] = (byte) (val & 0xFF);

        return buffer;
    }

    /**
     * This method deserializes a short from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized short.
     */
    public static short deserializeShort(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        short val = (short) (((((int) buffer[0 + offsetInt]) & 0xFF) << 8)
                | (((int) buffer[1 + offsetInt]) & 0xFF));

        offset.add(Type.SHORT.getByteSize());
        return val;
    }

    /**
     * This method serializes the given short to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeShort(short val, byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        buffer[0 + offsetInt] = (byte) ((val >> 8) & 0xFF);
        buffer[1 + offsetInt] = (byte) (val & 0xFF);

        offset.add(Type.SHORT.getByteSize());
    }

    public static byte[] serializeShort(short val) {
        byte[] buffer = new byte[Type.SHORT.getByteSize()];

        buffer[0] = (byte) ((val >> 8) & 0xFF);
        buffer[1] = (byte) (val & 0xFF);

        return buffer;
    }

    /**
     * This method deserializes a byte from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized byte.
     */
    public static byte deserializeByte(byte[] buffer, MutableInt offset) {
        byte val = buffer[offset.intValue()];

        offset.add(Type.BYTE.getByteSize());
        return val;
    }

    /**
     * This method serializes the given byte to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeByte(byte val, byte[] buffer, MutableInt offset) {
        buffer[offset.intValue()] = val;

        offset.add(Type.BYTE.getByteSize());
    }

    public static byte[] serializeByte(byte val) {
        return new byte[] { val };
    }

    /**
     * This method deserializes a boolean from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized boolean.
     */
    public static boolean deserializeBoolean(byte[] buffer, MutableInt offset) {
        boolean val = buffer[offset.intValue()] != 0;

        offset.add(Type.BOOLEAN.getByteSize());
        return val;
    }

    /**
     * This method serializes the given boolean to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeBoolean(boolean val, byte[] buffer, MutableInt offset) {
        buffer[offset.intValue()] = (byte) (val ? 1 : 0);

        offset.add(Type.BOOLEAN.getByteSize());
    }

    public static byte[] serializeBoolean(boolean val) {
        return new byte[] { (byte) (val ? 1 : 0) };
    }

    /**
     * This method deserializes a character from the given byte array from the given offset,
     * and increments the offset appropriately.
     * @param buffer The byte buffer to deserialize from.
     * @param offset The offset to deserialize from.
     * @return The deserialized character.
     */
    public static char deserializeChar(byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        char val = (char) (((((int) buffer[0 + offsetInt]) & 0xFF) << 8) | (((int) buffer[1 + offsetInt]) & 0xFF));

        offset.add(Type.CHAR.getByteSize());
        return val;
    }

    /**
     * This method serializes the given character to the given byte buffer to the given offset,
     * the method also increments the offset appropriately.
     * @param val The value to serialize.
     * @param buffer The byte buffer to serialize to.
     * @param offset The offset in the buffer to serialize to and also to increment appropriately.
     */
    public static void serializeChar(char val, byte[] buffer, MutableInt offset) {
        int offsetInt = offset.intValue();
        buffer[0 + offsetInt] = (byte) ((val >> 8) & 0xFF);
        buffer[1 + offsetInt] = (byte) (val & 0xFF);

        offset.add(Type.CHAR.getByteSize());
    }

    public static byte[] serializeChar(char val) {
        byte[] buffer = new byte[Type.CHAR.getByteSize()];

        buffer[0] = (byte) ((val >> 8) & 0xFF);
        buffer[1] = (byte) (val & 0xFF);

        return buffer;
    }

    /**
     * Utility method for creating getters. This method is useful for creating a {@link GPOGetters} object
     * which can be used to copy POJOs into GPOMutable objects.
     * @param fields The fields to create getters for. The order of the fields in this list will be the same order
     * that the getters will be returned in.
     * @param valueToExpression A map from field names to the corresponding java expression to be used for getting
     * the fields.
     * @param clazz The Class of the POJO to extract values from.
     * @param getterClazz The class of the getter object to create.
     * @param getterMethodClazz The class of the getter method.
     * @return An array of boolean getters for given fields.
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] createGetters(List<String> fields, Map<String, String> valueToExpression, Class<?> clazz,
            Class<?> getterClazz, Class<?> getterMethodClazz) {
        @SuppressWarnings("unchecked")
        T[] getters = (T[]) Array.newInstance(getterMethodClazz, fields.size());

        for (int getterIndex = 0; getterIndex < fields.size(); getterIndex++) {
            String field = fields.get(getterIndex);
            getters[getterIndex] = (T) PojoUtils.constructGetter(clazz, valueToExpression.get(field), getterClazz);
        }

        return getters;
    }

    /**
     * Utility method for creating getters. This method is useful for creating a {@link GPOGetters} object
     * which can be used to copy POJOs into GPOMutable objects.
     * @param fields The fields to create getters for. The order of the fields in this list will be the same order
     * that the getters will be returned in.
     * @param valueToExpression A map from field names to the corresponding java expression to be used for getting
     * the fields.
     * @param clazz The Class of the POJO to extract values from.
     * @return An array of boolean getters for given fields.
     */
    public static Getter<Object, String>[] createGettersString(List<String> fields,
            Map<String, String> valueToExpression, Class<?> clazz) {
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Getter<Object, String>[] getters = new Getter[fields.size()];

        for (int getterIndex = 0; getterIndex < fields.size(); getterIndex++) {
            String field = fields.get(getterIndex);
            getters[getterIndex] = PojoUtils.createGetter(clazz, valueToExpression.get(field), String.class);
        }

        return getters;
    }

    /**
     * Utility method for creating getters. This method is useful for creating a {@link GPOGetters} object
     * which can be used to copy POJOs into GPOMutable objects.
     * @param fields The fields to create getters for. The order of the fields in this list will be the same order
     * that the getters will be returned in.
     * @param valueToExpression A map from field names to the corresponding java expression to be used for getting
     * the fields.
     * @param clazz The Class of the POJO to extract values from.
     * @return An array of boolean getters for given fields.
     */
    public static Getter<Object, Object>[] createGettersObject(List<String> fields,
            Map<String, String> valueToExpression, Class<?> clazz) {
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Getter<Object, Object>[] getters = new Getter[fields.size()];

        for (int getterIndex = 0; getterIndex < fields.size(); getterIndex++) {
            String field = fields.get(getterIndex);
            getters[getterIndex] = PojoUtils.createGetter(clazz, valueToExpression.get(field), Object.class);
        }

        return getters;
    }

    /**
     * This is a utility method which builds a {@link GPOGetters} object corresponding to the given {@link FieldsDescriptor}.
     * This utility method is helpful for converting POJOs into GPOMutable objects.
     * @param fieldToGetter A map whose keys are field names and whose values correspond to java getter expressions. Field names
     * in this map should be the same as field names in the provided {@link FieldsDescriptor} object.
     * @param fieldsDescriptor A {@link FieldsDescriptor} object which describes the type name and order of fields.
     * @param clazz The Class of the POJO that the getters will be applied to.
     * @return The {@link GPOGetters} object which can be used to convert POJOs into {@link GPOMutable} objects initialized
     * with the same {@link FieldsDescriptor} object.
     */
    public static GPOGetters buildGPOGetters(Map<String, String> fieldToGetter, FieldsDescriptor fieldsDescriptor,
            Class<?> clazz) {
        GPOGetters gpoGetters = new GPOGetters();
        Map<Type, List<String>> typeToFields = fieldsDescriptor.getTypeToFields();

        for (Map.Entry<Type, List<String>> entry : typeToFields.entrySet()) {
            Type inputType = entry.getKey();
            GPOType gpoType = GPOType.GPO_TYPE_ARRAY[inputType.ordinal()];
            List<String> fields = entry.getValue();
            gpoType.buildGPOGetters(gpoGetters, fields, fieldToGetter, clazz);
        }

        return gpoGetters;
    }

    /**
     * This is a utility method for converting a POJO to a {@link GPOMutable} object. This method assumes that the provided
     * GPOMutable is initialized with the correct {@link FieldsDescriptor}, and that the given {@link GPOGetters} object has getters
     * specified in the same order as fields in the {@link GPOMutable} object.
     * @param mutable The {@link GPOMutable} object to copy POJO values into. It is assumed this object is initialized with
     * the correct {@link FieldsDescriptor}.
     * @param getters The getters to use when retrieving values from the provided POJO. It is assumed that the getters are
     * specified in the same order as their corresponding fields in the {@link GPOMutable} object.
     * @param object The POJO to extract values from.
     */
    public static void copyPOJOToGPO(GPOMutable mutable, GPOGetters getters, Object object) {
        {
            boolean[] tempBools = mutable.getFieldsBoolean();
            GetterBoolean<Object>[] tempGetterBools = getters.gettersBoolean;

            if (tempBools != null) {
                for (int index = 0; index < tempBools.length; index++) {
                    tempBools[index] = tempGetterBools[index].get(object);
                }
            }
        }

        {
            byte[] tempBytes = mutable.getFieldsByte();
            GetterByte<Object>[] tempGetterByte = getters.gettersByte;

            if (tempBytes != null) {
                for (int index = 0; index < tempBytes.length; index++) {
                    tempBytes[index] = tempGetterByte[index].get(object);
                }
            }
        }

        {
            char[] tempChar = mutable.getFieldsCharacter();
            GetterChar<Object>[] tempGetterChar = getters.gettersChar;

            if (tempChar != null) {
                for (int index = 0; index < tempChar.length; index++) {
                    tempChar[index] = tempGetterChar[index].get(object);
                }
            }
        }

        {
            double[] tempDouble = mutable.getFieldsDouble();
            GetterDouble<Object>[] tempGetterDouble = getters.gettersDouble;

            if (tempDouble != null) {
                for (int index = 0; index < tempDouble.length; index++) {
                    tempDouble[index] = tempGetterDouble[index].get(object);
                }
            }
        }

        {
            float[] tempFloat = mutable.getFieldsFloat();
            GetterFloat<Object>[] tempGetterFloat = getters.gettersFloat;

            if (tempFloat != null) {
                for (int index = 0; index < tempFloat.length; index++) {
                    tempFloat[index] = tempGetterFloat[index].get(object);
                }
            }
        }

        {
            int[] tempInt = mutable.getFieldsInteger();
            GetterInt<Object>[] tempGetterInt = getters.gettersInteger;

            if (tempInt != null) {
                for (int index = 0; index < tempInt.length; index++) {
                    tempInt[index] = tempGetterInt[index].get(object);
                }
            }
        }

        {
            long[] tempLong = mutable.getFieldsLong();
            GetterLong<Object>[] tempGetterLong = getters.gettersLong;

            if (tempLong != null) {
                for (int index = 0; index < tempLong.length; index++) {
                    tempLong[index] = tempGetterLong[index].get(object);
                }
            }
        }

        {
            short[] tempShort = mutable.getFieldsShort();
            GetterShort<Object>[] tempGetterShort = getters.gettersShort;

            if (tempShort != null) {
                for (int index = 0; index < tempShort.length; index++) {
                    tempShort[index] = tempGetterShort[index].get(object);
                }
            }
        }

        {
            String[] tempString = mutable.getFieldsString();
            Getter<Object, String>[] tempGetterString = getters.gettersString;

            if (tempString != null) {
                for (int index = 0; index < tempString.length; index++) {
                    tempString[index] = tempGetterString[index].get(object);
                }
            }
        }
    }

    public static void indirectCopy(GPOMutable dest, GPOMutable src, IndexSubset indexSubset) {
        {
            String[] destString = dest.getFieldsString();
            String[] srcString = src.getFieldsString();
            int[] srcIndex = indexSubset.fieldsStringIndexSubset;
            if (destString != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destString[index] = srcString[srcIndex[index]];
                }
            }
        }

        {
            boolean[] destBoolean = dest.getFieldsBoolean();
            boolean[] srcBoolean = src.getFieldsBoolean();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (destBoolean != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destBoolean[index] = srcBoolean[srcIndex[index]];
                }
            }
        }

        {
            char[] destChar = dest.getFieldsCharacter();
            char[] srcChar = src.getFieldsCharacter();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (destChar != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destChar[index] = srcChar[srcIndex[index]];
                }
            }
        }

        {
            byte[] destByte = dest.getFieldsByte();
            byte[] srcByte = src.getFieldsByte();
            int[] srcIndex = indexSubset.fieldsByteIndexSubset;
            if (destByte != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destByte[index] = srcByte[srcIndex[index]];
                }
            }
        }

        {
            short[] destShort = dest.getFieldsShort();
            short[] srcShort = src.getFieldsShort();
            int[] srcIndex = indexSubset.fieldsShortIndexSubset;
            if (destShort != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destShort[index] = srcShort[srcIndex[index]];
                }
            }
        }

        {
            int[] destInteger = dest.getFieldsInteger();
            int[] srcInteger = src.getFieldsInteger();
            int[] srcIndex = indexSubset.fieldsIntegerIndexSubset;
            if (destInteger != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destInteger[index] = srcInteger[srcIndex[index]];
                }
            }
        }

        {
            long[] destLong = dest.getFieldsLong();
            long[] srcLong = src.getFieldsLong();
            int[] srcIndex = indexSubset.fieldsLongIndexSubset;
            if (destLong != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }

                    destLong[index] = srcLong[srcIndex[index]];
                }
            }
        }

        {
            float[] destFloat = dest.getFieldsFloat();
            float[] srcFloat = src.getFieldsFloat();
            int[] srcIndex = indexSubset.fieldsFloatIndexSubset;
            if (destFloat != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destFloat[index] = srcFloat[srcIndex[index]];
                }
            }
        }

        {
            double[] destDouble = dest.getFieldsDouble();
            double[] srcDouble = src.getFieldsDouble();
            int[] srcIndex = indexSubset.fieldsDoubleIndexSubset;
            if (destDouble != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destDouble[index] = srcDouble[srcIndex[index]];
                }
            }
        }

        {
            Object[] destObject = dest.getFieldsObject();
            Object[] srcObject = src.getFieldsObject();
            int[] srcIndex = indexSubset.fieldsObjectIndexSubset;
            if (destObject != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    destObject[index] = srcObject[srcIndex[index]];
                }
            }
        }
    }

    public static int hashcode(GPOMutable gpo) {
        int hashCode = 0;

        {
            String[] stringArray = gpo.getFieldsString();
            if (stringArray != null) {
                for (int index = 0; index < stringArray.length; index++) {
                    hashCode ^= stringArray[index].hashCode();
                }
            }
        }

        {
            boolean[] booleanArray = gpo.getFieldsBoolean();
            if (booleanArray != null) {
                for (int index = 0; index < booleanArray.length; index++) {
                    hashCode ^= booleanArray[index] ? 1 : 0;
                }
            }
        }

        {
            char[] charArray = gpo.getFieldsCharacter();
            if (charArray != null) {
                for (int index = 0; index < charArray.length; index++) {
                    hashCode ^= Character.getNumericValue(charArray[index]);
                }
            }
        }

        {
            byte[] byteArray = gpo.getFieldsByte();
            if (byteArray != null) {
                for (int index = 0; index < byteArray.length; index++) {
                    hashCode ^= byteArray[index];
                }
            }
        }

        {
            short[] shortArray = gpo.getFieldsShort();
            if (shortArray != null) {
                for (int index = 0; index < shortArray.length; index++) {
                    hashCode ^= shortArray[index];
                }
            }
        }

        {
            int[] integerArray = gpo.getFieldsInteger();
            if (integerArray != null) {
                for (int index = 0; index < integerArray.length; index++) {
                    hashCode ^= integerArray[index];
                }
            }
        }

        {
            long[] longArray = gpo.getFieldsLong();
            if (longArray != null) {
                for (int index = 0; index < longArray.length; index++) {
                    hashCode ^= longArray[index];
                }
            }
        }

        {
            float[] floatArray = gpo.getFieldsFloat();
            if (floatArray != null) {
                for (int index = 0; index < floatArray.length; index++) {
                    hashCode ^= Float.floatToIntBits(floatArray[index]);
                }
            }
        }

        {
            double[] doubleArray = gpo.getFieldsDouble();
            if (doubleArray != null) {
                for (int index = 0; index < doubleArray.length; index++) {
                    hashCode ^= Double.doubleToLongBits(doubleArray[index]);
                }
            }
        }

        {
            Object[] objectArray = gpo.getFieldsObject();
            if (objectArray != null) {
                for (int index = 0; index < objectArray.length; index++) {
                    hashCode ^= objectArray[index].hashCode();
                }
            }
        }

        return hashCode;
    }

    /**
     * This function computes the hashcode of a {@link GPOMutable} based on a specified subset of its data.
     * <br/>
     * <br/>
     * <b>Note:</b> In some cases a {@link GPOMutable} object contains a field which is bucketed. In the case of
     * bucketed fields, you may want to preserve the original value of the field, but only use the bucketed value
     * of the field for computing a hashcode. In order to do this you can store the original value of {@link GPOMutable}'s
     * field before calling this function, and replace it with the bucketed value. Then after the hashcode is computed, the
     * original value of the field can be restored.
     *
     * @param gpo The {@link GPOMutable} to compute a hashcode for.
     * @param indexSubset The subset of the {@link GPOMutable} used to compute the hashcode.
     * @return The hashcode for the given {@link GPOMutable} computed from the specified subset of its data.
     */
    public static int indirectHashcode(GPOMutable gpo, IndexSubset indexSubset) {
        int hashCode = 7;
        final int hashMultiplier = 23;

        {
            String[] stringArray = gpo.getFieldsString();
            int[] srcIndex = indexSubset.fieldsStringIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + stringArray[srcIndex[index]].hashCode();
                }
            }
        }

        {
            boolean[] booleanArray = gpo.getFieldsBoolean();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + (booleanArray[srcIndex[index]] ? 1 : 0);
                }
            }
        }

        {
            char[] charArray = gpo.getFieldsCharacter();
            int[] srcIndex = indexSubset.fieldsCharacterIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + Character.getNumericValue(charArray[srcIndex[index]]);
                }
            }
        }

        {
            byte[] byteArray = gpo.getFieldsByte();
            int[] srcIndex = indexSubset.fieldsByteIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + byteArray[srcIndex[index]];
                }
            }
        }

        {
            short[] shortArray = gpo.getFieldsShort();
            int[] srcIndex = indexSubset.fieldsShortIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + shortArray[srcIndex[index]];
                }
            }
        }

        {
            int[] integerArray = gpo.getFieldsInteger();
            int[] srcIndex = indexSubset.fieldsIntegerIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + integerArray[srcIndex[index]];
                }
            }
        }

        {
            long[] longArray = gpo.getFieldsLong();
            int[] srcIndex = indexSubset.fieldsLongIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    long element = longArray[srcIndex[index]];
                    int elementHash = (int) (element ^ (element >>> 32));
                    hashCode = hashMultiplier * hashCode + elementHash;
                }
            }
        }

        {
            float[] floatArray = gpo.getFieldsFloat();
            int[] srcIndex = indexSubset.fieldsFloatIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    hashCode = hashMultiplier * hashCode + Float.floatToIntBits(floatArray[srcIndex[index]]);
                }
            }
        }

        {
            double[] doubleArray = gpo.getFieldsDouble();
            int[] srcIndex = indexSubset.fieldsDoubleIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    long element = Double.doubleToLongBits(doubleArray[srcIndex[index]]);
                    int elementHash = (int) (element ^ (element >>> 32));
                    hashCode = hashMultiplier * hashCode + elementHash;
                }
            }
        }

        {
            Object[] objectArray = gpo.getFieldsObject();
            int[] srcIndex = indexSubset.fieldsObjectIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }

                    hashCode = hashMultiplier * hashCode + objectArray[srcIndex[index]].hashCode();
                }
            }
        }

        return hashCode;
    }

    public static boolean equals(GPOMutable dest, GPOMutable src) {
        {
            String[] destString = dest.getFieldsString();
            String[] srcString = src.getFieldsString();
            if (destString != null) {
                for (int index = 0; index < srcString.length; index++) {
                    if (!destString[index].equals(srcString[index])) {
                        return false;
                    }
                }
            }
        }

        {
            boolean[] destBoolean = dest.getFieldsBoolean();
            boolean[] srcBoolean = src.getFieldsBoolean();
            if (destBoolean != null) {
                for (int index = 0; index < srcBoolean.length; index++) {
                    if (destBoolean[index] != srcBoolean[index]) {
                        return false;
                    }
                }
            }
        }

        {
            char[] destChar = dest.getFieldsCharacter();
            char[] srcChar = src.getFieldsCharacter();
            if (destChar != null) {
                for (int index = 0; index < srcChar.length; index++) {
                    if (destChar[index] != srcChar[index]) {
                        return false;
                    }
                }
            }
        }

        {
            byte[] destByte = dest.getFieldsByte();
            byte[] srcByte = src.getFieldsByte();
            if (destByte != null) {
                for (int index = 0; index < srcByte.length; index++) {
                    if (destByte[index] != srcByte[index]) {
                        return false;
                    }
                }
            }
        }

        {
            short[] destShort = dest.getFieldsShort();
            short[] srcShort = src.getFieldsShort();
            if (destShort != null) {
                for (int index = 0; index < srcShort.length; index++) {
                    if (destShort[index] != srcShort[index]) {
                        return false;
                    }
                }
            }
        }

        {
            int[] destInteger = dest.getFieldsInteger();
            int[] srcInteger = src.getFieldsInteger();
            if (destInteger != null) {
                for (int index = 0; index < srcInteger.length; index++) {
                    if (destInteger[index] != srcInteger[index]) {
                        return false;
                    }
                }
            }
        }

        {
            long[] destLong = dest.getFieldsLong();
            long[] srcLong = src.getFieldsLong();
            if (destLong != null) {
                for (int index = 0; index < srcLong.length; index++) {
                    if (destLong[index] != srcLong[index]) {
                        return false;
                    }
                }
            }
        }

        {
            float[] destFloat = dest.getFieldsFloat();
            float[] srcFloat = src.getFieldsFloat();
            if (destFloat != null) {
                for (int index = 0; index < srcFloat.length; index++) {
                    if (destFloat[index] != srcFloat[index]) {
                        return false;
                    }
                }
            }
        }

        {
            double[] destDouble = dest.getFieldsDouble();
            double[] srcDouble = src.getFieldsDouble();
            if (destDouble != null) {
                for (int index = 0; index < srcDouble.length; index++) {
                    if (destDouble[index] != srcDouble[index]) {
                        return false;
                    }
                }
            }
        }

        {
            Object[] destObject = dest.getFieldsObject();
            Object[] srcObject = src.getFieldsObject();
            if (destObject != null) {
                for (int index = 0; index < srcObject.length; index++) {
                    if (!destObject[index].equals(srcObject[index])) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public static boolean subsetEquals(GPOMutable dest, GPOMutable src, IndexSubset indexSubset) {
        {
            String[] destString = dest.getFieldsString();
            String[] srcString = src.getFieldsString();
            int[] srcIndex = indexSubset.fieldsStringIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (!destString[srcIndex[index]].equals(srcString[srcIndex[index]])) {
                        return false;
                    }
                }
            }
        }

        {
            boolean[] destBoolean = dest.getFieldsBoolean();
            boolean[] srcBoolean = src.getFieldsBoolean();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destBoolean[srcIndex[index]] != srcBoolean[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            char[] destChar = dest.getFieldsCharacter();
            char[] srcChar = src.getFieldsCharacter();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destChar[srcIndex[index]] != srcChar[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            byte[] destByte = dest.getFieldsByte();
            byte[] srcByte = src.getFieldsByte();
            int[] srcIndex = indexSubset.fieldsByteIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destByte[srcIndex[index]] != srcByte[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            short[] destShort = dest.getFieldsShort();
            short[] srcShort = src.getFieldsShort();
            int[] srcIndex = indexSubset.fieldsShortIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destShort[srcIndex[index]] != srcShort[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            int[] destInteger = dest.getFieldsInteger();
            int[] srcInteger = src.getFieldsInteger();
            int[] srcIndex = indexSubset.fieldsIntegerIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destInteger[srcIndex[index]] != srcInteger[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            long[] destLong = dest.getFieldsLong();
            long[] srcLong = src.getFieldsLong();
            int[] srcIndex = indexSubset.fieldsLongIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destLong[srcIndex[index]] != srcLong[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            float[] destFloat = dest.getFieldsFloat();
            float[] srcFloat = src.getFieldsFloat();
            int[] srcIndex = indexSubset.fieldsFloatIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destFloat[srcIndex[index]] != srcFloat[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            double[] destDouble = dest.getFieldsDouble();
            double[] srcDouble = src.getFieldsDouble();
            int[] srcIndex = indexSubset.fieldsDoubleIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destDouble[srcIndex[index]] != srcDouble[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            Object[] destObject = dest.getFieldsObject();
            Object[] srcObject = src.getFieldsObject();
            int[] srcIndex = indexSubset.fieldsObjectIndexSubset;
            if (srcIndex != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (!destObject[srcIndex[index]].equals(srcObject[srcIndex[index]])) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public static boolean indirectEquals(GPOMutable dest, GPOMutable src, IndexSubset indexSubset) {
        {
            String[] destString = dest.getFieldsString();
            String[] srcString = src.getFieldsString();
            int[] srcIndex = indexSubset.fieldsStringIndexSubset;
            if (destString != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (!destString[index].equals(srcString[srcIndex[index]])) {
                        return false;
                    }
                }
            }
        }

        {
            boolean[] destBoolean = dest.getFieldsBoolean();
            boolean[] srcBoolean = src.getFieldsBoolean();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (destBoolean != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destBoolean[index] != srcBoolean[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            char[] destChar = dest.getFieldsCharacter();
            char[] srcChar = src.getFieldsCharacter();
            int[] srcIndex = indexSubset.fieldsBooleanIndexSubset;
            if (destChar != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destChar[index] != srcChar[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            byte[] destByte = dest.getFieldsByte();
            byte[] srcByte = src.getFieldsByte();
            int[] srcIndex = indexSubset.fieldsByteIndexSubset;
            if (destByte != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destByte[index] != srcByte[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            short[] destShort = dest.getFieldsShort();
            short[] srcShort = src.getFieldsShort();
            int[] srcIndex = indexSubset.fieldsShortIndexSubset;
            if (destShort != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destShort[index] != srcShort[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            int[] destInteger = dest.getFieldsInteger();
            int[] srcInteger = src.getFieldsInteger();
            int[] srcIndex = indexSubset.fieldsIntegerIndexSubset;
            if (destInteger != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destInteger[index] != srcInteger[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            long[] destLong = dest.getFieldsLong();
            long[] srcLong = src.getFieldsLong();
            int[] srcIndex = indexSubset.fieldsLongIndexSubset;
            if (destLong != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destLong[index] != srcLong[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            float[] destFloat = dest.getFieldsFloat();
            float[] srcFloat = src.getFieldsFloat();
            int[] srcIndex = indexSubset.fieldsFloatIndexSubset;
            if (destFloat != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destFloat[index] != srcFloat[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            double[] destDouble = dest.getFieldsDouble();
            double[] srcDouble = src.getFieldsDouble();
            int[] srcIndex = indexSubset.fieldsDoubleIndexSubset;
            if (destDouble != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (destDouble[index] != srcDouble[srcIndex[index]]) {
                        return false;
                    }
                }
            }
        }

        {
            Object[] destObject = dest.getFieldsObject();
            Object[] srcObject = src.getFieldsObject();
            int[] srcIndex = indexSubset.fieldsObjectIndexSubset;
            if (destObject != null) {
                for (int index = 0; index < srcIndex.length; index++) {
                    if (srcIndex[index] == -1) {
                        continue;
                    }
                    if (!destObject[index].equals(srcObject[srcIndex[index]])) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public static void zeroFillNumeric(GPOMutable value) {
        if (value.getFieldsByte() != null) {
            Arrays.fill(value.getFieldsByte(), (byte) 0);
        }

        if (value.getFieldsShort() != null) {
            Arrays.fill(value.getFieldsShort(), (short) 0);
        }

        if (value.getFieldsInteger() != null) {
            Arrays.fill(value.getFieldsInteger(), 0);
        }

        if (value.getFieldsLong() != null) {
            Arrays.fill(value.getFieldsLong(), 0L);
        }

        if (value.getFieldsFloat() != null) {
            Arrays.fill(value.getFieldsFloat(), 0.0f);
        }

        if (value.getFieldsDouble() != null) {
            Arrays.fill(value.getFieldsDouble(), 0.0);
        }
    }

    public static IndexSubset computeSubIndices(FieldsDescriptor child, FieldsDescriptor parent) {
        IndexSubset indexSubset = new IndexSubset();

        for (Map.Entry<Type, List<String>> entry : child.getTypeToFields().entrySet()) {
            Type type = entry.getKey();
            List<String> childFields = entry.getValue();
            List<String> parentFields = parent.getTypeToFields().get(type);

            int size = child.getTypeToSize().get(type);
            int[] indices;
            if (child.getTypeToFields().get(type) != null && child.getCompressedTypes().contains(type)) {
                indices = new int[1];
            } else {
                indices = new int[size];

                for (int index = 0; index < size; index++) {
                    if (parentFields == null) {
                        indices[index] = -1;
                    } else {
                        indices[index] = parentFields.indexOf(childFields.get(index));
                    }
                }
            }

            switch (type) {
            case BOOLEAN: {
                indexSubset.fieldsBooleanIndexSubset = indices;
                break;
            }
            case CHAR: {
                indexSubset.fieldsCharacterIndexSubset = indices;
                break;
            }
            case STRING: {
                indexSubset.fieldsStringIndexSubset = indices;
                break;
            }
            case BYTE: {
                indexSubset.fieldsByteIndexSubset = indices;
                break;
            }
            case SHORT: {
                indexSubset.fieldsShortIndexSubset = indices;
                break;
            }
            case INTEGER: {
                indexSubset.fieldsIntegerIndexSubset = indices;
                break;
            }
            case LONG: {
                indexSubset.fieldsLongIndexSubset = indices;
                break;
            }
            case FLOAT: {
                indexSubset.fieldsFloatIndexSubset = indices;
                break;
            }
            case DOUBLE: {
                indexSubset.fieldsDoubleIndexSubset = indices;
                break;
            }
            default: {
                throw new UnsupportedOperationException("Type " + type);
            }
            }
        }

        return indexSubset;
    }

    /**
     * This object is used to define the subset of data contained in a {@link GPOMutable}
     * object that we are interested in.
     */
    public static class IndexSubset implements Serializable {
        private static final long serialVersionUID = 201506251015L;

        /**
         * Index of boolean fields of interest. Null if there are no boolean fields of interest.
         */
        public int[] fieldsBooleanIndexSubset;
        /**
         * Index of character fields of interest. Null if there are no character fields of interest.
         */
        public int[] fieldsCharacterIndexSubset;
        /**
         * Index of byte fields of interest. Null if there are no byte fields of interest.
         */
        public int[] fieldsByteIndexSubset;
        /**
         * Index of short fields of interest. Null if there are no short fields of interest.
         */
        public int[] fieldsShortIndexSubset;
        /**
         * Index of integer fields of interest. Null if there are no integer fields of interest.
         */
        public int[] fieldsIntegerIndexSubset;
        /**
         * Index of long fields of interest. Null if there are no long fields of interest.
         */
        public int[] fieldsLongIndexSubset;
        /**
         * Index of float fields of interest. Null if there are no float fields of interest.
         */
        public int[] fieldsFloatIndexSubset;
        /**
         * Index of double fields of interest. Null if there are no double fields of interest.
         */
        public int[] fieldsDoubleIndexSubset;
        /**
         * Index of String fields of interest. Null if there are no String fields of interest.
         */
        public int[] fieldsStringIndexSubset;
        /**
         * Index of Object fields of interest. Null if there are no Object fields of interest.
         */
        public int[] fieldsObjectIndexSubset;

        public IndexSubset() {
            //Do nothing
        }

        @Override
        public String toString() {
            return "IndexSubset{" + "fieldsBooleanIndexSubset=" + fieldsBooleanIndexSubset
                    + ", fieldsCharacterIndexSubset=" + fieldsCharacterIndexSubset + ", fieldsByteIndexSubset="
                    + fieldsByteIndexSubset + ", fieldsShortIndexSubset=" + fieldsShortIndexSubset
                    + ", fieldsIntegerIndexSubset=" + fieldsIntegerIndexSubset + ", fieldsLongIndexSubset="
                    + fieldsLongIndexSubset + ", fieldsFloatIndexSubset=" + fieldsFloatIndexSubset
                    + ", fieldsDoubleIndexSubset=" + fieldsDoubleIndexSubset + ", fieldsStringIndexSubset="
                    + fieldsStringIndexSubset + '}';
        }
    }

    public static Map<String, Object> getDestringedData(FieldsDescriptor fd, Map<String, String> stringMap) {
        Map<String, Object> fieldToData = Maps.newHashMap();
        Map<String, Type> fieldToType = fd.getFieldToType();

        for (Map.Entry<String, String> entry : stringMap.entrySet()) {
            Object objValue;
            String valueString = entry.getValue();
            Type valueType = fieldToType.get(entry.getKey());

            switch (valueType) {
            case BOOLEAN: {
                objValue = Boolean.valueOf(valueString);
                break;
            }
            case BYTE: {
                objValue = Byte.valueOf(valueString);
                break;
            }
            case SHORT: {
                objValue = Short.valueOf(valueString);
                break;
            }
            case INTEGER: {
                objValue = Integer.valueOf(valueString);
                break;
            }
            case LONG: {
                objValue = Long.valueOf(valueString);
                break;
            }
            case FLOAT: {
                objValue = Float.valueOf(valueString);
                break;
            }
            case DOUBLE: {
                objValue = Double.valueOf(valueString);
                break;
            }
            case STRING: {
                objValue = valueString;
                break;
            }
            case OBJECT:
                throw new UnsupportedOperationException("The given type " + entry.getValue() + " is unsupported.");
            default:
                throw new UnsupportedOperationException("The given type " + entry.getValue() + " is unsupported.");
            }

            fieldToData.put(entry.getKey(), objValue);
        }

        return fieldToData;
    }

    public static Map<String, Object> convertToMapIntersection(GPOMutable gpo, Fields fields) {
        Map<String, Object> values = Maps.newHashMap();

        for (String field : fields.getFields()) {
            if (!gpo.getFieldDescriptor().getFields().getFields().contains(field)) {
                continue;
            }

            Object valueObj = gpo.getField(field);
            values.put(field, valueObj);
        }

        return values;
    }

    /**
     * Determines if the given value is within the range of the specified type.
     * @param type The type to determine the range of. Valid types can be byte or short.
     * @param val The value to check the range of.
     * @return True if the given int value is within the range of the specified type, false otherwise.
     */
    public static boolean insideRange(Type type, int val) {
        switch (type) {
        case BYTE: {
            return !(val < (int) Byte.MIN_VALUE || val > (int) Byte.MAX_VALUE);
        }
        case SHORT: {
            return !(val < (int) Short.MIN_VALUE || val > (int) Short.MAX_VALUE);
        }
        default:
            throw new UnsupportedOperationException("This operation is not supported for the type " + type);
        }
    }

    /**
     * Returns true if the given type is of type byte, short, or integer.
     * @param type The type to check.
     * @return True if the given type is of type byte, short or integer.
     */
    public static boolean numericTypeIntOrSmaller(Type type) {
        return type == Type.BYTE || type == Type.SHORT || type == Type.INTEGER;
    }
}