// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
// Lluis Sanchez Gual (lluis@ximian.com)
+// Atsushi Enomoto (atsushi@ximian.com)
//
// (C) 2002 Ximian, Inc (http://www.ximian.com)
//
using System;
using System.Collections;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
using System.Globalization;
using System.Reflection;
+using System.Text;
using System.Xml.Schema;
namespace System.Xml.Serialization
{
+ [AttributeUsage (AttributeTargets.Class, AllowMultiple = false)]
+ internal class XmlTypeConvertorAttribute : Attribute
+ {
+ /*
+ * Bug #12571:
+ *
+ * System.Xml.Linq.XElement should be deserializable from an XmlElement.
+ *
+ * Types can now register a custom deserializer by adding this custom attribute.
+ * Method is the name of a private 'static method (static object)' method that will
+ * be invoked to construct an instance of the object.
+ *
+ */
+ public string Method {
+ get;
+ private set;
+ }
+
+ public XmlTypeConvertorAttribute (string method)
+ {
+ Method = method;
+ }
+ }
+
internal class TypeData
{
Type type;
Type listItemType;
string typeName;
string fullTypeName;
+ string csharpName;
+ string csharpFullName;
TypeData listItemTypeData;
TypeData listTypeData;
TypeData mappedType;
XmlSchemaPatternFacet facet;
+ MethodInfo typeConvertor;
bool hasPublicConstructor = true;
+ bool nullableOverride;
public TypeData (Type type, string elementName, bool isPrimitive) :
this(type, elementName, isPrimitive, null, null) {}
public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
{
+#if NET_2_0
+ if (type.IsGenericTypeDefinition)
+ throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
+#endif
this.mappedType = mappedType;
this.facet = facet;
this.type = type;
if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
}
+
+ LookupTypeConvertor ();
}
internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
this.hasPublicConstructor = true;
}
+ void LookupTypeConvertor ()
+ {
+#if NET_4_5
+ // We only need this for System.Xml.Linq.
+ var convertor = type.GetCustomAttribute<XmlTypeConvertorAttribute> ();
+ if (convertor != null)
+ typeConvertor = type.GetMethod (convertor.Method, BindingFlags.Static | BindingFlags.NonPublic);
+#endif
+ }
+
+ internal void ConvertForAssignment (ref object value)
+ {
+ // Has this object registered a custom type converter?
+ if (typeConvertor != null)
+ value = typeConvertor.Invoke (null, new object[] { value });
+ }
+
public string TypeName
{
get {
}
}
+ public string CSharpName
+ {
+ get {
+ if (csharpName == null)
+ csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
+ return csharpName;
+ }
+ }
+
+ public string CSharpFullName
+ {
+ get {
+ if (csharpFullName == null)
+ csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
+ return csharpFullName;
+ }
+ }
+
+ // static Microsoft.CSharp.CSharpCodeProvider csprovider =
+ // new Microsoft.CSharp.CSharpCodeProvider ();
+
+ public static string ToCSharpName (Type type, bool full)
+ {
+ // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
+ StringBuilder sb = new StringBuilder ();
+
+ if (type.IsArray) {
+ sb.Append (ToCSharpName (type.GetElementType (), full));
+ sb.Append ('[');
+ int rank = type.GetArrayRank ();
+ for (int i = 1; i < rank; i++)
+ sb.Append (',');
+ sb.Append (']');
+ return sb.ToString ();
+ }
+#if NET_2_0
+ // Generic nested types return the complete list of type arguments,
+ // including type arguments for the declaring class. This requires
+ // some special handling
+ if (type.IsGenericType && !type.IsGenericTypeDefinition) {
+ Type[] args = type.GetGenericArguments ();
+ int nt = args.Length - 1;
+ Type pt = type;
+ // Loop throguh the declaring class chain, consuming type arguments for every
+ // generic class in the chain
+ while (pt != null) {
+ if (sb.Length > 0)
+ sb.Insert (0, '.');
+ int i = pt.Name.IndexOf ('`');
+ if (i != -1) {
+ int na = nt - int.Parse (pt.Name.Substring (i+1));
+ sb.Insert (0,'>');
+ for (;nt > na; nt--) {
+ sb.Insert (0, ToCSharpName (args[nt],full));
+ if (nt - 1 != na)
+ sb.Insert (0, ',');
+ }
+ sb.Insert (0,'<');
+ sb.Insert (0, pt.Name.Substring (0, i));
+ } else
+ sb.Insert (0, pt.Name);
+ pt = pt.DeclaringType;
+ }
+ if (full && type.Namespace.Length > 0)
+ sb.Insert (0, type.Namespace + ".");
+ return sb.ToString ();
+ }
+#endif
+ if (type.DeclaringType != null) {
+ sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
+ sb.Append (type.Name);
+ }
+ else {
+ if (full && type.Namespace.Length > 0)
+ sb.Append (type.Namespace).Append ('.');
+ sb.Append (type.Name);
+ }
+ return sb.ToString ();
+ }
+
+ static bool IsKeyword (string name)
+ {
+ if (keywordsTable == null) {
+ Hashtable t = new Hashtable ();
+ foreach (string s in keywords)
+ t [s] = s;
+ keywordsTable = t;
+ }
+ return keywordsTable.Contains (name);
+ }
+
public SchemaTypes SchemaType
{
get {
}
}
+ public bool NullableOverride
+ {
+ get { return nullableOverride; }
+ }
+
+ public bool IsNullable
+ {
+ get
+ {
+ if (nullableOverride)
+ return true;
+#if NET_2_0
+ return !IsValueType ||
+ (type != null &&
+ type.IsGenericType &&
+ type.GetGenericTypeDefinition () == typeof (Nullable<>));
+#else
+ return !IsValueType;
+#endif
+ }
+
+ set
+ {
+ nullableOverride = value;
+ }
+ }
+
public TypeData ListItemTypeData
{
get
if (listItemType != null) return listItemType;
+ Type genericArgument = null;
+
if (SchemaType != SchemaTypes.Array)
throw new InvalidOperationException (Type.FullName + " is not a collection");
else if (type.IsArray)
listItemType = type.GetElementType ();
- else if (typeof(ICollection).IsAssignableFrom (type))
+#if NET_2_0
+ else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
+#else
+ else if (typeof (ICollection).IsAssignableFrom (type))
+#endif
{
if (typeof (IDictionary).IsAssignableFrom (type))
throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
"The type {0} is not supported because it implements" +
" IDictionary.", type.FullName));
- PropertyInfo prop = GetIndexerProperty (type);
- if (prop == null)
- throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
-
- listItemType = prop.PropertyType;
+ if (genericArgument != null)
+ listItemType = genericArgument;
+ else {
+ PropertyInfo prop = GetIndexerProperty (type);
+ if (prop == null)
+ throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
+ listItemType = prop.PropertyType;
+ }
MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
if (addMethod == null)
"hierarchy. {2} does not implement Add({1}).", inheritFrom,
argumentType.FullName, type.FullName));
}
+
+ private static Hashtable keywordsTable;
+ private static string[] keywords = new string[] {
+ "abstract","event","new","struct","as","explicit","null","switch","base","extern",
+ "this","false","operator","throw","break","finally","out","true",
+ "fixed","override","try","case","params","typeof","catch","for",
+ "private","foreach","protected","checked","goto","public",
+ "unchecked","class","if","readonly","unsafe","const","implicit","ref",
+ "continue","in","return","using","virtual","default",
+ "interface","sealed","volatile","delegate","internal","do","is",
+ "sizeof","while","lock","stackalloc","else","static","enum",
+ "namespace",
+ "object","bool","byte","float","uint","char","ulong","ushort",
+ "decimal","int","sbyte","short","double","long","string","void",
+#if NET_2_0
+ "partial", "yield", "where"
+#endif
+ };
+
+#if NET_2_0
+ internal static Type GetGenericListItemType (Type type)
+ {
+ if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
+ Type [] gatypes = type.GetGenericArguments ();
+ if (type.GetMethod ("Add", gatypes) != null)
+ return gatypes [0];
+ }
+ Type t = null;
+ foreach (Type i in type.GetInterfaces ())
+ if ((t = GetGenericListItemType (i)) != null)
+ return t;
+ return null;
+ }
+#endif
}
}