Replace SIZEOF_REGISTER with sizeof(mgreg_t) for consistency with sizeof(gpointer)
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlSchemaContext.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.Generic;
25 using System.ComponentModel;
26 using System.Linq;
27 using System.Reflection;
28 using System.Windows.Markup;
29 using System.Xaml.Schema;
30
31 using Pair = System.Collections.Generic.KeyValuePair<string,string>;
32
33 namespace System.Xaml
34 {
35         // This type caches assembly attribute search results. To do this,
36         // it registers AssemblyLoaded event on CurrentDomain when it should
37         // reflect dynamic in-scope asemblies.
38         // It should be released at finalizer.
39         public class XamlSchemaContext
40         {
41                 public XamlSchemaContext ()
42                         : this (null, null)
43                 {
44                 }
45
46                 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies)
47                         : this (referenceAssemblies, null)
48                 {
49                 }
50
51                 public XamlSchemaContext (XamlSchemaContextSettings settings)
52                         : this (null, settings)
53                 {
54                 }
55
56                 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies, XamlSchemaContextSettings settings)
57                 {
58                         if (referenceAssemblies != null)
59                                 reference_assemblies = new List<Assembly> (referenceAssemblies);
60 #if !NET_2_1
61                         else
62                                 AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
63 #endif
64
65                         if (settings == null)
66                                 return;
67
68                         FullyQualifyAssemblyNamesInClrNamespaces = settings.FullyQualifyAssemblyNamesInClrNamespaces;
69                         SupportMarkupExtensionsWithDuplicateArity = settings.SupportMarkupExtensionsWithDuplicateArity;
70                 }
71
72 #if !NET_2_1
73                 ~XamlSchemaContext ()
74                 {
75                         if (reference_assemblies == null)
76                                 AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoaded;
77                 }
78 #endif
79
80                 IList<Assembly> reference_assemblies;
81
82                 // assembly attribute caches
83                 List<string> xaml_nss;
84                 Dictionary<string,string> prefixes;
85                 Dictionary<string,string> compat_nss;
86                 Dictionary<string,List<XamlType>> all_xaml_types;
87                 XamlType [] empty_xaml_types = new XamlType [0];
88                 List<XamlType> run_time_types = new List<XamlType> ();
89
90                 public bool FullyQualifyAssemblyNamesInClrNamespaces { get; private set; }
91
92                 public IList<Assembly> ReferenceAssemblies {
93                         get { return reference_assemblies; }
94                 }
95
96                 IEnumerable<Assembly> AssembliesInScope {
97 #if NET_2_1
98                         get { return reference_assemblies; }
99 #else
100                         get { return reference_assemblies ?? AppDomain.CurrentDomain.GetAssemblies (); }
101 #endif
102                 }
103
104                 public bool SupportMarkupExtensionsWithDuplicateArity { get; private set; }
105
106                 public virtual IEnumerable<string> GetAllXamlNamespaces ()
107                 {
108                         if (xaml_nss == null) {
109                                 xaml_nss = new List<string> ();
110                                 foreach (var ass in AssembliesInScope)
111                                         FillXamlNamespaces (ass);
112                         }
113                         return xaml_nss;
114                 }
115
116                 public virtual ICollection<XamlType> GetAllXamlTypes (string xamlNamespace)
117                 {
118                         if (xamlNamespace == null)
119                                 throw new ArgumentNullException ("xamlNamespace");
120                         if (all_xaml_types == null) {
121                                 all_xaml_types = new Dictionary<string,List<XamlType>> ();
122                                 foreach (var ass in AssembliesInScope)
123                                         FillAllXamlTypes (ass);
124                         }
125
126                         List<XamlType> l;
127                         if (all_xaml_types.TryGetValue (xamlNamespace, out l))
128                                 return l;
129                         else
130                                 return empty_xaml_types;
131                 }
132
133                 public virtual string GetPreferredPrefix (string xmlns)
134                 {
135                         if (xmlns == null)
136                                 throw new ArgumentNullException ("xmlns");
137                         if (xmlns == XamlLanguage.Xaml2006Namespace)
138                                 return "x";
139                         if (prefixes == null) {
140                                 prefixes = new Dictionary<string,string> ();
141                                 foreach (var ass in AssembliesInScope)
142                                         FillPrefixes (ass);
143                         }
144                         string ret;
145                         return prefixes.TryGetValue (xmlns, out ret) ? ret : "p"; // default
146                 }
147
148                 protected internal XamlValueConverter<TConverterBase> GetValueConverter<TConverterBase> (Type converterType, XamlType targetType)
149                         where TConverterBase : class
150                 {
151                         return new XamlValueConverter<TConverterBase> (converterType, targetType);
152                 }
153                 
154                 Dictionary<Pair,XamlDirective> xaml_directives = new Dictionary<Pair,XamlDirective> ();
155                 
156                 public virtual XamlDirective GetXamlDirective (string xamlNamespace, string name)
157                 {
158                         XamlDirective t;
159                         var p = new Pair (xamlNamespace, name);
160                         if (!xaml_directives.TryGetValue (p, out t)) {
161                                 t = new XamlDirective (xamlNamespace, name);
162                                 xaml_directives.Add (p, t);
163                         }
164                         return t;
165                 }
166                 
167                 public virtual XamlType GetXamlType (Type type)
168                 {
169                         XamlType xt = run_time_types.FirstOrDefault (t => t.UnderlyingType == type);
170                         if (xt == null)
171                                 foreach (var ns in GetAllXamlNamespaces ())
172                                         if ((xt = GetAllXamlTypes (ns).FirstOrDefault (t => t.UnderlyingType == type)) != null)
173                                                 break;
174                         if (xt == null) {
175                                 xt = new XamlType (type, this);
176                                 run_time_types.Add (xt);
177                         }
178                         return xt;
179                 }
180                 
181                 public XamlType GetXamlType (XamlTypeName xamlTypeName)
182                 {
183                         if (xamlTypeName == null)
184                                 throw new ArgumentNullException ("xamlTypeName");
185
186                         var n = xamlTypeName;
187                         if (n.TypeArguments.Count == 0) // non-generic
188                                 return GetXamlType (n.Namespace, n.Name);
189
190                         // generic
191                         XamlType [] typeArgs = new XamlType [n.TypeArguments.Count];
192                         for (int i = 0; i < typeArgs.Length; i++)
193                                 typeArgs [i] = GetXamlType (n.TypeArguments [i]);
194                         return GetXamlType (n.Namespace, n.Name, typeArgs);
195                 }
196                 
197                 protected internal virtual XamlType GetXamlType (string xamlNamespace, string name, params XamlType [] typeArguments)
198                 {
199                         string dummy;
200                         if (TryGetCompatibleXamlNamespace (xamlNamespace, out dummy))
201                                 xamlNamespace = dummy;
202
203                         XamlType ret;
204                         if (xamlNamespace == XamlLanguage.Xaml2006Namespace) {
205                                 ret = XamlLanguage.SpecialNames.Find (name, xamlNamespace);
206                                 if (ret == null)
207                                         ret = XamlLanguage.AllTypes.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
208                                 if (ret != null)
209                                         return ret;
210                         }
211                         ret = run_time_types.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
212                         if (ret == null)
213                                 ret = GetAllXamlTypes (xamlNamespace).FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
214
215                         if (reference_assemblies == null) {
216                                 var type = ResolveXamlTypeName (xamlNamespace, name, typeArguments);
217                                 if (type != null)
218                                         ret = GetXamlType (type);
219                         }
220
221                         // If the type was not found, it just returns null.
222                         return ret;
223                 }
224
225                 bool TypeMatches (XamlType t, string ns, string name, XamlType [] typeArgs)
226                 {
227                         if (t.PreferredXamlNamespace == ns && t.Name == name && t.TypeArguments.ListEquals (typeArgs))
228                                 return true;
229                         if (t.IsMarkupExtension)
230                                 return t.PreferredXamlNamespace == ns && t.Name.Substring (0, t.Name.Length - 9) == name && t.TypeArguments.ListEquals (typeArgs);
231                         else
232                                 return false;
233                 }
234
235                 protected internal virtual Assembly OnAssemblyResolve (string assemblyName)
236                 {
237 #if NET_2_1
238                         return Assembly.Load (assemblyName);
239 #else
240                         return Assembly.LoadWithPartialName (assemblyName);
241 #endif
242                 }
243
244                 public virtual bool TryGetCompatibleXamlNamespace (string xamlNamespace, out string compatibleNamespace)
245                 {
246                         if (xamlNamespace == null)
247                                 throw new ArgumentNullException ("xamlNamespace");
248                         if (compat_nss == null) {
249                                 compat_nss = new Dictionary<string,string> ();
250                                 foreach (var ass in AssembliesInScope)
251                                         FillCompatibilities (ass);
252                         }
253                         return compat_nss.TryGetValue (xamlNamespace, out compatibleNamespace);
254                 }
255
256 #if !NET_2_1
257                 void OnAssemblyLoaded (object o, AssemblyLoadEventArgs e)
258                 {
259                         if (reference_assemblies != null)
260                                 return; // do nothing
261
262                         if (xaml_nss != null)
263                                 FillXamlNamespaces (e.LoadedAssembly);
264                         if (prefixes != null)
265                                 FillPrefixes (e.LoadedAssembly);
266                         if (compat_nss != null)
267                                 FillCompatibilities (e.LoadedAssembly);
268                         if (all_xaml_types != null)
269                                 FillAllXamlTypes (e.LoadedAssembly);
270                 }
271 #endif
272                 
273                 // cache updater methods
274                 void FillXamlNamespaces (Assembly ass)
275                 {
276                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false))
277                                 xaml_nss.Add (xda.XmlNamespace);
278                 }
279                 
280                 void FillPrefixes (Assembly ass)
281                 {
282                         foreach (XmlnsPrefixAttribute xpa in ass.GetCustomAttributes (typeof (XmlnsPrefixAttribute), false))
283                                 prefixes.Add (xpa.XmlNamespace, xpa.Prefix);
284                 }
285                 
286                 void FillCompatibilities (Assembly ass)
287                 {
288                         foreach (XmlnsCompatibleWithAttribute xca in ass.GetCustomAttributes (typeof (XmlnsCompatibleWithAttribute), false))
289                                 compat_nss.Add (xca.OldNamespace, xca.NewNamespace);
290                 }
291
292                 void FillAllXamlTypes (Assembly ass)
293                 {
294                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false)) {
295                                 var l = new List<XamlType> ();
296                                 all_xaml_types.Add (xda.XmlNamespace, l);
297                                 foreach (var t in ass.GetTypes ())
298                                         if (t.Namespace == xda.ClrNamespace)
299                                                 l.Add (GetXamlType (t));
300                         }
301                 }
302
303                 // XamlTypeName -> Type resolution
304
305                 static readonly int clr_ns_len = "clr-namespace:".Length;
306                 static readonly int clr_ass_len = "assembly=".Length;
307
308                 Type ResolveXamlTypeName (string xmlNamespace, string xmlLocalName, IList<XamlType> typeArguments)
309                 {
310                         string ns = xmlNamespace;
311                         string name = xmlLocalName;
312
313                         if (ns == XamlLanguage.Xaml2006Namespace) {
314                                 var xt = XamlLanguage.SpecialNames.Find (name, ns);
315                                 if (xt == null)
316                                         xt = XamlLanguage.AllTypes.FirstOrDefault (t => t.Name == xmlLocalName);
317                                 if (xt == null)
318                                         throw new FormatException (string.Format ("There is no type '{0}' in XAML namespace", name));
319                                 return xt.UnderlyingType;
320                         }
321                         else if (!ns.StartsWith ("clr-namespace:", StringComparison.Ordinal))
322                                 return null;
323
324                         Type [] genArgs = null;
325                         if (typeArguments != null && typeArguments.Count > 0) {
326                                 var xtns = typeArguments;
327                                 genArgs = (from t in typeArguments select t.UnderlyingType).ToArray ();
328                                 if (genArgs.Any (t => t == null))
329                                         return null;
330                         }
331
332                         // convert xml namespace to clr namespace and assembly
333                         string [] split = ns.Split (';');
334                         if (split.Length != 2 || split [0].Length < clr_ns_len || split [1].Length <= clr_ass_len)
335                                 throw new XamlParseException (string.Format ("Cannot resolve runtime namespace from XML namespace '{0}'", ns));
336                         string tns = split [0].Substring (clr_ns_len);
337                         string aname = split [1].Substring (clr_ass_len);
338
339                         string taqn = GetTypeName (tns, name, genArgs);
340                         var ass = OnAssemblyResolve (aname);
341                         // MarkupExtension type could omit "Extension" part in XML name.
342                         Type ret = ass == null ? null : ass.GetType (taqn) ?? ass.GetType (GetTypeName (tns, name + "Extension", genArgs));
343                         if (ret == null)
344                                 throw new XamlParseException (string.Format ("Cannot resolve runtime type from XML namespace '{0}', local name '{1}' with {2} type arguments ({3})", ns, name, typeArguments !=null ? typeArguments.Count : 0, taqn));
345                         return genArgs == null ? ret : ret.MakeGenericType (genArgs);
346                 }
347                 
348                 static string GetTypeName (string tns, string name, Type [] genArgs)
349                 {
350                         string tfn = tns.Length > 0 ? tns + '.' + name : name;
351                         if (genArgs != null)
352                                 tfn += "`" + genArgs.Length;
353                         return tfn;
354                 }
355         }
356 }