    /**
     * @author Ed Spencer
     * @class Ext.data.reader.Reader
     * @extends Object
     *
     * <p>Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link Ext.data.Store Store}
     * - usually in response to an AJAX request. This is normally handled transparently by passing some configuration to either the
     * {@link Ext.data.Model Model} or the {@link Ext.data.Store Store} in question - see their documentation for further details.</p>
     *
     * <p><u>Loading Nested Data</u></p>
     *
     * <p>Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association associations}
     * configured on each Model. Below is an example demonstrating the flexibility of these associations in a fictional CRM system which
     * manages a User, their Orders, OrderItems and Products. First we'll define the models:
     *
    <pre><code>
    Ext.define("User", {
        extend: 'Ext.data.Model',
        fields: [
            'id', 'name'
        ],

        hasMany: {model: 'Order', name: 'orders'},

        proxy: {
            type: 'rest',
            url : 'users.json',
            reader: {
                type: 'json',
                root: 'users'
            }
        }
    });

    Ext.define("Order", {
        extend: 'Ext.data.Model',
        fields: [
            'id', 'total'
        ],

        hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
        belongsTo: 'User'
    });

    Ext.define("OrderItem", {
        extend: 'Ext.data.Model',
        fields: [
            'id', 'price', 'quantity', 'order_id', 'product_id'
        ],

        belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
    });

    Ext.define("Product", {
        extend: 'Ext.data.Model',
        fields: [
            'id', 'name'
        ],

        hasMany: 'OrderItem'
    });
    </code></pre>
     *
     * <p>This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. Finally,
     * each OrderItem has a single Product. This allows us to consume data like this:</p>
     *
    <pre><code>
    {
        "users": [
            {
                "id": 123,
                "name": "Ed",
                "orders": [
                    {
                        "id": 50,
                        "total": 100,
                        "order_items": [
                            {
                                "id"      : 20,
                                "price"   : 40,
                                "quantity": 2,
                                "product" : {
                                    "id": 1000,
                                    "name": "MacBook Pro"
                                }
                            },
                            {
                                "id"      : 21,
                                "price"   : 20,
                                "quantity": 3,
                                "product" : {
                                    "id": 1001,
                                    "name": "iPhone"
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    }
    </code></pre>
     *
     * <p>The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the Orders
     * for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), and finally
     * the Product associated with each OrderItem. Now we can read the data and use it as follows:
     *
    <pre><code>
    var store = new Ext.data.Store({
        model: "User"
    });

    store.load({
        callback: function() {
            //the user that was loaded
            var user = store.first();

            console.log("Orders for " + user.get('name') + ":")

            //iterate over the Orders for each User
            user.orders().each(function(order) {
                console.log("Order ID: " + order.getId() + ", which contains items:");

                //iterate over the OrderItems for each Order
                order.orderItems().each(function(orderItem) {
                    //we know that the Product data is already loaded, so we can use the synchronous getProduct
                    //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
                    var product = orderItem.getProduct();

                    console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
                });
            });
        }
    });
    </code></pre>
     *
     * <p>Running the code above results in the following:</p>
     *
    <pre><code>
    Orders for Ed:
    Order ID: 50, which contains items:
    2 orders of MacBook Pro
    3 orders of iPhone
    </code></pre>
     *
     * @constructor
     * @param {Object} config Optional config object
     */
    Ext.define('Ext.data.reader.Reader', {
        requires: ['Ext.data.ResultSet'],
        alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],

        /**
         * @cfg {String} idProperty Name of the property within a row object
         * that contains a record identifier value.  Defaults to <tt>The id of the model</tt>.
         * If an idProperty is explicitly specified it will override that of the one specified
         * on the model
         */

        /**
         * @cfg {String} totalProperty Name of the property from which to
         * retrieve the total number of records in the dataset. This is only needed
         * if the whole dataset is not passed in one go, but is being paged from
         * the remote server.  Defaults to <tt>total</tt>.
         */
        totalProperty: 'total',

        /**
         * @cfg {String} successProperty Name of the property from which to
         * retrieve the success attribute. Defaults to <tt>success</tt>.  See
         * {@link Ext.data.proxy.Proxy}.{@link Ext.data.proxy.Proxy#exception exception}
         * for additional information.
         */
        successProperty: 'success',

        /**
         * @cfg {String} root <b>Required</b>.  The name of the property
         * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
         * An exception will be thrown if the root property is undefined. The data
         * packet value for this property should be an empty array to clear the data
         * or show no data.
         */
        root: '',

        /**
         * @cfg {String} messageProperty The name of the property which contains a response message.
         * This property is optional.
         */

        /**
         * @cfg {Boolean} implicitIncludes True to automatically parse models nested within other models in a response
         * object. See the Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
         */
        implicitIncludes: true,

        isReader: true,

        constructor: function(config) {
            var me = this;

            Ext.apply(me, config || {});
            me.fieldCount = 0;
            me.model = Ext.ModelManager.getModel(config.model);
            if (me.model) {
                me.buildExtractors();
            }
        },

        /**
         * Sets a new model for the reader.
         * @private
         * @param {Object} model The model to set.
         * @param {Boolean} setOnProxy True to also set on the Proxy, if one is configured
         */
        setModel: function(model, setOnProxy) {
            var me = this;

            me.model = Ext.ModelManager.getModel(model);
            me.buildExtractors(true);

            if (setOnProxy && me.proxy) {
                me.proxy.setModel(me.model, true);
            }
        },

        /**
         * Reads the given response object. This method normalizes the different types of response object that may be passed
         * to it, before handing off the reading of records to the {@link #readRecords} function.
         * @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
         * @return {Ext.data.ResultSet} The parsed ResultSet object
         */
        read: function(response) {
            var data = response;

            if (response && response.responseText) {
                data = this.getResponseData(response);
            }

            if (data) {
                return this.readRecords(data);
            } else {
                return this.nullResultSet;
            }
        },

        /**
         * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call
         * this function before running its own logic and returning the Ext.data.ResultSet instance. For most
         * Readers additional processing should not be needed.
         * @param {Mixed} data The raw data object
         * @return {Ext.data.ResultSet} A ResultSet object
         */
        readRecords: function(data) {
            var me  = this;

            /*
             * We check here whether the number of fields has changed since the last read.
             * This works around an issue when a Model is used for both a Tree and another
             * source, because the tree decorates the model with extra fields and it causes
             * issues because the readers aren't notified.
             */
            if (me.fieldCount !== me.getFields().length) {
                me.buildExtractors(true);
            }

            /**
             * The raw data object that was last passed to readRecords. Stored for further processing if needed
             * @property rawData
             * @type Mixed
             */
            me.rawData = data;

            data = me.getData(data);

            // If we pass an array as the data, we dont use getRoot on the data.
            // Instead the root equals to the data.
            var root    = Ext.isArray(data) ? data : me.getRoot(data),
                success = true,
                recordCount = 0,
                total, value, records, message;

            if (root) {
                total = root.length;
            }

            if (me.totalProperty) {
                value = parseInt(me.getTotal(data), 10);
                if (!isNaN(value)) {
                    total = value;
                }
            }

            if (me.successProperty) {
                value = me.getSuccess(data);
                if (value === false || value === 'false') {
                    success = false;
                }
            }

            if (me.messageProperty) {
                message = me.getMessage(data);
            }

            if (root) {
                records = me.extractData(root);
                recordCount = records.length;
            } else {
                recordCount = 0;
                records = [];
            }

            return Ext.create('Ext.data.ResultSet', {
                total  : total || recordCount,
                count  : recordCount,
                records: records,
                success: success,
                message: message
            });
        },

        /**
         * Returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
         * @param {Object[]/Object} data-root from server response
         * @private
         */
        extractData : function(root) {
            var me = this,
                values  = [],
                records = [],
                Model   = me.model,
                i       = 0,
                length  = root.length,
                idProp  = me.getIdProperty(),
                node, id, record;

            if (!root.length && Ext.isObject(root)) {
                root = [root];
                length = 1;
            }

            for (; i < length; i++) {
                node   = root[i];
                values = me.extractValues(node);
                id     = me.getId(node);

                record = new Model(values, id, node);
                records.push(record);

                if (me.implicitIncludes) {
                    me.readAssociated(record, node);
                }
            }

            return records;
        },

        /**
         * @private
         * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
         * on the record provided.
         * @param {Ext.data.Model} record The record to load associations for
         * @param {Mixed} data The data object
         * @return {String} Return value description
         */
        readAssociated: function(record, data) {
            var associations = record.associations.items,
                i            = 0,
                length       = associations.length,
                association, associationData, proxy, reader;

            for (; i < length; i++) {
                association     = associations[i];
                associationData = this.getAssociatedDataRoot(data, association.associationKey || association.name);

                if (associationData) {
                    reader = association.getReader();
                    if (!reader) {
                        proxy = association.associatedModel.proxy;
                        // if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
                        if (proxy) {
                            reader = proxy.getReader();
                        } else {
                            reader = new this.constructor({
                                model: association.associatedName
                            });
                        }
                    }
                    association.read(record, reader, associationData);
                }
            }
        },

        /**
         * @private
         * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
         * record, this should return the relevant part of that data for the given association name. This is only really
         * needed to support the XML Reader, which has to do a query to get the associated data object
         * @param {Mixed} data The raw data object
         * @param {String} associationName The name of the association to get data for (uses associationKey if present)
         * @return {Mixed} The root
         */
        getAssociatedDataRoot: function(data, associationName) {
            return data[associationName];
        },

        getFields: function() {
            return this.model.prototype.fields.items;
        },

        /**
         * @private
         * Given an object representing a single model instance's data, iterates over the model's fields and
         * builds an object with the value for each field.
         * @param {Object} data The data object to convert
         * @return {Object} Data object suitable for use with a model constructor
         */
        extractValues: function(data) {
            var fields = this.getFields(),
                i      = 0,
                length = fields.length,
                output = {},
                field, value;

            for (; i < length; i++) {
                field = fields[i];
                value = this.extractorFunctions[i](data);

                output[field.name] = value;
            }

            return output;
        },

        /**
         * @private
         * By default this function just returns what is passed to it. It can be overridden in a subclass
         * to return something else. See XmlReader for an example.
         * @param {Object} data The data object
         * @return {Object} The normalized data object
         */
        getData: function(data) {
            return data;
        },

        /**
         * @private
         * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
         * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
         * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
         * @param {Mixed} data The data object
         * @return {Mixed} The same data object
         */
        getRoot: function(data) {
            return data;
        },

        /**
         * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be implemented by each subclass
         * @param {Object} response The responce object
         * @return {Object} The useful data from the response
         */
        getResponseData: function(response) {
            //<debug>
            Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
            //</debug>
        },

        /**
         * @private
         * Reconfigures the meta data tied to this Reader
         */
        onMetaChange : function(meta) {
            var fields = meta.fields,
                newModel;

            Ext.apply(this, meta);

            if (fields) {
                newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
                    extend: 'Ext.data.Model',
                    fields: fields
                });
                this.setModel(newModel, true);
            } else {
                this.buildExtractors(true);
            }
        },

        /**
         * Get the idProperty to use for extracting data
         * @private
         * @return {String} The id property
         */
        getIdProperty: function(){
            var prop = this.idProperty;
            if (Ext.isEmpty(prop)) {
                prop = this.model.prototype.idProperty;
            }
            return prop;
        },

        /**
         * @private
         * This builds optimized functions for retrieving record data and meta data from an object.
         * Subclasses may need to implement their own getRoot function.
         * @param {Boolean} force True to automatically remove existing extractor functions first (defaults to false)
         */
        buildExtractors: function(force) {
            var me          = this,
                idProp      = me.getIdProperty(),
                totalProp   = me.totalProperty,
                successProp = me.successProperty,
                messageProp = me.messageProperty,
                accessor;

            if (force === true) {
                delete me.extractorFunctions;
            }

            if (me.extractorFunctions) {
                return;
            }

            //build the extractors for all the meta data
            if (totalProp) {
                me.getTotal = me.createAccessor(totalProp);
            }

            if (successProp) {
                me.getSuccess = me.createAccessor(successProp);
            }

            if (messageProp) {
                me.getMessage = me.createAccessor(messageProp);
            }

            if (idProp) {
                accessor = me.createAccessor(idProp);

                me.getId = function(record) {
                    var id = accessor.call(me, record);
                    return (id === undefined || id === '') ? null : id;
                };
            } else {
                me.getId = function() {
                    return null;
                };
            }
            me.buildFieldExtractors();
        },

        /**
         * @private
         */
        buildFieldExtractors: function() {
            //now build the extractors for all the fields
            var me = this,
                fields = me.getFields(),
                ln = fields.length,
                i  = 0,
                extractorFunctions = [],
                field, map;

            for (; i < ln; i++) {
                field = fields[i];
                map   = (field.mapping !== undefined && field.mapping !== null) ? field.mapping : field.name;

                extractorFunctions.push(me.createAccessor(map));
            }
            me.fieldCount = ln;

            me.extractorFunctions = extractorFunctions;
        }
    }, function() {
        Ext.apply(this, {
            // Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
            nullResultSet: Ext.create('Ext.data.ResultSet', {
                total  : 0,
                count  : 0,
                records: [],
                success: true
            })
        });
    });