Merge branch 'xml-fixes' of https://github.com/myeisha/mono into myeisha-xml-fixes
[mono.git] / mcs / class / System.Xaml / System.Windows.Markup / ValueSerializer.cs
1 //
2 // Copyright (C) 2010 Novell Inc. http://novell.com
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 using System;
24 using System.Collections;
25 using System.Collections.Generic;
26 using System.ComponentModel;
27 using System.Globalization;
28 using System.Linq;
29 using System.Reflection;
30 using System.Xaml;
31 using System.Xaml.Schema;
32
33 namespace System.Windows.Markup
34 {
35         [System.Runtime.CompilerServices.TypeForwardedFrom (Consts.AssemblyWindowsBase)]
36         public abstract class ValueSerializer
37         {
38                 public static ValueSerializer GetSerializerFor (PropertyDescriptor descriptor)
39                 {
40                         return GetSerializerFor (descriptor, null);
41                 }
42
43                 public static ValueSerializer GetSerializerFor (Type type)
44                 {
45                         return GetSerializerFor (type, null);
46                 }
47
48                 // untested
49                 public static ValueSerializer GetSerializerFor (PropertyDescriptor descriptor, IValueSerializerContext context)
50                 {
51                         if (descriptor == null)
52                                 throw new ArgumentNullException ("descriptor");
53                         if (context != null)
54                                 return context.GetValueSerializerFor (descriptor);
55
56                         var tc = descriptor.Converter;
57                         if (tc != null && tc.GetType () != typeof (TypeConverter))
58                                 return new TypeConverterValueSerializer (tc);
59                         return null;
60                 }
61
62                 public static ValueSerializer GetSerializerFor (Type type, IValueSerializerContext context)
63                 {
64                         if (type == null)
65                                 throw new ArgumentNullException ("type");
66                         if (context != null)
67                                 return context.GetValueSerializerFor (type);
68
69                         // Standard MarkupExtensions are serialized without ValueSerializer.
70                         if (typeof (MarkupExtension).IsAssignableFrom (type) && XamlLanguage.AllTypes.Any (x => x.UnderlyingType == type))
71                                 return null;
72
73                         // DateTime is documented as special.
74                         if (type == typeof (DateTime))
75                                 return new DateTimeValueSerializer ();
76                         // String too.
77                         if (type == typeof (string))
78                                 return new StringValueSerializer ();
79
80                         // FIXME: this is hack. The complete condition is fully documented at http://msdn.microsoft.com/en-us/library/ms590363.aspx
81                         if (type.GetCustomAttribute<TypeConverterAttribute> (true) != null) {
82                                 var tc = TypeDescriptor.GetConverter (type);
83                                 if (tc != null && tc.GetType () != typeof (TypeConverter))
84                                         return new TypeConverterValueSerializer (tc);
85                         }
86
87                         // Undocumented, but System.Type seems also special. While other MarkupExtension returned types are not handled specially, this method returns a valid instance for System.Type. Note that it doesn't for TypeExtension.
88                         if (type == typeof (Type))
89                                 // Since System.Type does not have a valid TypeConverter, I use TypeExtensionConverter (may sound funny considering the above notes!) for this serializer.
90                                 return new TypeValueSerializer ();
91
92                         // Undocumented, but several primitive types get a valid serializer while it does not have TypeConverter.
93                         switch (Type.GetTypeCode (type)) {
94                         case TypeCode.Object:
95                         case TypeCode.DBNull:
96                                 break;
97                         default:
98                                 return new TypeConverterValueSerializer (TypeDescriptor.GetConverter (type));
99                         }
100
101                         // There is still exceptional type! TimeSpan. Why aren't they documented?
102                         if (type == typeof (TimeSpan))
103                                 return new TypeConverterValueSerializer (TypeDescriptor.GetConverter (type));
104
105                         return null;
106                 }
107
108                 // instance members
109
110                 public virtual bool CanConvertFromString (string value, IValueSerializerContext context)
111                 {
112                         return false;
113                 }
114
115                 public virtual bool CanConvertToString (object value, IValueSerializerContext context)
116                 {
117                         return false;
118                 }
119
120                 public virtual object ConvertFromString (string value, IValueSerializerContext context)
121                 {
122                         throw GetConvertFromException (value);
123                 }
124
125                 public virtual string ConvertToString (object value,     IValueSerializerContext context)
126                 {
127                         throw GetConvertToException (value, typeof (string));
128                 }
129
130                 protected Exception GetConvertFromException (object value)
131                 {
132                         return new NotSupportedException (String.Format ("Conversion from string '{0}' is not supported", value));
133                 }
134
135                 protected Exception GetConvertToException (object value, Type destinationType)
136                 {
137                         return new NotSupportedException (String.Format ("Conversion from '{0}' to {1} is not supported", value != null ? value.GetType ().Name : "(null)", destinationType));
138                 }
139
140                 public virtual IEnumerable<Type> TypeReferences (object value, IValueSerializerContext context)
141                 {
142                         yield break;
143                 }
144         }
145
146         #region Internal implementations.
147
148         internal class StringValueSerializer : ValueSerializer
149         {
150                 public override bool CanConvertFromString (string value, IValueSerializerContext context)
151                 {
152                         return true;
153                 }
154
155                 public override bool CanConvertToString (object value, IValueSerializerContext context)
156                 {
157                         return true;
158                 }
159
160                 public override object ConvertFromString (string value, IValueSerializerContext context)
161                 {
162                         return value;
163                 }
164
165                 public override string ConvertToString (object value,     IValueSerializerContext context)
166                 {
167                         return (string) value;
168                 }
169
170                 public override IEnumerable<Type> TypeReferences (object value, IValueSerializerContext context)
171                 {
172                         throw new NotImplementedException ();
173                 }
174         }
175
176         internal class TypeValueSerializer : ValueSerializer
177         {
178                 TypeExtensionConverter txc = new TypeExtensionConverter ();
179
180                 public override bool CanConvertFromString (string value, IValueSerializerContext context)
181                 {
182                         return true;
183                 }
184
185                 public override bool CanConvertToString (object value, IValueSerializerContext context)
186                 {
187                         return true;
188                 }
189
190                 public override object ConvertFromString (string value, IValueSerializerContext context)
191                 {
192                         var nsr = (IXamlNamespaceResolver) context.GetService (typeof (IXamlNamespaceResolver));
193                         var scp = (IXamlSchemaContextProvider) context.GetService (typeof (IXamlSchemaContextProvider));
194                         return scp.SchemaContext.GetXamlType (XamlTypeName.Parse (value, nsr)).UnderlyingType;
195                 }
196
197                 public override string ConvertToString (object value,     IValueSerializerContext context)
198                 {
199                         return (string) txc.ConvertTo (context, CultureInfo.InvariantCulture, value, typeof (string));
200                 }
201
202                 public override IEnumerable<Type> TypeReferences (object value, IValueSerializerContext context)
203                 {
204                         throw new NotImplementedException ();
205                 }
206         }
207
208         internal class TypeConverterValueSerializer : ValueSerializer
209         {
210                 public TypeConverterValueSerializer (TypeConverter typeConverter)
211                 {
212                         c = typeConverter;
213                 }
214
215                 TypeConverter c;
216
217                 public override bool CanConvertFromString (string value, IValueSerializerContext context)
218                 {
219                         return c.CanConvertFrom (context, typeof (string));
220                 }
221
222                 public override bool CanConvertToString (object value, IValueSerializerContext context)
223                 {
224                         return c.CanConvertTo (context, typeof (string));
225                 }
226
227                 public override object ConvertFromString (string value, IValueSerializerContext context)
228                 {
229                         return c.ConvertFromInvariantString (context, value);
230                 }
231
232                 public override string ConvertToString (object value,     IValueSerializerContext context)
233                 {
234                         return value == null ? String.Empty : c.ConvertToInvariantString (context, value);
235                 }
236
237                 public override IEnumerable<Type> TypeReferences (object value, IValueSerializerContext context)
238                 {
239                         throw new NotImplementedException ();
240                 }
241         }
242         
243         #endregion
244 }