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