Ejemplo n.º 1
0
        public Array System__Method__Signature___(string MethodName)
        {
            //TODO: support overloaded methods
            XmlRpcServiceInfo svcInfo = XmlRpcServiceInfo.CreateServiceInfo(
                this.GetType());
            XmlRpcMethodInfo mthdInfo = svcInfo.GetMethod(MethodName);

            if (mthdInfo == null)
            {
                throw new XmlRpcFaultException(880,
                                               "Request for information on unsupported method");
            }
            if (mthdInfo.IsHidden)
            {
                throw new XmlRpcFaultException(881,
                                               "Information not available on this method");
            }
            //XmlRpcTypes.CheckIsXmlRpcMethod(mi);
            ArrayList alist = new ArrayList();

            alist.Add(XmlRpcTypeInfo.GetXmlRpcTypeString(mthdInfo.ReturnType));
            foreach (XmlRpcParameterInfo paramInfo in mthdInfo.Parameters)
            {
                alist.Add(XmlRpcTypeInfo.GetXmlRpcTypeString(paramInfo.Type));
            }
            string[]  types    = (string[])alist.ToArray(typeof(string));
            ArrayList retalist = new ArrayList();

            retalist.Add(types);
            Array retarray = retalist.ToArray(typeof(string[]));

            return(retarray);
        }
Ejemplo n.º 2
0
        public void XmlRpcStruct()
        {
            Type       type    = typeof(XmlRpcStruct);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tHashtable, rpcType, "XmlRpcStruct doesn't map to XmlRpcType.tHashtable");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "struct", "XmlRpcStruct doesn't map to 'struct'");
        }
Ejemplo n.º 3
0
        public void String()
        {
            Type       type    = typeof(string);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tString, rpcType, "String doesn't map to XmlRpcType.tString");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "string", "String doesn't map to 'string'");
        }
Ejemplo n.º 4
0
        public void XmlRpcBoolean()
        {
            Type       type    = typeof(bool?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tBoolean, rpcType, "XmlRpcBoolean doesn't map to XmlRpcType.tBoolean");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "boolean", "XmlRpcBoolean doesn't map to 'boolean'");
        }
Ejemplo n.º 5
0
        public void XmlRpcInt()
        {
            Type       type    = typeof(int?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tInt32, rpcType, "XmlRpcInt doesn't map to XmlRpcType.tInt32");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "integer", "XmlRpcInt doesn't map to 'integer'");
        }
Ejemplo n.º 6
0
        public void NullableStruct()
        {
            Type       type    = typeof(TestStruct?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tStruct, rpcType, "TestStruct? doesn't map to XmlRpcType.tStruct");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "struct", "TestStruct? doesn't map to 'struct'");
        }
Ejemplo n.º 7
0
        public void NullableDateTime()
        {
            Type       type    = typeof(DateTime?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tDateTime, rpcType, "DateTime? doesn't map to XmlRpcType.tDateTime");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "dateTime", "DateTime? doesn't map to 'dateTime'");
        }
Ejemplo n.º 8
0
        public void NullableIn64()
        {
            Type       type    = typeof(long?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tInt64, rpcType, "long? doesn't map to XmlRpcType.tInt64");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "i8", "long? doesn't map to 'i8'");
        }
Ejemplo n.º 9
0
        public void Void()
        {
            Type       type    = typeof(void);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tVoid, rpcType, "void doesn't map to XmlRpcType.tVoid");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "void", "void doesn't map to 'void'");
        }
Ejemplo n.º 10
0
        public void JaggedIntArray()
        {
            Type       type    = typeof(Int32[][]);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tArray, rpcType, "Int32[] doesn't map to XmlRpcType.tArray");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "array", "Int32[] doesn't map to 'array'");
        }
Ejemplo n.º 11
0
        public void Array()
        {
            Type       type    = typeof(Array);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tArray, rpcType, "Array doesn't map to XmlRpcType.tArray");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "array", "Array doesn't map to 'array'");
        }
Ejemplo n.º 12
0
        public void XmlRpcDouble()
        {
            Type       type    = typeof(double?);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tDouble, rpcType, "XmlRpcDouble doesn't map to XmlRpcType.tDouble");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "double", "XmlRpcDouble doesn't map to 'double'");
        }
Ejemplo n.º 13
0
        public void Base64()
        {
            Type       type    = typeof(byte[]);
            XmlRpcType rpcType = XmlRpcTypeInfo.GetXmlRpcType(type);

            Assert.AreEqual(XmlRpcType.tBase64, rpcType, "Byte[] doesn't map to XmlRpcType.tBase64");
            string rpcString = XmlRpcTypeInfo.GetXmlRpcTypeString(type);

            Assert.AreEqual(rpcString, "base64", "Byte[] doesn't map to 'base64'");
        }
Ejemplo n.º 14
0
 private void CheckImplictString(Type valType, MappingStack mappingStack)
 {
     if (valType != null && valType != typeof(string))
     {
         throw new XmlRpcTypeMismatchException(mappingStack.MappingType
                                               + " contains implicit string value where "
                                               + XmlRpcTypeInfo.GetXmlRpcTypeString(valType)
                                               + " expected " + StackDump(mappingStack));
     }
 }
Ejemplo n.º 15
0
 private static void CheckImplictString(Type valType, MappingStack mappingStack)
 {
     if (valType != null && valType != typeof(string) && !valType.IsEnum)
     {
         throw new XmlRpcTypeMismatchException(
                   string.Format(
                       "{0} contains implicit string value where {1} expected {2}",
                       mappingStack.MappingType,
                       XmlRpcTypeInfo.GetXmlRpcTypeString(valType),
                       StackDump(mappingStack)));
     }
 }
Ejemplo n.º 16
0
        private static void CheckExpectedType(Type expectedType, Type actualType, MappingStack mappingStack)
        {
            if (expectedType != null && expectedType.IsEnum)
            {
                var fourBitTypes   = new[] { typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int) };
                var eightBitTypes  = new[] { typeof(uint), typeof(long) };
                var underlyingType = Enum.GetUnderlyingType(expectedType);
                if (Array.IndexOf(fourBitTypes, underlyingType) >= 0)
                {
                    expectedType = typeof(int);
                }
                else if (Array.IndexOf(eightBitTypes, underlyingType) >= 0)
                {
                    expectedType = typeof(long);
                }
                else
                {
                    throw new XmlRpcInvalidEnumValue(
                              string.Format(
                                  "{0} contains {1} which cannot be mapped to  {2} {3}",
                                  mappingStack.MappingType,
                                  XmlRpcTypeInfo.GetXmlRpcTypeString(actualType),
                                  XmlRpcTypeInfo.GetXmlRpcTypeString(expectedType),
                                  StackDump(mappingStack)));
                }
            }

            // TODO: throw exception for invalid enum type
            if (expectedType != null &&
                expectedType != typeof(object) &&
                expectedType != actualType &&
                (actualType.IsValueType && expectedType != typeof(Nullable <>).MakeGenericType(actualType)))
            {
                throw new XmlRpcTypeMismatchException(
                          string.Format(
                              "{0} contains {1} value where {2} expected {3}",
                              mappingStack.MappingType,
                              XmlRpcTypeInfo.GetXmlRpcTypeString(actualType),
                              XmlRpcTypeInfo.GetXmlRpcTypeString(expectedType),
                              StackDump(mappingStack)));
            }
        }
Ejemplo n.º 17
0
 private void CheckExpectedType(Type expectedType, Type actualType, MappingStack mappingStack)
 {
     if (expectedType != null && expectedType.IsEnum)
     {
         Type[] i4Types = new Type[] { typeof(byte), typeof(sbyte), typeof(short),
                                       typeof(ushort), typeof(int) };
         Type[] i8Types        = new Type[] { typeof(uint), typeof(long) };
         Type   underlyingType = Enum.GetUnderlyingType(expectedType);
         if (Array.IndexOf(i4Types, underlyingType) >= 0)
         {
             expectedType = typeof(Int32);
         }
         else if (Array.IndexOf(i8Types, underlyingType) >= 0)
         {
             expectedType = typeof(long);
         }
         else
         {
             throw new XmlRpcInvalidEnumValue(mappingStack.MappingType +
                                              " contains "
                                              + XmlRpcTypeInfo.GetXmlRpcTypeString(actualType)
                                              + " which cannot be mapped to  "
                                              + XmlRpcTypeInfo.GetXmlRpcTypeString(expectedType)
                                              + " " + StackDump(mappingStack));
         }
     }
     // TODO: throw exception for invalid enum type
     if (expectedType != null && expectedType != typeof(Object) &&
         expectedType != actualType &&
         (actualType.IsValueType &&
          expectedType != typeof(Nullable <>).MakeGenericType(actualType)))
     {
         throw new XmlRpcTypeMismatchException(mappingStack.MappingType +
                                               " contains "
                                               + XmlRpcTypeInfo.GetXmlRpcTypeString(actualType)
                                               + " value where "
                                               + XmlRpcTypeInfo.GetXmlRpcTypeString(expectedType)
                                               + " expected " + StackDump(mappingStack));
     }
 }
Ejemplo n.º 18
0
        private object MapArray(IEnumerator <Node> iter, Type valType,
                                MappingStack mappingStack, MappingAction mappingAction,
                                out Type mappedType)
        {
            mappedType = null;
            // required type must be an array
            if (valType != null &&
                !(valType.IsArray == true ||
                  valType == typeof(Array) ||
                  valType == typeof(object)))
            {
                throw new XmlRpcTypeMismatchException(mappingStack.MappingType
                                                      + " contains array value where "
                                                      + XmlRpcTypeInfo.GetXmlRpcTypeString(valType)
                                                      + " expected " + StackDump(mappingStack));
            }
            if (valType != null)
            {
                XmlRpcType xmlRpcType = XmlRpcTypeInfo.GetXmlRpcType(valType);
                if (xmlRpcType == XmlRpcType.tMultiDimArray)
                {
                    mappingStack.Push("array mapped to type " + valType.Name);
                    Object ret = MapMultiDimArray(iter, valType, mappingStack,
                                                  mappingAction);
                    return(ret);
                }
                mappingStack.Push("array mapped to type " + valType.Name);
            }
            else
            {
                mappingStack.Push("array");
            }

            var  values   = new List <object>();
            Type elemType = DetermineArrayItemType(valType);

            bool bGotType = false;
            Type useType  = null;

            while (iter.MoveNext() && iter.Current is ValueNode)
            {
                mappingStack.Push(String.Format("element {0}", values.Count));
                object value = MapValueNode(iter, elemType, mappingStack, mappingAction);
                values.Add(value);
                mappingStack.Pop();
            }

            foreach (object value in values)
            {
                if (value == null)
                {
                    continue;
                }
                if (bGotType == false)
                {
                    useType  = value.GetType();
                    bGotType = true;
                }
                else
                {
                    if (useType != value.GetType())
                    {
                        useType = null;
                    }
                }
            }

            Object[] args = new Object[1];
            args[0] = values.Count;
            Object retObj = null;

            if (valType != null &&
                valType != typeof(Array) &&
                valType != typeof(object))
            {
                retObj = CreateArrayInstance(valType, args);
            }
            else
            {
                if (useType == null)
                {
                    retObj = CreateArrayInstance(typeof(object[]), args);
                }
                else
                {
                    retObj = Array.CreateInstance(useType, (int)args[0]);
                };
            }
            for (int j = 0; j < values.Count; j++)
            {
                ((Array)retObj).SetValue(values[j], j);
            }

            mappingStack.Pop();

            return(retObj);
        }
Ejemplo n.º 19
0
        private object MapStruct(IEnumerator <Node> iter, Type valueType, MappingStack mappingStack,
                                 MappingAction mappingAction, out Type mappedType)
        {
            mappedType = null;

            if (valueType.IsPrimitive)
            {
                throw new XmlRpcTypeMismatchException(mappingStack.MappingType
                                                      + " contains struct value where "
                                                      + XmlRpcTypeInfo.GetXmlRpcTypeString(valueType)
                                                      + " expected " + StackDump(mappingStack));
            }
            if (valueType.IsGenericType &&
                valueType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                valueType = valueType.GetGenericArguments()[0];
            }
            object retObj;

            try
            {
                retObj = Activator.CreateInstance(valueType);
            }
            catch (Exception)
            {
                throw new XmlRpcTypeMismatchException(mappingStack.MappingType
                                                      + " contains struct value where "
                                                      + XmlRpcTypeInfo.GetXmlRpcTypeString(valueType)
                                                      + " expected (as type " + valueType.Name + ") "
                                                      + StackDump(mappingStack));
            }
            // Note: mapping action on a struct is only applied locally - it
            // does not override the global mapping action when members of the
            // struct are mapped
            MappingAction localAction = mappingAction;

            if (valueType != null)
            {
                mappingStack.Push("struct mapped to type " + valueType.Name);
                localAction = StructMappingAction(valueType, mappingAction);
            }
            else
            {
                mappingStack.Push("struct");
            }
            // create map of field names and remove each name from it as
            // processed so we can determine which fields are missing
            var names = new List <string>();

            CreateFieldNamesMap(valueType, names);
            int           fieldCount = 0;
            List <string> rpcNames   = new List <string>();

            try
            {
                while (iter.MoveNext())
                {
                    if (!(iter.Current is StructMember))
                    {
                        break;
                    }
                    string rpcName = (iter.Current as StructMember).Value;
                    if (rpcNames.Contains(rpcName))
                    {
                        if (!IgnoreDuplicateMembers)
                        {
                            throw new XmlRpcInvalidXmlRpcException(mappingStack.MappingType
                                                                   + " contains struct value with duplicate member "
                                                                   + rpcName
                                                                   + " " + StackDump(mappingStack));
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        rpcNames.Add(rpcName);
                    }

                    string     name = GetStructName(valueType, rpcName) ?? rpcName;
                    MemberInfo mi   = valueType.GetField(name);
                    if (mi == null)
                    {
                        mi = valueType.GetProperty(name);
                    }
                    if (mi == null)
                    {
                        iter.MoveNext();  // move to value
                        if (iter.Current is ComplexValueNode)
                        {
                            int depth = iter.Current.Depth;
                            while (!(iter.Current is EndComplexValueNode && iter.Current.Depth == depth))
                            {
                                iter.MoveNext();
                            }
                        }
                        continue;
                    }
                    if (names.Contains(name))
                    {
                        names.Remove(name);
                    }
                    else
                    {
                        if (Attribute.IsDefined(mi, typeof(NonSerializedAttribute)))
                        {
                            mappingStack.Push(String.Format("member {0}", name));
                            throw new XmlRpcNonSerializedMember("Cannot map XML-RPC struct "
                                                                + "member onto member marked as [NonSerialized]: "
                                                                + " " + StackDump(mappingStack));
                        }
                    }
                    Type memberType = mi.MemberType == MemberTypes.Field
                    ? (mi as FieldInfo).FieldType : (mi as PropertyInfo).PropertyType;
                    string mappingMsg = valueType == null
                        ? String.Format("member {0}", name)
                        : String.Format("member {0} mapped to type {1}", name, memberType.Name);

                    iter.MoveNext();
                    object valObj = OnStack(mappingMsg, mappingStack, delegate()
                    {
                        return(MapValueNode(iter, memberType, mappingStack, mappingAction));
                    });

                    if (mi.MemberType == MemberTypes.Field)
                    {
                        try
                        {
                            (mi as FieldInfo).SetValue(retObj, valObj);
                        }
                        catch
                        {
                            (mi as FieldInfo).SetValue(retObj, Enum.Parse((mi as FieldInfo).FieldType, (string)valObj, true));
                        }
                    }
                    else
                    {
                        (mi as PropertyInfo).SetValue(retObj, valObj, null);
                    }
                    fieldCount++;
                }

                if (localAction == MappingAction.Error && names.Count > 0)
                {
                    ReportMissingMembers(valueType, names, mappingStack);
                }
                return(retObj);
            }
            finally
            {
                mappingStack.Pop();
            }
        }
Ejemplo n.º 20
0
        static void ExtractMethodInfo(Dictionary <string, XmlRpcMethodInfo> methods,
                                      MethodInfo mi, Type type)
        {
            XmlRpcMethodAttribute attr = (XmlRpcMethodAttribute)
                                         Attribute.GetCustomAttribute(mi,
                                                                      typeof(XmlRpcMethodAttribute));

            if (attr == null)
            {
                return;
            }
            XmlRpcMethodInfo mthdInfo = new XmlRpcMethodInfo();

            mthdInfo.MethodInfo = mi;
            mthdInfo.XmlRpcName = XmlRpcTypeInfo.GetXmlRpcMethodName(mi);
            mthdInfo.MiName     = mi.Name;
            mthdInfo.Doc        = attr.Description;
            mthdInfo.IsHidden   = attr.IntrospectionMethod | attr.Hidden;
            // extract parameters information
            var parmList = new List <XmlRpcParameterInfo>();

            ParameterInfo[] parms = mi.GetParameters();
            foreach (ParameterInfo parm in parms)
            {
                XmlRpcParameterInfo parmInfo = new XmlRpcParameterInfo();
                parmInfo.Name       = parm.Name;
                parmInfo.Type       = parm.ParameterType;
                parmInfo.XmlRpcType = XmlRpcTypeInfo.GetXmlRpcTypeString(parm.ParameterType);
                // retrieve optional attributed info
                parmInfo.Doc = "";
                XmlRpcParameterAttribute pattr = (XmlRpcParameterAttribute)
                                                 Attribute.GetCustomAttribute(parm,
                                                                              typeof(XmlRpcParameterAttribute));
                if (pattr != null)
                {
                    parmInfo.Doc        = pattr.Description;
                    parmInfo.XmlRpcName = pattr.Name;
                }
                parmInfo.IsParams = Attribute.IsDefined(parm,
                                                        typeof(ParamArrayAttribute));
                parmList.Add(parmInfo);
            }
            mthdInfo.Parameters = parmList.ToArray();
            // extract return type information
            mthdInfo.ReturnType       = mi.ReturnType;
            mthdInfo.ReturnXmlRpcType = XmlRpcTypeInfo.GetXmlRpcTypeString(mi.ReturnType);
            object[] orattrs = mi.ReturnTypeCustomAttributes.GetCustomAttributes(
                typeof(XmlRpcReturnValueAttribute), false);
            if (orattrs.Length > 0)
            {
                mthdInfo.ReturnDoc = ((XmlRpcReturnValueAttribute)orattrs[0]).Description;
            }

            if (methods.ContainsKey(mthdInfo.XmlRpcName))
            {
                throw new XmlRpcDupXmlRpcMethodNames(String.Format("Method "
                                                                   + "{0} in type {1} has duplicate XmlRpc method name {2}",
                                                                   mi.Name, type.Name, mthdInfo.XmlRpcName));
            }
            else
            {
                methods.Add(mthdInfo.XmlRpcName, mthdInfo);
            }
        }
Ejemplo n.º 21
0
        private static void WriteType(
            HtmlTextWriter wrtr,
            Type type,
            bool isparams,
            IList structs)
        {
            // TODO: following is hack for case when type is Object
            string xmlRpcType;

            if (!isparams)
            {
                xmlRpcType = type != typeof(object)
                    ? XmlRpcTypeInfo.GetXmlRpcTypeString(type)
                    : "any";
            }
            else
            {
                xmlRpcType = "varargs";
            }

            wrtr.Write(xmlRpcType);
            if (string.Equals(xmlRpcType, "struct", StringComparison.Ordinal) && type != typeof(XmlRpcStruct))
            {
                if (!structs.Contains(type))
                {
                    structs.Add(type);
                }

                wrtr.Write(" ");
                wrtr.WriteBeginTag("a");
                wrtr.WriteAttribute("href", "#" + type.Name);
                wrtr.Write(HtmlTextWriter.TagRightChar);
                wrtr.Write(type.Name);
                wrtr.WriteEndTag("a");
            }
            else if (string.Equals(xmlRpcType, "array", StringComparison.Ordinal) ||
                     string.Equals(xmlRpcType, "varargs", StringComparison.Ordinal))
            {
                if (type.GetArrayRank() == 1)  // single dim array
                {
                    wrtr.Write(" of ");
                    var    elemType = type.GetElementType();
                    string elemXmlRpcType;
                    elemXmlRpcType = elemType != typeof(object)
                        ? XmlRpcTypeInfo.GetXmlRpcTypeString(elemType)
                        : "any";

                    wrtr.Write(elemXmlRpcType);
                    if (string.Equals(elemXmlRpcType, "struct", StringComparison.Ordinal) && elemType != typeof(XmlRpcStruct))
                    {
                        if (!structs.Contains(elemType))
                        {
                            structs.Add(elemType);
                        }

                        wrtr.Write(" ");
                        wrtr.WriteBeginTag("a");
                        wrtr.WriteAttribute("href", "#" + elemType.Name);
                        wrtr.Write(HtmlTextWriter.TagRightChar);
                        wrtr.Write(elemType.Name);
                        wrtr.WriteEndTag("a");
                    }
                }
            }
        }
Ejemplo n.º 22
0
        static void WriteType(
            HtmlTextWriter wrtr,
            Type type,
            bool isparams,
            ArrayList structs)
        {
            // TODO: following is hack for case when type is Object
            string xmlRpcType;

            if (!isparams)
            {
                if (type != typeof(Object))
                {
                    xmlRpcType = XmlRpcTypeInfo.GetXmlRpcTypeString(type);
                }
                else
                {
                    xmlRpcType = "any";
                }
            }
            else
            {
                xmlRpcType = "varargs";
            }
            wrtr.Write(xmlRpcType);
            if (xmlRpcType == "struct" && type != typeof(XmlRpcStruct))
            {
                if (!structs.Contains(type))
                {
                    structs.Add(type);
                }
                wrtr.Write(" ");
                wrtr.WriteBeginTag("a");
                wrtr.WriteAttribute("href", "#" + type.Name);
                wrtr.Write(HtmlTextWriter.TagRightChar);
                wrtr.Write(type.Name);
                wrtr.WriteEndTag("a");
            }
            else if (xmlRpcType == "array" || xmlRpcType == "varargs")
            {
                if (type.GetArrayRank() == 1) // single dim array
                {
                    wrtr.Write(" of ");
                    Type   elemType = type.GetElementType();
                    string elemXmlRpcType;
                    if (elemType != typeof(Object))
                    {
                        elemXmlRpcType = XmlRpcTypeInfo.GetXmlRpcTypeString(elemType);
                    }
                    else
                    {
                        elemXmlRpcType = "any";
                    }
                    wrtr.Write(elemXmlRpcType);
                    if (elemXmlRpcType == "struct" && elemType != typeof(XmlRpcStruct))
                    {
                        if (!structs.Contains(elemType))
                        {
                            structs.Add(elemType);
                        }
                        wrtr.Write(" ");
                        wrtr.WriteBeginTag("a");
                        wrtr.WriteAttribute("href", "#" + elemType.Name);
                        wrtr.Write(HtmlTextWriter.TagRightChar);
                        wrtr.Write(elemType.Name);
                        wrtr.WriteEndTag("a");
                    }
                }
            }
        }