ObjectReader

  1. // ==++==
  2. // 
  3. //   
  4. //    Copyright (c) 2002 Microsoft Corporation.  All rights reserved.
  5. //   
  6. //    The use and distribution terms for this software are contained in the file
  7. //    named license.txt, which can be found in the root of this distribution.
  8. //    By using this software in any fashion, you are agreeing to be bound by the
  9. //    terms of this license.
  10. //   
  11. //    You must not remove this notice, or any other, from this software.
  12. //   
  13. // 
  14. // ==--==
  15. //============================================================
  16. //
  17. // Class: ObjectReader
  18. // Purpose: DeSerializes XML in SOAP Format into an an object graph
  19. //
  20. // Date:  June 10, 1999
  21. //
  22. //============================================================
  23. namespace System.Runtime.Serialization.Formatters.Soap {
  24.     using System;
  25.     using System.IO;
  26.     using System.Reflection;
  27.     using System.Collections;
  28.     using System.Text;
  29.     using System.Runtime.Remoting;
  30.     using System.Runtime.Remoting.Metadata.W3cXsd2001;
  31.     using System.Runtime.Remoting.Messaging;    
  32.     using System.Runtime.Serialization;
  33.     using System.Security;
  34.     using System.Security.Permissions;
  35.     using System.Diagnostics;
  36.     using System.Globalization;
  37.     internal sealed class ObjectReader
  38.     {
  39.         // System.Serializer information
  40.         internal ObjectIDGenerator m_idGenerator;
  41.         internal Stream m_stream;
  42.         internal ISurrogateSelector m_surrogates;
  43.         internal StreamingContext m_context;
  44.         internal ObjectManager m_objectManager;
  45.         internal InternalFE formatterEnums;
  46.         internal SerializationBinder m_binder;
  47.         internal SoapHandler soapHandler; //Set from SoapHandler
  48.         // Fake Top object and headers
  49.         internal long topId = 0;
  50.         internal SerStack topStack; // Stack for placing ProcessRecords if the top record cannot be serialized on the first pass.
  51.         internal bool isTopObjectSecondPass = false;
  52.         internal bool isTopObjectResolved = true;
  53.         internal bool isHeaderHandlerCalled = false;
  54.         internal Exception deserializationSecurityException = null;
  55.         internal Object handlerObject = null;
  56.         internal Object topObject;
  57.         internal long soapFaultId;
  58.         internal Header[] headers;
  59.         internal Header[] newheaders;
  60.         internal bool IsFakeTopObject = false;
  61.         internal HeaderHandler handler;
  62.         internal SerObjectInfoInit serObjectInfoInit = null;
  63.         internal IFormatterConverter m_formatterConverter = null;
  64.         // Stack of Object ParseRecords
  65.         internal SerStack stack = new SerStack("ObjectReader Object Stack");
  66.         // ValueType Fixup Stack
  67.         internal SerStack valueFixupStack = new SerStack("ValueType Fixup Stack");
  68.         // Generate Object Id's
  69.         internal Hashtable objectIdTable = new Hashtable(25); // holds the keyId value from the XML input and associated internal Id
  70.         internal long objectIds = 0;
  71.         internal int paramPosition = 0; //Position of parameter if soap top fake record.
  72.         internal int majorVersion = 0;
  73.         internal int minorVersion = 0;
  74.         internal String faultString = null;
  75.         // GetType - eliminate redundant Type.GetType()
  76.         //internal Hashtable typeTable = new Hashtable(10);     
  77.         internal static SecurityPermission serializationPermission = new SecurityPermission(SecurityPermissionFlag.SerializationFormatter);
  78.         
  79.         internal ObjectReader(Stream stream, ISurrogateSelector selector, StreamingContext context, InternalFE formatterEnums, SerializationBinder binder)
  80.         {
  81.             InternalST.Soap( this"Constructor ISurrogateSelector ",((selector == null)?"null selector ":"selector present"));                 
  82.             if (stream==null)
  83.             {
  84.                 throw new ArgumentNullException("stream", SoapUtil.GetResourceString("ArgumentNull_Stream"));
  85.             }
  86.             m_stream=stream;
  87.             m_surrogates = selector;
  88.             m_context = context;
  89.             m_binder =  binder;
  90.             this.formatterEnums = formatterEnums;
  91.             InternalST.Soap( this"Constructor formatterEnums.FEtopObject ",formatterEnums.FEtopObject);
  92.             if (formatterEnums.FEtopObject != null
  93.                 IsFakeTopObject = true;
  94.             else
  95.                 IsFakeTopObject = false;
  96.             m_formatterConverter = new FormatterConverter();
  97.         }
  98.         private ObjectManager GetObjectManager() {
  99.             new SecurityPermission(SecurityPermissionFlag.SerializationFormatter).Assert();
  100.             return new ObjectManager(m_surrogates, m_context);
  101.       }
  102.         // Deserialize the stream into an object graph.
  103.         internal Object Deserialize(HeaderHandler handler, ISerParser serParser)
  104.         {
  105.             InternalST.Soap( this"Deserialize Entry handler", handler);
  106.             if (serParser == null)
  107.                 throw new ArgumentNullException("serParser", String.Format(SoapUtil.GetResourceString("ArgumentNull_WithParamName"), serParser));
  108.             deserializationSecurityException = null;
  109.             try {
  110.                 serializationPermission.Demand();
  111.             } catch(Exception e ) {
  112.                 deserializationSecurityException = e;
  113.             }
  114.             this.handler = handler;
  115.             isTopObjectSecondPass = false;
  116.             isHeaderHandlerCalled = false;
  117.             if (handler != null)
  118.                 IsFakeTopObject = true;
  119.             m_idGenerator = new ObjectIDGenerator();
  120.             m_objectManager = GetObjectManager();
  121.             serObjectInfoInit = new SerObjectInfoInit();
  122.             objectIdTable.Clear();
  123.             objectIds = 0;
  124.             // Will call back to ParseObject, ParseHeader for each object found
  125.             serParser.Run();
  126.             if (handler != null)
  127.             {
  128.                 InternalST.Soap( this"Deserialize Fixup Before Delegate Invoke");         
  129.                 m_objectManager.DoFixups(); // Fixup for headers
  130.                 // Header handler isn't invoked until method name is known from body fake record
  131.                 // Except for SoapFault, in which case it is invoked below
  132.                 if (handlerObject == null)
  133.                 {
  134.                     InternalST.Soap( this"Deserialize Before SoapFault Delegate Invoke ");
  135.                     handlerObject = handler(newheaders);
  136.                     InternalST.Soap( this"Deserialize after SoapFault Delegate Invoke");
  137.                 }
  138.                 // SoapFault creation Create a fake Pr for the handlerObject to use.
  139.                 // Create a member for the fake pr with name __fault;
  140.                 if ((soapFaultId > 0) && (handlerObject != null))
  141.                 {
  142.                     InternalST.Soap( this"Deserialize SoapFault ");
  143.                     topStack = new SerStack("Top ParseRecords");                
  144.                     ParseRecord pr = new ParseRecord();
  145.                     pr.PRparseTypeEnum = InternalParseTypeE.Object;
  146.                     pr.PRobjectPositionEnum = InternalObjectPositionE.Top;
  147.                     pr.PRparseStateEnum = InternalParseStateE.Object;
  148.                     pr.PRname = "Response";
  149.                     topStack.Push(pr);
  150.                     pr = new ParseRecord();
  151.                     pr.PRparseTypeEnum = InternalParseTypeE.Member;
  152.                     pr.PRobjectPositionEnum = InternalObjectPositionE.Child;
  153.                     pr.PRmemberTypeEnum = InternalMemberTypeE.Field;
  154.                     pr.PRmemberValueEnum = InternalMemberValueE.Reference;
  155.                     pr.PRparseStateEnum = InternalParseStateE.Member;
  156.                     pr.PRname = "__fault";
  157.                     pr.PRidRef = soapFaultId;
  158.                     topStack.Push(pr);
  159.                     pr = new ParseRecord();
  160.                     pr.PRparseTypeEnum = InternalParseTypeE.ObjectEnd;
  161.                     pr.PRobjectPositionEnum = InternalObjectPositionE.Top;
  162.                     pr.PRparseStateEnum = InternalParseStateE.Object;
  163.                     pr.PRname = "Response";
  164.                     topStack.Push(pr);
  165.                     isTopObjectResolved = false;
  166.                 }
  167.             }
  168.             // Resolve fake top object if necessary
  169.             if (!isTopObjectResolved)
  170.             {
  171.                 //resolve top object
  172.                 InternalST.Soap( this"Deserialize TopObject Second Pass");                
  173.                 isTopObjectSecondPass = true;
  174.                 topStack.Reverse();
  175.                 // The top of the stack now contains the fake record
  176.                 // When it is Parsed, the handler object will be substituted
  177.                 // for it in ParseObject.
  178.                 int topStackLength = topStack.Count();
  179.                 ParseRecord pr = null;
  180.                 for (int i=0; i<topStackLength; i++)
  181.                 {
  182.                     pr = (ParseRecord)topStack.Pop();
  183.                     Parse(pr);
  184.                 }
  185.             }
  186.             InternalST.Soap( this"Deserialize Finished Parsing DoFixups");
  187.             m_objectManager.DoFixups();
  188.             if (topObject == null)
  189.                 throw new SerializationException(SoapUtil.GetResourceString("Serialization_TopObject"));
  190.             if (topObject is IObjectReference) {
  191.                 topObject = ((IObjectReference)topObject).GetRealObject(m_context);
  192.             }       
  193.             InternalST.Soap( this"Deserialize Exit ",topObject);
  194.             m_objectManager.RaiseDeserializationEvent();
  195.             if ((formatterEnums.FEtopObject != null) &
  196.                   (topObject is InternalSoapMessage))
  197.             {
  198.                 // Convert InternalSoapMessage to SoapMessage           
  199.                 InternalST.Soap( this"Deserialize SoapMessage Entry ");           
  200.                 InternalSoapMessage ismc = (InternalSoapMessage)topObject;
  201.                 ISoapMessage smc = (ISoapMessage)formatterEnums.FEtopObject;
  202.                 smc.MethodName = ismc.methodName;
  203.                 smc.XmlNameSpace = ismc.xmlNameSpace;
  204.                 smc.ParamNames = ismc.paramNames;
  205.                 smc.ParamValues = ismc.paramValues;
  206.                 smc.Headers = headers;
  207.                 topObject = smc;
  208.                 isTopObjectResolved = true;
  209.                 InternalST.Soap( this"Deserialize SoapMessage Exit topObject ",topObject," method name ",smc.MethodName);                         
  210.             }
  211.             return topObject;
  212.         }
  213.         internal ReadObjectInfo CreateReadObjectInfo(Type objectType, String assemblyName)
  214.         {
  215.             ReadObjectInfo objectInfo = ReadObjectInfo.Create(objectType, m_surrogates, m_context, m_objectManager, serObjectInfoInit, m_formatterConverter, assemblyName);
  216.             objectInfo.SetVersion(majorVersion, minorVersion);
  217.             return objectInfo;
  218.         }
  219.         internal ReadObjectInfo CreateReadObjectInfo(Type objectType, String[] memberNames, String assemblyName)
  220.         {
  221.             ReadObjectInfo objectInfo = ReadObjectInfo.Create(objectType, memberNames, null, m_surrogates, m_context, m_objectManager, serObjectInfoInit, m_formatterConverter, assemblyName);
  222.             objectInfo.SetVersion(majorVersion, minorVersion);
  223.             return objectInfo;
  224.         }
  225.         internal ReadObjectInfo CreateReadObjectInfo(Type objectType, String[] memberNames, Type[] memberTypes, String assemblyName)
  226.         {
  227.             ReadObjectInfo objectInfo = ReadObjectInfo.Create(objectType, memberNames, memberTypes, m_surrogates, m_context, m_objectManager, serObjectInfoInit, m_formatterConverter, assemblyName);
  228.             objectInfo.SetVersion(majorVersion, minorVersion);
  229.             return objectInfo;
  230.         }
  231.         // Main Parse routine, called by the XML Parse Handlers in XMLParser and also called internally to
  232.         // parse the fake top object.
  233.         internal void Parse(ParseRecord pr)
  234.         {
  235.             InternalST.Soap( this"Parse Entry");
  236.             stack.Dump();
  237.             pr.Dump();
  238.             switch(pr.PRparseTypeEnum)
  239.             {
  240.                 case InternalParseTypeE.SerializedStreamHeader:
  241.                     ParseSerializedStreamHeader(pr);
  242.                     break;
  243.                 case InternalParseTypeE.SerializedStreamHeaderEnd:
  244.                     ParseSerializedStreamHeaderEnd(pr);
  245.                     break;                  
  246.                 case InternalParseTypeE.Object:
  247.                     ParseObject(pr);
  248.                     break;
  249.                 case InternalParseTypeE.ObjectEnd:
  250.                     ParseObjectEnd(pr);
  251.                     break;
  252.                 case InternalParseTypeE.Member:
  253.                     ParseMember(pr);
  254.                     break;
  255.                 case InternalParseTypeE.MemberEnd:
  256.                     ParseMemberEnd(pr);
  257.                     break;
  258.                 case InternalParseTypeE.Body:
  259.                 case InternalParseTypeE.BodyEnd:
  260.                 case InternalParseTypeE.Envelope:
  261.                 case InternalParseTypeE.EnvelopeEnd:
  262.                     break;
  263.                 case InternalParseTypeE.Empty:
  264.                 default:
  265.                     throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_XMLElement"), pr.PRname));                 
  266.             }
  267.         }
  268.         // Styled ParseError output
  269.         private void ParseError(ParseRecord processing, ParseRecord onStack)
  270.         {
  271.             InternalST.Soap( this" ParseError ",processing," ",onStack);
  272.             throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_ParseError"),onStack.PRname+" "+((Enum)onStack.PRparseTypeEnum).ToString()+" "+processing.PRname+" "+((Enum)processing.PRparseTypeEnum).ToString()));                              
  273.         }
  274.         // Parse the SerializedStreamHeader element. This is the first element in the stream if present
  275.         private void ParseSerializedStreamHeader(ParseRecord pr)
  276.         {
  277.             InternalST.Soap( this"SerializedHeader ",pr);
  278.             stack.Push(pr);
  279.         }
  280.         // Parse the SerializedStreamHeader end element. This is the last element in the stream if present
  281.         private void ParseSerializedStreamHeaderEnd(ParseRecord pr)
  282.         {
  283.             InternalST.Soap( this"SerializedHeaderEnd ",pr);
  284.             stack.Pop();
  285.         }
  286.         private bool IsRemoting {
  287.             get {
  288.                 //return (m_context.State & (StreamingContextStates.Persistence|StreamingContextStates.File|StreamingContextStates.Clone)) == 0;
  289.                 return  IsFakeTopObject;
  290.             }
  291.         }
  292.     
  293.         private void CheckSecurity(ParseRecord pr)
  294.          {
  295.             InternalST.SoapAssert(pr!=null"[BinaryObjectReader.CheckSecurity]pr!=null");
  296.             Type t = pr.PRdtType;
  297.             if (t != null && IsRemoting){
  298.                 if (typeof(MarshalByRefObject).IsAssignableFrom(t))
  299.                     throw new ArgumentException(String.Format(SoapUtil.GetResourceString("Serialization_MBRAsMBV"), t.FullName));
  300.             }
  301.             //If we passed the security check, they can do whatever they'd like,
  302.             //so we'll just short-circuit this.
  303.             if (deserializationSecurityException==null) {
  304.                 return;
  305.             }
  306.             // BaseTypes and Array of basetypes allowed
  307.             if (t != null)
  308.             {
  309.                 if (t.IsPrimitive || t == Converter.typeofString)
  310.                     return;
  311.                 if (typeof(Enum).IsAssignableFrom(t))
  312.                     return;
  313.                 if (t.IsArray)
  314.                 {
  315.                     Type type = t.GetElementType();
  316.                     if (type.IsPrimitive || type == Converter.typeofString)
  317.                         return;
  318.                 }
  319.             }
  320.             throw deserializationSecurityException;
  321.         }
  322.         // New object encountered in stream
  323.         private void ParseObject(ParseRecord pr)
  324.         {
  325.             InternalST.Soap( this"ParseObject Entry ");
  326.             if (pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  327.                 topId = pr.PRobjectId;
  328.             if (pr.PRparseTypeEnum == InternalParseTypeE.Object)
  329.             {
  330.                 InternalST.Soap( this"ParseObject Push "+pr.PRname);
  331.                 stack.Push(pr); // Nested objects member names are already on stack
  332.             }
  333.             if (pr.PRobjectTypeEnum == InternalObjectTypeE.Array)
  334.             {
  335.                 ParseArray(pr);
  336.                 InternalST.Soap( this"ParseObject Exit, ParseArray ");
  337.                 return;
  338.             }
  339.             if ((pr.PRdtType == null) && !IsFakeTopObject)
  340.                 throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_TopObjectInstantiate"),pr.PRname));
  341.             if (pr.PRobjectPositionEnum == InternalObjectPositionE.Top && IsFakeTopObject && pr.PRdtType != Converter.typeofSoapFault)
  342.             {
  343.                 // Soap fake top object
  344.                 if (handler != null
  345.                 {
  346.                     // Handler object will supply real top object
  347.                     InternalST.Soap( this"ParseObject FakeTopObject with handlerObject ");    
  348.                     // Now know the method name, can call header handler 
  349.                     //Create header for method name
  350.                     if (!isHeaderHandlerCalled)
  351.                     {
  352.                         newheaders = null;
  353.                         isHeaderHandlerCalled = true;
  354.                         if (headers == null)
  355.                         {
  356.                             newheaders = new Header[1];
  357.                         }
  358.                         else
  359.                         {
  360.                             newheaders = new Header[headers.Length+1];
  361.                             Array.Copy(headers, 0, newheaders, 1, headers.Length);
  362.                         }
  363.                         Header methodNameHeader = new Header("__methodName", pr.PRname, false, pr.PRnameXmlKey);
  364.                         newheaders[0] = methodNameHeader;
  365.                         InternalST.Soap( this"Deserialize Before Delegate Invoke ");
  366.                         handlerObject = handler(newheaders);
  367.                         InternalST.Soap( this"Deserialize after Delegate Invoke");
  368.                         InternalST.Soap( this"Deserialize delgate object ",((handlerObject == null)?"null":handlerObject));                   
  369.                     }
  370.                     if (isHeaderHandlerCalled)
  371.                     {
  372.                         // Handler object has supplied the real object for the fake object
  373.                         // which is on top of the stack
  374.                         pr.PRnewObj = handlerObject;
  375.                         pr.PRdtType = handlerObject.GetType();
  376.                         CheckSecurity(pr);
  377.                         if (pr.PRnewObj is IFieldInfo)
  378.                         {
  379.                             IFieldInfo fi = (IFieldInfo)pr.PRnewObj;
  380.                             if ((fi.FieldTypes != null) && (fi.FieldTypes.Length > 0))
  381.                             {
  382.                                 pr.PRobjectInfo = CreateReadObjectInfo(pr.PRdtType, fi.FieldNames, fi.FieldTypes, pr.PRassemblyName);
  383.                             }
  384.                         }                       
  385.                     }
  386.                     else
  387.                     {
  388.                         // Handler object has not yet been asked for the real object
  389.                         // Stack the parse record until the second pass
  390.                         isTopObjectResolved = false;
  391.                         topStack = new SerStack("Top ParseRecords");
  392.                         InternalST.Soap( this"ParseObject Handler Push "+pr.PRname);
  393.                         topStack.Push(pr.Copy());
  394.                         return;
  395.                     }
  396.                 }
  397.                 else if (formatterEnums.FEtopObject != null)
  398.                 {
  399.                     // SoapMessage will be used as the real object
  400.                     InternalST.Soap( this"ParseObject FakeTopObject with SoapMessage ");                                  
  401.                     if (isTopObjectSecondPass)
  402.                     {
  403.                         // This creates a the SoapMessage object as the real object, at this point it is an unitialized object.
  404.                         pr.PRnewObj = new InternalSoapMessage();
  405.                         pr.PRdtType = typeof(InternalSoapMessage);
  406.                         CheckSecurity(pr);
  407.                         if (formatterEnums.FEtopObject != null)
  408.                         {
  409.                             ISoapMessage soapMessage = (ISoapMessage)formatterEnums.FEtopObject;
  410.                             pr.PRobjectInfo = CreateReadObjectInfo(pr.PRdtType, soapMessage.ParamNames, soapMessage.ParamTypes, pr.PRassemblyName);
  411.                         }
  412.                     }
  413.                     else
  414.                     {
  415.                         // Stack the parse record until the second pass
  416.                         isTopObjectResolved = false;
  417.                         topStack = new SerStack("Top ParseRecords");
  418.                         topStack.Push(pr.Copy());
  419.                         return;
  420.                     }
  421.                 }
  422.             }
  423.             else if (pr.PRdtType == Converter.typeofString)
  424.             {
  425.                 // String as a top level object
  426.                 if (pr.PRvalue != null)
  427.                 {
  428.                     pr.PRnewObj = pr.PRvalue;
  429.                     if (pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  430.                     {
  431.                         InternalST.Soap( this"ParseObject String as top level, Top Object Resolved");
  432.                         isTopObjectResolved = true;
  433.                         topObject = pr.PRnewObj;
  434.                         //stack.Pop();
  435.                         return;
  436.                     }
  437.                     else
  438.                     {
  439.                         InternalST.Soap( this"ParseObject  String as an object");
  440.                         stack.Pop();                        
  441.                         RegisterObject(pr.PRnewObj, pr, (ParseRecord)stack.Peek());                         
  442.                         return;
  443.                     }
  444.                 }
  445.                 else
  446.                 {
  447.                     // xml Doesn't have the value until later
  448.                     return;
  449.                 }
  450.             }
  451.             else 
  452.             {
  453.                 if (pr.PRdtType == null)
  454.                 {
  455.                     ParseRecord objectPr = (ParseRecord)stack.Peek();
  456.                     if (objectPr.PRdtType == Converter.typeofSoapFault)
  457.                     {
  458.                         InternalST.Soap( this"ParseObject unknown SoapFault detail");
  459.                         throw new ServerException(String.Format(SoapUtil.GetResourceString("Serialization_SoapFault"),faultString));                
  460.                     }
  461.                     throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_TypeElement"),pr.PRname));             
  462.                 }
  463.                 pr.PRnewObj = FormatterServices.GetUninitializedObject(pr.PRdtType);    
  464.                 CheckSecurity(pr);
  465.             }
  466.             if (pr.PRnewObj == null)
  467.                 throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_TopObjectInstantiate"),pr.PRdtType));              
  468.             long genId = pr.PRobjectId;
  469.             if (genId < 1)
  470.                 pr.PRobjectId = GetId("GenId-"+objectIds);
  471.             if (IsFakeTopObject && pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  472.             {
  473.                 InternalST.Soap( this"ParseObject fake Top object resolved ",pr.PRnewObj);
  474.                 isTopObjectResolved = true;
  475.                 topObject = pr.PRnewObj;
  476.             }
  477.             if (pr.PRobjectInfo == null)
  478.                 pr.PRobjectInfo = CreateReadObjectInfo(pr.PRdtType, pr.PRassemblyName);
  479.             pr.PRobjectInfo.obj = pr.PRnewObj;
  480.             if (IsFakeTopObject && pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  481.             {
  482.                 InternalST.Soap( this"ParseObject AddValue to fake object ",pr.PRobjectInfo.obj);
  483.                 // Add the methodName to top object, either InternalSoapMessage or object returned by handler
  484.                 pr.PRobjectInfo.AddValue("__methodName", pr.PRname);
  485.                 pr.PRobjectInfo.AddValue("__keyToNamespaceTable", soapHandler.keyToNamespaceTable);
  486.                 pr.PRobjectInfo.AddValue("__paramNameList", pr.PRobjectInfo.SetFakeObject());
  487.                 if (formatterEnums.FEtopObject != null)
  488.                     pr.PRobjectInfo.AddValue("__xmlNameSpace", pr.PRxmlNameSpace);
  489.             }
  490.             InternalST.Soap( this"ParseObject Exit ");        
  491.         }
  492.         private bool IsWhiteSpace(String value)
  493.         {
  494.             for (int i=0; i<value.Length; i++)
  495.             {
  496.                 if (value[i] == ' ' || value[i] == '/n' || value[i] == '/r')
  497.                     continue;
  498.                 else
  499.                     return false;
  500.             }
  501.             return true;
  502.         }
  503.         // End of object encountered in stream
  504.         
  505.         private void ParseObjectEnd(ParseRecord pr)
  506.         {
  507.             InternalST.Soap( this"ParseObjectEnd Entry ",pr.Trace());
  508.             ParseRecord objectPr = (ParseRecord)stack.Peek();
  509.             if (objectPr == null)
  510.                 objectPr = pr;
  511.             //BCLDebug.Assert(objectPr != null, "[System.Runtime.Serialization.Formatters.ParseObjectEnd]objectPr != null");
  512.             InternalST.Soap( this"ParseObjectEnd objectPr ",objectPr.Trace());
  513.             if (objectPr.PRobjectPositionEnum == InternalObjectPositionE.Top) 
  514.             {
  515.                 InternalST.Soap( this"ParseObjectEnd Top Object dtType ",objectPr.PRdtType);
  516.                 if (objectPr.PRdtType == Converter.typeofString)
  517.                 {
  518.                     InternalST.Soap( this"ParseObjectEnd Top String");
  519.                     if (objectPr.PRvalue == null)
  520.                         objectPr.PRvalue = String.Empty; // Not a null object, but an empty string
  521.                     objectPr.PRnewObj = objectPr.PRvalue;
  522.                     CheckSecurity(objectPr);
  523.                     isTopObjectResolved = true;
  524.                     topObject = objectPr.PRnewObj;
  525.                     return;
  526.                 }
  527.                 else if (objectPr.PRdtType != null  && objectPr.PRvalue != null && !IsWhiteSpace(objectPr.PRvalue) && (objectPr.PRdtType.IsPrimitive || objectPr.PRdtType == Converter.typeofTimeSpan))
  528.                 {
  529.                     // When an xsd type is transmitted as a top level string <xsd:int>111</xsd:int>
  530.                     objectPr.PRnewObj = Converter.FromString(objectPr.PRvalue, Converter.ToCode(objectPr.PRdtType));
  531.                     CheckSecurity(objectPr);
  532.                     isTopObjectResolved = true;
  533.                     topObject = objectPr.PRnewObj;
  534.                     return;
  535.                 }
  536.                 else if ((!isTopObjectResolved) && (objectPr.PRdtType != Converter.typeofSoapFault))
  537.                 {
  538.                     InternalST.Soap( this"ParseObjectEnd Top but not String");                    
  539.                     // Need to keep top record on object stack until finished building top stack
  540.                     topStack.Push(pr.Copy());
  541.                     // Note this is PRparseRecordId and not objectId
  542.                     if (objectPr.PRparseRecordId == pr.PRparseRecordId)
  543.                     {
  544.                         // This handles the case of top stack containing nested objects and
  545.                         // referenced objects. If nested objects the objects are not placed
  546.                         // on stack, only topstack. If referenced objects they are placed on
  547.                         // stack and need to be popped.
  548.                         stack.Pop();
  549.                     }
  550.                     return;
  551.                 }
  552.             }
  553.             stack.Pop();
  554.             ParseRecord parentPr = (ParseRecord)stack.Peek();
  555.             if (objectPr.PRobjectTypeEnum == InternalObjectTypeE.Array)
  556.             {
  557.                 if (objectPr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  558.                 {
  559.                     InternalST.Soap( this"ParseObjectEnd  Top Object (Array) Resolved");
  560.                     isTopObjectResolved = true;
  561.                     topObject = objectPr.PRnewObj;
  562.                 }
  563.                 InternalST.Soap( this"ParseArray  RegisterObject ",objectPr.PRobjectId," ",objectPr.PRnewObj.GetType());
  564.                 RegisterObject(objectPr.PRnewObj, objectPr, parentPr);                  
  565.                 return;
  566.             }
  567.             if (objectPr.PRobjectInfo != null)
  568.             {
  569.                 objectPr.PRobjectInfo.PopulateObjectMembers();
  570.             }
  571.             if (objectPr.PRnewObj == null)
  572.             {
  573.                 if (objectPr.PRdtType == Converter.typeofString)
  574.                 {
  575.                     InternalST.Soap( this"ParseObjectEnd String ");
  576.                     if (objectPr.PRvalue == null)
  577.                         objectPr.PRvalue = String.Empty; // Not a null object, but an empty string
  578.                     objectPr.PRnewObj = objectPr.PRvalue;
  579.                     CheckSecurity(objectPr);
  580.                 }
  581.                 else
  582.                     throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_ObjectMissing"),pr.PRname));               
  583.             }
  584.             // Registration is after object is populated
  585.             if (!objectPr.PRisRegistered && objectPr.PRobjectId > 0)
  586.             {
  587.                 InternalST.Soap( this"ParseObjectEnd Register Object ",objectPr.PRobjectId," ",objectPr.PRnewObj.GetType());
  588.                 RegisterObject(objectPr.PRnewObj, objectPr, parentPr);
  589.             }
  590.             if (objectPr.PRisValueTypeFixup)
  591.             {
  592.                 InternalST.Soap( this"ParseObjectEnd  ValueTypeFixup ",objectPr.PRnewObj.GetType());
  593.                 ValueFixup fixup = (ValueFixup)valueFixupStack.Pop(); //Value fixup
  594.                 fixup.Fixup(objectPr, parentPr);  // Value fixup
  595.             }
  596.             if (objectPr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  597.             {
  598.                 InternalST.Soap( this"ParseObjectEnd  Top Object Resolved ",objectPr.PRnewObj.GetType());
  599.                 isTopObjectResolved = true;
  600.                 topObject = objectPr.PRnewObj;
  601.             }
  602.             if (objectPr.PRnewObj is SoapFault)
  603.                 soapFaultId = objectPr.PRobjectId;
  604.             if (objectPr.PRobjectInfo != null)
  605.             {
  606.                 if (objectPr.PRobjectInfo.bfake && !objectPr.PRobjectInfo.bSoapFault)
  607.                     objectPr.PRobjectInfo.AddValue("__fault"null); // need this because SerializationObjectInfo throws an exception if a name being referenced is missing
  608.                 objectPr.PRobjectInfo.ObjectEnd();
  609.             }
  610.             InternalST.Soap( this"ParseObjectEnd  Exit ",objectPr.PRnewObj," id: ",objectPr.PRobjectId);      
  611.         }
  612.         // Array object encountered in stream
  613.         private void ParseArray(ParseRecord pr)
  614.         {
  615.             InternalST.Soap( this"ParseArray Entry");
  616.             pr.Dump();
  617.             long genId = pr.PRobjectId;
  618.             if (genId < 1)
  619.                 pr.PRobjectId = GetId("GenId-"+objectIds);
  620.             if ((pr.PRarrayElementType != null) && (pr.PRarrayElementType.IsEnum))
  621.                 pr.PRisEnum = true;
  622.             if (pr.PRarrayTypeEnum == InternalArrayTypeE.Base64)
  623.             {
  624.                 if (pr.PRvalue == null)
  625.                 {
  626.                     pr.PRnewObj = new Byte[0];
  627.                     CheckSecurity(pr);
  628.                 }
  629.                 else
  630.                 {
  631.                     // Used for arrays of Base64 and also for a parameter of Base64
  632.                     InternalST.Soap( this"ParseArray bin.base64 ",pr.PRvalue.Length," ",pr.PRvalue);
  633.                     if (pr.PRdtType == Converter.typeofSoapBase64Binary)
  634.                     {
  635.                         // Parameter - Case where the return type is a SoapENC:base64 but the parameter type is xsd:base64Binary
  636.                         pr.PRnewObj = SoapBase64Binary.Parse(pr.PRvalue);
  637.                         CheckSecurity(pr);
  638.                     }
  639.                     else
  640.                     {
  641.                         // ByteArray
  642.                         if (pr.PRvalue.Length > 0)
  643.                         {
  644.                             pr.PRnewObj = Convert.FromBase64String(FilterBin64(pr.PRvalue));
  645.                             CheckSecurity(pr);
  646.                         }
  647.                         else
  648.                         {
  649.                             pr.PRnewObj = new Byte[0];
  650.                             CheckSecurity(pr);
  651.                         }
  652.                     }
  653.                 }
  654.                 if (stack.Peek() == pr)
  655.                 {
  656.                     InternalST.Soap( this"ParseArray, bin.base64 has been stacked");
  657.                     stack.Pop();
  658.                 }
  659.                 if (pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  660.                 {
  661.                     InternalST.Soap( this"ParseArray, bin.base64 Top Object");
  662.                     topObject = pr.PRnewObj;
  663.                     isTopObjectResolved = true;
  664.                 }
  665.                 ParseRecord parentPr = (ParseRecord)stack.Peek();                                           
  666.                 // Base64 can be registered at this point because it is populated
  667.                 InternalST.Soap( this"ParseArray  RegisterObject ",pr.PRobjectId," ",pr.PRnewObj.GetType());
  668.                 RegisterObject(pr.PRnewObj, pr, parentPr);
  669.             }
  670.             else if ((pr.PRnewObj != null) && Converter.IsWriteAsByteArray(pr.PRarrayElementTypeCode))
  671.             {
  672.                 // Primtive typed Array has already been read
  673.                 if (pr.PRobjectPositionEnum == InternalObjectPositionE.Top)
  674.                 {
  675.                     topObject = pr.PRnewObj;
  676.                     isTopObjectResolved = true;
  677.                 }
  678.                 ParseRecord parentPr = (ParseRecord)stack.Peek();                                           
  679.                 // Primitive typed array can be registered at this point because it is populated
  680.                 InternalST.Soap( this"ParseArray  RegisterObject ",pr.PRobjectId," ",pr.PRnewObj.GetType());
  681.                 RegisterObject(pr.PRnewObj, pr, parentPr);
  682.             }
  683.             else if ((pr.PRarrayTypeEnum == InternalArrayTypeE.Jagged) || (pr.PRarrayTypeEnum == InternalArrayTypeE.Single))
  684.             {
  685.                 // Multidimensional jagged array or single array
  686.                 InternalST.Soap( this"ParseArray Before Jagged,Simple create ",pr.PRarrayElementType," ",(pr.PRrank >0?pr.PRlengthA[0].ToString():"0"));
  687.                 if ((pr.PRlowerBoundA == null) || (pr.PRlowerBoundA[0] == 0))
  688.                 {
  689.                     pr.PRnewObj = Array.CreateInstance(pr.PRarrayElementType, (pr.PRrank>0?pr.PRlengthA[0]:0));
  690.                     pr.PRisLowerBound = false;
  691.                 }
  692.                 else
  693.                 {
  694.                     pr.PRnewObj = Array.CreateInstance(pr.PRarrayElementType, pr.PRlengthA, pr.PRlowerBoundA);
  695.                     pr.PRisLowerBound = true;
  696.                 }
  697.                 if (pr.PRarrayTypeEnum == InternalArrayTypeE.Single)
  698.                 {
  699.                     if (!pr.PRisLowerBound && (Converter.IsWriteAsByteArray(pr.PRarrayElementTypeCode)))
  700.                     {
  701.                         pr.PRprimitiveArray = new PrimitiveArray(pr.PRarrayElementTypeCode, (Array)pr.PRnewObj);
  702.                     }
  703.                     else if (!pr.PRarrayElementType.IsValueType)
  704.                         pr.PRobjectA = (Object[])pr.PRnewObj;
  705.                 }
  706.                 CheckSecurity(pr);
  707.                     
  708.                 InternalST.Soap( this"ParseArray Jagged,Simple Array ",pr.PRnewObj.GetType());
  709.                 // For binary, headers comes in as an array of header objects
  710.                 if (pr.PRobjectPositionEnum == InternalObjectPositionE.Headers)
  711.                 {
  712.                     InternalST.Soap( this"ParseArray header array");
  713.                     headers = (Header[])pr.PRnewObj;
  714.                 }
  715.                 pr.PRindexMap = new int[1];
  716.             }
  717.             else if (pr.PRarrayTypeEnum == InternalArrayTypeE.Rectangular)
  718.             {
  719.                 // Rectangle array
  720.                 pr.PRisLowerBound = false;
  721.                 if (pr.PRlowerBoundA != null)
  722.                 {
  723.                     for (int i=0; i<pr.PRrank; i++)
  724.                     {
  725.                         if (pr.PRlowerBoundA[i] != 0)
  726.                             pr.PRisLowerBound = true;
  727.                     }
  728.                 }
  729.                 if (!pr.PRisLowerBound)
  730.                     pr.PRnewObj = Array.CreateInstance(pr.PRarrayElementType, pr.PRlengthA);
  731.                 else
  732.                     pr.PRnewObj = Array.CreateInstance(pr.PRarrayElementType, pr.PRlengthA, pr.PRlowerBoundA);
  733.                 CheckSecurity(pr);
  734.                 InternalST.Soap( this"ParseArray Rectangle Array ",pr.PRnewObj.GetType()," lower Bound ",pr.PRisLowerBound);
  735.                 // Calculate number of items
  736.                 int sum = 1;
  737.                 for (int i=0; i<pr.PRrank; i++)
  738.                 {
  739.                     sum = sum*pr.PRlengthA[i];
  740.                 }
  741.                 pr.PRindexMap = new int[pr.PRrank];
  742.                 pr.PRrectangularMap = new int[pr.PRrank];
  743.                 pr.PRlinearlength = sum;
  744.             }
  745.             else
  746.                 throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_ArrayType"),((Enum)pr.PRarrayTypeEnum).ToString()));                               
  747.             InternalST.Soap( this"ParseArray Exit");      
  748.         }
  749.         // Builds a map for each item in an incoming rectangle array. The map specifies where the item is placed in the output Array Object
  750.         private void NextRectangleMap(ParseRecord pr)
  751.         {
  752.             // For each invocation, calculate the next rectangular array position
  753.             // example
  754.             // indexMap 0 [0,0,0]
  755.             // indexMap 1 [0,0,1]
  756.             // indexMap 2 [0,0,2]
  757.             // indexMap 3 [0,0,3]
  758.             // indexMap 4 [0,1,0]       
  759.             for (int irank = pr.PRrank-1; irank>-1; irank--)
  760.             {
  761.                 // Find the current or lower dimension which can be incremented.
  762.                 if (pr.PRrectangularMap[irank] < pr.PRlengthA[irank]-1)
  763.                 {
  764.                     // The current dimension is at maximum. Increase the next lower dimension by 1
  765.                     pr.PRrectangularMap[irank]++;
  766.                     if (irank < pr.PRrank-1)
  767.                     {
  768.                         // The current dimension and higher dimensions are zeroed.
  769.                         for (int i = irank+1; i<pr.PRrank; i++)
  770.                             pr.PRrectangularMap[i] = 0;
  771.                     }
  772.                     Array.Copy(pr.PRrectangularMap, pr.PRindexMap, pr.PRrank);              
  773.                     break;                  
  774.                 }
  775.             }
  776.         }
  777.         // Array object item encountered in stream
  778.         private void ParseArrayMember(ParseRecord pr)
  779.         {
  780.             InternalST.Soap( this"ParseArrayMember Entry");
  781.             ParseRecord objectPr = (ParseRecord)stack.Peek();
  782.             // Set up for inserting value into correct array position
  783.             if (objectPr.PRarrayTypeEnum == InternalArrayTypeE.Rectangular)
  784.             {
  785.                 if (objectPr.PRmemberIndex > 0)
  786.                     NextRectangleMap(objectPr); // Rectangle array, calculate position in array
  787.                 if (objectPr.PRisLowerBound)
  788.                 {
  789.                     for (int i=0; i<objectPr.PRrank; i++)
  790.                     {
  791.                         if (objectPr.PRpositionA == null)
  792.                             objectPr.PRindexMap[i] = objectPr.PRrectangularMap[i] + objectPr.PRlowerBoundA[i];
  793.                         else
  794.                             objectPr.PRindexMap[i] = objectPr.PRpositionA[i];
  795.                     }
  796.                 }
  797.             }
  798.             else
  799.             {
  800.                 if (!objectPr.PRisLowerBound)
  801.                 {
  802.                     if (objectPr.PRpositionA == null)
  803.                         objectPr.PRindexMap[0] = objectPr.PRmemberIndex; // Zero based array
  804.                     else
  805.                         objectPr.PRindexMap[0] = objectPr.PRpositionA[0]; // item position specified in SOAP stream
  806.                 }
  807.                 else
  808.                     objectPr.PRindexMap[0] = objectPr.PRlowerBoundA[0]+objectPr.PRmemberIndex; // Lower Bound based array
  809.             }
  810.             IndexTraceMessage("ParseArrayMember isLowerBound "+objectPr.PRisLowerBound+" indexMap  ", objectPr.PRindexMap);     
  811.             // Set Array element according to type of element
  812.             if (pr.PRmemberValueEnum == InternalMemberValueE.Reference)
  813.             {
  814.                 // Object Reference
  815.                 // See if object has already been instantiated
  816.                 Object refObj = m_objectManager.GetObject(pr.PRidRef);
  817.                 if (refObj == null)
  818.                 {
  819.                     // Object not instantiated
  820.                     // Array fixup manager
  821.                     IndexTraceMessage("ParseArrayMember Record Fixup  "+objectPr.PRnewObj.GetType(), objectPr.PRindexMap);
  822.                     int[] fixupIndex = new int[objectPr.PRrank];
  823.                     Array.Copy(objectPr.PRindexMap, 0, fixupIndex, 0, objectPr.PRrank);
  824.                     InternalST.Soap( this"ParseArrayMember RecordArrayElementFixup objectId ",objectPr.PRobjectId," idRef ",pr.PRidRef);                                                          
  825.                     m_objectManager.RecordArrayElementFixup(objectPr.PRobjectId, fixupIndex, pr.PRidRef);
  826.                 }
  827.                 else
  828.                 {
  829.                     IndexTraceMessage("ParseArrayMember SetValue ObjectReference "+objectPr.PRnewObj.GetType()+" "+refObj, objectPr.PRindexMap);
  830.                     if (objectPr.PRobjectA != null)
  831.                         objectPr.PRobjectA[objectPr.PRindexMap[0]] = refObj;
  832.                     else
  833.                         ((Array)objectPr.PRnewObj).SetValue(refObj, objectPr.PRindexMap); // Object has been instantiated
  834.                 }
  835.             }
  836.             else if (pr.PRmemberValueEnum == InternalMemberValueE.Nested)
  837.             {
  838.                 //Set up dtType for ParseObject
  839.                 InternalST.Soap( this"ParseArrayMember Nested ");
  840.                 if (pr.PRdtType == null)
  841.                 {
  842.                     pr.PRdtType = objectPr.PRarrayElementType;
  843.                 }
  844.                 ParseObject(pr);
  845.                 InternalST.Soap( "ParseArrayMember Push object "+pr.PRname);
  846.                 stack.Push(pr);
  847.                 if ((objectPr.PRarrayElementType.IsValueType) && (pr.PRarrayElementTypeCode == InternalPrimitiveTypeE.Invalid))
  848.                 {
  849.                     InternalST.Soap( "ParseArrayMember ValueType ObjectPr ",objectPr.PRnewObj," index ",objectPr.PRmemberIndex);
  850.                     pr.PRisValueTypeFixup = true//Valuefixup
  851.                     valueFixupStack.Push(new ValueFixup((Array)objectPr.PRnewObj, objectPr.PRindexMap)); //valuefixup
  852.                     RegisterObject(pr.PRnewObj, pr, objectPr);                  
  853.                 }
  854.                 else
  855.                 {
  856.                     InternalST.Soap( "ParseArrayMember SetValue Nested, memberIndex ",objectPr.PRmemberIndex);
  857.                     IndexTraceMessage("ParseArrayMember SetValue Nested ContainerObject "+objectPr.PRnewObj.GetType()+" "+objectPr.PRnewObj+" item Object "+pr.PRnewObj+" index ", objectPr.PRindexMap);
  858.                     stack.Dump();               
  859.                     InternalST.Soap( "ParseArrayMember SetValue Nested ContainerObject objectPr ",objectPr.Trace());
  860.                     InternalST.Soap( "ParseArrayMember SetValue Nested ContainerObject pr ",pr.Trace());
  861.                     if (objectPr.PRobjectA != null)
  862.                         objectPr.PRobjectA[objectPr.PRindexMap[0]] = pr.PRnewObj;
  863.                     else
  864.                         ((Array)objectPr.PRnewObj).SetValue(pr.PRnewObj, objectPr.PRindexMap);
  865.                 }
  866.             }
  867.             else if (pr.PRmemberValueEnum == InternalMemberValueE.InlineValue)
  868.             {
  869.                 if (objectPr.PRarrayElementType == Converter.typeofString)
  870.                 {
  871.                     // String
  872.                     ParseString(pr, objectPr);
  873.                     IndexTraceMessage("ParseArrayMember SetValue String "+objectPr.PRnewObj.GetType()+" "+pr.PRvalue, objectPr.PRindexMap);
  874.                     if (objectPr.PRobjectA != null)
  875.                         objectPr.PRobjectA[objectPr.PRindexMap[0]] = (Object)pr.PRvalue;
  876.                     else
  877.                         ((Array)objectPr.PRnewObj).SetValue((Object)pr.PRvalue, objectPr.PRindexMap);
  878.                 }
  879.                 else if (objectPr.PRisEnum)
  880.                 {
  881.                             // Soap sends Enums as strings
  882.                     Object var = Enum.Parse(objectPr.PRarrayElementType, pr.PRvalue);
  883.                     if (objectPr.PRobjectA != null)
  884.                         objectPr.PRobjectA[objectPr.PRindexMap[0]] = (Enum)var;
  885.                     else
  886.                         ((Array)objectPr.PRnewObj).SetValue((Enum)var, objectPr.PRindexMap);                    
  887.                     InternalST.Soap( this"ParseArrayMember Enum 1");
  888.                 }
  889.                 else if (objectPr.PRisArrayVariant)
  890.                 {
  891.                     // Array of type object
  892.                     if (pr.PRdtType == null && pr.PRkeyDt == null)
  893.                         throw new SerializationException(SoapUtil.GetResourceString("Serialization_ArrayTypeObject"));
  894.                     Object var = null;
  895.                     if (pr.PRdtType == Converter.typeofString)
  896.                     {
  897.                         ParseString(pr, objectPr);
  898.                         var = pr.PRvalue;
  899.                     }
  900.                     else if (pr.PRdtType.IsEnum)
  901.                     {
  902.                             // Soap sends Enums as strings
  903.                         var = Enum.Parse(pr.PRdtType, pr.PRvalue);
  904.                         InternalST.Soap( this"ParseArrayMember Enum 2");
  905.                     }
  906.                     else if (pr.PRdtTypeCode == InternalPrimitiveTypeE.Invalid)
  907.                     {
  908.                         // Not nested and invalid, so it is an empty object
  909.                         var = FormatterServices.GetUninitializedObject(pr.PRdtType);
  910.                     }
  911.                     else 
  912.                     {
  913.                         if (pr.PRvarValue != null)
  914.                             var = pr.PRvarValue;
  915.                         else
  916.                             var = Converter.FromString(pr.PRvalue, pr.PRdtTypeCode);
  917.                     }
  918.                     IndexTraceMessage("ParseArrayMember SetValue variant or Object "+objectPr.PRnewObj.GetType()+" var "+var+" indexMap ", objectPr.PRindexMap);
  919.                     if (objectPr.PRobjectA != null)
  920.                         objectPr.PRobjectA[objectPr.PRindexMap[0]] = var;
  921.                     else
  922.                         ((Array)objectPr.PRnewObj).SetValue(var, objectPr.PRindexMap); // Primitive type
  923.                 }
  924.                 else
  925.                 {
  926.                     // Primitive type
  927.                     if (objectPr.PRprimitiveArray != null)
  928.                     {
  929.                         // Fast path for Soap primitive arrays. Binary was handled in the BinaryParser
  930.                         objectPr.PRprimitiveArray.SetValue(pr.PRvalue, objectPr.PRindexMap[0]);
  931.                     }
  932.                     else
  933.                     {
  934.                         Object var = null;
  935.                         if (pr.PRvarValue != null)
  936.                             var = pr.PRvarValue;
  937.                         else
  938.                             var = Converter.FromString(pr.PRvalue, objectPr.PRarrayElementTypeCode);
  939.                         if (objectPr.PRarrayElementTypeCode == InternalPrimitiveTypeE.QName)
  940.                         {
  941.                             InternalST.Soap( this"ParseArrayMember Primitive QName");
  942.                             SoapQName soapQName = (SoapQName)var;
  943.                             if (soapQName.Key.Length == 0)
  944.                                 soapQName.Namespace = (String)soapHandler.keyToNamespaceTable["xmlns"];
  945.                             else
  946.                                 soapQName.Namespace = (String)soapHandler.keyToNamespaceTable["xmlns"+":"+soapQName.Key];
  947.                         }
  948.                         InternalST.Soap( this"ParseArrayMember SetValue Primitive pr.PRvalue "+var," elementTypeCode ",((Enum)objectPr.PRdtTypeCode).ToString());
  949.                         IndexTraceMessage("ParseArrayMember SetValue Primitive "+objectPr.PRnewObj.GetType()+" var: "+var+" varType "+var.GetType(), objectPr.PRindexMap);
  950.                         if (objectPr.PRobjectA != null)
  951.                             objectPr.PRobjectA[objectPr.PRindexMap[0]] = var;
  952.                         else
  953.                             ((Array)objectPr.PRnewObj).SetValue(var, objectPr.PRindexMap); // Primitive type   
  954.                         InternalST.Soap( this"ParseArrayMember SetValue Primitive after");
  955.                     }
  956.                 }
  957.             }
  958.             else if (pr.PRmemberValueEnum == InternalMemberValueE.Null)
  959.             {
  960.                 InternalST.Soap( "ParseArrayMember Null item ",pr.PRmemberIndex);
  961.             }
  962.             else
  963.                 ParseError(pr, objectPr);
  964.             InternalST.Soap( "ParseArrayMember increment memberIndex ",objectPr.PRmemberIndex," ",objectPr.Trace());                
  965.             objectPr.PRmemberIndex++;
  966.             InternalST.Soap( "ParseArrayMember Exit");      
  967.         }
  968.         private void ParseArrayMemberEnd(ParseRecord pr)
  969.         {
  970.             InternalST.Soap( this"ParseArrayMemberEnd");
  971.             // If this is a nested array object, then pop the stack
  972.             if (pr.PRmemberValueEnum == InternalMemberValueE.Nested)
  973.             {
  974.                 ParseObjectEnd(pr);
  975.             }
  976.         }
  977.         // Object member encountered in stream
  978.         private void ParseMember(ParseRecord pr)
  979.         {
  980.             InternalST.Soap( this"ParseMember Entry ");
  981.             ParseRecord objectPr = (ParseRecord)stack.Peek();
  982.             String objName = null;
  983.             if (objectPr != null)
  984.                 objName = objectPr.PRname;
  985.                 
  986.             InternalST.Soap( this"ParseMember ",objectPr.PRobjectId," ",pr.PRname);
  987.             InternalST.Soap( this"ParseMember objectPr ",objectPr.Trace());
  988.             InternalST.Soap( this"ParseMember pr ",pr.Trace());
  989.             if (objectPr.PRdtType == Converter.typeofSoapFault && pr.PRname.ToLower(CultureInfo.InvariantCulture) == "faultstring")
  990.                 faultString = pr.PRvalue; // Save fault string in case rest of SoapFault cannot be parsed
  991.             if ((objectPr.PRobjectPositionEnum == InternalObjectPositionE.Top) && !isTopObjectResolved)
  992.             {
  993.                 InternalST.Soap( this"ParseMember Top not resolved");
  994.                 if (pr.PRdtType == Converter.typeofString)
  995.                 {
  996.                     ParseString(pr, objectPr);
  997.                 }
  998.                 topStack.Push(pr.Copy());
  999.                 return;
  1000.             }
  1001.             switch(pr.PRmemberTypeEnum)
  1002.             {
  1003.                 case InternalMemberTypeE.Item:
  1004.                     ParseArrayMember(pr);
  1005.                     return;
  1006.                 case InternalMemberTypeE.Field:
  1007.                     break;
  1008.             }
  1009.             
  1010.             if (objectPr.PRobjectInfo != null)
  1011.                 objectPr.PRobjectInfo.AddMemberSeen();
  1012.             bool bParams = (IsFakeTopObject &
  1013.                     objectPr.PRobjectPositionEnum == InternalObjectPositionE.Top && 
  1014.                     objectPr.PRobjectInfo != null && objectPr.PRdtType != Converter.typeofSoapFault);
  1015.             if ((pr.PRdtType == null) && objectPr.PRobjectInfo.isTyped)         
  1016.             {
  1017.                 InternalST.Soap( this"ParseMember pr.PRdtType null and not isSi");
  1018.                 if (bParams)
  1019.                 {
  1020.                     // Get type of parameters
  1021.                     InternalST.Soap( this"ParseMember pr.PRdtType Get Param Type position "+paramPosition+" name "+pr.PRname);    
  1022.                     pr.PRdtType = objectPr.PRobjectInfo.GetType(paramPosition++);
  1023.                     InternalST.Soap( this"ParseMember pr.PRdtType Get Param Type Type "+pr.PRdtType);
  1024.                 }
  1025.                 else
  1026.                     pr.PRdtType = objectPr.PRobjectInfo.GetType(pr.PRname);
  1027.                 if (pr.PRdtType == null)
  1028.                     throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_TypeResolved"), objectPr.PRnewObj+" "+pr.PRname));
  1029.                 pr.PRdtTypeCode = Converter.ToCode(pr.PRdtType);
  1030.             }
  1031.             else
  1032.             {
  1033.                 if (bParams)
  1034.                 {
  1035.                     paramPosition++;
  1036.                 }
  1037.             }
  1038.             if (pr.PRmemberValueEnum == InternalMemberValueE.Null)
  1039.             {
  1040.                 // Value is Null
  1041.                 InternalST.Soap( this"ParseMember null member: ",pr.PRname);
  1042.                 InternalST.Soap( this"AddValue 1");
  1043.                 objectPr.PRobjectInfo.AddValue(pr.PRname, null);
  1044.             }
  1045.             else if (pr.PRmemberValueEnum == InternalMemberValueE.Nested)
  1046.             {
  1047.                 InternalST.Soap( this"ParseMember Nested Type member: ",pr.PRname," objectPr.PRnewObj ",objectPr.PRnewObj);
  1048.                 ParseObject(pr);
  1049.                 InternalST.Soap( "ParseMember Push object "+pr.PRname);
  1050.                 stack.Push(pr);
  1051.                 InternalST.Soap( this"AddValue 2 ",pr.PRnewObj," is value type ",pr.PRnewObj.GetType().IsValueType);
  1052.                 if ((pr.PRobjectInfo != null) && (pr.PRobjectInfo.objectType.IsValueType))
  1053.                 {
  1054.                     InternalST.Soap( "ParseMember ValueType ObjectPr ",objectPr.PRnewObj," memberName  ",pr.PRname," nested object ",pr.PRnewObj);
  1055.                     if (IsFakeTopObject)
  1056.                         objectPr.PRobjectInfo.AddParamName(pr.PRname);
  1057.                     
  1058.                     pr.PRisValueTypeFixup = true//Valuefixup
  1059.                     valueFixupStack.Push(new ValueFixup(objectPr.PRnewObj, pr.PRname, objectPr.PRobjectInfo));//valuefixup
  1060.                 }
  1061.                 else
  1062.                 {
  1063.                     InternalST.Soap( this"AddValue 2A ");
  1064.                     objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRnewObj);
  1065.                 }
  1066.             }
  1067.             else if (pr.PRmemberValueEnum == InternalMemberValueE.Reference)
  1068.             {
  1069.                 InternalST.Soap( this"ParseMember Reference Type member: ",pr.PRname);            
  1070.                 // See if object has already been instantiated
  1071.                 Object refObj = m_objectManager.GetObject(pr.PRidRef);
  1072.                 if (refObj == null)
  1073.                 {
  1074.                     InternalST.Soap( this"ParseMember RecordFixup: ",pr.PRname);
  1075.                     InternalST.Soap( this"AddValue 3");                   
  1076.                     objectPr.PRobjectInfo.AddValue(pr.PRname, null);                    
  1077.                     objectPr.PRobjectInfo.RecordFixup(objectPr.PRobjectId, pr.PRname, pr.PRidRef); // Object not instantiated
  1078.                 }
  1079.                 else
  1080.                 {
  1081.                     InternalST.Soap( this"ParseMember Referenced Object Known ",pr.PRname," ",refObj);
  1082.                     InternalST.Soap( this"AddValue 5");               
  1083.                     objectPr.PRobjectInfo.AddValue(pr.PRname, refObj);
  1084.                 }
  1085.             }
  1086.             else if (pr.PRmemberValueEnum == InternalMemberValueE.InlineValue) 
  1087.             {
  1088.                 // Primitive type or String
  1089.                 InternalST.Soap( this"ParseMember primitive or String member: ",pr.PRname);
  1090.                 if (pr.PRdtType == Converter.typeofString)
  1091.                 {
  1092.                     ParseString(pr, objectPr);
  1093.                     InternalST.Soap( this"AddValue 6");               
  1094.                     objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRvalue);  
  1095.                 }
  1096.                 else if (pr.PRdtTypeCode == InternalPrimitiveTypeE.Invalid)
  1097.                 {
  1098.                     // The member field was an object put the value is Inline either  bin.Base64 or invalid
  1099.                     if (pr.PRarrayTypeEnum == InternalArrayTypeE.Base64)
  1100.                     {
  1101.                         InternalST.Soap( this"AddValue 7");                   
  1102.                         objectPr.PRobjectInfo.AddValue(pr.PRname, Convert.FromBase64String(FilterBin64(pr.PRvalue)));                                   
  1103.                     }
  1104.                     else if (pr.PRdtType == Converter.typeofObject && pr.PRvalue != null
  1105.                     {
  1106.                         if (objectPr != null && objectPr.PRdtType == Converter.typeofHeader)
  1107.                         {
  1108.                             // assume the type is a string for a header
  1109.                             pr.PRdtType = Converter.typeofString;
  1110.                             ParseString(pr, objectPr);
  1111.                             InternalST.Soap( this"AddValue 6");               
  1112.                             objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRvalue);  
  1113.                         }
  1114.                         else
  1115.                             throw new SerializationException(String.Format(SoapUtil.GetResourceString("Serialization_TypeMissing"), pr.PRname));
  1116.                     }
  1117.                     else
  1118.                     {
  1119.                         InternalST.Soap( this"Object Class with no memberInfo data  Member "+pr.PRname+" type "+pr.PRdtType);
  1120.                         if (pr.PRdtType != null && pr.PRdtType.IsEnum)
  1121.                         {
  1122.                             // Soap sends Enums as strings
  1123.                             Object obj = Enum.Parse(pr.PRdtType, pr.PRvalue);
  1124.                             InternalST.Soap( this"AddValue 8");                           
  1125.                             objectPr.PRobjectInfo.AddValue(pr.PRname, obj);                         
  1126.                         }
  1127.                         else if (pr.PRdtType != null && pr.PRdtType == Converter.typeofTypeArray)
  1128.                         {
  1129.                             // Soap sends MethodSignature as an array of types
  1130.                             InternalST.Soap( this"AddValue 8A");                          
  1131.                             objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRvarValue);                                                       
  1132.                         }
  1133.                         else
  1134.                         {
  1135.                             if  ((!pr.PRisRegistered) && (pr.PRobjectId > 0))
  1136.                             {
  1137.                                 if (pr.PRvalue == null)
  1138.                                     pr.PRvalue = ""//can't register null value, this must be a string so set to empty string.
  1139.                                 InternalST.Soap( this"ParseMember RegisterObject ",pr.PRvalue," ",pr.PRobjectId);                         
  1140.                                 RegisterObject(pr.PRvalue, pr, objectPr);
  1141.                             }
  1142.                             // Object Class with no memberInfo data
  1143.                             if (pr.PRdtType == Converter.typeofSystemVoid)
  1144.                             {
  1145.                                 InternalST.Soap( this"AddValue 9");
  1146.                                 objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRdtType);
  1147.                             }
  1148.                             else if (objectPr.PRobjectInfo.isSi)
  1149.                             {
  1150.                                 // ISerializable are added as strings, the conversion to type is done by the
  1151.                                 // ISerializable object
  1152.                                 InternalST.Soap( this"AddValue 10");
  1153.                                 objectPr.PRobjectInfo.AddValue(pr.PRname, pr.PRvalue);                          
  1154.                             }
  1155.                         }
  1156.                     }
  1157.                 }
  1158.                 else
  1159.                 {
  1160.                     Object var = null;
  1161.                     if (pr.PRvarValue != null)
  1162.                         var = pr.PRvarValue;
  1163.                     else
  1164.                         var = Converter.FromString(pr.PRvalue, pr.PRdtTypeCode);
  1165.                     // Not a string, convert the value
  1166.                     InternalST.Soap( this"ParseMember Converting primitive and storing");
  1167.                     stack.Dump();
  1168.                     InternalST.Soap( this"ParseMember pr "+pr.Trace());
  1169.                     InternalST.Soap( this"ParseMember objectPr ",objectPr.Trace());
  1170.                     InternalST.Soap( this"AddValue 11");  
  1171.                     if (pr.PRdtTypeCode == InternalPrimitiveTypeE.QName && var != null)
  1172.                     {
  1173.                         SoapQName soapQName = (SoapQName)var;
  1174.                         if (soapQName.Key != null)
  1175.                         {
  1176.                             if (soapQName.Key.Length == 0)
  1177.                                 soapQName.Namespace = (String)soapHandler.keyToNamespaceTable["xmlns"];
  1178.                             else
  1179.                                 soapQName.Namespace = (String)soapHandler.keyToNamespaceTable["xmlns"+":"+soapQName.Key];
  1180.                         }
  1181.                     }
  1182.                     objectPr.PRobjectInfo.AddValue(pr.PRname, var);             
  1183.                 }
  1184.             }
  1185.             else
  1186.                 ParseError(pr, objectPr);
  1187.         }
  1188.         // Object member end encountered in stream
  1189.         private void ParseMemberEnd(ParseRecord pr)
  1190.         {
  1191.             InternalST.Soap( this"ParseMemberEnd");
  1192.             switch(pr.PRmemberTypeEnum)
  1193.             {
  1194.                 case InternalMemberTypeE.Item:
  1195.                     ParseArrayMemberEnd(pr);
  1196.                     return;
  1197.                 case InternalMemberTypeE.Field:
  1198.                     if (pr.PRmemberValueEnum == InternalMemberValueE.Nested)
  1199.                         ParseObjectEnd(pr);
  1200.                     break;
  1201.                 default:
  1202.                     if (pr.PRmemberValueEnum == InternalMemberValueE.Nested)
  1203.                         ParseObjectEnd(pr);
  1204.                     else
  1205.                         ParseError(pr, (ParseRecord)stack.Peek());
  1206.                     break;
  1207.             }
  1208.         }
  1209.         // Processes a string object by getting an internal ID for it and registering it with the objectManager
  1210.         private void ParseString(ParseRecord pr, ParseRecord parentPr)
  1211.         {
  1212.             InternalST.Soap( this"ParseString Entry ",pr.PRobjectId," ",pr.PRvalue," ",pr.PRisRegistered);
  1213.             // If there is a string and it wasn't marked as a null object, then it is an empty string
  1214.             if (pr.PRvalue == null)
  1215.                 pr.PRvalue = ""
  1216.             // Process String class
  1217.             if  ((!pr.PRisRegistered) && (pr.PRobjectId > 0))
  1218.             {
  1219.                 InternalST.Soap( this"ParseString  RegisterObject ",pr.PRvalue," ",pr.PRobjectId);                            
  1220.                 // String is treated as an object if it has an id
  1221.                 //m_objectManager.RegisterObject(pr.PRvalue, pr.PRobjectId);
  1222.                 RegisterObject(pr.PRvalue, pr, parentPr);
  1223.             }
  1224.         }
  1225.         private void RegisterObject(Object obj, ParseRecord pr, ParseRecord objectPr)
  1226.         {
  1227.             if (!pr.PRisRegistered)
  1228.             {
  1229.                 pr.PRisRegistered = true;
  1230.                 SerializationInfo si = null;
  1231.                 long parentId = 0;
  1232.                 MemberInfo memberInfo = null;
  1233.                 int[] indexMap = null;
  1234.                 if (objectPr != null)
  1235.                 {
  1236.                     indexMap = objectPr.PRindexMap;
  1237.                     parentId = objectPr.PRobjectId;                 
  1238.                     if (objectPr.PRobjectInfo != null)
  1239.                     {
  1240.                         if (!objectPr.PRobjectInfo.isSi)
  1241.                         {
  1242.                             // ParentId is only used if there is a memberInfo
  1243.                             InternalST.Soap( this"RegisterObject GetMemberInfo parent ",objectPr.PRobjectInfo.objectType," name looking for ", pr.PRname," field obj "+obj); 
  1244.                             memberInfo = objectPr.PRobjectInfo.GetMemberInfo(pr.PRname);
  1245.                         }
  1246.                     }
  1247.                 }
  1248.                 if (pr.PRobjectInfo != null)
  1249.                 {
  1250.                     // SerializationInfo is always needed for ISerialization                        
  1251.                     si = pr.PRobjectInfo.si;
  1252.                 }
  1253.                 InternalST.Soap( this"RegisterObject 0bj ",obj," objectId ",pr.PRobjectId," si ", si," parentId ",parentId," memberInfo ",memberInfo, " indexMap "+indexMap);
  1254.                 m_objectManager.RegisterObject(obj, pr.PRobjectId, si, parentId, memberInfo, indexMap); 
  1255.             }
  1256.         }
  1257.         internal void SetVersion(int major, int minor)
  1258.         {
  1259.             // Don't do version checking if property is set to Simple
  1260.             if (formatterEnums.FEassemblyFormat != FormatterAssemblyStyle.Simple)
  1261.             {
  1262.                 this.majorVersion = major;
  1263.                 this.minorVersion = minor;
  1264.             }
  1265.         }
  1266.         // Assigns an internal ID associated with the xml id attribute
  1267.         String inKeyId = null;
  1268.         long outKeyId = 0;      
  1269.         internal long GetId(String keyId)
  1270.         {
  1271.             if (keyId == null)
  1272.                 throw new ArgumentNullException("keyId", String.Format(SoapUtil.GetResourceString("ArgumentNull_WithParamName"), "keyId"));
  1273.             if (keyId != inKeyId)
  1274.             {               
  1275.                 inKeyId = keyId;
  1276.                 String idString = null;
  1277.                 InternalST.Soap( this"GetId Entry ",keyId);
  1278.                 if (keyId[0] == '#')
  1279.                     idString = keyId.Substring(1);
  1280.                 else
  1281.                     idString = keyId;
  1282.                 Object idObj = objectIdTable[idString];
  1283.                 if (idObj == null)
  1284.                 {
  1285.                     outKeyId = ++objectIds;
  1286.                     objectIdTable[idString] = outKeyId;
  1287.                     InternalST.Soap( this"GetId Exit new ID ",outKeyId);
  1288.                 }
  1289.                 else
  1290.                 {
  1291.                     InternalST.Soap( this"GetId Exit oldId ",(Int64)idObj);           
  1292.                     outKeyId =  (Int64)idObj;
  1293.                 }
  1294.             }
  1295.             InternalST.Soap( this"GetId  in id ",keyId, " out id ", outKeyId);
  1296.             return outKeyId;
  1297.         }
  1298.         // Assigns an internal ID associated with the binary id number
  1299.         long inId = 0;
  1300.         long outId = 0;
  1301.         internal long GetId(long objectId)
  1302.         {
  1303.             if (inId != objectId)
  1304.             {
  1305.                 inId = objectId;
  1306.                 Object idObj = null;
  1307.                 if (objectId > 0)
  1308.                     idObj = objectIdTable[objectId];
  1309.                 if (idObj == null)          
  1310.                 {
  1311.                     outId = ++objectIds;
  1312.                     objectIdTable[objectId] = outId;
  1313.                     InternalST.Soap( this"GetId Exit new ID ",outId);
  1314.                 }
  1315.                 else
  1316.                 {
  1317.                     InternalST.Soap( this"GetId Exit oldId ",(Int64)idObj);           
  1318.                     outId = (Int64)idObj;
  1319.                 }
  1320.             }
  1321.             return outId;
  1322.         }
  1323.         // Trace which includes a single dimensional int array
  1324.         [Conditional("SER_LOGGING")]                        
  1325.         private void IndexTraceMessage(String message, int[] index)
  1326.         {
  1327.             StringBuilder sb = new StringBuilder(10);
  1328.             sb.Append("[");     
  1329.             for (int i=0; i<index.Length; i++)
  1330.             {
  1331.                 sb.Append(index[i]);
  1332.                 if (i != index.Length -1)
  1333.                     sb.Append(",");
  1334.             }
  1335.             sb.Append("]");             
  1336.             InternalST.Soap( this, message," ",sb.ToString());
  1337.         }
  1338.         internal Assembly LoadAssemblyFromString(String assemblyString)
  1339.         {
  1340.             Assembly assm = null;
  1341.             if(formatterEnums.FEassemblyFormat == FormatterAssemblyStyle.Simple) {
  1342.                 assm  = Assembly.LoadWithPartialName(assemblyString);
  1343.             }
  1344.             else {
  1345.                 try
  1346.                     {
  1347.                         assm = Assembly.Load(assemblyString);
  1348.                     }
  1349.                 catch (Exception)
  1350.                     {
  1351.                     }
  1352.             }
  1353.             return assm;
  1354.         }
  1355.         internal Type Bind(String assemblyString, String typeString)
  1356.         {
  1357.             Type type = null;
  1358.             if (m_binder != null)
  1359.                 type = m_binder.BindToType(assemblyString, typeString);
  1360.             return type;
  1361.         }
  1362.         internal class TypeNAssembly
  1363.         {
  1364.             public Type type;
  1365.             public String assemblyName;
  1366.         }
  1367.         NameCache typeCache = new NameCache();
  1368.         internal Type FastBindToType(String assemblyName, String typeName)
  1369.         {
  1370.             Type type = null;
  1371.             TypeNAssembly entry = (TypeNAssembly)typeCache.GetCachedValue(typeName);
  1372.             if (entry == null || entry.assemblyName != assemblyName)
  1373.             {
  1374.                 Assembly assm = LoadAssemblyFromString(assemblyName);
  1375.                 if (assm == null)
  1376.                     return null;
  1377.                 type = FormatterServices.GetTypeFromAssembly(assm, typeName);
  1378.                 if (type == null)
  1379.                     return null;
  1380.                 entry = new TypeNAssembly();
  1381.                 entry.type = type;
  1382.                 entry.assemblyName = assemblyName;
  1383.                 typeCache.SetCachedValue(entry);
  1384.             }
  1385.            return entry.type;
  1386.         }
  1387.         /*
  1388.         static Hashtable typeNames = new Hashtable();
  1389.         static int s_numEntries = 0;
  1390.         private const int MAX_CACHE_ENTRIES = 128; // No data gathered to arrive at this number !
  1391.         internal Type FastBindToType(String assemblyName, String typeName)
  1392.         {
  1393.             TypeNAssembly match = (TypeNAssembly)typeNames[typeName];
  1394.             if (match == null)
  1395.             {
  1396.                 match = new TypeNAssembly();
  1397.                 Assembly assm = null;
  1398.                 try
  1399.                 {
  1400.                     assm = Assembly.Load(assemblyName);
  1401.                 }
  1402.                 catch (Exception)
  1403.                 {
  1404.                     return null;
  1405.                 }
  1406.                 if (assm == null)
  1407.                     return null;
  1408.                 match.type = FormatterServices.GetTypeFromAssembly(assm, typeName);
  1409.                 if (match.type == null)
  1410.                     return null;
  1411.                 match.assemblyName = assemblyName;
  1412.                 lock (typeNames)
  1413.                 {
  1414.                     if (s_numEntries == MAX_CACHE_ENTRIES)
  1415.                     {
  1416.                         typeNames.Clear();
  1417.                         s_numEntries = 0;
  1418.                     }
  1419.                     typeNames[typeName] = match;
  1420.                     s_numEntries ++;
  1421.                 }
  1422.                 return match.type;
  1423.             }
  1424.             if (match.assemblyName.Equals(assemblyName))
  1425.                 return match.type;
  1426.             // So if same type name occurs in 2 assemblies, the second guy loses
  1427.             return null;
  1428.         }
  1429.         */
  1430.         StringBuilder sbf = new StringBuilder();
  1431.         internal String FilterBin64(String value)
  1432.         {
  1433.             sbf.Length = 0;
  1434.             for (int i=0; i<value.Length; i++)
  1435.             {
  1436.                 if (!(value[i] == ' '|| value[i] == '/n' || value[i] == '/r'))
  1437.                     sbf.Append(value[i]);
  1438.             }
  1439.             return sbf.ToString();
  1440.         }
  1441.     }
  1442. }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值