New tests.
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlLanguage.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.Collections.ObjectModel;
26 using System.Globalization;
27 using System.Linq;
28 using System.Reflection;
29 using System.Xaml.Schema;
30 using System.Windows.Markup;
31
32 [assembly:XmlnsDefinition (System.Xaml.XamlLanguage.Xaml2006Namespace, "System.Windows.Markup")] // FIXME: verify.
33
34 namespace System.Xaml
35 {
36         public static class XamlLanguage
37         {
38                 public const string Xaml2006Namespace = "http://schemas.microsoft.com/winfx/2006/xaml";
39                 public const string Xml1998Namespace = "http://www.w3.org/XML/1998/namespace";
40                 internal const string Xmlns2000Namespace = "http://www.w3.org/2000/xmlns/";
41
42                 static readonly XamlSchemaContext sctx = new XamlSchemaContext (new Assembly [] {typeof (XamlType).Assembly});
43
44                 static XamlType XT<T> ()
45                 {
46                         return sctx.GetXamlType (typeof (T));
47                 }
48
49                 internal static readonly bool InitializingDirectives;
50                 internal static readonly bool InitializingTypes;
51
52                 static XamlLanguage ()
53                 {
54                         InitializingTypes = true;
55                         // types
56
57                         Array = XT<ArrayExtension> ();
58                         Boolean = XT<bool> ();
59                         Byte = XT<byte> ();
60                         Char = XT<char> ();
61                         Decimal = XT<decimal> ();
62                         Double = XT<double> ();
63                         Int16 = XT<short> ();
64                         Int32 = XT<int> ();
65                         Int64 = XT<long> ();
66                         Member = XT<MemberDefinition> ();
67                         Null = XT<NullExtension> ();
68                         Object = XT<object> ();
69                         Property = XT<PropertyDefinition> ();
70                         Reference = XT<Reference> ();
71                         Single = XT<float> ();
72                         Static = XT<StaticExtension> ();
73                         String = XT<string> ();
74                         TimeSpan = XT<TimeSpan> ();
75                         Type = XT<TypeExtension> ();
76                         Uri = XT<Uri> ();
77                         XData = XT<XData> ();
78
79                         InitializingTypes = false;
80
81                         AllTypes = new ReadOnlyCollection<XamlType> (new XamlType [] {Array, Boolean, Byte, Char, Decimal, Double, Int16, Int32, Int64, Member, Null, Object, Property, Reference, Single, Static, String, TimeSpan, Type, Uri, XData});
82
83                         // directives
84
85                         // Looks like predefined XamlDirectives have no ValueSerializer. 
86                         // To handle this situation, differentiate them from non-primitive XamlMembers.
87                         InitializingDirectives = true;
88
89                         var nss = new string [] {XamlLanguage.Xaml2006Namespace};
90                         var nssXml = new string [] {XamlLanguage.Xml1998Namespace};
91
92                         Arguments = new XamlDirective (nss, "Arguments", XT<List<object>> (), null, AllowedMemberLocations.Any);
93                         AsyncRecords = new XamlDirective (nss, "AsyncRecords", XT<string> (), null, AllowedMemberLocations.Attribute);
94                         Base = new XamlDirective (nssXml, "base", XT<string> (), null, AllowedMemberLocations.Attribute);
95                         Class = new XamlDirective (nss, "Class", XT<string> (), null, AllowedMemberLocations.Attribute);
96                         ClassAttributes = new XamlDirective (nss, "ClassAttributes", XT<List<Attribute>> (), null, AllowedMemberLocations.MemberElement);
97                         ClassModifier = new XamlDirective (nss, "ClassModifier", XT<string> (), null, AllowedMemberLocations.Attribute);
98                         Code = new XamlDirective (nss, "Code", XT<string> (), null, AllowedMemberLocations.Attribute);
99                         ConnectionId = new XamlDirective (nss, "ConnectionId", XT<string> (), null, AllowedMemberLocations.Any);
100                         FactoryMethod = new XamlDirective (nss, "FactoryMethod", XT<string> (), null, AllowedMemberLocations.Any);
101                         FieldModifier = new XamlDirective (nss, "FieldModifier", XT<string> (), null, AllowedMemberLocations.Attribute);
102                         Initialization = new XamlDirective (nss, "_Initialization", XT<object> (), null, AllowedMemberLocations.Any);
103                         Items = new XamlDirective (nss, "_Items", XT<List<object>> (), null, AllowedMemberLocations.Any);
104                         Key = new XamlDirective (nss, "Key", XT<object> (), null, AllowedMemberLocations.Any);
105                         Lang = new XamlDirective (nssXml, "lang", XT<string> (), null, AllowedMemberLocations.Attribute);
106                         Members = new XamlDirective (nss, "Members", XT<List<MemberDefinition>> (), null, AllowedMemberLocations.MemberElement);
107                         Name = new XamlDirective (nss, "Name", XT<string> (), null, AllowedMemberLocations.Attribute);
108                         PositionalParameters = new XamlDirective (nss, "_PositionalParameters", XT<List<object>> (), null, AllowedMemberLocations.Any);
109                         Space = new XamlDirective (nssXml, "space", XT<string> (), null, AllowedMemberLocations.Attribute);
110                         Subclass = new XamlDirective (nss, "Subclass", XT<string> (), null, AllowedMemberLocations.Attribute);
111                         SynchronousMode = new XamlDirective (nss, "SynchronousMode", XT<string> (), null, AllowedMemberLocations.Attribute);
112                         Shared = new XamlDirective (nss, "Shared", XT<string> (), null, AllowedMemberLocations.Attribute);
113                         TypeArguments = new XamlDirective (nss, "TypeArguments", XT<string> (), null, AllowedMemberLocations.Attribute);
114                         Uid = new XamlDirective (nss, "Uid", XT<string> (), null, AllowedMemberLocations.Attribute);
115                         UnknownContent = new XamlDirective (nss, "_UnknownContent", XT<object> (), null, AllowedMemberLocations.MemberElement) { InternalIsUnknown = true };
116
117                         AllDirectives = new ReadOnlyCollection<XamlDirective> (new XamlDirective [] {Arguments, AsyncRecords, Base, Class, ClassAttributes, ClassModifier, Code, ConnectionId, FactoryMethod, FieldModifier, Initialization, Items, Key, Lang, Members, Name, PositionalParameters, Space, Subclass, SynchronousMode, Shared, TypeArguments, Uid, UnknownContent});
118
119                         InitializingDirectives = false;
120                 }
121
122                 static readonly string [] xaml_nss = new string [] {Xaml2006Namespace};
123
124                 public static IList<string> XamlNamespaces {
125                         get { return xaml_nss; }
126                 }
127
128                 static readonly string [] xml_nss = new string [] {Xml1998Namespace};
129
130                 public static IList<string> XmlNamespaces {
131                         get { return xml_nss; }
132                 }
133
134                 public static ReadOnlyCollection<XamlDirective> AllDirectives { get; private set; }
135
136                 public static XamlDirective Arguments { get; private set; }
137                 public static XamlDirective AsyncRecords { get; private set; }
138                 public static XamlDirective Base { get; private set; }
139                 public static XamlDirective Class { get; private set; }
140                 public static XamlDirective ClassAttributes { get; private set; }
141                 public static XamlDirective ClassModifier { get; private set; }
142                 public static XamlDirective Code { get; private set; }
143                 public static XamlDirective ConnectionId { get; private set; }
144                 public static XamlDirective FactoryMethod { get; private set; }
145                 public static XamlDirective FieldModifier { get; private set; }
146                 public static XamlDirective Initialization { get; private set; }
147                 public static XamlDirective Items { get; private set; }
148                 public static XamlDirective Key { get; private set; }
149                 public static XamlDirective Lang { get; private set; }
150                 public static XamlDirective Members { get; private set; }
151                 public static XamlDirective Name { get; private set; }
152                 public static XamlDirective PositionalParameters { get; private set; }
153                 public static XamlDirective Subclass { get; private set; }
154                 public static XamlDirective SynchronousMode { get; private set; }
155                 public static XamlDirective Shared { get; private set; }
156                 public static XamlDirective Space { get; private set; }
157                 public static XamlDirective TypeArguments { get; private set; }
158                 public static XamlDirective Uid { get; private set; }
159                 public static XamlDirective UnknownContent { get; private set; }
160
161                 public static ReadOnlyCollection<XamlType> AllTypes { get; private set; }
162
163                 public static XamlType Array { get; private set; }
164                 public static XamlType Boolean { get; private set; }
165                 public static XamlType Byte { get; private set; }
166                 public static XamlType Char { get; private set; }
167                 public static XamlType Decimal { get; private set; }
168                 public static XamlType Double { get; private set; }
169                 public static XamlType Int16 { get; private set; }
170                 public static XamlType Int32 { get; private set; }
171                 public static XamlType Int64 { get; private set; }
172                 public static XamlType Member { get; private set; }
173                 public static XamlType Null { get; private set; }
174                 public static XamlType Object { get; private set; }
175                 public static XamlType Property { get; private set; }
176                 public static XamlType Reference { get; private set; }
177                 public static XamlType Single { get; private set; }
178                 public static XamlType Static { get; private set; }
179                 public static XamlType String { get; private set; }
180                 public static XamlType TimeSpan { get; private set; }
181                 public static XamlType Type { get; private set; }
182                 public static XamlType Uri { get; private set; }
183                 public static XamlType XData { get; private set; }
184
185                 internal static bool IsValidXamlName (string name)
186                 {
187                         if (string.IsNullOrEmpty (name))
188                                 return false;
189                         if (!IsValidXamlName (name [0], true))
190                                 return false;
191                         foreach (char c in name)
192                                 if (!IsValidXamlName (c, false))
193                                         return false;
194                         return true;
195                 }
196
197                 static bool IsValidXamlName (char c, bool first)
198                 {
199                         if (c == '_')
200                                 return true;
201                         switch (char.GetUnicodeCategory (c)) {
202                         case UnicodeCategory.LowercaseLetter:
203                         case UnicodeCategory.UppercaseLetter:
204                         case UnicodeCategory.TitlecaseLetter:
205                         case UnicodeCategory.OtherLetter:
206                         case UnicodeCategory.LetterNumber:
207                                 return true;
208                         case UnicodeCategory.NonSpacingMark:
209                         case UnicodeCategory.DecimalDigitNumber:
210                         case UnicodeCategory.SpacingCombiningMark:
211                         case UnicodeCategory.ModifierLetter:
212                                 return !first;
213                         default:
214                                 return false;
215                         }
216                 }
217
218                 internal static XamlType GetSpecialXaml2006Type (string name)
219                 {
220                         // FIXME: I'm not really sure if these *special* names 
221                         // should be resolved here and there. There just does
222                         // not seem to be any other appropriate places.
223                         switch (name) {
224                         case "Array":
225                                 return XamlLanguage.Array;
226                         case "Member":
227                                 return XamlLanguage.Member;
228                         case "Null":
229                                 return XamlLanguage.Null;
230                         case "Property":
231                                 return XamlLanguage.Property;
232                         case "Static":
233                                 return XamlLanguage.Static;
234                         case "Type":
235                                 return XamlLanguage.Type;
236                         }
237                         return null;
238                 }
239
240                 static readonly int clr_ns_len = "clr-namespace:".Length;
241                 static readonly int clr_ass_len = "assembly=".Length;
242
243                 internal static Type ResolveXamlTypeName (string xmlNamespace, string xmlLocalName, IList<XamlTypeName> typeArguments, IXamlNamespaceResolver nsResolver)
244                 {
245                         string ns = xmlNamespace;
246                         string name = xmlLocalName;
247
248                         if (ns == XamlLanguage.Xaml2006Namespace) {
249                                 var xt = GetSpecialXaml2006Type (name);
250                                 if (xt == null)
251                                         xt = AllTypes.FirstOrDefault (t => t.Name == xmlLocalName);
252                                 if (xt == null)
253                                         throw new FormatException (string.Format ("There is no type '{0}' in XAML namespace", name));
254                                 return xt.UnderlyingType;
255                         }
256                         else if (!ns.StartsWith ("clr-namespace:", StringComparison.Ordinal))
257                                 throw new FormatException (string.Format ("Unexpected XAML namespace '{0}'", ns));
258
259                         Type [] genArgs = null;
260                         if (typeArguments != null) {
261                                 var xtns = typeArguments;
262                                 genArgs = new Type [xtns.Count];
263                                 for (int i = 0; i < genArgs.Length; i++) {
264                                         var xtn = xtns [i];
265                                         genArgs [i] = ResolveXamlTypeName (xtn.Namespace, xtn.Name, xtn.TypeArguments, nsResolver);
266                                 }
267                         }
268
269                         // convert xml namespace to clr namespace and assembly
270                         string [] split = ns.Split (';');
271                         if (split.Length != 2 || split [0].Length <= clr_ns_len || split [1].Length <= clr_ass_len)
272                                 throw new XamlParseException (string.Format ("Cannot resolve runtime namespace from XML namespace '{0}'", ns));
273                         string tns = split [0].Substring (clr_ns_len);
274                         string aname = split [1].Substring (clr_ass_len);
275
276                         string tfn = tns.Length > 0 ? tns + '.' + name : name;
277                         if (genArgs != null)
278                                 tfn += "`" + genArgs.Length;
279                         string taqn = tfn + (aname.Length > 0 ? ", " + aname : string.Empty);
280                         var ret = System.Type.GetType (taqn);
281                         if (ret == null)
282                                 throw new XamlParseException (string.Format ("Cannot resolve runtime type from XML namespace '{0}', local name '{1}' with {2} type arguments ({3})", ns, name, typeArguments.Count, taqn));
283                         return genArgs == null ? ret : ret.MakeGenericType (genArgs);
284                 }
285         }
286 }