2010-04-14 Atsushi Enomoto <atsushi@ximian.com>
[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 namespace System.Xaml
32 {
33         // This type caches assembly attribute search results. To do this,
34         // it registers AssemblyLoaded event on CurrentDomain when it should
35         // reflect dynamic in-scope asemblies.
36         // It should be released at finalizer.
37         public class XamlSchemaContext
38         {
39                 public XamlSchemaContext ()
40                         : this (null, null)
41                 {
42                 }
43
44                 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies)
45                         : this (referenceAssemblies, null)
46                 {
47                 }
48
49                 public XamlSchemaContext (XamlSchemaContextSettings settings)
50                         : this (null, settings)
51                 {
52                 }
53
54                 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies, XamlSchemaContextSettings settings)
55                 {
56                         if (referenceAssemblies != null)
57                                 reference_assemblies = new List<Assembly> (referenceAssemblies);
58                         else
59                                 AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
60
61                         if (settings == null)
62                                 return;
63
64                         FullyQualifyAssemblyNamesInClrNamespaces = settings.FullyQualifyAssemblyNamesInClrNamespaces;
65                         SupportMarkupExtensionsWithDuplicateArity = settings.SupportMarkupExtensionsWithDuplicateArity;
66                 }
67
68                 ~XamlSchemaContext ()
69                 {
70                         if (reference_assemblies == null)
71                                 AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoaded;
72                 }
73
74                 IList<Assembly> reference_assemblies;
75
76                 // assembly attribute caches
77                 List<string> xaml_nss;
78                 Dictionary<string,string> prefixes;
79                 Dictionary<string,string> compat_nss;
80                 Dictionary<string,List<XamlType>> all_xaml_types;
81                 XamlType [] empty_xaml_types = new XamlType [0];
82
83                 public bool FullyQualifyAssemblyNamesInClrNamespaces { get; private set; }
84
85                 public IList<Assembly> ReferenceAssemblies {
86                         get { return reference_assemblies; }
87                 }
88
89                 IEnumerable<Assembly> AssembliesInScope {
90                         get { return reference_assemblies ?? AppDomain.CurrentDomain.GetAssemblies (); }
91                 }
92
93                 public bool SupportMarkupExtensionsWithDuplicateArity { get; private set; }
94
95                 public virtual IEnumerable<string> GetAllXamlNamespaces ()
96                 {
97                         if (xaml_nss == null) {
98                                 xaml_nss = new List<string> ();
99                                 foreach (var ass in AssembliesInScope)
100                                         FillXamlNamespaces (ass);
101                         }
102                         return xaml_nss;
103                 }
104
105                 public virtual ICollection<XamlType> GetAllXamlTypes (string xamlNamespace)
106                 {
107                         if (xamlNamespace == null)
108                                 throw new ArgumentNullException ("xamlNamespace");
109                         if (all_xaml_types == null) {
110                                 all_xaml_types = new Dictionary<string,List<XamlType>> ();
111                                 foreach (var ass in AssembliesInScope)
112                                         FillAllXamlTypes (ass);
113                         }
114
115                         List<XamlType> l;
116                         if (all_xaml_types.TryGetValue (xamlNamespace, out l))
117                                 return l;
118                         else
119                                 return empty_xaml_types;
120                 }
121
122                 public virtual string GetPreferredPrefix (string xmlns)
123                 {
124                         if (xmlns == null)
125                                 throw new ArgumentNullException ("xmlns");
126                         if (prefixes == null) {
127                                 prefixes = new Dictionary<string,string> ();
128                                 foreach (var ass in AssembliesInScope)
129                                         FillPrefixes (ass);
130                         }
131                         string ret;
132                         return prefixes.TryGetValue (xmlns, out ret) ? ret : "p"; // default
133                 }
134
135                 protected internal XamlValueConverter<TConverterBase> GetValueConverter<TConverterBase> (Type converterType, XamlType targetType)
136                         where TConverterBase : class
137                 {
138                         return new XamlValueConverter<TConverterBase> (converterType, targetType);
139                 }
140                 
141                 public virtual XamlDirective GetXamlDirective (string xamlNamespace, string name)
142                 {
143                         throw new NotImplementedException ();
144                 }
145                 
146                 Dictionary<Type,XamlType> xaml_types = new Dictionary<Type,XamlType> ();
147                 
148                 public virtual XamlType GetXamlType (Type type)
149                 {
150                         XamlType t;
151                         if (!xaml_types.TryGetValue (type, out t)) {
152                                 t = new XamlType (type, this);
153                                 xaml_types.Add (type, t);
154                         }
155                         return t;
156                 }
157                 
158                 public XamlType GetXamlType (XamlTypeName xamlTypeName)
159                 {
160                         if (xamlTypeName == null)
161                                 throw new ArgumentNullException ("xamlTypeName");
162
163                         var n = xamlTypeName;
164                         string dummy;
165                         if (TryGetCompatibleXamlNamespace (n.Namespace, out dummy))
166                                 n = new XamlTypeName (dummy, n.Name, n.TypeArguments);
167
168                         XamlType ret;
169                         if (n.Namespace == XamlLanguage.Xaml2006Namespace) {
170                                 // FIXME: I'm not really sure if these *special*
171                                 // names should be resolved here. There just
172                                 // does not seem to be any other appropriate
173                                 // places.
174                                 switch (n.Name) {
175                                 case "Array":
176                                         return XamlLanguage.Array;
177                                 case "Member":
178                                         return XamlLanguage.Member;
179                                 case "Null":
180                                         return XamlLanguage.Null;
181                                 case "Property":
182                                         return XamlLanguage.Property;
183                                 case "Static":
184                                         return XamlLanguage.Static;
185                                 case "Type":
186                                         return XamlLanguage.Type;
187                                 }
188                                 ret = XamlLanguage.AllTypes.FirstOrDefault (t => t.NameEquals (n));
189                                 if (ret != null)
190                                         return ret;
191                         }
192                         return GetAllXamlTypes (n.Namespace).FirstOrDefault (t => t.NameEquals (n));
193                 }
194                 
195                 protected internal virtual XamlType GetXamlType (string xamlNamespace, string name, params XamlType [] typeArguments)
196                 {
197                         return GetXamlType (new XamlTypeName (xamlNamespace, name, typeArguments.ToTypeNames ()));
198                 }
199
200                 protected internal virtual Assembly OnAssemblyResolve (string assemblyName)
201                 {
202                         return null;
203                 }
204
205                 public virtual bool TryGetCompatibleXamlNamespace (string xamlNamespace, out string compatibleNamespace)
206                 {
207                         if (xamlNamespace == null)
208                                 throw new ArgumentNullException ("xamlNamespace");
209                         if (compat_nss == null) {
210                                 compat_nss = new Dictionary<string,string> ();
211                                 foreach (var ass in AssembliesInScope)
212                                         FillCompatibilities (ass);
213                         }
214                         return compat_nss.TryGetValue (xamlNamespace, out compatibleNamespace);
215                 }
216
217                 void OnAssemblyLoaded (object o, AssemblyLoadEventArgs e)
218                 {
219                         if (reference_assemblies != null)
220                                 return; // do nothing
221
222                         if (xaml_nss != null)
223                                 FillXamlNamespaces (e.LoadedAssembly);
224                         if (prefixes != null)
225                                 FillPrefixes (e.LoadedAssembly);
226                         if (compat_nss != null)
227                                 FillCompatibilities (e.LoadedAssembly);
228                         if (all_xaml_types != null)
229                                 FillAllXamlTypes (e.LoadedAssembly);
230                 }
231                 
232                 // cache updater methods
233                 void FillXamlNamespaces (Assembly ass)
234                 {
235                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false))
236                                 xaml_nss.Add (xda.XmlNamespace);
237                 }
238                 
239                 void FillPrefixes (Assembly ass)
240                 {
241                         foreach (XmlnsPrefixAttribute xpa in ass.GetCustomAttributes (typeof (XmlnsPrefixAttribute), false))
242                                 prefixes.Add (xpa.XmlNamespace, xpa.Prefix);
243                 }
244                 
245                 void FillCompatibilities (Assembly ass)
246                 {
247                         foreach (XmlnsCompatibleWithAttribute xca in ass.GetCustomAttributes (typeof (XmlnsCompatibleWithAttribute), false))
248                                 compat_nss.Add (xca.OldNamespace, xca.NewNamespace);
249                 }
250
251                 void FillAllXamlTypes (Assembly ass)
252                 {
253                         foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false)) {
254                                 var l = new List<XamlType> ();
255                                 all_xaml_types.Add (xda.XmlNamespace, l);
256                                 foreach (var t in ass.GetTypes ())
257                                         if (t.Namespace == xda.ClrNamespace)
258                                                 l.Add (GetXamlType (t));
259                         }
260                 }
261         }
262 }