/// <summary>
        /// Examine the children of the specified element and process them as an aggregate.  An aggregate is essentially a "reduce" operation
        /// that combines a series of inputs into a single value.
        /// </summary>
        /// <param name="aggFuncType"></param>
        /// <param name="aggFunc"></param>
        /// <param name="fieldOrProperty"></param>
        /// <param name="element"></param>
        /// <param name="ti"></param>
        /// <param name="o"></param>
        void _RecurseAggregate(Type aggFuncType, string aggFunc, IFieldOrProperty fieldOrProperty, XmlNode element, TypeInfo ti, ref object o)
        {
            // create list for storage of aggregate data
            List<object> data = new List<object>();

            foreach (XmlNode child in element.ChildNodes)
            {
                if (child.NodeType != XmlNodeType.Element)
                    continue;

                // check for obj type ref
                IXmlPostProcessAction action = null;
                action = _LookupObjectTypeRef(child);

                if (action != null)
                {
                    data.Add(action);
                    continue;
                }

                // check for name ref
                action = _LookupNameRef(child);

                if (action != null)
                {
                    data.Add(action);
                    continue;
                }

                // recurse on type
                TypeInfo subType = null;
                object subObj = null;

                // create new object
                _MakeNewObject(ti.Type.FullName, out subObj, out subType);

                if (subObj == null)
                {
                    _Warn("Ignoring aggregate subelement: " + child.LocalName);
                    continue;
                }

                // if the subType is a primitive or enum, do not recurse on it - instead just set the value directly
                if (TypeUtil.IsPrimitiveType(subType.Type))
                {
                    try
                    {
                        subObj = TypeUtil.GetPrimitiveValue(subType.Type, child.InnerText);
                    }
                    catch (Exception)
                    {
                        _Error("TorqueXmlDeserializer._SetFieldOrProperty - Unable to parse format string {0} for type {1}.", child.InnerText, subType.Type.FullName);
                    }
                }
                else
                {
                    _Recurse(child, ref subObj, subType);
                }

                data.Add(subObj);
            }

            // if we have data, create a post process task to handle the aggregation and set the final value
            if (data.Count > 0)
            {
                IXmlPostProcessAction action = new ProcessAggregateAction(aggFuncType, aggFunc, data, fieldOrProperty, ref o);
                _postProcessActions.Add(action);
            }
        }
        /// <summary>
        /// Set the value of the input field or property using the element.  This function will examine the inputs to determine the 
        /// best match for the element.  In some cases this may trigger a recursive examination of the element's children.
        /// </summary>
        /// <param name="fieldOrProperty">Field or property whose value will be set.</param>
        /// <param name="element">Element to obtain the value from.</param>
        /// <param name="o">Object instance whose field or property will be set.</param>
        void _SetFieldOrProperty(IFieldOrProperty fieldOrProperty, XmlNode element, ref object o)
        {
            // first check for valueOf
            object value = _CheckValueOf(element);

            if (value != null)
            {
                fieldOrProperty.SetValue(o, value);
                // done
                return;
            }

            // check for aggregate
            if (_CheckAggregate(fieldOrProperty, element, ref o))
                // no need to set value; values of aggregates are set as post process action.
                return;

            string objName = _GetObjectName(o);

            // if its a string we can just set it
            if (fieldOrProperty.DeclaredType == typeof(System.String))
            {
                fieldOrProperty.SetValue(o, element.InnerText);
            }
            else if (TypeUtil.IsPrimitiveType(fieldOrProperty.DeclaredType))
            {
                if (element.InnerText == null || element.InnerText.Trim() == string.Empty)
                {
                    _Error("Empty primitive value for type, won't set it: element: " + element.LocalName + ", type: " + fieldOrProperty.DeclaredType.FullName + ", object: " + objName);
                }
                else
                {
                    // extract primitive value and set it
                    try
                    {
                        fieldOrProperty.SetValue(o, TypeUtil.GetPrimitiveValue(fieldOrProperty.DeclaredType, element.InnerText));
                    }
                    catch (Exception)
                    {
                        _Error("TorqueXmlDeserializer._SetFieldOrProperty - Unable to parse format string {0} for type {1} for element {2} in object {3}.", element.InnerText, fieldOrProperty.DeclaredType.FullName, element.LocalName, objName);
                    }
                }
            }
            // if it is a reference type, we can deserialize in to it
            else if (fieldOrProperty.DeclaredType.IsClass || fieldOrProperty.DeclaredType.IsInterface || fieldOrProperty.DeclaredType.IsAbstract)
            {
                bool isInstantiable = !(fieldOrProperty.DeclaredType.IsInterface || fieldOrProperty.DeclaredType.IsAbstract);

                object subObj;
                TypeInfo subType;

                // check for name ref
                if (_BindNameRef(element, fieldOrProperty, ref o) != null)
                    // done with this field
                    return;

                // for reference types, allow deserializing into the existing object, if the attribute is present
                // and there is a valid object.
                subObj = fieldOrProperty.GetValue(o);
                bool inPlace = false;
                if (subObj != null)
                {
                    // instance exists, check for attribute - use the property/field type for this, not
                    // declared type.
                    object[] attributes = fieldOrProperty.GetCustomAttributes(true);
                    foreach (object attr in attributes)
                        if (attr is TorqueXmlDeserializeInPlace)
                        {
                            inPlace = true;
                            break;
                        }

                    if (!inPlace)
                    {
                        // check for xml attribute
                        XmlNode inPlaceAttribute = element.Attributes.GetNamedItem("inPlace");
                        if (inPlaceAttribute != null && Boolean.Parse(inPlaceAttribute.Value))
                            inPlace = true;
                    }
                }

                if (subObj != null && inPlace)
                {
                    // ok to deserialize in place; get the typeinfo object for later use.
                    subType = TypeUtil.FindTypeInfo(subObj.GetType().FullName);
                }
                else
                {
                    // log if we are replacing, but there was an instance already
                    //if (subObj != null)
                    //    _Info("Field has object instance but TorqueXmlDeserializeInPlace/inPlace attribute not specified: creating new object:" + element.LocalName);

                    // in case we are making a fixed size list, pass the number of children down to the object maker
                    int childCount = element.ChildNodes.Count;

                    // if type is not instantiable, we might be able to come up with an instantiable type by looking for type mappings
                    if (!isInstantiable)
                        _MakeNewObject(element, out subObj, out subType, childCount);
                    else
                        // instantiable type, so make object used declared type
                        _MakeNewObject(fieldOrProperty.DeclaredType.FullName, out subObj, out subType, childCount);

                    if (subObj == null)
                    {
                        _Error("Unable to create new instance for element {0} on object {1}", element.LocalName, objName);
                        return;
                    }
                }

                // if it is a list of objects, handle it specially
                if (subType.IsList)
                {
                    DeserializedList dlist = new DeserializedList(subObj);
                    _RecurseList(element, ref dlist, subType);
                }
                else
                    _Recurse(element, ref subObj, subType);

                // set value
                // it is important to do this after the element has been deserialized - some property accessors
                // assume this (such as lists of link points and object types).
                fieldOrProperty.SetValue(o, subObj);
            }
            // if it is a value type, then it must be a struct (because we handled primitives earlier)
            else if (fieldOrProperty.DeclaredType.IsValueType)
            {
                // look for object type ref
                if (_BindObjectTypeRef(element, fieldOrProperty, ref o) != null)
                    return;

                object subObj;
                TypeInfo subType;
                _MakeNewObject(fieldOrProperty.DeclaredType.FullName, out subObj, out subType);
                if (subType == null) // check type for null instead of object, since this is a value type
                {
                    _Error("Unable to create new instance for element {0} on object {1}", element.LocalName, objName);
                    return;
                }

                _Recurse(element, ref subObj, subType);

                // set value
                fieldOrProperty.SetValue(o, subObj);
            }
            else
                _Error("Don't know how to set field or property of type {0} for element {1} on object: {2}", fieldOrProperty.DeclaredType.FullName, element.LocalName, objName);
        }
        /// <summary>
        /// Process the specified name or object type ref.
        /// </summary>
        /// <param name="nameRefAttribute">the attribute to look for</param>
        /// <param name="lookupOnly">whether this is just a lookup (not a bind).  if true, fieldOrProperty and object can be null.</param>
        /// <param name="isObjectTypeRef">true if this is an object type ref lookup, false if it is a nameref lookup</param>
        /// <param name="element">the element to examine</param>
        /// <param name="fieldOrProperty">the field or property to set the value on</param>
        /// <param name="o">the object instance to set the value on</param>
        /// <returns>A post process action, or null if none is appropriate.</returns>
        IXmlPostProcessAction _CheckNameRef(string nameRefAttribute, bool lookupOnly, bool isObjectTypeRef, XmlNode element, IFieldOrProperty fieldOrProperty, ref object o)
        {
            Assert.Fatal(lookupOnly || (fieldOrProperty != null && o != null), "field and instance required for name bind operations");

            // see if the element has a nameRef
            IXmlPostProcessAction action = null;
            XmlNode nameRefAttr = element.Attributes[nameRefAttribute];

            if (nameRefAttr != null)
            {
                if (lookupOnly)
                    action = new LookupNameRefAction(isObjectTypeRef, nameRefAttr.Value, this);
                else
                    action = new BindNameRefAction(isObjectTypeRef, nameRefAttr.Value, fieldOrProperty, ref o, this);
                _postProcessActions.Add(action);
                return action;
            }

            return action;
        }
        /// <summary>
        /// Check to see if the element has an aggregate attribute and process it appropriately if so.  
        /// </summary>
        /// <param name="fieldOrProperty">The IFieldOrProperty instance where the aggregate value will be set.</param>
        /// <param name="element">The element to check.</param>
        /// <param name="o">The object instance to set the field or property value on.</param>
        /// <returns>true if there is an aggregate, false otherwise</returns>
        bool _CheckAggregate(IFieldOrProperty fieldOrProperty, XmlNode element, ref object o)
        {
            // if the element has no child nodes, it cannot be an aggregate
            if (element.ChildNodes.Count == 0)
                return false;

            TypeInfo ti = TypeUtil.FindTypeInfo(fieldOrProperty.DeclaredType.FullName);

            if (ti == null)
                return false;

            bool isAggregate = false;
            string aggregateFunction = string.Empty;

            if (ti.Type == typeof(TorqueObjectType))
            {
                isAggregate = true;
                aggregateFunction = "Aggregate";
            }

            // allow override with aggregate attribute
            XmlNode aggAttr = element.Attributes.GetNamedItem("aggregateWith");

            if (aggAttr != null && aggAttr.Value != null && aggAttr.Value.Trim() != string.Empty)
            {
                isAggregate = true;
                aggregateFunction = aggAttr.Value.Trim();
            }

            if (!isAggregate)
                return false;

            Type aggFuncType = null;
            string baseType = string.Empty;
            string target = string.Empty;

            if (aggregateFunction.IndexOf(".") != -1)
            {
                // its possible to aggregate data using an arbitrary function type.  lookup that type
                if (!TypeUtil.ParseType(aggregateFunction, out baseType, out target))
                {
                    _Error("Unable to parse aggregate function: " + aggregateFunction);
                    return false;
                }

                if (baseType != string.Empty)
                {
                    TypeInfo ati = TypeUtil.FindTypeInfo(baseType);
                    if (ati == null)
                    {
                        _Error("Unable to find aggregate type: " + baseType);
                        return false;
                    }
                    aggFuncType = ati.Type;
                }
            }
            else
            {
                // no "." in name, so use unqualifed function name on the declared type
                target = aggregateFunction;
            }

            _RecurseAggregate(aggFuncType, target, fieldOrProperty, element, ti, ref o);
            return true;
        }
 /// <summary>
 /// Create an object type ref bind action for the specified element, if applicable.
 /// </summary>
 /// <param name="element"></param>
 /// <returns>The object type bind action, or null if no action is required.</returns>
 IXmlPostProcessAction _BindObjectTypeRef(XmlNode element, IFieldOrProperty fieldOrProperty, ref object o)
 {
     IXmlPostProcessAction action = _CheckNameRef("objTypeRef", false, true, element, fieldOrProperty, ref o);
     // cannot bind these on value types, because we cannot store a ref reference.
     Assert.Fatal(action == null || !(o is ValueType), "Cannot bind object type refs on valuetypes");
     return action;
 }
        /// <summary>
        /// Sets the various properties associated with this action.
        /// </summary>
        /// <param name="aggFuncType">The type of aggregate this is.</param>
        /// <param name="aggFunc">The function to use to aggregate the data.</param>
        /// <param name="data">The list of objects to aggregate.</param>
        /// <param name="fieldOrProperty">The field or property to set.</param>
        /// <param name="targetInstance">The object whose field or property is to be set.</param>
        public ProcessAggregateAction(Type aggFuncType, string aggFunc, List<object> data, IFieldOrProperty fieldOrProperty, ref object targetInstance)
        {
            Assert.Fatal(data != null && data.Count > 0, "ProcessAggregateAction Constructor - Invalid data!");
            Assert.Fatal(fieldOrProperty != null, "ProcessAggregateAction Constructor - Invalid field or property!");
            Assert.Fatal(targetInstance != null, "ProcessAggregateAction Constructor - Invalid target!");

            _aggFuncType = aggFuncType;
            _aggFunc = aggFunc;
            _data = data;
            _fieldOrProperty = fieldOrProperty;
            _targetInstance = targetInstance;
        }
        /// <summary>
        /// Sets the various properties associated with this action.
        /// </summary>
        /// <param name="isObjTypeRef">True if the name is an object type, false if it is a named object.</param>
        /// <param name="nameRef">The name.</param>
        /// <param name="fieldOrProperty">The field or property instance to set.</param>
        /// <param name="targetInstance">The object whose property is being set.</param>
        /// <param name="d">The deserializer.</param>
        public BindNameRefAction(bool isObjTypeRef, string nameRef, IFieldOrProperty fieldOrProperty, ref object targetInstance, TorqueXmlDeserializer d)
            : base(d)
        {
            Assert.Fatal(nameRef != null && nameRef != string.Empty, "BindNameRefAction Constructor - Invalid name specified!");
            Assert.Fatal(fieldOrProperty != null, "BindNameRefAction Constructor - Invalid field or property specified!");
            Assert.Fatal(targetInstance != null, "BindNameRefAction Constructor - Invalid target instance specified!");

            _lookup = new LookupNameRefAction(isObjTypeRef, nameRef, d);
            _fieldOrProperty = fieldOrProperty;
            _targetInstance = targetInstance;
        }