// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
/*============================================================
 **
 ** Class: ObjectWriter
 **
 **
 ** Purpose: Serializes an object graph into XML in SOAP format
 **
 **
 ===========================================================*/

namespace System.Runtime.Serialization.Formatters.Binary
{    
    using System;
    using System.IO;
    using System.Reflection;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Messaging;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Security;
    using System.Diagnostics;
    using System.Globalization;
    using System.Diagnostics.Contracts;

    internal sealed  class ObjectWriter
    {
        private Queue m_objectQueue;
        private ObjectIDGenerator m_idGenerator;
        private int m_currentId;

        private ISurrogateSelector m_surrogates;
        private StreamingContext m_context;
        private __BinaryWriter serWriter;
        private SerializationObjectManager m_objectManager;

        private long topId;
        private String topName = null;
        private Header[] headers;

        private InternalFE formatterEnums;
        private SerializationBinder m_binder;

        private SerObjectInfoInit serObjectInfoInit = null;

        private IFormatterConverter m_formatterConverter;

        internal Object[] crossAppDomainArray = null;
        internal ArrayList internalCrossAppDomainArray = null;


        // XMLObjectWriter Constructor
        internal ObjectWriter(ISurrogateSelector selector, StreamingContext context, InternalFE formatterEnums, SerializationBinder binder)
        {
            m_currentId = 1;
            m_surrogates = selector;
            m_context = context;
            m_binder = binder;
            this.formatterEnums = formatterEnums;
            m_objectManager = new SerializationObjectManager(context);
            SerTrace.InfoLog(
                            formatterEnums.FEtypeFormat +" "+
                            ((Enum)formatterEnums.FEserializerTypeEnum));


        }

        // Commences the process of serializing the entire graph.
        // initialize the graph walker.
        [System.Security.SecurityCritical]  // auto-generated
        internal void Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, bool fCheck)
        {
            if (graph == null)
                throw new ArgumentNullException("graph", Environment.GetResourceString("ArgumentNull_Graph"));

            if (serWriter == null)
                throw new ArgumentNullException("serWriter", Environment.GetResourceString("ArgumentNull_WithParamName", "serWriter"));
            Contract.EndContractBlock();

            SerTrace.Log(this, "Serialize Entry 2 ", graph, ((headers == null) ? " no headers " : "headers "));
#if !MONO
            if (fCheck)
            {
                CodeAccessPermission.Demand(PermissionType.SecuritySerialization);          
            }
#endif
            this.serWriter = serWriter;
            this.headers = inHeaders;

            SerTrace.Log( this, "Serialize New SerializedTypeTable");
            serWriter.WriteBegin();
            long headerId = 0;
            Object obj;
            long objectId;
            bool isNew;
            bool bMethodCall = false;
            bool bMethodReturn = false;

#if FEATURE_REMOTING        
            // Special case IMethodCallMessage and IMethodReturnMessage for performance
            IMethodCallMessage mess = graph as IMethodCallMessage;
            if (mess != null)
            {
                bMethodCall = true;
                graph = WriteMethodCall(mess);
            }
            else
            {
                IMethodReturnMessage mr = graph as IMethodReturnMessage;
                if (mr != null)
                {
                    bMethodReturn = true;
                    graph = WriteMethodReturn(mr);
                }
            }
#endif // FEATURE_REMOTING        

            if (graph == null)
            {
                WriteSerializedStreamHeader(topId, headerId);

                if (bMethodCall)
                    serWriter.WriteMethodCall();
                else if (bMethodReturn)
                    serWriter.WriteMethodReturn();

                serWriter.WriteSerializationHeaderEnd();
                serWriter.WriteEnd();
                return;
            }

            // allocations if methodCall or methodResponse and no graph
            m_idGenerator = new ObjectIDGenerator();
            m_objectQueue = new Queue();
            m_formatterConverter = new FormatterConverter();
            serObjectInfoInit = new SerObjectInfoInit();        

            topId = InternalGetId(graph, false, null, out isNew);


            if (headers != null)
                headerId = InternalGetId(headers, false, null, out isNew);
            else
                headerId = -1;

            WriteSerializedStreamHeader(topId, headerId);


            if (bMethodCall)
                serWriter.WriteMethodCall();
            else if (bMethodReturn)
                serWriter.WriteMethodReturn();


            SerTrace.Log( this, "Serialize Schedule 0");
            // Write out SerializedStream header
            if ((headers != null) && (headers.Length > 0))
                m_objectQueue.Enqueue(headers);                 

            if (graph != null)
                m_objectQueue.Enqueue(graph);
            while ((obj = GetNext(out objectId))!=null)
            {
                SerTrace.Log( this, "Serialize GetNext ",obj);
                WriteObjectInfo objectInfo = null;

                // GetNext will return either an object or a WriteObjectInfo. 
                // A WriteObjectInfo is returned if this object was member of another object
                if (obj is WriteObjectInfo)
                {
                    SerTrace.Log( this, "Serialize GetNext recognizes WriteObjectInfo");
                    objectInfo = (WriteObjectInfo)obj;
                }
                else
                {
                    objectInfo = WriteObjectInfo.Serialize(obj, m_surrogates, m_context, serObjectInfoInit, m_formatterConverter, this, m_binder);
                    objectInfo.assemId = GetAssemblyId(objectInfo);
                }


                objectInfo.objectId = objectId;
                NameInfo typeNameInfo = TypeToNameInfo(objectInfo);
                Write(objectInfo, typeNameInfo, typeNameInfo);
                PutNameInfo(typeNameInfo);
                objectInfo.ObjectEnd();
            }

            serWriter.WriteSerializationHeaderEnd();
            serWriter.WriteEnd();

            // Invoke OnSerialized Event
            m_objectManager.RaiseOnSerializedEvent();
            
            SerTrace.Log( this, "Serialize Exit ");
        }

#if FEATURE_REMOTING
        [System.Security.SecurityCritical]  // auto-generated
        private Object[] WriteMethodCall(IMethodCallMessage mcm)
        {
            // In header
            String uri = mcm.Uri;
            String methodName = mcm.MethodName;
            String typeName = mcm.TypeName;

            // Optional
            Object methodSignature = null;
            Object callContext = null;
            Object[] properties = null;

            // instantiation args
            Type[] instArgs = null;
            if (mcm.MethodBase.IsGenericMethod) 
                instArgs = mcm.MethodBase.GetGenericArguments();

            // args
            Object[] args = mcm.Args;

            IInternalMessage iim = mcm as IInternalMessage;

            // user properties (everything but special entries)
            if ((iim == null) || iim.HasProperties())
                properties = StoreUserPropertiesForMethodMessage(mcm);

            // handle method signature
            if (mcm.MethodSignature != null && RemotingServices.IsMethodOverloaded(mcm))
                methodSignature = mcm.MethodSignature;

            // handle call context
            LogicalCallContext lcc = mcm.LogicalCallContext;
            if (lcc == null)
            {
                callContext = null;
            }
            else if (lcc.HasInfo)
                callContext = lcc;
            else
            {
                // just smuggle the call id string
                callContext = lcc.RemotingData.LogicalCallID;
            }

            return serWriter.WriteCallArray(uri, methodName, typeName, instArgs, args, methodSignature, callContext, properties);
        }


        [System.Security.SecurityCritical]  // auto-generated
        private Object[] WriteMethodReturn(IMethodReturnMessage mrm)
        {
            Object    returnValue = mrm.ReturnValue;
            Object[]  args = mrm.Args;
            Exception exception = mrm.Exception;
            Object callContext;
            Object[] properties = null;

            ReturnMessage retMsg = mrm as ReturnMessage;

            // user properties (everything but special entries)
            if ((retMsg == null) || retMsg.HasProperties())
                properties = StoreUserPropertiesForMethodMessage(mrm);

            // handle call context
            LogicalCallContext lcc = mrm.LogicalCallContext;
            if (lcc == null)
            {
                callContext = null;
            }
            else if (lcc.HasInfo)
                callContext = lcc;
            else
            {
                // just smuggle the call id string
                callContext = lcc.RemotingData.LogicalCallID;
            }

           return serWriter.WriteReturnArray(returnValue, args, exception, callContext, properties);
        }

        // returns number of entries added to argsToSerialize
        [System.Security.SecurityCritical]  // auto-generated
        private static Object[] StoreUserPropertiesForMethodMessage(IMethodMessage msg)
        {
            ArrayList argsToSerialize = null;
            IDictionary properties = msg.Properties;

            if (properties == null)
                return null;

            MessageDictionary dict = properties as MessageDictionary;
            if (dict != null)
            {
                if (dict.HasUserData())
                {
                    int co = 0;
                    foreach (DictionaryEntry entry in dict.InternalDictionary)
                    {
                        if (argsToSerialize == null)
                            argsToSerialize = new ArrayList();
                        argsToSerialize.Add(entry);
                        co++;
                    }

                    return argsToSerialize.ToArray();
                }
                else
                {
                    return null;
                }
            }
            else
            {
                // <


                int co = 0;
                foreach (DictionaryEntry entry in properties)
                {
                    if (argsToSerialize == null)
                        argsToSerialize = new ArrayList();
                    argsToSerialize.Add(entry);
                    co++;
                }

                if (argsToSerialize != null)
                    return argsToSerialize.ToArray();
                else
                    return null;
            }

        } // StoreUserPropertiesForMethodMessage
#endif // FEATURE_REMOTING
        internal SerializationObjectManager ObjectManager
        {
            get { return m_objectManager; }
        }

        // Writes a given object to the stream.
        [System.Security.SecurityCritical]  // auto-generated
        private void Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
        {       
#if _DEBUG                        
            SerTrace.Log( this, "Write 1 Entry objectInfo ",objectInfo,", memberNameInfo ",memberNameInfo,", typeNameInfo ",typeNameInfo);
            memberNameInfo.Dump("Write memberNameInfo");
            typeNameInfo.Dump("Write typeNameInfo");
#endif            
            Object obj = objectInfo.obj;
            if (obj==null)
                throw new ArgumentNullException("objectInfo.obj", Environment.GetResourceString("ArgumentNull_Obj"));

            SerTrace.Log( this, "Write 1 objectInfo obj ",objectInfo.obj," objectId ", objectInfo.objectId, " objectType ", objectInfo.objectType);
            Type objType = objectInfo.objectType;
            long objectId = objectInfo.objectId;


            SerTrace.Log( this, "Write 1 ",obj," ObjectId ",objectId);

            if (Object.ReferenceEquals(objType, Converter.typeofString))
            {
                // Top level String
                memberNameInfo.NIobjectId = objectId;
                serWriter.WriteObjectString((int)objectId, obj.ToString());
            }
            else
            {

                if (objectInfo.isArray)
                {
                    WriteArray(objectInfo, memberNameInfo, null); 
                }
                else
                {
                    String[] memberNames;
                    Type[] memberTypes;
                    Object[] memberData;

                    objectInfo.GetMemberInfo(out memberNames, out memberTypes, out memberData);

                    // Only Binary needs to transmit types for ISerializable because the binary formatter transmits the types in URT format.
                    // Soap transmits all types as strings, so it is up to the ISerializable object to convert the string back to its URT type
                    if (objectInfo.isSi || CheckTypeFormat(formatterEnums.FEtypeFormat, FormatterTypeStyle.TypesAlways))
                    {
                        SerTrace.Log( this, "Write 1 TransmitOnObject ");
                        memberNameInfo.NItransmitTypeOnObject = true;
                        memberNameInfo.NIisParentTypeOnObject = true;
                        typeNameInfo.NItransmitTypeOnObject = true;
                        typeNameInfo.NIisParentTypeOnObject = true;                                             
                    }

                    WriteObjectInfo[] memberObjectInfos = new WriteObjectInfo[memberNames.Length];

                    // Get assembly information
                    // Binary Serializer, assembly names need to be
                    // written before objects are referenced.
                    // GetAssemId here will write out the
                    // assemblyStrings at the right Binary
                    // Serialization object boundary.
                    for (int i=0; i<memberTypes.Length; i++)
                    {
                        Type type;
                        if ((object)memberTypes[i] != null)
                            type = memberTypes[i];
                        else if (memberData[i] != null)
                            type = GetType(memberData[i]);
                        else
                            type = Converter.typeofObject;

                        SerTrace.Log( this, "Write 1 member type ",type);
                        InternalPrimitiveTypeE code = ToCode(type);
                        if ((code == InternalPrimitiveTypeE.Invalid) &&
                            (!Object.ReferenceEquals(type, Converter.typeofString)))
                        {
                            SerTrace.Log( this, "Write 1 Create ObjectInfo ", memberTypes[i], " memberData ",memberData[i]);
                            if (memberData[i] != null)
                            {
                                memberObjectInfos[i] =
                                WriteObjectInfo.Serialize
                                (
                                memberData[i],
                                m_surrogates,
                                m_context,
                                serObjectInfoInit,
                                m_formatterConverter,
                                this,
                                m_binder);                                    
                                memberObjectInfos[i].assemId = GetAssemblyId(memberObjectInfos[i]);
                            }
                            else
                            {
                                memberObjectInfos[i] =
                                WriteObjectInfo.Serialize
                                (
                                memberTypes[i],
                                m_surrogates,
                                m_context,
                                serObjectInfoInit,
                                m_formatterConverter,
                                m_binder
                                );
                                memberObjectInfos[i].assemId = GetAssemblyId(memberObjectInfos[i]);
                            }
                        }
                    }           
                    Write(objectInfo, memberNameInfo, typeNameInfo, memberNames, memberTypes, memberData, memberObjectInfos);
                    SerTrace.Log( this, "Write 1 ",obj," type ",GetType(obj));     
                }
            }
            SerTrace.Log( this, "Write 1 Exit ",obj);       
        }

        // Writes a given object to the stream.
        [System.Security.SecurityCritical]  // auto-generated
        private void Write(WriteObjectInfo objectInfo,   
                           NameInfo memberNameInfo,          
                           NameInfo typeNameInfo,            
                           String[] memberNames,             
                           Type[] memberTypes,               
                           Object[] memberData,              
                           WriteObjectInfo[] memberObjectInfos)
        {
            SerTrace.Log( this, "Write 2 Entry obj ",objectInfo.obj,". objectId ",objectInfo.objectId,", objType ",typeNameInfo.NIname,", memberName ",memberNameInfo.NIname,", memberType ",typeNameInfo.NIname);

            int numItems = memberNames.Length;
            NameInfo topNameInfo = null;

            if (memberNameInfo != null)
            {
                SerTrace.Log( this, "Write 2 ObjectBegin, memberName ",memberNameInfo.NIname);
                memberNameInfo.NIobjectId = objectInfo.objectId;
                serWriter.WriteObject(memberNameInfo, typeNameInfo, numItems, memberNames, memberTypes, memberObjectInfos);
            }
            else if ((objectInfo.objectId == topId) && (topName != null))
            {
                SerTrace.Log( this, "Write 2 ObjectBegin, topId method name ",topName);
                topNameInfo = MemberToNameInfo(topName);
                topNameInfo.NIobjectId = objectInfo.objectId;
                serWriter.WriteObject(topNameInfo, typeNameInfo, numItems, memberNames, memberTypes, memberObjectInfos);
            }
            else
            {
                if (!Object.ReferenceEquals(objectInfo.objectType, Converter.typeofString))
                {
                    SerTrace.Log( this, "Write 2 ObjectBegin, default ", typeNameInfo.NIname);
                    typeNameInfo.NIobjectId = objectInfo.objectId;
                    serWriter.WriteObject(typeNameInfo, null, numItems, memberNames, memberTypes, memberObjectInfos);
                }
            }

            if (memberNameInfo.NIisParentTypeOnObject)
            {
                memberNameInfo.NItransmitTypeOnObject = true;
                memberNameInfo.NIisParentTypeOnObject = false;
            }
            else
                memberNameInfo.NItransmitTypeOnObject = false;


            // Write members
            for (int i=0; i<numItems; i++)
            {
                WriteMemberSetup(objectInfo, memberNameInfo, typeNameInfo, memberNames[i], memberTypes[i], memberData[i], memberObjectInfos[i]);
            }

            if (memberNameInfo != null)
            {
                memberNameInfo.NIobjectId = objectInfo.objectId;
                serWriter.WriteObjectEnd(memberNameInfo, typeNameInfo);
            }
            else if ((objectInfo.objectId == topId) && (topName != null))
            {
                serWriter.WriteObjectEnd(topNameInfo, typeNameInfo);
                PutNameInfo(topNameInfo);
            }
            else
            {
                if (!Object.ReferenceEquals(objectInfo.objectType, Converter.typeofString))
                {
                    serWriter.WriteObjectEnd(typeNameInfo, typeNameInfo);                       
                }
            }

            SerTrace.Log( this, "Write 2 Exit");
        }

        [System.Security.SecurityCritical]  // auto-generated
        private void WriteMemberSetup(WriteObjectInfo objectInfo,        
                                      NameInfo memberNameInfo,           
                                      NameInfo typeNameInfo,             
                                      String memberName,             
                                      Type memberType,               
                                      Object memberData,                 
                                      WriteObjectInfo memberObjectInfo
                                     )
        {
            NameInfo newMemberNameInfo = MemberToNameInfo(memberName);
            // newMemberNameInfo contains the member type

            if (memberObjectInfo != null)
                newMemberNameInfo.NIassemId = memberObjectInfo.assemId;
            newMemberNameInfo.NItype = memberType;

            // newTypeNameInfo contains the data type
            NameInfo newTypeNameInfo = null;
            if (memberObjectInfo == null)
            {
                newTypeNameInfo = TypeToNameInfo(memberType);
            }
            else
            {
                newTypeNameInfo = TypeToNameInfo(memberObjectInfo);
            }

            newMemberNameInfo.NItransmitTypeOnObject = memberNameInfo.NItransmitTypeOnObject;
            newMemberNameInfo.NIisParentTypeOnObject = memberNameInfo.NIisParentTypeOnObject;               
            WriteMembers(newMemberNameInfo, newTypeNameInfo, memberData, objectInfo, typeNameInfo, memberObjectInfo);
            PutNameInfo(newMemberNameInfo);
            PutNameInfo(newTypeNameInfo);
        }


        // Writes the members of an object
        [System.Security.SecurityCritical]  // auto-generated
        private void WriteMembers(NameInfo memberNameInfo,
                                  NameInfo memberTypeNameInfo,
                                  Object   memberData,
                                  WriteObjectInfo objectInfo,
                                  NameInfo typeNameInfo,
                                  WriteObjectInfo memberObjectInfo
                                 )
        {
            SerTrace.Log( this, "WriteMembers Entry memberType: ",memberTypeNameInfo.NIname," memberName: ",memberNameInfo.NIname," data: ",memberData," objectId: ",objectInfo.objectId, " Container object ",objectInfo.obj, " memberObjectinfo ",memberObjectInfo);
            Type memberType = memberNameInfo.NItype;
            bool assignUniqueIdToValueType = false;

            // Types are transmitted for a member as follows:
            // The member is of type object
            // The member object of type is ISerializable and
            //  Binary - Types always transmitted.

            if (Object.ReferenceEquals(memberType, Converter.typeofObject) || (object)Nullable.GetUnderlyingType(memberType) != null)
            {
                memberTypeNameInfo.NItransmitTypeOnMember  = true;
                memberNameInfo.NItransmitTypeOnMember  = true;              
            }

            if (CheckTypeFormat(formatterEnums.FEtypeFormat, FormatterTypeStyle.TypesAlways) || (objectInfo.isSi) )
            {
                memberTypeNameInfo.NItransmitTypeOnObject  = true;
                memberNameInfo.NItransmitTypeOnObject  = true;
                memberNameInfo.NIisParentTypeOnObject = true;
            }

            if (CheckForNull(objectInfo, memberNameInfo, memberTypeNameInfo, memberData))
            {
                return;
            }

            Object outObj = memberData;
            Type outType = null;

            // If member type does not equal data type, transmit type on object.
            if (memberTypeNameInfo.NIprimitiveTypeEnum == InternalPrimitiveTypeE.Invalid)
            {
                outType = GetType(outObj);
                if (!Object.ReferenceEquals(memberType, outType))
                {
                    memberTypeNameInfo.NItransmitTypeOnMember  = true;
                    memberNameInfo.NItransmitTypeOnMember  = true;    
                }
            }

            if (Object.ReferenceEquals(memberType, Converter.typeofObject))
            {
                assignUniqueIdToValueType = true;
                memberType = GetType(memberData);
                if (memberObjectInfo == null)
                    TypeToNameInfo(memberType, memberTypeNameInfo);
                else
                    TypeToNameInfo(memberObjectInfo, memberTypeNameInfo);                   
                SerTrace.Log( this, "WriteMembers memberType Object, actual memberType ",memberType);                                                                               
            }

            if (memberObjectInfo != null && memberObjectInfo.isArray)
            {
                // Array
                SerTrace.Log( this, "WriteMembers IsArray");

                long arrayId = 0;
                if ((object)outType == null)
                    outType = GetType(outObj);
                // outObj is an array. It can never be a value type..
                arrayId = Schedule(outObj, false, null, memberObjectInfo);
                if (arrayId > 0)
                {
                    // Array as object
                    SerTrace.Log( this, "WriteMembers Schedule 3");
                    memberNameInfo.NIobjectId = arrayId;
                    WriteObjectRef(memberNameInfo, arrayId); 
                }
                else
                {
                    // Nested Array
                    serWriter.WriteMemberNested(memberNameInfo);

                    memberObjectInfo.objectId = arrayId;
                    memberNameInfo.NIobjectId = arrayId;
                    WriteArray(memberObjectInfo, memberNameInfo, memberObjectInfo);
                    objectInfo.ObjectEnd();

                }
                SerTrace.Log( this, "WriteMembers Array Exit ");
                return;
            }

            if (!WriteKnownValueClass(memberNameInfo, memberTypeNameInfo, memberData))
            {
                SerTrace.Log( this, "WriteMembers Object ",memberData);

#if false
                // Value or NO_ID, need to explicitly check for IsValue because a top level
                // value class has an objectId of 1
                /*
                if (memberType.IsValueType)
                {
                    SerTrace.Log( this, "WriteMembers Value Type or NO_ID parameter");

                    bool isNew;
                    memberObjectInfo.objectId = InternalGetId(outObj, assignUniqueIdToValueType, memberType, out isNew) ;
                    NameInfo newTypeNameInfo = TypeToNameInfo(memberObjectInfo);
                    newTypeNameInfo.NIobjectId = memberObjectInfo.objectId;
                    Write( memberObjectInfo, memberNameInfo, newTypeNameInfo);
                    PutNameInfo(newTypeNameInfo);
                    memberObjectInfo.ObjectEnd();
                }
                else
                */
#endif
                {
                    SerTrace.Log( this, "WriteMembers Schedule 4 ", outType, " memberInfo ",memberObjectInfo);
                    if ((object)outType == null)
                        outType = GetType(outObj);
                    long memberObjectId = Schedule(outObj, assignUniqueIdToValueType, outType, memberObjectInfo);
                    if (memberObjectId < 0)
                    {
                        // Nested object
                        SerTrace.Log( this, "WriteMembers Nesting");

                        memberObjectInfo.objectId = memberObjectId;
                        NameInfo newTypeNameInfo = TypeToNameInfo(memberObjectInfo);
                        newTypeNameInfo.NIobjectId = memberObjectId;
                        Write(memberObjectInfo, memberNameInfo, newTypeNameInfo);
                        PutNameInfo(newTypeNameInfo);
                        memberObjectInfo.ObjectEnd();
                    }
                    else
                    {
                        // Object reference
                        memberNameInfo.NIobjectId = memberObjectId;
                        WriteObjectRef(memberNameInfo, memberObjectId); 
                    }
                }
            }

            SerTrace.Log( this, "WriteMembers Exit ");
        }

        // Writes out an array
        [System.Security.SecurityCritical]  // auto-generated
        private void WriteArray(WriteObjectInfo objectInfo, NameInfo memberNameInfo, WriteObjectInfo memberObjectInfo)          
        {
            SerTrace.Log( this, "WriteArray Entry ",objectInfo.obj," ",objectInfo.objectId);

            bool isAllocatedMemberNameInfo = false;
            if (memberNameInfo == null)
            {
                memberNameInfo = TypeToNameInfo(objectInfo);
                isAllocatedMemberNameInfo = true;
            }

            memberNameInfo.NIisArray = true;

            long objectId = objectInfo.objectId;
            memberNameInfo.NIobjectId = objectInfo.objectId;

            // Get array type
            System.Array array = (System.Array)objectInfo.obj;
            //Type arrayType = array.GetType();
            Type arrayType = objectInfo.objectType;         

            // Get type of array element 
            Type arrayElemType = arrayType.GetElementType();
            WriteObjectInfo arrayElemObjectInfo = null;
            if (!arrayElemType.IsPrimitive)
            {
                arrayElemObjectInfo = WriteObjectInfo.Serialize(arrayElemType, m_surrogates, m_context, serObjectInfoInit, m_formatterConverter, m_binder);
                arrayElemObjectInfo.assemId = GetAssemblyId(arrayElemObjectInfo);
            }


            NameInfo arrayElemTypeNameInfo = null;
            if (arrayElemObjectInfo == null)
                arrayElemTypeNameInfo = TypeToNameInfo(arrayElemType);
            else
                arrayElemTypeNameInfo = TypeToNameInfo(arrayElemObjectInfo);
            arrayElemTypeNameInfo.NIisArray = arrayElemTypeNameInfo.NItype.IsArray;

            NameInfo arrayNameInfo = memberNameInfo;
            arrayNameInfo.NIobjectId = objectId;
            arrayNameInfo.NIisArray = true;
            arrayElemTypeNameInfo.NIobjectId = objectId;
            arrayElemTypeNameInfo.NItransmitTypeOnMember = memberNameInfo.NItransmitTypeOnMember;
            arrayElemTypeNameInfo.NItransmitTypeOnObject = memberNameInfo.NItransmitTypeOnObject;
            arrayElemTypeNameInfo.NIisParentTypeOnObject = memberNameInfo.NIisParentTypeOnObject;

            // Get rank and length information
            int rank = array.Rank;
            int[] lengthA = new int[rank];
            int[] lowerBoundA = new int[rank];
            int[] upperBoundA = new int[rank];                  
            for (int i=0; i<rank; i++)
            {
                lengthA[i] = array.GetLength(i);
                lowerBoundA[i] = array.GetLowerBound(i);
                upperBoundA[i] = array.GetUpperBound(i);                            
            }

            InternalArrayTypeE arrayEnum;

            if (arrayElemTypeNameInfo.NIisArray)
            {
                if (rank == 1)
                    arrayEnum = InternalArrayTypeE.Jagged;
                else
                    arrayEnum = InternalArrayTypeE.Rectangular;
            }
            else if (rank == 1)
                arrayEnum = InternalArrayTypeE.Single;
            else
                arrayEnum = InternalArrayTypeE.Rectangular;

            arrayElemTypeNameInfo.NIarrayEnum = arrayEnum;

            SerTrace.Log( this, "WriteArray ArrayInfo type ",arrayType," rank ",rank);


            // Byte array
            if ((Object.ReferenceEquals(arrayElemType, Converter.typeofByte)) && (rank == 1) && (lowerBoundA[0] == 0))
            {
                serWriter.WriteObjectByteArray(memberNameInfo, arrayNameInfo, arrayElemObjectInfo, arrayElemTypeNameInfo, lengthA[0], lowerBoundA[0], (byte[])array);
                return;
            }

            if (Object.ReferenceEquals(arrayElemType, Converter.typeofObject) || (object)Nullable.GetUnderlyingType(arrayElemType) != null)
            {
                memberNameInfo.NItransmitTypeOnMember = true;
                arrayElemTypeNameInfo.NItransmitTypeOnMember = true;
            }

            if (CheckTypeFormat(formatterEnums.FEtypeFormat, FormatterTypeStyle.TypesAlways))
            {
                memberNameInfo.NItransmitTypeOnObject = true;
                arrayElemTypeNameInfo.NItransmitTypeOnObject = true;                
            }

            if (arrayEnum == InternalArrayTypeE.Single)
            {
                // Single Dimensional array
                SerTrace.Log( this, "WriteArray ARRAY_SINGLE ");

                // BinaryFormatter array of primitive types is written out in the WriteSingleArray statement
                // as a byte buffer
                serWriter.WriteSingleArray(memberNameInfo, arrayNameInfo, arrayElemObjectInfo, arrayElemTypeNameInfo, lengthA[0], lowerBoundA[0], array);

                if (!(Converter.IsWriteAsByteArray(arrayElemTypeNameInfo.NIprimitiveTypeEnum) && (lowerBoundA[0] == 0)))
                {
                    Object[] objectA = null;
                    if (!arrayElemType.IsValueType)
                    {
                        // Non-primitive type array                 
                        objectA = (Object[])array;
                    }

                    int upperBound = upperBoundA[0]+1;
                    for (int i = lowerBoundA[0]; i < upperBound; i++)
                    {
                        if (objectA == null)
                            WriteArrayMember(objectInfo, arrayElemTypeNameInfo, array.GetValue(i));
                        else
                            WriteArrayMember(objectInfo, arrayElemTypeNameInfo, objectA[i]);
                    }
                    serWriter.WriteItemEnd();
                }
            }
            else if (arrayEnum == InternalArrayTypeE.Jagged)
            {
                // Jagged Array
                SerTrace.Log( this, "WriteArray ARRAY_JAGGED");

                arrayNameInfo.NIobjectId = objectId;

                serWriter.WriteJaggedArray(memberNameInfo, arrayNameInfo, arrayElemObjectInfo, arrayElemTypeNameInfo, lengthA[0], lowerBoundA[0]);

                Object[] objectA = (Object[])array;
                for (int i = lowerBoundA[0]; i < upperBoundA[0]+1; i++)
                {
                    WriteArrayMember(objectInfo, arrayElemTypeNameInfo, objectA[i]);
                }
                serWriter.WriteItemEnd();
            }
            else
            {
                // Rectangle Array
                // Get the length for all the ranks
                SerTrace.Log( this, "WriteArray ARRAY_RECTANGLE");                      

                arrayNameInfo.NIobjectId = objectId;
                serWriter.WriteRectangleArray(memberNameInfo, arrayNameInfo, arrayElemObjectInfo, arrayElemTypeNameInfo, rank, lengthA, lowerBoundA);

                IndexTraceMessage("WriteArray Rectangle  ", lengthA);

                // Check for a length of zero
                bool bzero = false;
                for (int i=0; i<rank; i++)
                {
                    if (lengthA[i] == 0)
                    {
                        bzero = true;
                        break;
                    }
                }

                if (!bzero)
                    WriteRectangle(objectInfo, rank, lengthA, array, arrayElemTypeNameInfo, lowerBoundA);
                serWriter.WriteItemEnd();
            }

            serWriter.WriteObjectEnd(memberNameInfo, arrayNameInfo); 

            PutNameInfo(arrayElemTypeNameInfo);
            if (isAllocatedMemberNameInfo)
                PutNameInfo(memberNameInfo);

            SerTrace.Log( this, "WriteArray Exit ");
        }

        // Writes out an array element
        [System.Security.SecurityCritical]  // auto-generated
        private void WriteArrayMember(WriteObjectInfo objectInfo, NameInfo arrayElemTypeNameInfo, Object data)
        {
            SerTrace.Log( this, "WriteArrayMember ",data," baseArrayName ",arrayElemTypeNameInfo.NIname);

            arrayElemTypeNameInfo.NIisArrayItem = true;

            if (CheckForNull(objectInfo, arrayElemTypeNameInfo, arrayElemTypeNameInfo, data))
                return;

            NameInfo actualTypeInfo = null;

            Type dataType = null;

            bool isObjectOnMember = false;

            if (arrayElemTypeNameInfo.NItransmitTypeOnMember)
                isObjectOnMember = true;

            if (!isObjectOnMember && !arrayElemTypeNameInfo.IsSealed)
            {
                dataType = GetType(data);
                if (!Object.ReferenceEquals(arrayElemTypeNameInfo.NItype, dataType))
                    isObjectOnMember = true;
            }

            if (isObjectOnMember)
            {
                // Object array, need type of member
                if ((object)dataType == null)
                    dataType = GetType(data);
                actualTypeInfo = TypeToNameInfo(dataType);
                actualTypeInfo.NItransmitTypeOnMember = true;
                actualTypeInfo.NIobjectId = arrayElemTypeNameInfo.NIobjectId;
                actualTypeInfo.NIassemId = arrayElemTypeNameInfo.NIassemId;
                actualTypeInfo.NIisArrayItem = true;
            }
            else
            {
                actualTypeInfo = arrayElemTypeNameInfo;
                actualTypeInfo.NIisArrayItem = true;
            }

            if (!WriteKnownValueClass(arrayElemTypeNameInfo, actualTypeInfo, data))
            {
                Object obj = data;
                bool assignUniqueIdForValueTypes = false;
                if (Object.ReferenceEquals(arrayElemTypeNameInfo.NItype, Converter.typeofObject))
                    assignUniqueIdForValueTypes = true;

                long arrayId = Schedule(obj, assignUniqueIdForValueTypes, actualTypeInfo.NItype);
                arrayElemTypeNameInfo.NIobjectId = arrayId;
                actualTypeInfo.NIobjectId = arrayId;
                if (arrayId < 1)
                {
                        WriteObjectInfo newObjectInfo = WriteObjectInfo.Serialize(obj, m_surrogates, m_context, serObjectInfoInit, m_formatterConverter, this, m_binder);
                        newObjectInfo.objectId = arrayId;
                        if (!Object.ReferenceEquals(arrayElemTypeNameInfo.NItype, Converter.typeofObject) && (object)Nullable.GetUnderlyingType(arrayElemTypeNameInfo.NItype) == null)
                            newObjectInfo.assemId = actualTypeInfo.NIassemId; 
                        else
                            newObjectInfo.assemId = GetAssemblyId(newObjectInfo);
                        SerTrace.Log( this, "WriteArrayMembers nested");
                        NameInfo typeNameInfo = TypeToNameInfo(newObjectInfo);
                        typeNameInfo.NIobjectId = arrayId;
                        newObjectInfo.objectId = arrayId;
                        Write(newObjectInfo, actualTypeInfo, typeNameInfo);

                        newObjectInfo.ObjectEnd();
                }
                else
                {
                    serWriter.WriteItemObjectRef(arrayElemTypeNameInfo, (int)arrayId);
                }

            }
            if (arrayElemTypeNameInfo.NItransmitTypeOnMember)
                PutNameInfo(actualTypeInfo);
        }


        // Iterates over a Rectangle array, for each element of the array invokes WriteArrayMember

        [System.Security.SecurityCritical]  // auto-generated
        private void WriteRectangle(WriteObjectInfo objectInfo, int rank, int[] maxA, System.Array array, NameInfo arrayElemNameTypeInfo, int[] lowerBoundA)
        {
            IndexTraceMessage("WriteRectangle  Entry "+rank, maxA);
            int[] currentA = new int[rank];
            int[] indexMap = null;
            bool isLowerBound = false;
            if (lowerBoundA != null)
            {
                for (int i=0; i<rank; i++)
                {
                    if (lowerBoundA[i] != 0)
                        isLowerBound = true;
                }
            }
            if (isLowerBound)
                indexMap = new int[rank];

            bool isLoop = true;
            while (isLoop)
            {
                isLoop = false;
                if (isLowerBound)
                { 
                    for (int i=0; i<rank; i++)
                    {
                        indexMap[i] = currentA[i]+lowerBoundA[i];
                    }

                    WriteArrayMember(objectInfo, arrayElemNameTypeInfo, array.GetValue(indexMap));
                }
                else
                    WriteArrayMember(objectInfo, arrayElemNameTypeInfo, array.GetValue(currentA));          
                for (int irank = rank-1; irank>-1; irank--)
                {
                    // Find the current or lower dimension which can be incremented.
                    if (currentA[irank] < maxA[irank]-1)
                    {
                        // The current dimension is at maximum. Increase the next lower dimension by 1
                        currentA[irank]++;
                        if (irank < rank-1)
                        {
                            // The current dimension and higher dimensions are zeroed.
                            for (int i = irank+1; i<rank; i++)
                                currentA[i] = 0;
                        }
                        isLoop = true;
                        break;                  
                    }

                }
            }
            SerTrace.Log( this, "WriteRectangle  Exit ");
        }

        // Traces a message with an array of int
        [Conditional("SER_LOGGING")]                            
        private void IndexTraceMessage(String message, int[] index)
        {
            StringBuilder sb = StringBuilderCache.Acquire(10);
            sb.Append("[");     
            for (int i=0; i<index.Length; i++)
            {
                sb.Append(index[i]);
                if (i != index.Length -1)
                    sb.Append(",");
            }
            sb.Append("]");             
            SerTrace.Log( this, message+" ", StringBuilderCache.GetStringAndRelease(sb));
        }


        // This gives back the next object to be serialized.  Objects
        // are returned in a FIFO order based on how they were passed
        // to Schedule.  The id of the object is put into the objID parameter
        // and the Object itself is returned from the function.
        private Object GetNext(out long objID)
        {
            SerTrace.Log( this, "GetNext Entry ");      
            bool isNew;

            //The Queue is empty here.  We'll throw if we try to dequeue the empty queue.
            if (m_objectQueue.Count==0)
            {
                objID=0;
                SerTrace.Log( this, "GetNext Exit null");
                return null;
            }

            Object obj = m_objectQueue.Dequeue();
            Object realObj = null;

            // A WriteObjectInfo is queued if this object was a member of another object
            SerTrace.Log( this, "GetNext ",obj);
            if (obj is WriteObjectInfo)
            {
                SerTrace.Log( this, "GetNext recognizes WriteObjectInfo");
                realObj = ((WriteObjectInfo)obj).obj;
            }
            else
                realObj = obj;
            objID = m_idGenerator.HasId(realObj, out isNew);
            if (isNew)
            {
                SerTrace.Log( this, "Object " , realObj , " has never been assigned an id.");
                throw new SerializationException(Environment.GetResourceString("Serialization_ObjNoID",realObj));                                                    
            }

            SerTrace.Log( this, "GetNext Exit "+objID," ",realObj);            
            return obj;
        }

        Object previousObj = null;
        long previousId = 0;
        // If the type is a value type, we dont attempt to generate a unique id, unless its a boxed entity
        // (in which case, there might be 2 references to the same boxed obj. in a graph.)
        // "assignUniqueIdToValueType" is true, if the field type holding reference to "obj" is Object.
        private long InternalGetId(Object obj, bool assignUniqueIdToValueType, Type type, out bool isNew)
        {
            if (obj == previousObj)
            {
                // good for benchmarks
                isNew = false;
                return previousId;
            }
            m_idGenerator.m_currentCount = m_currentId;
            if ((object)type != null && type.IsValueType)
            {
                if (!assignUniqueIdToValueType)
                {
                    isNew = false;
                    return -1 * m_currentId++;
                }
            }
            m_currentId++;
            long retId = m_idGenerator.GetId(obj, out isNew);

            previousObj = obj;
            previousId = retId;
            return retId;
        }


        // Schedules an object for later serialization if it hasn't already been scheduled.
        // We get an ID for obj and put it on the queue for later serialization
        // if this is a new object id.

        private long Schedule(Object obj, bool assignUniqueIdToValueType, Type type)
        {
            return Schedule(obj, assignUniqueIdToValueType, type, null);
        }

        private long Schedule(Object obj, bool assignUniqueIdToValueType, Type type, WriteObjectInfo objectInfo)
        {
            SerTrace.Log( this, "Schedule Entry obj ",obj," type ", type, " objectInfo ",objectInfo);

            bool isNew;
            long id;

            if (obj==null)
            {
                SerTrace.Log(this, "Schedule Obj Null, id = 0 ");
                return 0;
            }

            id = InternalGetId(obj, assignUniqueIdToValueType, type, out isNew);           

            if (isNew && id > 0)
            {
                if (objectInfo == null)
                    m_objectQueue.Enqueue(obj);
                else
                    m_objectQueue.Enqueue(objectInfo);

            }
            SerTrace.Log( this, "Schedule Exit, id: ",id," isNew: ",isNew);     
            return id;

        }


        // Determines if a type is a primitive type, if it is it is written

        private bool WriteKnownValueClass(NameInfo memberNameInfo, NameInfo typeNameInfo, Object data) 
        {
#if _DEBUG                        
            SerTrace.Log( this, "WriteKnownValueClass Entry ",typeNameInfo.NIname," ",data," ",memberNameInfo.NIname);
            memberNameInfo.Dump("WriteKnownValueClass memberNameInfo");         
            typeNameInfo.Dump("WriteKnownValueClass typeNameInfo");
#endif            

            if (Object.ReferenceEquals(typeNameInfo.NItype, Converter.typeofString))
            {
                WriteString(memberNameInfo, typeNameInfo, data);
            }
            else
            {
                if (typeNameInfo.NIprimitiveTypeEnum == InternalPrimitiveTypeE.Invalid)
                {
                    SerTrace.Log( this, "WriteKnownValueClass Exit false");                     
                    return false;
                }
                else
                {
                    if (typeNameInfo.NIisArray) // null if an array
                        serWriter.WriteItem(memberNameInfo, typeNameInfo, data);
                    else
                    {
                        serWriter.WriteMember(memberNameInfo, typeNameInfo, data);
                    }
                }
            }

            SerTrace.Log( this, "WriteKnownValueClass Exit true");
            return true;
        }


        // Writes an object reference to the stream.
        private void WriteObjectRef(NameInfo nameInfo, long objectId)
        {
            SerTrace.Log( this, "WriteObjectRef Entry ",nameInfo.NIname," ",objectId);
            serWriter.WriteMemberObjectRef(nameInfo, (int)objectId);

            SerTrace.Log( this, "WriteObjectRef Exit ");
        }



        // Writes a string into the XML stream
        private void WriteString(NameInfo memberNameInfo, NameInfo typeNameInfo, Object stringObject)
        {
            SerTrace.Log( this, "WriteString stringObject ",stringObject," memberName ",memberNameInfo.NIname);
            bool isFirstTime = true;

            long stringId = -1;

            if (!CheckTypeFormat(formatterEnums.FEtypeFormat, FormatterTypeStyle.XsdString))
                stringId= InternalGetId(stringObject, false, null, out isFirstTime);

            typeNameInfo.NIobjectId = stringId;
            SerTrace.Log( this, "WriteString stringId ",stringId," isFirstTime ",isFirstTime);

            if ((isFirstTime) || (stringId < 0))
                serWriter.WriteMemberString(memberNameInfo, typeNameInfo, (String)stringObject);
            else
                WriteObjectRef(memberNameInfo, stringId);
        }

        // Writes a null member into the stream
        private bool CheckForNull(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, Object data)
        {
#if _DEBUG            
            SerTrace.Log( this, "CheckForNull Entry data ",Util.PString(data),", memberType ",Util.PString(typeNameInfo.NItype));
#endif
            bool isNull = false;

            if (data == null) // || Convert.IsDBNull(data)
                isNull = true;

            // Optimization, Null members are only written for Binary
            if ((isNull) && (((formatterEnums.FEserializerTypeEnum == InternalSerializerTypeE.Binary)) ||
                             memberNameInfo.NIisArrayItem ||
                             memberNameInfo.NItransmitTypeOnObject ||
                             memberNameInfo.NItransmitTypeOnMember ||
                             objectInfo.isSi ||
                             (CheckTypeFormat(formatterEnums.FEtypeFormat, FormatterTypeStyle.TypesAlways))))
            {
                SerTrace.Log( this, "CheckForNull Write");

                if (typeNameInfo.NIisArrayItem)
                {
                    if (typeNameInfo.NIarrayEnum == InternalArrayTypeE.Single)
                        serWriter.WriteDelayedNullItem();
                    else
                        serWriter.WriteNullItem(memberNameInfo, typeNameInfo);
                }

                else
                    serWriter.WriteNullMember(memberNameInfo, typeNameInfo);
            }
            SerTrace.Log( this, "CheckForNull Exit ",isNull);
            return isNull;
        }


        // Writes the SerializedStreamHeader
        private void WriteSerializedStreamHeader(long topId, long headerId)
        {
            serWriter.WriteSerializationHeader((int)topId, (int)headerId, 1, 0);
        }


        // Transforms a type to the serialized string form. URT Primitive types are converted to XMLData Types
        private NameInfo TypeToNameInfo(Type type, WriteObjectInfo objectInfo, InternalPrimitiveTypeE code, NameInfo nameInfo)
        {
            SerTrace.Log( this, "TypeToNameInfo Entry type ",type,", objectInfo ",objectInfo,", code ", ((Enum)code).ToString());
            if (nameInfo == null)
                nameInfo = GetNameInfo();
            else
                nameInfo.Init();

            if (code == InternalPrimitiveTypeE.Invalid)
            {
                if (objectInfo != null)
                {
                    nameInfo.NIname = objectInfo.GetTypeFullName();
                    nameInfo.NIassemId = objectInfo.assemId;                    
                }
            }
            nameInfo.NIprimitiveTypeEnum = code; 
            nameInfo.NItype = type;

            SerTrace.Log( this, "TypeToNameInfo Exit ",type, " typeName "+nameInfo.NIname);
            return nameInfo;            
        }

        private NameInfo TypeToNameInfo(Type type)
        {
            return TypeToNameInfo(type, null, ToCode(type), null);
        }

        private NameInfo TypeToNameInfo(WriteObjectInfo objectInfo)
        {
            return TypeToNameInfo(objectInfo.objectType, objectInfo, ToCode(objectInfo.objectType), null);
        }

        private NameInfo TypeToNameInfo(WriteObjectInfo objectInfo, NameInfo nameInfo)
        {
            return TypeToNameInfo(objectInfo.objectType, objectInfo, ToCode(objectInfo.objectType), nameInfo);
        }

        private void TypeToNameInfo(Type type, NameInfo nameInfo)
        {
            TypeToNameInfo(type, null, ToCode(type), nameInfo);
        }

        private NameInfo MemberToNameInfo(String name)
        {
            NameInfo memberNameInfo = GetNameInfo();
            memberNameInfo.NIname = name;
            return memberNameInfo;
        }

        Type previousType = null;
        InternalPrimitiveTypeE previousCode = InternalPrimitiveTypeE.Invalid;
        internal InternalPrimitiveTypeE ToCode(Type type)
        {
            if (Object.ReferenceEquals(previousType, type))
            {
                return previousCode;
            }
            else
            {
                InternalPrimitiveTypeE code = Converter.ToCode(type);
                if (code != InternalPrimitiveTypeE.Invalid)
                {
                    previousType = type;
                    previousCode = code;
                }
                return code;
            }
        }

        private Hashtable assemblyToIdTable = null;
        private long GetAssemblyId(WriteObjectInfo objectInfo)
        {
            //use objectInfo to get assembly string with new criteria
            SerTrace.Log( this, "GetAssemblyId Entry ",objectInfo.objectType," isSi ",objectInfo.isSi);
            if (assemblyToIdTable == null)
                assemblyToIdTable = new Hashtable(5);

            long assemId = 0;
            bool isNew = false;
            String assemblyString = objectInfo.GetAssemblyString();

            String serializedAssemblyString = assemblyString;
            if (assemblyString.Length == 0)
            {
                assemId = 0;
            }
            else if (assemblyString.Equals(Converter.urtAssemblyString))
            {
                // Urt type is an assemId of 0. No assemblyString needs
                // to be sent 
                SerTrace.Log( this, "GetAssemblyId urt Assembly String ");
                assemId = 0;
            }
            else
            {
                // Assembly needs to be sent
                // Need to prefix assembly string to separate the string names from the
                // assemblyName string names. That is a string can have the same value
                // as an assemblyNameString, but it is serialized differently

                if (assemblyToIdTable.ContainsKey(assemblyString))
                {
                    assemId = (long)assemblyToIdTable[assemblyString];
                    isNew = false;
                }
                else
                {
                    assemId = InternalGetId("___AssemblyString___"+assemblyString, false, null, out isNew);
                    assemblyToIdTable[assemblyString] = assemId;
                }

                serWriter.WriteAssembly(objectInfo.objectType, serializedAssemblyString, (int)assemId, isNew);
            }
            SerTrace.Log( this, "GetAssemblyId Exit id ",assemId," isNew ",isNew," assemblyString ",serializedAssemblyString);
            return assemId;
        }

        [System.Security.SecurityCritical]  // auto-generated
        private Type GetType(Object obj)
        {
            Type type = null;
#if FEATURE_REMOTING        
            if (RemotingServices.IsTransparentProxy(obj))
                type = Converter.typeofMarshalByRefObject;
            else
#endif // FEATURE_REMOTING        
                type = obj.GetType();
            return type;
        }

        private SerStack niPool = new SerStack("NameInfo Pool");

        private NameInfo GetNameInfo()
        {
            NameInfo nameInfo = null;

            if (!niPool.IsEmpty())
            {
                nameInfo = (NameInfo)niPool.Pop();
                nameInfo.Init();
            }
            else
                nameInfo = new NameInfo();

            return nameInfo;
        }

        private bool CheckTypeFormat(FormatterTypeStyle test, FormatterTypeStyle want)
        {
            return(test & want) == want;
        }

        private void PutNameInfo(NameInfo nameInfo)
        {
            niPool.Push(nameInfo);
        }

    }
}



    
