text:
//
// This file is part of the Fuel Java SDK.
//
// Copyright (c) 2013, 2014, 2015, ExactTarget, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the name of ExactTarget, Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package com.exacttarget.fuelsdk;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;

import com.exacttarget.fuelsdk.annotations.ExternalName;
import com.exacttarget.fuelsdk.annotations.InternalName;
import com.exacttarget.fuelsdk.annotations.InternalProperty;
import com.exacttarget.fuelsdk.annotations.SoapObject;
import com.exacttarget.fuelsdk.internal.APIObject;
import com.exacttarget.fuelsdk.internal.APIProperty;
import com.exacttarget.fuelsdk.internal.Attribute;
import com.exacttarget.fuelsdk.internal.ComplexFilterPart;
import com.exacttarget.fuelsdk.internal.CreateOptions;
import com.exacttarget.fuelsdk.internal.CreateRequest;
import com.exacttarget.fuelsdk.internal.CreateResponse;
import com.exacttarget.fuelsdk.internal.CreateResult;
import com.exacttarget.fuelsdk.internal.DataExtension;
import com.exacttarget.fuelsdk.internal.DataExtensionField;
import com.exacttarget.fuelsdk.internal.DataExtensionFieldType;
import com.exacttarget.fuelsdk.internal.DataExtensionObject;
import com.exacttarget.fuelsdk.internal.DataFolder;
import com.exacttarget.fuelsdk.internal.DeleteOptions;
import com.exacttarget.fuelsdk.internal.DeleteRequest;
import com.exacttarget.fuelsdk.internal.DeleteResponse;
import com.exacttarget.fuelsdk.internal.DeleteResult;
import com.exacttarget.fuelsdk.internal.FilterPart;
import com.exacttarget.fuelsdk.internal.Email;
import com.exacttarget.fuelsdk.internal.EmailType;
import com.exacttarget.fuelsdk.internal.ExtractDescription;
import com.exacttarget.fuelsdk.internal.ListClassificationEnum;
import com.exacttarget.fuelsdk.internal.ListTypeEnum;
import com.exacttarget.fuelsdk.internal.LogicalOperators;
import com.exacttarget.fuelsdk.internal.ObjectExtension;
import com.exacttarget.fuelsdk.internal.RetrieveRequest;
import com.exacttarget.fuelsdk.internal.RetrieveRequestMsg;
import com.exacttarget.fuelsdk.internal.RetrieveResponseMsg;
import com.exacttarget.fuelsdk.internal.SimpleFilterPart;
import com.exacttarget.fuelsdk.internal.SimpleOperators;
import com.exacttarget.fuelsdk.internal.Soap;
import com.exacttarget.fuelsdk.internal.Subscriber;
import com.exacttarget.fuelsdk.internal.SubscriberStatus;
import com.exacttarget.fuelsdk.internal.TriggeredSendDefinition;
import com.exacttarget.fuelsdk.internal.TriggeredSendStatusEnum;
import com.exacttarget.fuelsdk.internal.UpdateOptions;
import com.exacttarget.fuelsdk.internal.UpdateRequest;
import com.exacttarget.fuelsdk.internal.UpdateResponse;
import com.exacttarget.fuelsdk.internal.UpdateResult;

/**
 * An ETSoapObject represents an object
 * available via the SOAP API.
 */

public abstract class ETSoapObject extends ETApiObject {
    private static Logger logger = Logger.getLogger(ETSoapObject.class);

    /**
     * The page size
     */
    public final static int PAGE_SIZE = 2500;

    /**
    * Class constructor, Initializes a new instance of the class.
    */
    public ETSoapObject() {
        registerConverters();
    }

    /**
     *
     * @param <T>           The type which extends from ETSoapObject
     * @param client        The ETClient object
     * @param type          The class type to retrieve
     * @param page          The page number
     * @param pageSize      The page size
     * @param filter        The ETFilter object
     * @return              The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    public static <T extends ETSoapObject> ETResponse<T> retrieve(ETClient client,
                                                                  Class<T> type,
                                                                  Integer page,
                                                                  Integer pageSize,
                                                                  ETFilter filter)
        throws ETSdkException
    {
        if (page != null) {
            throw new ETSdkException("page argument not supported on this object type");
        }
        if (pageSize != null) {
            throw new ETSdkException("pageSize argument not supported on this object type");
        }

        if (filter.getOrderBy().size() != 0) {
            throw new ETSdkException("order by argument not supported on this object type");
        }

        return retrieve(client, null, filter, type);
    }

    /**
     * ETSoapObject has additional retrieve methods that take the
     * SOAP object name and request ID for continuing requests.
     * In most cases, the SOAP object name is the same as the
     * internal class name, but in a few cases, it is not (e.g.,
     * DataExtensionObjects require the name of
     * the data extension in brackets: DataExtensionObject[foo]).
     * @param <T>           The type which extends from ETSoapObject
     * @param client        The ETClient object
     * @param soapObjectName The object name to retrieve for SOAP
     * @param filter        The ETFilter object
     * @param type          The class type to retrieve
     * @return              The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    protected static <T extends ETSoapObject> ETResponse<T> retrieve(ETClient client,
                                                                     String soapObjectName,
                                                                     ETFilter filter,
                                                                     Class<T> type)
        throws ETSdkException
    {
        return retrieve(client, soapObjectName, filter, null, type);
    }

    /**
     *
     * @param <T>           The type which extends from ETSoapObject
     * @param client        The ETClient object
     * @param type          The class type to retrieve
     * @param continueRequest The continue request
     * @return              The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    protected static <T extends ETSoapObject> ETResponse<T> retrieve(ETClient client,
                                                                     String continueRequest,
                                                                     Class<T> type)
        throws ETSdkException
    {
        return retrieve(client, null, new ETFilter(), continueRequest, type);
    }

    /**
     *
     * @param <T>           The type which extends from ETSoapObject
     * @param client        The ETClient object
     * @param soapObjectName The object name to retrieve for SOAP
     * @param filter        The ETFilter object
     * @param continueRequest The continue request
     * @param type          The class type to retrieve
     * @return              The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    protected static <T extends ETSoapObject> ETResponse<T> retrieve(ETClient client,
                                                                     String soapObjectName,
                                                                     ETFilter filter,
                                                                     String continueRequest,
                                                                     Class<T> type)
        throws ETSdkException
    {
        ETResponse<T> response = new ETResponse<T>();

        //
        // Get handle to the SOAP connection:
        //

        ETSoapConnection connection = client.getSoapConnection();

        //
        // Automatically refresh the token if necessary:
        //

        client.refreshToken();

        //
        // Read internal type from the SoapObject annotation:
        //

        Class<T> externalType = type; // for code readability

        SoapObject internalTypeAnnotation
            = externalType.getAnnotation(SoapObject.class);
        assert internalTypeAnnotation != null;
        Class<? extends APIObject> internalType = internalTypeAnnotation.internalType();
        assert internalType != null;

        ETExpression expression = filter.getExpression();

        //
        // Determine properties to retrieve:
        //

        List<String> externalProperties = filter.getProperties();
        List<String> internalProperties = null;

        if (externalProperties.size() > 0) {
            //
            // Only request those properties specified:
            //

            internalProperties = new ArrayList<String>();

            for (String externalProperty : externalProperties) {
                String internalProperty =
                        getInternalProperty(externalType, externalProperty);
                assert internalProperty != null;
                internalProperties.add(internalProperty);
            }
        } else {
            //
            // No properties were explicitly requested:
            //

            internalProperties = getInternalProperties(externalType);

            //
            // Remove properties that are unretrievable:
            //

            for (String property : internalTypeAnnotation.unretrievable()) {
                internalProperties.remove(property);
            }
        }

        //
        // Perform the SOAP retrieve:
        //

        //Soap soap = connection.getSoap();
        Soap soap = null;

        RetrieveRequest retrieveRequest = new RetrieveRequest();

        if (continueRequest == null) {
            // if soapObjectType is specified, use it; otherwise, default
            // to the name of the internal class representing the object:
            if (soapObjectName != null) {
                retrieveRequest.setObjectType(soapObjectName);
                soap = connection.getSoap("retrieve", soapObjectName);
            } else {
                retrieveRequest.setObjectType(internalType.getSimpleName());
                soap = connection.getSoap("retrieve", internalType.getSimpleName());
            }
            retrieveRequest.getProperties().addAll(internalProperties);

            if (expression.getOperator() != null) {
                //
                // Convert the property names to their internal counterparts:
                //

                String property = expression.getProperty();
                if (property != null) {
                    expression.setProperty(getInternalProperty(type, property));
                }
                for (ETExpression subexpression : expression.getSubexpressions()) {
                    String p = subexpression.getProperty();
                    if (p != null) {
                        subexpression.setProperty(getInternalProperty(type, p));
                    }
                }

                retrieveRequest.setFilter(toFilterPart(expression));
            }
        } else {
            if (continueRequest != null) {
                retrieveRequest.setContinueRequest(continueRequest);
            }
        }

        if (logger.isTraceEnabled()) {
            logger.trace("RetrieveRequest:");
            logger.trace("  objectType = " + retrieveRequest.getObjectType());
            StringBuilder line = new StringBuilder("  properties = { ");
            //String line = null;
            for (String property : retrieveRequest.getProperties()) {

            	 line.append(property).append(", ");
            	             }
            	            if (retrieveRequest.getProperties().size() > 0) {
            	                line.setLength(line.length() - 2);
                /*if (line == null) {
                    line = "  properties = { " + property;
                } else {
                    line += ", " + property;
                }*/
            }
            line.append(" }");
            logger.trace(line.toString());
            //logger.trace(line + " }");
            if (filter != null) {
                logger.trace("  filter = " + toFilterPart(expression));
            }
        }

        logger.trace("calling soap.retrieve...");

        RetrieveRequestMsg retrieveRequestMsg = new RetrieveRequestMsg();
        retrieveRequestMsg.setRetrieveRequest(retrieveRequest);

        RetrieveResponseMsg retrieveResponseMsg = soap.retrieve(retrieveRequestMsg);

        if (logger.isTraceEnabled()) {
            logger.trace("RetrieveResponseMsg:");
            logger.trace("  requestId = " + retrieveResponseMsg.getRequestID());
            logger.trace("  overallStatus = " + retrieveResponseMsg.getOverallStatus());
            logger.trace("  results = {");
            for (APIObject result : retrieveResponseMsg.getResults()) {
                logger.trace("    " + result);
            }
            logger.trace("  }");
        }

        response.setRequestId(retrieveResponseMsg.getRequestID());
        if (retrieveResponseMsg.getOverallStatus().equals("OK")) {
            response.setStatus(ETResult.Status.OK);
        } else if (retrieveResponseMsg.getOverallStatus().equals("Error")) {
            response.setStatus(ETResult.Status.ERROR);
        }
        response.setResponseCode(retrieveResponseMsg.getOverallStatus());
        response.setResponseMessage(retrieveResponseMsg.getOverallStatus());
        for (APIObject internalObject : retrieveResponseMsg.getResults()) {
            //
            // Allocate a new (external) object:
            //

            T externalObject = null;
            try {
                externalObject = externalType.newInstance();
            } catch (Exception ex) {
                throw new ETSdkException("could not instantiate "
                        + externalType.getName(), ex);
            }

            externalObject.setClient(client);

            //
            // Convert from internal representation:
            //

            externalObject.fromInternal(internalObject);

            //
            // Add result to the list of results:
            //

            ETResult<T> result = new ETResult<T>();
            result.setObject(externalObject);
            response.addResult(result);
        }

        if (retrieveResponseMsg.getOverallStatus().equals("MoreDataAvailable")) {
            response.setMoreResults(true);
        }

        return response;
    }

    /**
     * @param <T>                   The type which extends from ETSoapObject
     * @param client                The ETClient object
     * @param objects               The List of objects to create
     * @return                      The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    public static <T extends ETSoapObject> ETResponse<T> create(ETClient client,
                                                                List<T> objects)
        throws ETSdkException
    {
        ETResponse<T> response = new ETResponse<T>();

        if (objects == null || objects.size() == 0) {
            response.setStatus(ETResult.Status.OK);
            return response;
        }

        //
        // Get handle to the SOAP connection:
        //

        ETSoapConnection connection = client.getSoapConnection();

        //
        // Automatically refresh the token if necessary:
        //

        client.refreshToken();

        //
        // Perform the SOAP create:
        //

//        Soap soap = connection.getSoap();
//        Soap soap = connection.getSoap("create");
        String obj = "";

        CreateRequest createRequest = new CreateRequest();
        createRequest.setOptions(new CreateOptions());
        for (T object : objects) {
            object.setClient(client);
            createRequest.getObjects().add(object.toInternal());
            obj += object.getClass().getSimpleName().substring(2);
        }
        Soap soap = connection.getSoap("create", obj);

        if (logger.isTraceEnabled()) {
            logger.trace("CreateRequest:");
            logger.trace("  objects = {");
            for (APIObject object : createRequest.getObjects()) {
                logger.trace("    " + object);
            }
            logger.trace("  }");
        }

        logger.trace("calling soap.create...");

        CreateResponse createResponse = soap.create(createRequest);

        if (logger.isTraceEnabled()) {
            logger.trace("CreateResponse:");
            logger.trace("  requestId = " + createResponse.getRequestID());
            logger.trace("  overallStatus = " + createResponse.getOverallStatus());
            logger.trace("  results = {");
            for (CreateResult result : createResponse.getResults()) {
                logger.trace("    " + result);
            }
            logger.trace("  }");
        }

        response.setRequestId(createResponse.getRequestID());
        if (createResponse.getOverallStatus().equals("OK")) {
            response.setStatus(ETResult.Status.OK);
        } else if (createResponse.getOverallStatus().equals("Error")) {
            response.setStatus(ETResult.Status.ERROR);
        }
        response.setResponseCode(createResponse.getOverallStatus());
        response.setResponseMessage(createResponse.getOverallStatus());
        for (CreateResult createResult : createResponse.getResults()) {
            //
            // Allocate a new (external) object:
            //

            @SuppressWarnings("unchecked")
            Class<T> externalType = (Class<T>) objects.get(0).getClass();

            T externalObject = null;
            try {
                externalObject = externalType.newInstance();
            } catch (Exception ex) {
                throw new ETSdkException("could not instantiate "
                        + externalType.getName(), ex);
            }

            externalObject.setClient(client);

            //
            // Convert from internal representation:
            //

            // not all SOAP calls return the object though some do..
            APIObject internalObject = createResult.getObject();
            if (internalObject != null) {
                externalObject.fromInternal(createResult.getObject());
            } else {
                // XXX populate fields from the object passed to the call?
                externalObject.setId(Integer.toString(createResult.getNewID()));
            }

            //
            // Add result to the list of results:
            //

            ETResult<T> result = new ETResult<T>();
            if (createResult.getStatusCode().equals("OK")) {
                result.setStatus(ETResult.Status.OK);
            } else if (createResult.getStatusCode().equals("Error")) {
                result.setStatus(ETResult.Status.ERROR);
            }
            result.setResponseCode(createResult.getStatusCode());
            result.setResponseMessage(createResult.getStatusMessage());
            result.setErrorCode(createResult.getErrorCode());
            if (result.getResponseCode().equals("OK")) { // XXX?
                result.setObject(externalObject);
            }
            response.addResult(result);
        }

        return response;
    }

    /**
     * @param <T>                   The type which extends from ETSoapObject
     * @param client                The ETClient object
     * @param objects               The List of objects to update
     * @return                      The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    public static <T extends ETSoapObject> ETResponse<T> update(ETClient client,
                                                                List<T> objects)
        throws ETSdkException
    {
        ETResponse<T> response = new ETResponse<T>();

        if (objects == null || objects.size() == 0) {
            response.setStatus(ETResult.Status.OK);
            return response;
        }

        //
        // Get handle to the SOAP connection:
        //

        ETSoapConnection connection = client.getSoapConnection();

        //
        // Automatically refresh the token if necessary:
        //

        client.refreshToken();

        //
        // Perform the SOAP update:
        //

//        Soap soap = connection.getSoap();
//        Soap soap = connection.getSoap("update");
        String obj = "";

        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.setOptions(new UpdateOptions());
        for (T object : objects) {
            object.setClient(client);
            updateRequest.getObjects().add(object.toInternal());
            obj += object.getClass().getSimpleName().substring(2);
        }
        Soap soap = connection.getSoap("update", obj);

        if (logger.isTraceEnabled()) {
            logger.trace("UpdateRequest:");
            logger.trace("  objects = {");
            for (APIObject object : updateRequest.getObjects()) {
                logger.trace("    " + object);
            }
            logger.trace("  }");
        }

        logger.trace("calling soap.update...");

        UpdateResponse updateResponse = soap.update(updateRequest);

        if (logger.isTraceEnabled()) {
            logger.trace("UpdateResponse:");
            logger.trace("  requestId = " + updateResponse.getRequestID());
            logger.trace("  overallStatus = " + updateResponse.getOverallStatus());
            logger.trace("  results = {");
            for (UpdateResult result : updateResponse.getResults()) {
                logger.trace("    " + result);
            }
            logger.trace("  }");
        }

        response.setRequestId(updateResponse.getRequestID());
        if (updateResponse.getOverallStatus().equals("OK")) {
            response.setStatus(ETResult.Status.OK);
        } else if (updateResponse.getOverallStatus().equals("Error")) {
            response.setStatus(ETResult.Status.ERROR);
        }
        response.setResponseCode(updateResponse.getOverallStatus());
        response.setResponseMessage(updateResponse.getOverallStatus());
        for (UpdateResult updateResult : updateResponse.getResults()) {
            //
            // Allocate a new (external) object:
            //

            @SuppressWarnings("unchecked")
            Class<T> externalType = (Class<T>) objects.get(0).getClass();

            T externalObject = null;
            try {
                externalObject = externalType.newInstance();
            } catch (Exception ex) {
                throw new ETSdkException("could not instantiate "
                        + externalType.getName(), ex);
            }

            externalObject.setClient(client);

            //
            // Convert from internal representation:
            //

            // not all SOAP calls return the object though some do..
            APIObject internalObject = updateResult.getObject();
            if (internalObject != null) {
                externalObject.fromInternal(updateResult.getObject());
            } else {
                // XXX populate fields from the object passed to the call?
            }

            //
            // Add result to the list of results:
            //

            ETResult<T> result = new ETResult<T>();
            if (updateResult.getStatusCode().equals("OK")) {
                result.setStatus(ETResult.Status.OK);
            } else if (updateResult.getStatusCode().equals("Error")) {
                result.setStatus(ETResult.Status.ERROR);
            }
            result.setResponseCode(updateResult.getStatusCode());
            result.setResponseMessage(updateResult.getStatusMessage());
            result.setErrorCode(updateResult.getErrorCode());
            if (result.getResponseCode().equals("OK")) { // XXX?
                result.setObject(externalObject);
            }
            response.addResult(result);
        }

        return response;
    }

    /**
     * @param <T>                   The type which extends from ETSoapObject
     * @param client                The ETClient object
     * @param objects               The List of objects to delete
     * @return                      The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    public static <T extends ETSoapObject> ETResponse<T> delete(ETClient client,
                                                                List<T> objects)
        throws ETSdkException
    {
        List<APIObject> internalObjects = new ArrayList<APIObject>();

        //
        // Convert the external objects to internal objects:
        //

        for (T object : objects) {
            object.setClient(client);
            internalObjects.add(object.toInternal());
        }

        return delete(client, internalObjects, true);
    }

    /**
     * @param <T>                   The type which extends from ETSoapObject
     * @param client                The ETClient object
     * @param objects               The List of APIObjects to delete
     * @param internal              true if it is internal, false otherwise
     * @return                      The ETResponse object of type T which extends from ETSoapObject
     * @throws ETSdkException
     */
    protected static <T extends ETSoapObject> ETResponse<T> delete(ETClient client,
                                                                   List<APIObject> objects,
                                                                   boolean internal)
        throws ETSdkException
    {
        ETResponse<T> response = new ETResponse<T>();

        if (objects == null || objects.size() == 0) {
            response.setStatus(ETResult.Status.OK);
            return response;
        }

        //
        // Get handle to the SOAP connection:
        //

        ETSoapConnection connection = client.getSoapConnection();

        //
        // Automatically refresh the token if necessary:
        //

        client.refreshToken();

        //
        // Perform the SOAP delete:
        //
        String obj = "";
        for (APIObject object : objects)
            obj += object.getClass().getSimpleName();//.substring(2);

        Soap soap = connection.getSoap("delete", obj);

        DeleteRequest deleteRequest = new DeleteRequest();
        deleteRequest.setOptions(new DeleteOptions());
        deleteRequest.getObjects().addAll(objects);

        if (logger.isTraceEnabled()) {
            logger.trace("DeleteRequest:");
            logger.trace("  objects = {");
            for (APIObject object : deleteRequest.getObjects()) {
                logger.trace("    " + object);
            }
            logger.trace("  }");
        }

        logger.trace("calling soap.delete...");

        DeleteResponse deleteResponse = soap.delete(deleteRequest);

        if (logger.isTraceEnabled()) {
            logger.trace("DeleteResponse:");
            logger.trace("  requestId = " + deleteResponse.getRequestID());
            logger.trace("  overallStatus = " + deleteResponse.getOverallStatus());
            logger.trace("  results = {");
            for (DeleteResult result : deleteResponse.getResults()) {
                logger.trace("    " + result);
            }
            logger.trace("  }");
        }

        response.setRequestId(deleteResponse.getRequestID());
        if (deleteResponse.getOverallStatus().equals("OK")) {
            response.setStatus(ETResult.Status.OK);
        } else if (deleteResponse.getOverallStatus().equals("Error")) {
            response.setStatus(ETResult.Status.ERROR);
        }
        response.setResponseCode(deleteResponse.getOverallStatus());
        response.setResponseMessage(deleteResponse.getOverallStatus());
        for (DeleteResult deleteResult : deleteResponse.getResults()) {
            ETResult<T> result = new ETResult<T>();
            if (deleteResult.getStatusCode().equals("OK")) {
                result.setStatus(ETResult.Status.OK);
            } else if (deleteResult.getStatusCode().equals("Error")) {
                result.setStatus(ETResult.Status.ERROR);
            }
            result.setResponseCode(deleteResult.getStatusCode());
            result.setResponseMessage(deleteResult.getStatusMessage());
            result.setErrorCode(deleteResult.getErrorCode());
            response.addResult(result);
        }

        return response;
    }

    private void registerConverters() {
        //
        // Register converters:
        //

        ConvertUtilsBean convertUtils = BeanUtilsBean.getInstance().getConvertUtils();

        // ETDataExtension
        convertUtils.register(new ExternalObjectConverter(),
                ETDataExtension.class);
        convertUtils.register(new InternalObjectConverter(),
                DataExtension.class);

//        convertUtils.register(new ExternalObjectConverter(),
//                ETSendableDataExtension.class);
        convertUtils.register(new ExternalObjectConverter(),
                ETProfileAttribute.class);

        // ETDataExtensionColumn
        convertUtils.register(new ExternalObjectConverter(),
                ETDataExtensionColumn.class);
        convertUtils.register(new InternalObjectConverter(),
                DataExtensionField.class);

        // ETExtractDescription
        convertUtils.register(new ExternalObjectConverter(),
                ETExtractDescription.class);
        convertUtils.register(new InternalObjectConverter(),
                ExtractDescription.class);

        // ETDataExtensionColumnType
        convertUtils.register(new EnumConverter(),
                ETDataExtensionColumn.Type.class);
        convertUtils.register(new EnumConverter(),
                DataExtensionFieldType.class);

        // ETDataExtensionRow
        convertUtils.register(new ExternalObjectConverter(),
                ETDataExtensionRow.class);
        convertUtils.register(new InternalObjectConverter(),
                DataExtensionObject.class);
        // data extension row: internal to external
        convertUtils.register(new DataExtensionRowConverter(),
                Map.class);
        // data extension row: external to internal
        convertUtils.register(new DataExtensionRowConverter(),
                ObjectExtension.Properties.class);

        // ETEmail
        convertUtils.register(new ExternalObjectConverter(),
                ETEmail.class);
        convertUtils.register(new InternalObjectConverter(),
                Email.class);

        // ETEmail.Type
        convertUtils.register(new EnumConverter(),
                ETEmail.Type.class);
        convertUtils.register(new EnumConverter(),
                EmailType.class);

        // ETFolder
        convertUtils.register(new ExternalObjectConverter(),
                ETFolder.class);
        convertUtils.register(new InternalObjectConverter(),
                DataFolder.class);

        // ETList
        convertUtils.register(new ExternalObjectConverter(),
                ETList.class);
        convertUtils.register(new InternalObjectConverter(),
                com.exacttarget.fuelsdk.internal.List.class);

        // ETList.Classification
        convertUtils.register(new EnumConverter(),
                ETList.Classification.class);
        convertUtils.register(new EnumConverter(),
                ListClassificationEnum.class);

        // ETList.Type
        convertUtils.register(new EnumConverter(),
                ETList.Type.class);
        convertUtils.register(new EnumConverter(),
                ListTypeEnum.class);

        // ETSubscriber
        convertUtils.register(new ExternalObjectConverter(),
                ETSubscriber.class);
        convertUtils.register(new InternalObjectConverter(),
                Subscriber.class);

        // ETSubscriber.Attribute
        convertUtils.register(new EnumConverter(),
                ETSubscriber.Status.class);
//        convertUtils.register(new EnumConverter(),
//                Attribute.class);
//        convertUtils.register(new InternalObjectConverter(),
//                Attribute.class);

        // ETSubscriber.Status
        convertUtils.register(new EnumConverter(),
                ETSubscriber.Status.class);
        convertUtils.register(new EnumConverter(),
                SubscriberStatus.class);

        // ETTriggeredSend
        convertUtils.register(new EnumConverter(),
                ETTriggeredEmail.class);
        convertUtils.register(new EnumConverter(),
                TriggeredSendDefinition.class);

        // ETTriggeredSend.Status
        convertUtils.register(new EnumConverter(),
                ETTriggeredEmail.Status.class);
        convertUtils.register(new EnumConverter(),
                TriggeredSendStatusEnum.class);
    }

    public class ExternalObjectConverter implements Converter {
        @SuppressWarnings("rawtypes")
        /**
         * @param type      The class type to convert
         * @param value     The value to convert
         * @return          The converted object
         */
        public Object convert(Class type, Object value) {
            ETSoapObject externalObject = null;
            try {
                externalObject = (ETSoapObject) type.newInstance();
            } catch (Exception ex) {
                throw new ConversionException("could not convert object", ex);
            }
            try {
                externalObject.fromInternal((APIObject) value);
            } catch (ETSdkException ex) {
                throw new ConversionException("could not convert object", ex);
            }
            return externalObject;
        }
    }

    public class InternalObjectConverter implements Converter {
        @SuppressWarnings("rawtypes")
        /**
         * @param type      The class type to convert
         * @param value     The value to convert
         * @return          The converted object
         */
        public Object convert(Class type, Object value) {
            APIObject internalObject = null;
            try {
                internalObject = ((ETSoapObject) value).toInternal();
            } catch (ETSdkException ex) {
                throw new ConversionException("could not convert object", ex);
            }
            return internalObject;
        }
    }

    public class DataExtensionRowConverter implements Converter {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        /**
         * @param type      The class type to convert
         * @param value     The value to convert
         * @return          The converted object
         */
        public Object convert(Class type, Object value) {
            if (type == Map.class) {
                // we're converting from internal to external
                ObjectExtension.Properties properties
                    = (ObjectExtension.Properties) value;
                Map<String, String> columns = new HashMap<String, String>();
                for (APIProperty property : properties.getProperty()) {
                    columns.put(property.getName(), property.getValue());
                }
                return columns;
            } else if (type == ObjectExtension.Properties.class) {
                // we're converting from external to internal
                Map<String, String> columns = (Map<String, String>) value;
                ObjectExtension.Properties properties
                    = new ObjectExtension.Properties();
                for (String key : columns.keySet()) {
                    APIProperty property = new APIProperty();
                    property.setName(key); property.setValue(columns.get(key));
                    properties.getProperty().add(property);
                }
                return properties;
            }
            return value;
        }
    }

    public class EnumConverter implements Converter {
        @SuppressWarnings({ "rawtypes", "unchecked" })
        /**
         * @param type      The class type to convert
         * @param value     The value to convert
         * @return          The converted object
         */
        public Object convert(Class type, Object value) {
            return Enum.valueOf(type, value.toString());
        }
    }

    /**
     *
     * @param internalObject  The internal APIObject
     * @return The ETSoapObject that is create from the internal APIObject parameter
     * @throws ETSdkException
     */
    public ETSoapObject fromInternal(APIObject internalObject)
        throws ETSdkException
    {
        ETSoapObject externalObject = this; // for code readability

        Class<? extends ETSoapObject> externalType = externalObject.getClass();
        String externalTypeName = externalType.getSimpleName();
        Class<? extends APIObject> internalType = internalObject.getClass();
        String internalTypeName = internalType.getSimpleName();

        logger.trace("converting object from internal type "
                + internalTypeName);
        logger.trace("                    to external type "
                + externalTypeName);

        for (Field externalField : getAllFields(externalType)) {
            //
            // Skip this field if it doesn't have the @ExternalName
            // annotation (it's an internal field):
            //

            ExternalName externalName =
                    externalField.getAnnotation(ExternalName.class);
            if (externalName == null) {
                continue;
            }

            String externalFieldName = externalField.getName();
            String internalFieldName = null;

            InternalName internalName =
                    externalField.getAnnotation(InternalName.class);

            if (internalName != null) {
                internalFieldName = internalName.value();
            } else {
                // internal name is the same as external name
                internalFieldName = externalFieldName;
            }

            Object internalFieldValue = null;
            try {
                internalFieldValue =
                        PropertyUtils.getProperty(internalObject,
                                                  internalFieldName);
            } catch (Exception ex) {
                throw new ETSdkException("could not get property \""
                        + internalFieldName
                        + "\" of object "
                        + internalObject,
                        ex);
            }

            if (internalFieldValue == null) {
                continue;
            }

            if (internalFieldValue instanceof List) {
                externalField.setAccessible(true);

                List<ETSoapObject> externalList = new ArrayList<ETSoapObject>();
                @SuppressWarnings("unchecked")
                List<APIObject> internalList
                    = (List<APIObject>) internalFieldValue;

                Type fieldType = externalField.getGenericType();
                assert fieldType instanceof ParameterizedType;
                ParameterizedType parameterizedType
                    = (ParameterizedType) fieldType;
                assert parameterizedType.getActualTypeArguments().length == 1;
                Class<?> externalItemType
                    = (Class<?>) parameterizedType.getActualTypeArguments()[0];

                for (APIObject internalItem : internalList) {
                    ETSoapObject externalItem = null;
                    try {
                        externalItem = (ETSoapObject) externalItemType.newInstance();
                    } catch (Exception ex) {
                        throw new ETSdkException("could not instantiate "
                                + externalItemType.getName(), ex);
                    }
                    externalList.add(externalItem.fromInternal(internalItem));
                }

                try {
                    externalField.set(externalObject, externalList);
                } catch (Exception ex) {
                    throw new ETSdkException("could not set field \""
                            + externalFieldName
                            + "\" of object "
                            + externalObject,
                            ex);
                }

                continue;
            }

            try {
                BeanUtils.setProperty(externalObject,
                                      externalFieldName,
                                      internalFieldValue);
            } catch (Exception ex) {
                throw new ETSdkException("could not set property \""
                        + externalFieldName
                        + "\" of object "
                        + externalObject,
                        ex);
            }

            if (logger.isTraceEnabled()) {
                Field internalField = getField(internalType,
                                               internalFieldName);

                Object externalFieldValue = null;
                try {
                    externalFieldValue =
                            PropertyUtils.getProperty(externalObject,
                                                      externalFieldName);
                } catch (Exception ex) {
                    throw new ETSdkException("could not get property \""
                            + externalFieldName
                            + "\" of object "
                            + externalObject,
                            ex);
                }

                logger.trace("  converted field "
                        + internalTypeName + "." + internalFieldName
                        + " (type="
                        + internalField.getType().getSimpleName()
                        + ", value="
                        + internalFieldValue
                        + ")");
                logger.trace("         to field "
                        + externalTypeName + "." + externalFieldName
                        + " (type="
                        + externalField.getType().getSimpleName()
                        + ", value="
                        + externalFieldValue
                        + ")");
            }
        }

        return externalObject;
    }

    /**
     *
     * @return The internal APIObject
     * @throws ETSdkException
     */
    public APIObject toInternal()
        throws ETSdkException
    {
        ETSoapObject externalObject = this; // for code readability

        Class<? extends ETSoapObject> externalType = externalObject.getClass();

        //
        // Use the @SoapObject annotation to determine internalType:
        //

        SoapObject internalTypeAnnotation
            = externalType.getAnnotation(SoapObject.class);
        assert internalTypeAnnotation != null;
        Class<? extends APIObject> internalType = internalTypeAnnotation.internalType();
        assert internalType != null;

        String externalTypeName = externalType.getSimpleName();
        String internalTypeName = internalType.getSimpleName();

        logger.trace("converting object from external type "
                + externalTypeName);
        logger.trace("                    to internal type "
                + internalTypeName);

        APIObject internalObject = null;
        try {
            internalObject = internalType.newInstance();
        } catch (Exception ex) {
            throw new ETSdkException("could not instantiate "
                    + internalType.getName(), ex);
        }

        for (Field externalField : getAllFields(externalType)) {
            //
            // Skip this field if it doesn't have the @ExternalName
            // annotation (it's an internal field):
            //

            ExternalName externalName =
                    externalField.getAnnotation(ExternalName.class);
            if (externalName == null) {
                continue;
            }

            String externalFieldName = externalField.getName();
            String internalFieldName = null;

            InternalName internalName =
                    externalField.getAnnotation(InternalName.class);

            if (internalName != null) {
                internalFieldName = internalName.value();
            } else {
                // internal name is the same as external name
                internalFieldName = externalFieldName;
            }

            Object externalFieldValue = null;
            try {
                externalFieldValue =
                        PropertyUtils.getProperty(externalObject,
                                                  externalFieldName);
            } catch (Exception ex) {
                throw new ETSdkException("could not get property \""
                        + externalFieldName
                        + "\" of object "
                        + externalObject,
                        ex);
            }

            if (externalFieldValue == null) {
                continue;
            }

            if (externalFieldValue instanceof List) {
                Field internalField = getField(internalType,
                                               internalFieldName);

                internalField.setAccessible(true);

                List<APIObject> internalList = new ArrayList<APIObject>();
                @SuppressWarnings("unchecked")
                List<ETSoapObject> externalList
                    = (List<ETSoapObject>) externalFieldValue;

                for (ETSoapObject externalItem : externalList) {
                    internalList.add(externalItem.toInternal());
                }

                if (internalFieldName.equals("fields")) {
                    //
                    // This list contains data extension columns:
                    //

                    DataExtension.Fields fields = new DataExtension.Fields();
                    for (APIObject field : internalList) {
                        fields.getField().add((DataExtensionField) field);
                    }
                    try {
                        internalField.set(internalObject, fields);
                    } catch (Exception ex) {
                        throw new ETSdkException("could not set field \""
                                + internalFieldName
                                + "\" of object "
                                + internalObject,
                                ex);
                    }
                } else {
                    try {
                        internalField.set(internalObject, internalList);
                    } catch (Exception ex) {
                        throw new ETSdkException("could not set field \""
                                + internalFieldName
                                + "\" of object "
                                + internalObject,
                                ex);
                    }
                }

                continue;
            }

            try {
                BeanUtils.setProperty(internalObject,
                                      internalFieldName,
                                      externalFieldValue);
            } catch (Exception ex) {
                //ex.printStackTrace();
                throw new ETSdkException("could not set property \""
                        + internalFieldName
                        + "\" of object "
                        + internalObject,
                        ex);
            }

            if (logger.isTraceEnabled()) {
                Field internalField = getField(internalType,
                                               internalFieldName);

                Object internalFieldValue = null;
                try {
                    internalFieldValue =
                            PropertyUtils.getProperty(internalObject,
                                                      internalFieldName);
                } catch (Exception ex) {
                    throw new ETSdkException("could not get property \""
                            + internalFieldName
                            + "\" of object "
                            + internalObject,
                            ex);
                }

                logger.trace("  converted field "
                        + externalTypeName + "." + externalFieldName
                        + " (type="
                        + externalField.getType().getSimpleName()
                        + ", value="
                        + externalFieldValue
                        + ")");
                logger.trace("         to field "
                        + internalTypeName + "." + internalFieldName
                        + " (type="
                        + internalField.getType().getSimpleName()
                        + ", value="
                        + internalFieldValue
                        + ")");
            }
        }

        return internalObject;
    }

    protected static String getInternalProperty(Class<? extends ETSoapObject> type,
                                                String name)
        throws ETSdkException
    {
        String internalProperty = null;

        Class<? extends ETSoapObject> externalType = type; // for code readability

        Field externalField = null;
        try {
            externalField = getField(externalType, name);
        } catch (ETSdkException ex) {
            return name; // XXX
        }

        InternalProperty internalPropertyAnnotation =
                externalField.getAnnotation(InternalProperty.class);

        if (internalPropertyAnnotation != null) {
            internalProperty = internalPropertyAnnotation.value();
        } else {
            //
            // The internal property name can be found in the
            // @XmlElement (or @XmlElementRef) annotation on the
            // NAME internal field of the CXF generated class:
            //

            //
            // Use the @SoapObject annotation to determine internalType:
            //

            SoapObject internalTypeAnnotation
                = externalType.getAnnotation(SoapObject.class);
            assert internalTypeAnnotation != null;
            Class<? extends APIObject> internalType = internalTypeAnnotation.internalType();
            assert internalType != null;

            InternalName internalNameAnnotation =
                    externalField.getAnnotation(InternalName.class);

            String internalName = null;
            if (internalNameAnnotation != null) {
                internalName = internalNameAnnotation.value();
            } else {
                // internal name is the same as external name
                internalName = externalField.getName();
            }

            Field internalField = getField(internalType, internalName);

            XmlElement element =
                    internalField.getAnnotation(XmlElement.class);
            if (element != null) {
                internalProperty = element.name();
            } else {
                //
                // Optional dateTimes are annotated with @XmlElementRef:
                //

                XmlElementRef elementRef =
                        internalField.getAnnotation(XmlElementRef.class);
                if (elementRef != null) {
                    internalProperty = elementRef.name();
                }
            }
        }

        return internalProperty;
    }

    // XXX private?
    protected static List<String> getInternalProperties(Class<? extends ETSoapObject> type)
        throws ETSdkException
    {
        List<String> internalProperties = new ArrayList<String>();

        Class<? extends ETSoapObject> externalType = type; // for code readability

        //
        // Use the @SoapObject annotation to determine internalType:
        //

        SoapObject internalTypeAnnotation
            = externalType.getAnnotation(SoapObject.class);
        assert internalTypeAnnotation != null;
        Class<? extends APIObject> internalType = internalTypeAnnotation.internalType();
        assert internalType != null;

        //
        // Build a list of fields from externalType and all superclasses:
        //

        List<Field> externalFields = getAllFields(externalType);

        //
        // Walk the list of external fields, building a list of the
        // corresponding property names:
        //

        for (Field externalField : externalFields) {
            //
            // Skip this field if it doesn't have the @ExternalName
            // annotation (it's an internal field):
            //

            ExternalName externalName =
                    externalField.getAnnotation(ExternalName.class);
            if (externalName == null) {
                continue;
            }

            String internalProperty = getInternalProperty(externalType,
                                                          externalField.getName());
            assert internalProperty != null;
            internalProperties.add(internalProperty);
        }

        return internalProperties;
    }

    /**
     *
     * @param expression The ETExpression to create FilterPart
     * @return The FilterPart object, can be SimpleFilterPart or ComplexFilterPart
     */
    public static FilterPart toFilterPart(ETExpression expression) {
        ETExpression.Operator operator = expression.getOperator();
        if (operator == null) {
            return null;
        }
        if (operator == ETExpression.Operator.AND ||
            operator == ETExpression.Operator.OR)
        {
            List<ETExpression> subexpressions = expression.getSubexpressions();
            ComplexFilterPart complexFilterPart = new ComplexFilterPart();
            complexFilterPart.setLeftOperand(toFilterPart(subexpressions.get(0)));
            if (operator == ETExpression.Operator.AND) {
                complexFilterPart.setLogicalOperator(LogicalOperators.AND);
            } else if (operator == ETExpression.Operator.OR) {
                complexFilterPart.setLogicalOperator(LogicalOperators.OR);
            }
            complexFilterPart.setRightOperand(toFilterPart(subexpressions.get(1)));
            return complexFilterPart;
        } else {
            String property = expression.getProperty();
            List<String> values = expression.getValues();
            SimpleFilterPart simpleFilterPart = new SimpleFilterPart();
            simpleFilterPart.setProperty(property);
            switch (operator) {
              case EQUALS:
                simpleFilterPart.setSimpleOperator(SimpleOperators.EQUALS);
                break;
              case NOT_EQUALS:
                simpleFilterPart.setSimpleOperator(SimpleOperators.NOT_EQUALS);
                break;
              case LESS_THAN:
                simpleFilterPart.setSimpleOperator(SimpleOperators.LESS_THAN);
                break;
              case LESS_THAN_OR_EQUALS:
                simpleFilterPart.setSimpleOperator(SimpleOperators.LESS_THAN_OR_EQUAL);
                break;
              case GREATER_THAN:
                simpleFilterPart.setSimpleOperator(SimpleOperators.GREATER_THAN);
                break;
              case GREATER_THAN_OR_EQUALS:
                simpleFilterPart.setSimpleOperator(SimpleOperators.GREATER_THAN_OR_EQUAL);
                break;
              case IS_NULL:
                simpleFilterPart.setSimpleOperator(SimpleOperators.IS_NULL);
                break;
              case IS_NOT_NULL:
                simpleFilterPart.setSimpleOperator(SimpleOperators.IS_NOT_NULL);
                break;
              case BETWEEN:
                simpleFilterPart.setSimpleOperator(SimpleOperators.BETWEEN);
                break;
              case IN:
                simpleFilterPart.setSimpleOperator(SimpleOperators.IN);
                break;
              case LIKE:
                simpleFilterPart.setSimpleOperator(SimpleOperators.LIKE);
                break;
              default:
                break;
            }
            for (String value : values) {
                simpleFilterPart.getValue().add(value);
            }
            return simpleFilterPart;
        }
    }
}


text:
DEV=		ascii

.include "../Makefile.tty"
.include "../Makefile.dev"

redpajama_set_name: RedPajamaGithub


text:
package org.apache.cassandra.db.filter;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.base.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.context.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;

import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;

/**
 * A filter on which rows a given query should include or exclude.
 *

 * This corresponds to the restrictions on rows that are not handled by the query
 * {@link ClusteringIndexFilter}. Some of the expressions of this filter may
 * be handled by a 2ndary index, and the rest is simply filtered out from the
 * result set (the later can only happen if the query was using ALLOW FILTERING).
 */
public abstract class RowFilter implements Iterable<RowFilter.Expression>
{
    private static final Logger logger = LoggerFactory.getLogger(RowFilter.class);

    public static final Serializer serializer = new Serializer();
    public static final RowFilter NONE = new CQLFilter(Collections.emptyList());

    protected final List<Expression> expressions;

    protected RowFilter(List<Expression> expressions)
    {
        this.expressions = expressions;
    }

    public static RowFilter create()
    {
        return new CQLFilter(new ArrayList<>());
    }

    public static RowFilter create(int capacity)
    {
        return new CQLFilter(new ArrayList<>(capacity));
    }

    public SimpleExpression add(ColumnMetadata def, Operator op, ByteBuffer value)
    {
        SimpleExpression expression = new SimpleExpression(def, op, value);
        add(expression);
        return expression;
    }

    public void addMapEquality(ColumnMetadata def, ByteBuffer key, Operator op, ByteBuffer value)
    {
        add(new MapEqualityExpression(def, key, op, value));
    }

    public void addCustomIndexExpression(TableMetadata metadata, IndexMetadata targetIndex, ByteBuffer value)
    {
        add(new CustomExpression(metadata, targetIndex, value));
    }

    private void add(Expression expression)
    {
        expression.validate();
        expressions.add(expression);
    }

    public void addUserExpression(UserExpression e)
    {
        expressions.add(e);
    }

    public List<Expression> getExpressions()
    {
        return expressions;
    }

    /**
     * Filters the provided iterator so that only the row satisfying the expression of this filter
     * are included in the resulting iterator.
     *
     * @param iter the iterator to filter
     * @param nowInSec the time of query in seconds.
     * @return the filtered iterator.
     */
    public abstract UnfilteredPartitionIterator filter(UnfilteredPartitionIterator iter, int nowInSec);

    /**
     * Whether the provided row in the provided partition satisfies this filter.
     *
     * @param metadata the table metadata.
     * @param partitionKey the partition key for partition to test.
     * @param row the row to test.
     * @param nowInSec the current time in seconds (to know what is live and what isn't).
     * @return {@code true} if {@code row} in partition {@code partitionKey} satisfies this row filter.
     */
    public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, Row row, int nowInSec)
    {
        // We purge all tombstones as the expressions isSatisfiedBy methods expects it
        Row purged = row.purge(DeletionPurger.PURGE_ALL, nowInSec);
        if (purged == null)
            return expressions.isEmpty();

        for (Expression e : expressions)
        {
            if (!e.isSatisfiedBy(metadata, partitionKey, purged))
                return false;
        }
        return true;
    }

    /**
     * Returns true if all of the expressions within this filter that apply to the partition key are satisfied by
     * the given key, false otherwise.
     */
    public boolean partitionKeyRestrictionsAreSatisfiedBy(DecoratedKey key, AbstractType<?> keyValidator)
    {
        for (Expression e : expressions)
        {
            if (!e.column.isPartitionKey())
                continue;

            ByteBuffer value = keyValidator instanceof CompositeType
                             ? ((CompositeType) keyValidator).split(key.getKey())[e.column.position()]
                             : key.getKey();
            if (!e.operator().isSatisfiedBy(e.column.type, value, e.value))
                return false;
        }
        return true;
    }

    /**
     * Returns true if all of the expressions within this filter that apply to the clustering key are satisfied by
     * the given Clustering, false otherwise.
     */
    public boolean clusteringKeyRestrictionsAreSatisfiedBy(Clustering clustering)
    {
        for (Expression e : expressions)
        {
            if (!e.column.isClusteringColumn())
                continue;

            if (!e.operator().isSatisfiedBy(e.column.type, clustering.get(e.column.position()), e.value))
            {
                return false;
            }
        }
        return true;
    }

    /**
     * Returns this filter but without the provided expression. This method
     * *assumes* that the filter contains the provided expression.
     */
    public RowFilter without(Expression expression)
    {
        assert expressions.contains(expression);
        if (expressions.size() == 1)
            return RowFilter.NONE;

        List<Expression> newExpressions = new ArrayList<>(expressions.size() - 1);
        for (Expression e : expressions)
            if (!e.equals(expression))
                newExpressions.add(e);

        return withNewExpressions(newExpressions);
    }

    public RowFilter withoutExpressions()
    {
        return withNewExpressions(Collections.emptyList());
    }

    protected abstract RowFilter withNewExpressions(List<Expression> expressions);

    public boolean isEmpty()
    {
        return expressions.isEmpty();
    }

    public Iterator<Expression> iterator()
    {
        return expressions.iterator();
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < expressions.size(); i++)
        {
            if (i > 0)
                sb.append(" AND ");
            sb.append(expressions.get(i));
        }
        return sb.toString();
    }

    private static class CQLFilter extends RowFilter
    {
        private CQLFilter(List<Expression> expressions)
        {
            super(expressions);
        }

        public UnfilteredPartitionIterator filter(UnfilteredPartitionIterator iter, int nowInSec)
        {
            if (expressions.isEmpty())
                return iter;

            final TableMetadata metadata = iter.metadata();

            List<Expression> partitionLevelExpressions = new ArrayList<>();
            List<Expression> rowLevelExpressions = new ArrayList<>();
            for (Expression e: expressions)
            {
                if (e.column.isStatic() || e.column.isPartitionKey())
                    partitionLevelExpressions.add(e);
                else
                    rowLevelExpressions.add(e);
            }

            long numberOfRegularColumnExpressions = rowLevelExpressions.size();
            final boolean filterNonStaticColumns = numberOfRegularColumnExpressions > 0;

            class IsSatisfiedFilter extends Transformation<UnfilteredRowIterator>
            {
                DecoratedKey pk;
                public UnfilteredRowIterator applyToPartition(UnfilteredRowIterator partition)
                {
                    pk = partition.partitionKey();

                    // Short-circuit all partitions that won't match based on static and partition keys
                    for (Expression e : partitionLevelExpressions)
                        if (!e.isSatisfiedBy(metadata, partition.partitionKey(), partition.staticRow()))
                        {
                            partition.close();
                            return null;
                        }

                    UnfilteredRowIterator iterator = Transformation.apply(partition, this);
                    if (filterNonStaticColumns && !iterator.hasNext())
                    {
                        iterator.close();
                        return null;
                    }

                    return iterator;
                }

                public Row applyToRow(Row row)
                {
                    Row purged = row.purge(DeletionPurger.PURGE_ALL, nowInSec);
                    if (purged == null)
                        return null;

                    for (Expression e : rowLevelExpressions)
                        if (!e.isSatisfiedBy(metadata, pk, purged))
                            return null;

                    return row;
                }
            }

            return Transformation.apply(iter, new IsSatisfiedFilter());
        }

        protected RowFilter withNewExpressions(List<Expression> expressions)
        {
            return new CQLFilter(expressions);
        }
    }

    public static abstract class Expression
    {
        private static final Serializer serializer = new Serializer();

        // Note: the order of this enum matter, it's used for serialization,
        // and this is why we have some UNUSEDX for values we don't use anymore
        // (we could clean those on a major protocol update, but it's not worth
        // the trouble for now)
        protected enum Kind { SIMPLE, MAP_EQUALITY, UNUSED1, CUSTOM, USER }

        protected abstract Kind kind();
        protected final ColumnMetadata column;
        protected final Operator operator;
        protected final ByteBuffer value;

        protected Expression(ColumnMetadata column, Operator operator, ByteBuffer value)
        {
            this.column = column;
            this.operator = operator;
            this.value = value;
        }

        public boolean isCustom()
        {
            return kind() == Kind.CUSTOM;
        }

        public boolean isUserDefined()
        {
            return kind() == Kind.USER;
        }

        public ColumnMetadata column()
        {
            return column;
        }

        public Operator operator()
        {
            return operator;
        }

        /**
         * Checks if the operator of this IndexExpression is a CONTAINS operator.
         *
         * @return true if the operator of this IndexExpression is a CONTAINS
         * operator, false otherwise.
         */
        public boolean isContains()
        {
            return Operator.CONTAINS == operator;
        }

        /**
         * Checks if the operator of this IndexExpression is a CONTAINS_KEY operator.
         *
         * @return true if the operator of this IndexExpression is a CONTAINS_KEY
         * operator, false otherwise.
         */
        public boolean isContainsKey()
        {
            return Operator.CONTAINS_KEY == operator;
        }

        /**
         * If this expression is used to query an index, the value to use as
         * partition key for that index query.
         */
        public ByteBuffer getIndexValue()
        {
            return value;
        }

        public void validate()
        {
            checkNotNull(value, "Unsupported null value for column %s", column.name);
            checkBindValueSet(value, "Unsupported unset value for column %s", column.name);
        }

        @Deprecated
        public void validateForIndexing()
        {
            checkFalse(value.remaining() > FBUtilities.MAX_UNSIGNED_SHORT,
                       "Index expression values may not be larger than 64K");
        }

        /**
         * Returns whether the provided row satisfied this expression or not.
         *
         *
         * @param metadata
         * @param partitionKey the partition key for row to check.
         * @param row the row to check. It should *not* contain deleted cells
         * (i.e. it should come from a RowIterator).
         * @return whether the row is satisfied by this expression.
         */
        public abstract boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, Row row);

        protected ByteBuffer getValue(TableMetadata metadata, DecoratedKey partitionKey, Row row)
        {
            switch (column.kind)
            {
                case PARTITION_KEY:
                    return metadata.partitionKeyType instanceof CompositeType
                         ? CompositeType.extractComponent(partitionKey.getKey(), column.position())
                         : partitionKey.getKey();
                case CLUSTERING:
                    return row.clustering().get(column.position());
                default:
                    Cell cell = row.getCell(column);
                    return cell == null ? null : cell.value();
            }
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o)
                return true;

            if (!(o instanceof Expression))
                return false;

            Expression that = (Expression)o;

            return Objects.equal(this.kind(), that.kind())
                && Objects.equal(this.column.name, that.column.name)
                && Objects.equal(this.operator, that.operator)
                && Objects.equal(this.value, that.value);
        }

        @Override
        public int hashCode()
        {
            return Objects.hashCode(column.name, operator, value);
        }

        private static class Serializer
        {
            public void serialize(Expression expression, DataOutputPlus out, int version) throws IOException
            {
                out.writeByte(expression.kind().ordinal());

                // Custom expressions include neither a column or operator, but all
                // other expressions do.
                if (expression.kind() == Kind.CUSTOM)
                {
                    IndexMetadata.serializer.serialize(((CustomExpression)expression).targetIndex, out, version);
                    ByteBufferUtil.writeWithShortLength(expression.value, out);
                    return;
                }

                if (expression.kind() == Kind.USER)
                {
                    UserExpression.serialize((UserExpression)expression, out, version);
                    return;
                }

                ByteBufferUtil.writeWithShortLength(expression.column.name.bytes, out);
                expression.operator.writeTo(out);

                switch (expression.kind())
                {
                    case SIMPLE:
                        ByteBufferUtil.writeWithShortLength(((SimpleExpression)expression).value, out);
                        break;
                    case MAP_EQUALITY:
                        MapEqualityExpression mexpr = (MapEqualityExpression)expression;
                        ByteBufferUtil.writeWithShortLength(mexpr.key, out);
                        ByteBufferUtil.writeWithShortLength(mexpr.value, out);
                        break;
                }
            }

            public Expression deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
            {
                Kind kind = Kind.values()[in.readByte()];

                // custom expressions (3.0+ only) do not contain a column or operator, only a value
                if (kind == Kind.CUSTOM)
                {
                    return new CustomExpression(metadata,
                            IndexMetadata.serializer.deserialize(in, version, metadata),
                            ByteBufferUtil.readWithShortLength(in));
                }

                if (kind == Kind.USER)
                    return UserExpression.deserialize(in, version, metadata);

                ByteBuffer name = ByteBufferUtil.readWithShortLength(in);
                Operator operator = Operator.readFrom(in);
                ColumnMetadata column = metadata.getColumn(name);

                if (!metadata.isCompactTable() && column == null)
                    throw new RuntimeException("Unknown (or dropped) column " + UTF8Type.instance.getString(name) + " during deserialization");

                switch (kind)
                {
                    case SIMPLE:
                        return new SimpleExpression(column, operator, ByteBufferUtil.readWithShortLength(in));
                    case MAP_EQUALITY:
                        ByteBuffer key = ByteBufferUtil.readWithShortLength(in);
                        ByteBuffer value = ByteBufferUtil.readWithShortLength(in);
                        return new MapEqualityExpression(column, key, operator, value);
                }
                throw new AssertionError();
            }

            public long serializedSize(Expression expression, int version)
            {
                long size = 1; // kind byte

                // Custom expressions include neither a column or operator, but all
                // other expressions do.
                if (expression.kind() != Kind.CUSTOM && expression.kind() != Kind.USER)
                    size += ByteBufferUtil.serializedSizeWithShortLength(expression.column().name.bytes)
                            + expression.operator.serializedSize();

                switch (expression.kind())
                {
                    case SIMPLE:
                        size += ByteBufferUtil.serializedSizeWithShortLength(((SimpleExpression)expression).value);
                        break;
                    case MAP_EQUALITY:
                        MapEqualityExpression mexpr = (MapEqualityExpression)expression;
                        size += ByteBufferUtil.serializedSizeWithShortLength(mexpr.key)
                              + ByteBufferUtil.serializedSizeWithShortLength(mexpr.value);
                        break;
                    case CUSTOM:
                        size += IndexMetadata.serializer.serializedSize(((CustomExpression)expression).targetIndex, version)
                               + ByteBufferUtil.serializedSizeWithShortLength(expression.value);
                        break;
                    case USER:
                        size += UserExpression.serializedSize((UserExpression)expression, version);
                        break;
                }
                return size;
            }
        }
    }

    /**
     * An expression of the form 'column' 'op' 'value'.
     */
    public static class SimpleExpression extends Expression
    {
        SimpleExpression(ColumnMetadata column, Operator operator, ByteBuffer value)
        {
            super(column, operator, value);
        }

        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, Row row)
        {
            // We support null conditions for LWT (in ColumnCondition) but not for RowFilter.
            // TODO: we should try to merge both code someday.
            assert value != null;

            switch (operator)
            {
                case EQ:
                case LT:
                case LTE:
                case GTE:
                case GT:
                    {
                        assert !column.isComplex() : "Only CONTAINS and CONTAINS_KEY are supported for 'complex' types";

                        // In order to support operators on Counter types, their value has to be extracted from internal
                        // representation. See CASSANDRA-11629
                        if (column.type.isCounter())
                        {
                            ByteBuffer foundValue = getValue(metadata, partitionKey, row);
                            if (foundValue == null)
                                return false;

                            ByteBuffer counterValue = LongType.instance.decompose(CounterContext.instance().total(foundValue));
                            return operator.isSatisfiedBy(LongType.instance, counterValue, value);
                        }
                        else
                        {
                            // Note that CQL expression are always of the form 'x < 4', i.e. the tested value is on the left.
                            ByteBuffer foundValue = getValue(metadata, partitionKey, row);
                            return foundValue != null && operator.isSatisfiedBy(column.type, foundValue, value);
                        }
                    }
                case NEQ:
                case LIKE_PREFIX:
                case LIKE_SUFFIX:
                case LIKE_CONTAINS:
                case LIKE_MATCHES:
                    {
                        assert !column.isComplex() : "Only CONTAINS and CONTAINS_KEY are supported for 'complex' types";
                        ByteBuffer foundValue = getValue(metadata, partitionKey, row);
                        // Note that CQL expression are always of the form 'x < 4', i.e. the tested value is on the left.
                        return foundValue != null && operator.isSatisfiedBy(column.type, foundValue, value);
                    }
                case CONTAINS:
                    assert column.type.isCollection();
                    CollectionType<?> type = (CollectionType<?>)column.type;
                    if (column.isComplex())
                    {
                        ComplexColumnData complexData = row.getComplexColumnData(column);
                        if (complexData != null)
                        {
                            for (Cell cell : complexData)
                            {
                                if (type.kind == CollectionType.Kind.SET)
                                {
                                    if (type.nameComparator().compare(cell.path().get(0), value) == 0)
                                        return true;
                                }
                                else
                                {
                                    if (type.valueComparator().compare(cell.value(), value) == 0)
                                        return true;
                                }
                            }
                        }
                        return false;
                    }
                    else
                    {
                        ByteBuffer foundValue = getValue(metadata, partitionKey, row);
                        if (foundValue == null)
                            return false;

                        switch (type.kind)
                        {
                            case LIST:
                                ListType<?> listType = (ListType<?>)type;
                                return listType.compose(foundValue).contains(listType.getElementsType().compose(value));
                            case SET:
                                SetType<?> setType = (SetType<?>)type;
                                return setType.compose(foundValue).contains(setType.getElementsType().compose(value));
                            case MAP:
                                MapType<?,?> mapType = (MapType<?, ?>)type;
                                return mapType.compose(foundValue).containsValue(mapType.getValuesType().compose(value));
                        }
                        throw new AssertionError();
                    }
                case CONTAINS_KEY:
                    assert column.type.isCollection() && column.type instanceof MapType;
                    MapType<?, ?> mapType = (MapType<?, ?>)column.type;
                    if (column.isComplex())
                    {
                         return row.getCell(column, CellPath.create(value)) != null;
                    }
                    else
                    {
                        ByteBuffer foundValue = getValue(metadata, partitionKey, row);
                        return foundValue != null && mapType.getSerializer().getSerializedValue(foundValue, value, mapType.getKeysType()) != null;
                    }

                case IN:
                    // It wouldn't be terribly hard to support this (though doing so would imply supporting
                    // IN for 2ndary index) but currently we don't.
                    throw new AssertionError();
            }
            throw new AssertionError();
        }

        @Override
        public String toString()
        {
            AbstractType<?> type = column.type;
            switch (operator)
            {
                case CONTAINS:
                    assert type instanceof CollectionType;
                    CollectionType<?> ct = (CollectionType<?>)type;
                    type = ct.kind == CollectionType.Kind.SET ? ct.nameComparator() : ct.valueComparator();
                    break;
                case CONTAINS_KEY:
                    assert type instanceof MapType;
                    type = ((MapType<?, ?>)type).nameComparator();
                    break;
                case IN:
                    type = ListType.getInstance(type, false);
                    break;
                default:
                    break;
            }
            return String.format("%s %s %s", column.name, operator, type.getString(value));
        }

        @Override
        protected Kind kind()
        {
            return Kind.SIMPLE;
        }
    }

    /**
     * An expression of the form 'column' ['key'] = 'value' (which is only
     * supported when 'column' is a map).
     */
    private static class MapEqualityExpression extends Expression
    {
        private final ByteBuffer key;

        public MapEqualityExpression(ColumnMetadata column, ByteBuffer key, Operator operator, ByteBuffer value)
        {
            super(column, operator, value);
            assert column.type instanceof MapType && operator == Operator.EQ;
            this.key = key;
        }

        @Override
        public void validate() throws InvalidRequestException
        {
            checkNotNull(key, "Unsupported null map key for column %s", column.name);
            checkBindValueSet(key, "Unsupported unset map key for column %s", column.name);
            checkNotNull(value, "Unsupported null map value for column %s", column.name);
            checkBindValueSet(value, "Unsupported unset map value for column %s", column.name);
        }

        @Override
        public ByteBuffer getIndexValue()
        {
            return CompositeType.build(key, value);
        }

        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, Row row)
        {
            assert key != null;
            // We support null conditions for LWT (in ColumnCondition) but not for RowFilter.
            // TODO: we should try to merge both code someday.
            assert value != null;

            if (row.isStatic() != column.isStatic())
                return true;

            MapType<?, ?> mt = (MapType<?, ?>)column.type;
            if (column.isComplex())
            {
                Cell cell = row.getCell(column, CellPath.create(key));
                return cell != null && mt.valueComparator().compare(cell.value(), value) == 0;
            }
            else
            {
                ByteBuffer serializedMap = getValue(metadata, partitionKey, row);
                if (serializedMap == null)
                    return false;

                ByteBuffer foundValue = mt.getSerializer().getSerializedValue(serializedMap, key, mt.getKeysType());
                return foundValue != null && mt.valueComparator().compare(foundValue, value) == 0;
            }
        }

        @Override
        public String toString()
        {
            MapType<?, ?> mt = (MapType<?, ?>)column.type;
            return String.format("%s[%s] = %s", column.name, mt.nameComparator().getString(key), mt.valueComparator().getString(value));
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o)
                return true;

            if (!(o instanceof MapEqualityExpression))
                return false;

            MapEqualityExpression that = (MapEqualityExpression)o;

            return Objects.equal(this.column.name, that.column.name)
                && Objects.equal(this.operator, that.operator)
                && Objects.equal(this.key, that.key)
                && Objects.equal(this.value, that.value);
        }

        @Override
        public int hashCode()
        {
            return Objects.hashCode(column.name, operator, key, value);
        }

        @Override
        protected Kind kind()
        {
            return Kind.MAP_EQUALITY;
        }
    }

    /**
     * A custom index expression for use with 2i implementations which support custom syntax and which are not
     * necessarily linked to a single column in the base table.
     */
    public static final class CustomExpression extends Expression
    {
        private final IndexMetadata targetIndex;
        private final TableMetadata table;

        public CustomExpression(TableMetadata table, IndexMetadata targetIndex, ByteBuffer value)
        {
            // The operator is not relevant, but Expression requires it so for now we just hardcode EQ
            super(makeDefinition(table, targetIndex), Operator.EQ, value);
            this.targetIndex = targetIndex;
            this.table = table;
        }

        private static ColumnMetadata makeDefinition(TableMetadata table, IndexMetadata index)
        {
            // Similarly to how we handle non-defined columns in thift, we create a fake column definition to
            // represent the target index. This is definitely something that can be improved though.
            return ColumnMetadata.regularColumn(table, ByteBuffer.wrap(index.name.getBytes()), BytesType.instance);
        }

        public IndexMetadata getTargetIndex()
        {
            return targetIndex;
        }

        public ByteBuffer getValue()
        {
            return value;
        }

        public String toString()
        {
            return String.format("expr(%s, %s)",
                                 targetIndex.name,
                                 Keyspace.openAndGetStore(table)
                                         .indexManager
                                         .getIndex(targetIndex)
                                         .customExpressionValueType());
        }

        protected Kind kind()
        {
            return Kind.CUSTOM;
        }

        // Filtering by custom expressions isn't supported yet, so just accept any row
        public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, Row row)
        {
            return true;
        }
    }

    /**
     * A user defined filtering expression. These may be added to RowFilter programmatically by a
     * QueryHandler implementation. No concrete implementations are provided and adding custom impls
     * to the classpath is a task for operators (needless to say, this is something of a power
     * user feature). Care must also be taken to register implementations, via the static register
     * method during system startup. An implementation and its corresponding Deserializer must be
     * registered before sending or receiving any messages containing expressions of that type.
     * Use of custom filtering expressions in a mixed version cluster should be handled with caution
     * as the order in which types are registered is significant: if continuity of use during upgrades
     * is important, new types should registered last and obsoleted types should still be registered (
     * or dummy implementations registered in their place) to preserve consistent identifiers across
     * the cluster).
     *
     * During serialization, the identifier for the Deserializer implementation is prepended to the
     * implementation specific payload. To deserialize, the identifier is read first to obtain the
     * Deserializer, which then provides the concrete expression instance.
     */
    public static abstract class UserExpression extends Expression
    {
        private static final DeserializerRegistry deserializers = new DeserializerRegistry();
        private static final class DeserializerRegistry
        {
            private final AtomicInteger counter = new AtomicInteger(0);
            private final ConcurrentMap<Integer, Deserializer> deserializers = new ConcurrentHashMap<>();
            private final ConcurrentMap<Class<? extends UserExpression>, Integer> registeredClasses = new ConcurrentHashMap<>();

            public void registerUserExpressionClass(Class<? extends UserExpression> expressionClass,
                                                    UserExpression.Deserializer deserializer)
            {
                int id = registeredClasses.computeIfAbsent(expressionClass, (cls) -> counter.getAndIncrement());
                deserializers.put(id, deserializer);

                logger.debug("Registered user defined expression type {} and serializer {} with identifier {}",
                             expressionClass.getName(), deserializer.getClass().getName(), id);
            }

            public Integer getId(UserExpression expression)
            {
                return registeredClasses.get(expression.getClass());
            }

            public Deserializer getDeserializer(int id)
            {
                return deserializers.get(id);
            }
        }

        protected static abstract class Deserializer
        {
            protected abstract UserExpression deserialize(DataInputPlus in,
                                                          int version,
                                                          TableMetadata metadata) throws IOException;
        }

        public static void register(Class<? extends UserExpression> expressionClass, Deserializer deserializer)
        {
            deserializers.registerUserExpressionClass(expressionClass, deserializer);
        }

        private static UserExpression deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
        {
            int id = in.readInt();
            Deserializer deserializer = deserializers.getDeserializer(id);
            assert deserializer != null : "No user defined expression type registered with id " + id;
            return deserializer.deserialize(in, version, metadata);
        }

        private static void serialize(UserExpression expression, DataOutputPlus out, int version) throws IOException
        {
            Integer id = deserializers.getId(expression);
            assert id != null : "User defined expression type " + expression.getClass().getName() + " is not registered";
            out.writeInt(id);
            expression.serialize(out, version);
        }

        private static long serializedSize(UserExpression expression, int version)
        {   // 4 bytes for the expression type id
            return 4 + expression.serializedSize(version);
        }

        protected UserExpression(ColumnMetadata column, Operator operator, ByteBuffer value)
        {
            super(column, operator, value);
        }

        protected Kind kind()
        {
            return Kind.USER;
        }

        protected abstract void serialize(DataOutputPlus out, int version) throws IOException;
        protected abstract long serializedSize(int version);
    }

    public static class Serializer
    {
        public void serialize(RowFilter filter, DataOutputPlus out, int version) throws IOException
        {
            out.writeBoolean(false); // Old "is for thrift" boolean
            out.writeUnsignedVInt(filter.expressions.size());
            for (Expression expr : filter.expressions)
                Expression.serializer.serialize(expr, out, version);

        }

        public RowFilter deserialize(DataInputPlus in, int version, TableMetadata metadata) throws IOException
        {
            in.readBoolean(); // Unused
            int size = (int)in.readUnsignedVInt();
            List<Expression> expressions = new ArrayList<>(size);
            for (int i = 0; i < size; i++)
                expressions.add(Expression.serializer.deserialize(in, version, metadata));

            return new CQLFilter(expressions);
        }

        public long serializedSize(RowFilter filter, int version)
        {
            long size = 1 // unused boolean
                      + TypeSizes.sizeofUnsignedVInt(filter.expressions.size());
            for (Expression expr : filter.expressions)
                size += Expression.serializer.serializedSize(expr, version);
            return size;
        }
    }
}


text:
package org.typelibrary.dns;

public enum Truncated {

    YES,
    NO;

    public static final Truncated fromBoolean(boolean flag) {
        return flag ? YES : NO;
    }

    public static final boolean toBoolean(Truncated truncated) {
        return YES.equals(truncated) ? true : false;
    }

}


text:
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TypeNotRegisteredException.cs" company="Catel development team">
//   Copyright (c) 2008 - 2015 Catel development team. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Catel.IoC
{
    using System;
    using Reflection;

    /// <summary>
    /// Exception class in case an requested type from <see cref="IServiceLocator"/> is not registered.
    /// </summary>
    public class TypeNotRegisteredException : Exception
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TypeNotRegisteredException" /> class.
        /// </summary>
        /// <param name="requestedType">The requested type.</param>
        /// <param name="message">The message.</param>
        public TypeNotRegisteredException(Type requestedType, string message)
            : base(string.Format("The specified type '{0}' is not registered or could not be constructed. Please register type before using it. {1}",
            requestedType.GetSafeFullName(), message ?? string.Empty))
        {
            RequestedType = requestedType;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the requested type.
        /// </summary>
        /// <value>The type.</value>
        public Type RequestedType { get; private set; }
        #endregion
    }
}


text:
package com.stripe.model;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.stripe.BaseStripeTest;
import com.stripe.net.ApiResource;
import java.math.BigDecimal;
import org.junit.jupiter.api.Test;

public class TaxRateTest extends BaseStripeTest {
  @Test
  public void testDeserialize() throws Exception {
    final String data = getFixture("/v1/tax_rates/txr_123");
    final TaxRate taxRate = ApiResource.GSON.fromJson(data, TaxRate.class);
    assertNotNull(taxRate);
    assertNotNull(taxRate.getId());
    assertEquals("tax_rate", taxRate.getObject());
  }

  @Test
  @SuppressWarnings("BigDecimalEquals")
  public void testDeserializeBigDecimal() {
    final String data = "{\"object\": \"tax_rate\", \"percentage\": 0.3}";
    final TaxRate taxRate = ApiResource.GSON.fromJson(data, TaxRate.class);
    assertNotNull(taxRate);
    assertNotNull(taxRate.getPercentage());
    assertTrue(taxRate.getPercentage().equals(new BigDecimal("0.3")));
  }
}