2 // Copyright (C) 2010 Novell Inc. http://novell.com
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:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
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.
24 using System.Collections.Generic;
25 using System.ComponentModel;
27 using System.Reflection;
28 using System.Windows.Markup;
29 using System.Xaml.Schema;
31 using Pair = System.Collections.Generic.KeyValuePair<string,string>;
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
41 public XamlSchemaContext ()
46 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies)
47 : this (referenceAssemblies, null)
51 public XamlSchemaContext (XamlSchemaContextSettings settings)
52 : this (null, settings)
56 public XamlSchemaContext (IEnumerable<Assembly> referenceAssemblies, XamlSchemaContextSettings settings)
58 if (referenceAssemblies != null)
59 reference_assemblies = new List<Assembly> (referenceAssemblies);
62 AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
68 FullyQualifyAssemblyNamesInClrNamespaces = settings.FullyQualifyAssemblyNamesInClrNamespaces;
69 SupportMarkupExtensionsWithDuplicateArity = settings.SupportMarkupExtensionsWithDuplicateArity;
75 if (reference_assemblies == null)
76 AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoaded;
80 IList<Assembly> reference_assemblies;
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> ();
90 public bool FullyQualifyAssemblyNamesInClrNamespaces { get; private set; }
92 public IList<Assembly> ReferenceAssemblies {
93 get { return reference_assemblies; }
96 IEnumerable<Assembly> AssembliesInScope {
98 get { return reference_assemblies; }
100 get { return reference_assemblies ?? AppDomain.CurrentDomain.GetAssemblies (); }
104 public bool SupportMarkupExtensionsWithDuplicateArity { get; private set; }
106 public virtual IEnumerable<string> GetAllXamlNamespaces ()
108 if (xaml_nss == null) {
109 xaml_nss = new List<string> ();
110 foreach (var ass in AssembliesInScope)
111 FillXamlNamespaces (ass);
116 public virtual ICollection<XamlType> GetAllXamlTypes (string xamlNamespace)
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);
127 if (all_xaml_types.TryGetValue (xamlNamespace, out l))
130 return empty_xaml_types;
133 public virtual string GetPreferredPrefix (string xmlns)
136 throw new ArgumentNullException ("xmlns");
137 if (xmlns == XamlLanguage.Xaml2006Namespace)
139 if (prefixes == null) {
140 prefixes = new Dictionary<string,string> ();
141 foreach (var ass in AssembliesInScope)
145 return prefixes.TryGetValue (xmlns, out ret) ? ret : "p"; // default
148 protected internal XamlValueConverter<TConverterBase> GetValueConverter<TConverterBase> (Type converterType, XamlType targetType)
149 where TConverterBase : class
151 return new XamlValueConverter<TConverterBase> (converterType, targetType);
154 Dictionary<Pair,XamlDirective> xaml_directives = new Dictionary<Pair,XamlDirective> ();
156 public virtual XamlDirective GetXamlDirective (string xamlNamespace, string name)
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);
167 public virtual XamlType GetXamlType (Type type)
169 XamlType xt = run_time_types.FirstOrDefault (t => t.UnderlyingType == type);
171 foreach (var ns in GetAllXamlNamespaces ())
172 if ((xt = GetAllXamlTypes (ns).FirstOrDefault (t => t.UnderlyingType == type)) != null)
175 xt = new XamlType (type, this);
176 run_time_types.Add (xt);
181 public XamlType GetXamlType (XamlTypeName xamlTypeName)
183 if (xamlTypeName == null)
184 throw new ArgumentNullException ("xamlTypeName");
186 var n = xamlTypeName;
187 if (n.TypeArguments.Count == 0) // non-generic
188 return GetXamlType (n.Namespace, n.Name);
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);
197 protected internal virtual XamlType GetXamlType (string xamlNamespace, string name, params XamlType [] typeArguments)
200 if (TryGetCompatibleXamlNamespace (xamlNamespace, out dummy))
201 xamlNamespace = dummy;
204 if (xamlNamespace == XamlLanguage.Xaml2006Namespace) {
205 ret = XamlLanguage.SpecialNames.Find (name, xamlNamespace);
207 ret = XamlLanguage.AllTypes.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
211 ret = run_time_types.FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
213 ret = GetAllXamlTypes (xamlNamespace).FirstOrDefault (t => TypeMatches (t, xamlNamespace, name, typeArguments));
215 if (reference_assemblies == null) {
216 var type = ResolveXamlTypeName (xamlNamespace, name, typeArguments);
218 ret = GetXamlType (type);
221 // If the type was not found, it just returns null.
225 bool TypeMatches (XamlType t, string ns, string name, XamlType [] typeArgs)
227 if (t.PreferredXamlNamespace == ns && t.Name == name && t.TypeArguments.ListEquals (typeArgs))
229 if (t.IsMarkupExtension)
230 return t.PreferredXamlNamespace == ns && t.Name.Substring (0, t.Name.Length - 9) == name && t.TypeArguments.ListEquals (typeArgs);
235 protected internal virtual Assembly OnAssemblyResolve (string assemblyName)
238 return Assembly.Load (assemblyName);
240 return Assembly.LoadWithPartialName (assemblyName);
244 public virtual bool TryGetCompatibleXamlNamespace (string xamlNamespace, out string compatibleNamespace)
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);
253 return compat_nss.TryGetValue (xamlNamespace, out compatibleNamespace);
257 void OnAssemblyLoaded (object o, AssemblyLoadEventArgs e)
259 if (reference_assemblies != null)
260 return; // do nothing
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);
273 // cache updater methods
274 void FillXamlNamespaces (Assembly ass)
276 foreach (XmlnsDefinitionAttribute xda in ass.GetCustomAttributes (typeof (XmlnsDefinitionAttribute), false))
277 xaml_nss.Add (xda.XmlNamespace);
280 void FillPrefixes (Assembly ass)
282 foreach (XmlnsPrefixAttribute xpa in ass.GetCustomAttributes (typeof (XmlnsPrefixAttribute), false))
283 prefixes.Add (xpa.XmlNamespace, xpa.Prefix);
286 void FillCompatibilities (Assembly ass)
288 foreach (XmlnsCompatibleWithAttribute xca in ass.GetCustomAttributes (typeof (XmlnsCompatibleWithAttribute), false))
289 compat_nss.Add (xca.OldNamespace, xca.NewNamespace);
292 void FillAllXamlTypes (Assembly ass)
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));
303 // XamlTypeName -> Type resolution
305 static readonly int clr_ns_len = "clr-namespace:".Length;
306 static readonly int clr_ass_len = "assembly=".Length;
308 Type ResolveXamlTypeName (string xmlNamespace, string xmlLocalName, IList<XamlType> typeArguments)
310 string ns = xmlNamespace;
311 string name = xmlLocalName;
313 if (ns == XamlLanguage.Xaml2006Namespace) {
314 var xt = XamlLanguage.SpecialNames.Find (name, ns);
316 xt = XamlLanguage.AllTypes.FirstOrDefault (t => t.Name == xmlLocalName);
318 throw new FormatException (string.Format ("There is no type '{0}' in XAML namespace", name));
319 return xt.UnderlyingType;
321 else if (!ns.StartsWith ("clr-namespace:", StringComparison.Ordinal))
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))
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);
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));
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);
348 static string GetTypeName (string tns, string name, Type [] genArgs)
350 string tfn = tns.Length > 0 ? tns + '.' + name : name;
352 tfn += "`" + genArgs.Length;