Facilitate the merge
[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                         else
61                                 AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
62
63                         if (settings == null)
64                                 return;
65
66                         FullyQualifyAssemblyNamesInClrNamespaces = settings.FullyQualifyAssemblyNamesInClrNamespaces;
67                         SupportMarkupExtensionsWithDuplicateArity = settings.SupportMarkupExtensionsWithDuplicateArity;
68                 }
69
70                 ~XamlSchemaContext ()
71                 {
72                         if (reference_assemblies == null)
73                                 AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoaded;
74                 }
75
76                 IList<Assembly> reference_assemblies;
77
78                 // assembly attribute caches
79                 List<string> xaml_nss;
80                 Dictionary<string,string> prefixes;
81                 Dictionary<string,string> compat_nss;
82                 Dictionary<string,List<XamlType>> all_xaml_types;
83                 XamlType [] empty_xaml_types = new XamlType [0];
84                 List<XamlType> run_time_types = new List<XamlType> ();
85
86                 public bool FullyQualifyAssemblyNamesInClrNamespaces { get; private set; }
87
88                 public IList<Assembly> ReferenceAssemblies {
89                         get { return reference_assemblies; }
90                 }
91
92                 IEnumerable<Assembly> AssembliesInScope {
93                         get { return reference_assemblies ?? AppDomain.CurrentDomain.GetAssemblies (); }
94                 }
95
96                 public bool SupportMarkupExtensionsWithDuplicateArity { get; private set; }
97
98                 public virtual IEnumerable<string> GetAllXamlNamespaces ()
99                 {
100                         if (xaml_nss == null) {
101                                 xaml_nss = new List<string> ();
102                                 foreach (var ass in AssembliesInScope)
103                                         FillXamlNamespaces (ass);
104                         }
105                         return xaml_nss;
106                 }
107
108                 public virtual ICollection<XamlType> GetAllXamlTypes (string xamlNamespace)
109                 {
110                         if (xamlNamespace == null)
111                                 throw new ArgumentNullException ("xamlNamespace");
112                         if (all_xaml_types == null) {
113                                 all_xaml_types = new Dictionary<string,List<XamlType>> ();
114                                 foreach (var ass in AssembliesInScope)
115                                         FillAllXamlTypes (ass);
116                         }
117
118                         List<XamlType> l;
119                         if (all_xaml_types.TryGetValue (xamlNamespace, out l))
120                                 return l;
121                         else
122                                 return empty_xaml_types;
123                 }
124
125                 public virtual string GetPreferredPrefix (string xmlns)
126                 {
127                         if (xmlns == null)
128                                 throw new ArgumentNullException ("xmlns");
129                         if (xmlns == XamlLanguage.Xaml2006Namespace)
130                                 return "x";
131                         if (prefixes == null) {
132                                 prefixes = new Dictionary<string,string> ();
133                                 foreach (var ass in AssembliesInScope)
134                                         FillPrefixes (ass);
135                         }
136                         string ret;
137                         return prefixes.TryGetValue (xmlns, out ret) ? ret : "p"; // default
138                 }
139
140                 protected internal XamlValueConverter<TConverterBase> GetValueConverter<TConverterBase> (Type converterType, XamlType targetType)
141                         where TConverterBase : class
142                 {
143                         return new XamlValueConverter<TConverterBase> (converterType, targetType);
144                 }
145                 
146                 Dictionary<Pair,XamlDirective> xaml_directives = new Dictionary<Pair,XamlDirective> ();
147                 
148                 public virtual XamlDirective GetXamlDirective (string xamlNamespace, string name)
149                 {
150                         XamlDirective t;
151                         var p = new Pair (xamlNamespace, name);
152                         if (!xaml_directives.TryGetValue (p, out t)) {
153                                 t = new XamlDirective (xamlNamespace, name);
154                                 xaml_directives.Add (p, t);
155                         }
156                         return t;
157                 }
158                 
159                 public virtual XamlType GetXamlType (Type type)
160                 {
161                         XamlType xt = run_time_types.FirstOrDefault (t => t.UnderlyingType == type);
162                         if (xt == null)
163                                 foreach (var ns in GetAllXamlNamespaces ())
164                                         if ((xt = GetAllXamlTypes (ns).FirstOrDefault (t => t.UnderlyingType == type)) != null)
165                                                 break;
166                         if (xt == null) {
167                                 xt = new XamlType (type, this);
168                                 run_time_types.Add (xt);
169                         }
170                         return xt;
171                 }
172                 
173                 public XamlType GetXamlType (XamlTypeName xamlTypeName)
174                 {
175                         if (xamlTypeName == null)
176                                 throw new ArgumentNullException ("xamlTypeName");
177
178                         var n = xamlTypeName;
179                         if (n.TypeArguments.Count == 0) // non-generic
180                                 return GetXamlType (n.Namespace, n.Name);
181
182                         // generic
183                         XamlType [] typeArgs = new XamlType [n.TypeArguments.Count];
184                         for (int i = 0; i < typeArgs.Length; i++)
185                                 typeArgs [i] = GetXamlType (n.TypeArguments [i]);
186                         return GetXamlType (n.Namespace, n.Name, typeArgs);
187                 }
188                 
189                 protected internal virtual XamlType GetXamlType (string xamlNamespace, string name, params XamlType [] typeArguments)
190                 {
191                         string dummy;
192                         if (TryGetCompatibleXamlNamespace (xamlNamespace, out dummy))
193                                 xamlNamespace = dummy;
194
195                         XamlType ret;
196                         if (xamlNamespace == XamlLanguage.Xaml2006Namespace) {
197                                 ret = XamlLanguage.GetSpecialXaml2006Type (name);
198                                 if (ret == null)
199                                         ret = XamlLanguage.AllTypes.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
200                                 if (ret != null)
201                                         return ret;
202                         }
203                         ret = run_time_types.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
204                         if (ret == null)
205                                 ret = GetAllXamlTypes (xamlNamespace).FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
206                         // If the type was not found, it just returns null.
207                         return ret;
208                 }
209
210                 bool TypeMatches (XamlType t, string ns, string name, XamlType [] typeArgs)
211                 {
212                         return t.PreferredXamlNamespace == ns && t.Name == name && t.TypeArguments.ListEquals (typeArgs);
213                 }
214
215                 protected internal virtual Assembly OnAssemblyResolve (string assemblyName)
216                 {
217                         return null;
218                 }
219
220                 public virtual bool TryGetCompatibleXamlNamespace (string xamlNamespace, out string compatibleNamespace)
221                 {
222                         if (xamlNamespace == null)
223                                 throw new ArgumentNullException ("xamlNamespace");
224                         if (compat_nss == null) {
225                                 compat_nss = new Dictionary<string,string> ();
226                                 foreach (var ass in AssembliesInScope)
227                                         FillCompatibilities (ass);
228                         }
229                         return compat_nss.TryGetValue (xamlNamespace, out compatibleNamespace);
230                 }
231
232                 void OnAssemblyLoaded (object o, AssemblyLoadEventArgs e)
233                 {
234                         if (reference_assemblies != null)
235                                 return; // do nothing
236
237                         if (xaml_nss != null)
238                                 FillXamlNamespaces (e.LoadedAssembly);
239                         if (prefixes != null)
240                                 FillPrefixes (e.LoadedAssembly);
241                         if (compat_nss != null)
242                                 FillCompatibilities (e.LoadedAssembly);
243                         if (all_xaml_types != null)
244                                 FillAllXamlTypes (e.LoadedAssembly);
245                 }
246                 
247                 // cache updater methods
248                 void FillXamlNamespaces (Assembly ass)
249                 {
250                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false))
251                                 xaml_nss.Add (xda.XmlNamespace);
252                 }
253                 
254                 void FillPrefixes (Assembly ass)
255                 {
256                         foreach (XmlnsPrefixAttribute xpa in ass.GetCustomAttributes (typeof (XmlnsPrefixAttribute), false))
257                                 prefixes.Add (xpa.XmlNamespace, xpa.Prefix);
258                 }
259                 
260                 void FillCompatibilities (Assembly ass)
261                 {
262                         foreach (XmlnsCompatibleWithAttribute xca in ass.GetCustomAttributes (typeof (XmlnsCompatibleWithAttribute), false))
263                                 compat_nss.Add (xca.OldNamespace, xca.NewNamespace);
264                 }
265
266                 void FillAllXamlTypes (Assembly ass)
267                 {
268                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false)) {
269                                 var l = new List<XamlType> ();
270                                 all_xaml_types.Add (xda.XmlNamespace, l);
271                                 foreach (var t in ass.GetTypes ())
272                                         if (t.Namespace == xda.ClrNamespace)
273                                                 l.Add (GetXamlType (t));
274                         }
275                 }
276         }
277 }