// fieldInfo - the field who's value we want
        // objNode - the member node corresponding to the field
        // out topLevelObject - the object that contains the highest level
        //  struct.
        // s/b protected, stupid compiler
        internal static StructAccessor GetAccessorObject
            (IObjectNode objNode,
            out Object topLevelObject)
        {
            Stack       structInfoStack = new Stack();
            IObjectNode parentObjNode;

            if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
            {
                TraceUtil.WriteLineInfo
                    (typeof(StructAccessor),
                    "GetAccessor: " + objNode.ObjectInfo.ToString()
                    + " node: " + objNode.ToString());
            }


            // Build up the structFields array, the fields between the
            // top level object and the object we want to change
            while (true)
            {
                parentObjNode = objNode.ParentObjectNode;
                if (parentObjNode == null)
                {
                    throw new Exception("(bug) hit no parent when "
                                        + "looking for enclosing object "
                                        + "for a struct " + objNode);
                }

                // This is an enclosing struct, push the field
                // information of how to get to the child here
                FieldInfo fi = parentObjNode.ObjType.GetField
                                   (objNode.ObjectInfo.ObjMemberInfo.Name,
                                   ReflectionHelper.ALL_BINDINGS);
                if (fi == null)
                {
                    throw new Exception("(bug) field "
                                        + objNode.ObjType.Name
                                        + " not found in " + parentObjNode);
                }

                structInfoStack.Push(fi);

                // Found the containing object, either not a value
                // type, or this is not a field which means it can't
                // be inline any more
                // FIXME - double check the member thing here, is this
                // the right level to check?
                if (!parentObjNode.ObjType.IsValueType ||
                    !(parentObjNode.ObjectInfo.ObjMemberInfo is FieldInfo))
                {
                    break;
                }

                // Go up
                objNode = parentObjNode;
            }

            // Ok, at this point, parent is the ObjectInfo for the
            // object that contains the top-level struct
            Type objType = parentObjNode.ObjType;

            topLevelObject = parentObjNode.Obj;

            if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
            {
                TraceUtil.WriteLineInfo
                    (typeof(StructAccessor),
                    "GetAccessor - top level type/obj: "
                    + objType + "/" + topLevelObject);
            }

            int count = structInfoStack.Count;

            FieldInfo[] structFields =
                new FieldInfo[structInfoStack.Count];
            for (int i = 0; i < count; i++)
            {
                structFields[i] = (FieldInfo)structInfoStack.Pop();
            }

            StructAccessor vtAccessor;
            StringBuilder  keyBuilder = new StringBuilder();
            String         key;

            keyBuilder.Append(objType.FullName);
            keyBuilder.Append(".");
            foreach (FieldInfo fi in structFields)
            {
                keyBuilder.Append(fi.Name);
                keyBuilder.Append(".");
            }
            key = keyBuilder.ToString();

            if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
            {
                TraceUtil.WriteLineInfo
                    (typeof(StructAccessor),
                    "GetAccessor - key: " + key);
            }

            lock (typeof(StructAccessor))
            {
                vtAccessor = (StructAccessor)_accessors[key];
                if (vtAccessor != null)
                {
                    return(vtAccessor);
                }

                vtAccessor = new StructAccessor();
                vtAccessor.MakeAccessorClass(key,
                                             objType,
                                             structFields);
                _accessors.Add(key, vtAccessor);
                return(vtAccessor);
            }
        }
        protected void InvokeInternal(ObjectInfo parent,
                                      IObjectNode objectNode,
                                      Object[] parameterValues,
                                      Object fieldPropValue,
                                      bool setMember)
        {
            if (TraceUtil.If(this, TraceLevel.Verbose))
            {
                Trace.WriteLine("Start invoke: " + _objMemberInfo);
                if (fieldPropValue != null)
                {
                    Trace.WriteLine("  fieldPropVal: " + fieldPropValue);
                }
                if (parameterValues != null)
                {
                    foreach (Object pval in parameterValues)
                    {
                        Trace.WriteLine("  param: " + pval);
                    }
                }
            }

            switch (_objMemberInfo.MemberType)
            {
            case MemberTypes.Method:
            {
                MethodInfo m = (MethodInfo)_objMemberInfo;
                SetObject(m.Invoke(parent._obj, parameterValues));
                if (TraceUtil.If(this, TraceLevel.Verbose))
                {
                    Trace.WriteLine("Called: " + m
                                    + " on " + parent._obj
                                    + " return: " + _obj);
                }
            }
            break;

            case MemberTypes.Property:
            {
                PropertyInfo p = (PropertyInfo)_objMemberInfo;

                if (setMember)
                {
                    p.SetValue(parent._obj, fieldPropValue, parameterValues);
                }

                if (p.CanRead)
                {
                    SetObject(p.GetValue(parent._obj, parameterValues));
                }
                else
                {
                    // Update the new property value, if we can't
                    // read it from the property (on a set)
                    if (setMember)
                    {
                        SetObject(fieldPropValue);
                    }
                }

                // Save these in case we need to set the property,
                // like with the property propogation code below
                objectNode.CurrentPropIndexValues = parameterValues;

                if (TraceUtil.If(this, TraceLevel.Verbose))
                {
                    Trace.WriteLine("Property: " + p
                                    + " of " + parent._obj
                                    + " value: " + _obj);
                }
            }
            break;

            case MemberTypes.Field:
            {
                FieldInfo f = (FieldInfo)_objMemberInfo;

                if (parent._objType.IsValueType)
                {
                    // This is a struct, its handled a little
                    // different because of the inline value type
                    // issue
                    if (setMember)
                    {
                        StructAccessor.SetValue(objectNode, fieldPropValue);
                    }
                    SetObject(StructAccessor.GetValue(objectNode));
                }
                else
                {
                    if (setMember)
                    {
                        f.SetValue(parent._obj, fieldPropValue);
                    }
                    SetObject(f.GetValue(parent._obj));
                }
                if (TraceUtil.If(this, TraceLevel.Verbose))
                {
                    Trace.WriteLine("Field: " + f
                                    + " of " + parent._obj
                                    + " value: " + _obj);
                }
            }
            break;

            default:
                break;
            }

            // Everything worked, now lets handle any changes that
            // superiors need.
            if (!setMember)
            {
                return;
            }

            // If the parent member is a property, we want to do
            // a SetProperty on it with the current value of its object
            // so that the SetProperty can process the most recently
            // changed values.  We need to do this up the line.
            while (true)
            {
                IObjectNode parentObjNode;
                parentObjNode = objectNode.ParentObjectNode;
                if (parentObjNode == null)
                {
                    break;
                }

                // We hit an object node that has no member, we are done
                if (parentObjNode.ObjectInfo.ObjMemberInfo == null)
                {
                    break;
                }

                // Look at enclosing property members only
                if (!(parentObjNode.ObjectInfo.ObjMemberInfo is
                      PropertyInfo))
                {
                    break;
                }

                PropertyInfo propInfo = (PropertyInfo)parentObjNode.ObjectInfo.ObjMemberInfo;

                // Set the property
                try {
                    propInfo.SetValue(parentObjNode.ParentObjectNode.Obj,
                                      parentObjNode.Obj,
                                      parentObjNode.CurrentPropIndexValues);
                } catch (Exception ex) {
                    // A parent property might not have a set method
                    // for example.
                    TraceUtil.WriteLineInfo
                        (this, "Exception in SetValue in property "
                        + "propagation for prop: " + propInfo
                        + " ex: " + ex);
                    break;
                }
                parentObjNode.DoDisplayValue();
                objectNode = parentObjNode;
            }
        }
		// fieldInfo - the field who's value we want
		// objNode - the member node corresponding to the field
		// out topLevelObject - the object that contains the highest level
		//  struct.
		// s/b protected, stupid compiler
		internal static StructAccessor GetAccessorObject
			(IObjectNode objNode,
			 out Object topLevelObject)
		{
			Stack structInfoStack = new Stack();
			IObjectNode parentObjNode;

			if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
			{
				TraceUtil.WriteLineInfo
					(typeof(StructAccessor),
					 "GetAccessor: " + objNode.ObjectInfo.ToString()
					 + " node: " + objNode.ToString());
			}
								

			// Build up the structFields array, the fields between the
			// top level object and the object we want to change
			while (true)
			{
				parentObjNode = objNode.ParentObjectNode;
				if (parentObjNode == null)
				{
					throw new Exception("(bug) hit no parent when "
										+ "looking for enclosing object "
										+ "for a struct " + objNode);
				}

				// This is an enclosing struct, push the field 
				// information of how to get to the child here
				FieldInfo fi = parentObjNode.ObjType.GetField
					(objNode.ObjectInfo.ObjMemberInfo.Name,
					 ReflectionHelper.ALL_BINDINGS);
				if (fi == null)
				{
					throw new Exception("(bug) field " 
										+ objNode.ObjType.Name 
										+ " not found in " + parentObjNode);
				}

				structInfoStack.Push(fi);

				// Found the containing object, either not a value
				// type, or this is not a field which means it can't
				// be inline any more
				// FIXME - double check the member thing here, is this
				// the right level to check?
				if (!parentObjNode.ObjType.IsValueType ||
					!(parentObjNode.ObjectInfo.ObjMemberInfo is FieldInfo))
					break;

				// Go up
				objNode = parentObjNode;
			}

			// Ok, at this point, parent is the ObjectInfo for the
			// object that contains the top-level struct
			Type objType = parentObjNode.ObjType;
			topLevelObject = parentObjNode.Obj;

			if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
			{
				TraceUtil.WriteLineInfo
					(typeof(StructAccessor),
					 "GetAccessor - top level type/obj: " 
					 + objType + "/" + topLevelObject);
			}
								
			int count = structInfoStack.Count;
			FieldInfo[] structFields = 
				new FieldInfo[structInfoStack.Count];
			for (int i = 0; i < count; i++)
				structFields[i] = (FieldInfo)structInfoStack.Pop();

			StructAccessor vtAccessor;
			StringBuilder keyBuilder = new StringBuilder();
			String key;
			
			keyBuilder.Append(objType.FullName);
			keyBuilder.Append(".");
			foreach (FieldInfo fi in structFields)
			{
				keyBuilder.Append(fi.Name);
				keyBuilder.Append(".");
			}
			key = keyBuilder.ToString();

			if (TraceUtil.If(typeof(StructAccessor), TraceLevel.Info))
			{
				TraceUtil.WriteLineInfo
					(typeof(StructAccessor),
					 "GetAccessor - key: " + key);
			}
								
			lock (typeof(StructAccessor))
			{
				vtAccessor = (StructAccessor)_accessors[key];
				if (vtAccessor != null)
					return vtAccessor;

				vtAccessor = new StructAccessor();
				vtAccessor.MakeAccessorClass(key,
											 objType,
											 structFields);
				_accessors.Add(key, vtAccessor);
				return vtAccessor;
			}
		}