Merge remote branch 'upstream/master'
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlXmlReader.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.IO;
26 using System.Linq;
27 using System.Xml;
28 using System.Xaml.Schema;
29
30 using Pair = System.Collections.Generic.KeyValuePair<System.Xaml.XamlMember,string>;
31
32 namespace System.Xaml
33 {
34         public class XamlXmlReader : XamlReader, IXamlLineInfo
35         {
36                 #region constructors
37
38                 public XamlXmlReader (Stream stream)
39                         : this (stream, (XamlXmlReaderSettings) null)
40                 {
41                 }
42
43                 public XamlXmlReader (string fileName)
44                         : this (fileName, (XamlXmlReaderSettings) null)
45                 {
46                 }
47
48                 public XamlXmlReader (TextReader textReader)
49                         : this (textReader, (XamlXmlReaderSettings) null)
50                 {
51                 }
52
53                 public XamlXmlReader (XmlReader xmlReader)
54                         : this (xmlReader, (XamlXmlReaderSettings) null)
55                 {
56                 }
57
58                 public XamlXmlReader (Stream stream, XamlSchemaContext schemaContext)
59                         : this (stream, schemaContext, null)
60                 {
61                 }
62
63                 public XamlXmlReader (Stream stream, XamlXmlReaderSettings settings)
64                         : this (stream, new XamlSchemaContext (null, null), settings)
65                 {
66                 }
67
68                 public XamlXmlReader (string fileName, XamlSchemaContext schemaContext)
69                         : this (fileName, schemaContext, null)
70                 {
71                 }
72
73                 public XamlXmlReader (string fileName, XamlXmlReaderSettings settings)
74                         : this (fileName, new XamlSchemaContext (null, null), settings)
75                 {
76                 }
77
78                 public XamlXmlReader (TextReader textReader, XamlSchemaContext schemaContext)
79                         : this (textReader, schemaContext, null)
80                 {
81                 }
82
83                 public XamlXmlReader (TextReader textReader, XamlXmlReaderSettings settings)
84                         : this (textReader, new XamlSchemaContext (null, null), settings)
85                 {
86                 }
87
88                 public XamlXmlReader (XmlReader xmlReader, XamlSchemaContext schemaContext)
89                         : this (xmlReader, schemaContext, null)
90                 {
91                 }
92
93                 public XamlXmlReader (XmlReader xmlReader, XamlXmlReaderSettings settings)
94                         : this (xmlReader, new XamlSchemaContext (null, null), settings)
95                 {
96                 }
97
98                 public XamlXmlReader (Stream stream, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
99                         : this (XmlReader.Create (stream), schemaContext, settings)
100                 {
101                 }
102
103                 static readonly XmlReaderSettings file_reader_settings = new XmlReaderSettings () { CloseInput =true };
104
105                 public XamlXmlReader (string fileName, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
106                         : this (XmlReader.Create (fileName, file_reader_settings), schemaContext, settings)
107                 {
108                 }
109
110                 public XamlXmlReader (TextReader textReader, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
111                         : this (XmlReader.Create (textReader), schemaContext, settings)
112                 {
113                 }
114
115                 public XamlXmlReader (XmlReader xmlReader, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
116                 {
117                         if (xmlReader == null)
118                                 throw new ArgumentNullException ("xmlReader");
119                         if (schemaContext == null)
120                                 throw new ArgumentNullException ("schemaContext");
121
122                         sctx = schemaContext;
123                         this.settings = settings ?? new XamlXmlReaderSettings ();
124
125                         // filter out some nodes.
126                         var xrs = new XmlReaderSettings () {
127                                 CloseInput = this.settings.CloseInput,
128                                 IgnoreComments = true,
129                                 IgnoreProcessingInstructions = true,
130                                 IgnoreWhitespace = true };
131
132                         r = XmlReader.Create (xmlReader, xrs);
133                         line_info = r as IXmlLineInfo;
134                         xaml_namespace_resolver = new NamespaceResolver (r as IXmlNamespaceResolver);
135                 }
136                 
137                 #endregion
138
139                 XmlReader r;
140                 IXmlLineInfo line_info;
141                 XamlSchemaContext sctx;
142                 XamlXmlReaderSettings settings;
143                 bool is_eof;
144                 XamlNodeType node_type;
145                 
146                 object current;
147                 bool inside_object_not_member, is_xdata, is_empty_object, is_empty_member;
148                 Stack<XamlType> types = new Stack<XamlType> ();
149                 Stack<XamlMember> members = new Stack<XamlMember> ();
150                 XamlMember current_member;
151
152                 IEnumerator<Pair> stored_member_enumerator;
153                 IXamlNamespaceResolver xaml_namespace_resolver;
154
155                 public bool HasLineInfo {
156                         get { return line_info != null && line_info.HasLineInfo (); }
157                 }
158
159                 public override bool IsEof {
160                         get { return is_eof; }
161                 }
162
163                 public int LineNumber {
164                         get { return line_info != null ? line_info.LineNumber : 0; }
165                 }
166
167                 public int LinePosition {
168                         get { return line_info != null ? line_info.LinePosition : 0; }
169                 }
170
171                 public override XamlMember Member {
172                         get { return current as XamlMember; }
173                 }
174                 public override NamespaceDeclaration Namespace {
175                         get { return current as NamespaceDeclaration; }
176                 }
177
178                 public override XamlNodeType NodeType {
179                         get { return node_type; }
180                 }
181
182                 public override XamlSchemaContext SchemaContext {
183                         get { return sctx; }
184                 }
185
186                 public override XamlType Type {
187                         get { return current as XamlType; }
188                 }
189
190                 public override object Value {
191                         get { return NodeType == XamlNodeType.Value ? current : null; }
192                 }
193
194                 public override bool Read ()
195                 {
196                         if (IsDisposed)
197                                 throw new ObjectDisposedException ("reader");
198                         if (is_eof)
199                                 return false;
200
201                         // check this before is_empty_* so that they aren't ignored.
202                         if (MoveToNextStoredMember ())
203                                 return true;
204
205                         if (is_xdata) {
206                                 is_xdata = false;
207                                 SetEndOfObject ();
208                                 return true;
209                         }
210                         if (is_empty_object) {
211                                 is_empty_object = false;
212                                 ReadEndType ();
213                                 return true;
214                         }
215                         if (is_empty_member) {
216                                 is_empty_member = false;
217                                 ReadEndMember ();
218                                 return true;
219                         }
220
221                         bool attrIterated = false;
222                         if (r.NodeType == XmlNodeType.Attribute) {
223                                 attrIterated = true;
224                                 if (r.MoveToNextAttribute ())
225                                         if (CheckNextNamespace ())
226                                                 return true;
227                         }
228
229                         if (!r.EOF)
230                                 r.MoveToContent ();
231                         if (r.EOF) {
232                                 is_eof = true;
233                                 return false;
234                         }
235
236                         switch (r.NodeType) {
237                         case XmlNodeType.Element:
238
239                                 // could be: StartObject, StartMember, optionally preceding NamespaceDeclarations
240                                 if (!attrIterated && r.MoveToFirstAttribute ())
241                                         if (CheckNextNamespace ())
242                                                 return true;
243                                 r.MoveToElement ();
244                                 
245                                 if (inside_object_not_member) {
246                                         if (!ReadExtraStartMember ())
247                                                 ReadStartMember ();
248                                 } else {
249                                         if (node_type == XamlNodeType.StartMember && current_member != null && !current_member.IsWritePublic) {
250                                                 if (current_member.Type.IsXData)
251                                                         ReadStartXData ();
252                                                 else if (current_member.Type.IsCollection)
253                                                         SetGetObject ();
254                                                 else
255                                                         throw new XamlParseException (String.Format ("Read-only member '{0}' showed up in the source XML, and the xml contains element content that cannot be read.", current_member.Name)) { LineNumber = this.LineNumber, LinePosition = this.LinePosition };
256                                         }
257                                         else
258                                                 ReadStartType ();
259                                 }
260                                 return true;
261
262                         case XmlNodeType.EndElement:
263
264                                 // could be: EndObject, EndMember
265                                 if (inside_object_not_member) {
266                                         var xm = members.Count > 0 ? members.Peek () : null;
267                                         if (xm != null && !xm.IsWritePublic && xm.Type.IsCollection)
268                                                 SetEndOfObject ();
269                                         else
270                                                 ReadEndType ();
271                                 }
272                                 else {
273                                         if (!ReadExtraEndMember ())
274                                                 ReadEndMember ();
275                                 }
276                                 return true;
277
278                         default:
279
280                                 // could be: normal property Value (Initialization and ContentProperty are handled at ReadStartType()).
281                                 ReadValue ();
282                                 return true;
283                         }
284                 }
285
286                 // returns an optional member without xml node.
287                 XamlMember GetExtraMember (XamlType xt)
288                 {
289                         if (xt.IsCollection || xt.IsDictionary)
290                                 return XamlLanguage.Items;
291                         if (xt.ContentProperty != null) // e.g. Array.Items
292                                 return xt.ContentProperty;
293                         return null;
294                 }
295
296                 bool ReadExtraStartMember ()
297                 {
298                         var xm = GetExtraMember (types.Peek ());
299                         if (xm != null) {
300                                 inside_object_not_member = false;
301                                 current = current_member = xm;
302                                 members.Push (xm);
303                                 node_type = XamlNodeType.StartMember;
304                                 return true;
305                         }
306                         return false;
307                 }
308
309                 bool ReadExtraEndMember ()
310                 {
311                         var xm = GetExtraMember (types.Peek ());
312                         if (xm != null) {
313                                 inside_object_not_member = true;
314                                 current_member = members.Pop ();
315                                 node_type = XamlNodeType.EndMember;
316                                 return true;
317                         }
318                         return false;
319                 }
320
321                 bool CheckNextNamespace ()
322                 {
323                         do {
324                                 if (r.NamespaceURI == XamlLanguage.Xmlns2000Namespace) {
325                                         current = new NamespaceDeclaration (r.Value, r.Prefix == "xmlns" ? r.LocalName : String.Empty);
326                                         node_type = XamlNodeType.NamespaceDeclaration;
327                                         return true;
328                                 }
329                         } while (r.MoveToNextAttribute ());
330                         return false;
331                 }
332
333                 void ReadStartType ()
334                 {
335                         string name = r.LocalName;
336                         string ns = r.NamespaceURI;
337                         string typeArgNames = null;
338                         var members = new List<Pair> ();
339                         var atts = ProcessAttributes (members);
340
341                         // check TypeArguments to resolve Type, and remove them from the list. They don't appear as a node.
342                         var l = new List<Pair> ();
343                         foreach (var p in members) {
344                                 if (p.Key == XamlLanguage.TypeArguments) {
345                                         typeArgNames = p.Value;
346                                         l.Add (p);
347                                         break;
348                                 }
349                         }
350                         foreach (var p in l)
351                                 members.Remove (p);
352
353                         XamlType xt;
354                         IList<XamlTypeName> typeArgs = typeArgNames == null ? null : XamlTypeName.ParseList (typeArgNames, xaml_namespace_resolver);
355                         var xtn = new XamlTypeName (ns, name, typeArgs);
356                         xt = sctx.GetXamlType (xtn);
357                         if (xt == null)
358                                 // creates name-only XamlType. Also, it does not seem that it does not store this XamlType to XamlSchemaContext (Try GetXamlType(xtn) after reading such xaml node, it will return null).
359                                 xt = new XamlType (ns, name, typeArgs == null ? null : typeArgs.Select<XamlTypeName,XamlType> (xxtn => sctx.GetXamlType (xxtn)).ToArray (), sctx);
360                         types.Push (xt);
361                         current = xt;
362
363                         if (!r.IsEmptyElement) {
364                                 r.Read ();
365                                 do {
366                                         r.MoveToContent ();
367                                         switch (r.NodeType) {
368                                         case XmlNodeType.Element:
369                                         // FIXME: parse type arguments etc.
370                                         case XmlNodeType.EndElement:
371                                                 break;
372                                         default:
373                                                 // this value is for Initialization, or Content property value
374                                                 if (xt.ContentProperty != null)
375                                                         members.Add (new Pair (xt.ContentProperty, r.Value));
376                                                 else
377                                                         members.Add (new Pair (XamlLanguage.Initialization, r.Value));
378                                                 r.Read ();
379                                                 continue;
380                                         }
381                                         break;
382                                 } while (true);
383                         }
384                         else
385                                 is_empty_object = true;
386
387                         foreach (var p in atts) {
388                                 int idx = p.Key.IndexOf (':');
389                                 string prefix = idx > 0 ? p.Key.Substring (0, idx) : String.Empty;
390                                 string aname = idx > 0 ? p.Key.Substring (idx + 1) : p.Key;
391                                 idx = aname.IndexOf ('.');
392                                 if (idx > 0) {
393                                         string apns = prefix.Length > 0 ? r.LookupNamespace (prefix) : r.NamespaceURI;
394                                         var apname = aname.Substring (0, idx);
395                                         var axtn = new XamlTypeName (apns, apname, null);
396                                         var at = sctx.GetXamlType (axtn);
397                                         var am = at.GetAttachableMember (aname.Substring (idx + 1));
398                                         if (am != null)
399                                                 members.Add (new Pair (am, p.Value));
400                                         // ignore unknown attribute
401                                 }
402                                 var xm = xt.GetMember (aname);
403                                 if (xm != null)
404                                         members.Add (new Pair (xm, p.Value));
405                                 // ignore unknown attribute
406                         }
407
408                         node_type = XamlNodeType.StartObject;
409                         inside_object_not_member = true;
410
411                         // The next Read() results are likely directives.
412                         stored_member_enumerator = members.GetEnumerator ();
413                 }
414
415                 void ReadStartXData ()
416                 {
417                         var xt = XamlLanguage.XData;
418                         string xdata = r.ReadInnerXml ();
419                         stored_member_enumerator = new List<Pair> (new Pair [] { new Pair (xt.GetMember ("Text"), xdata) }).GetEnumerator ();
420                         
421                         types.Push (xt);
422                         current = xt;
423                         node_type = XamlNodeType.StartObject;
424                         inside_object_not_member = true;
425                         is_xdata = true;
426                 }
427                 
428                 void ReadStartMember ()
429                 {
430                         var xt = types.Peek ();
431                         var name = r.LocalName;
432                         int idx = name.IndexOf ('.');
433                         if (idx >= 0) {
434                                 string tname = name.Substring (0, idx);
435                                 var xtn = new XamlTypeName (r.NamespaceURI, tname, null);
436                                 xt = SchemaContext.GetXamlType (xtn) ?? new XamlType (xtn.Namespace, xtn.Name, null, SchemaContext);
437                                 name = name.Substring (idx + 1);
438                         }
439
440                         var xm = (XamlMember) FindStandardDirective (name, AllowedMemberLocations.MemberElement) ?? xt.GetAttachableMember (name) ?? xt.GetMember (name);
441                         if (xm == null)
442                                 // create unknown member.
443                                 xm = new XamlMember (name, xt, false); // FIXME: not sure if isAttachable is always false.
444                         current = current_member = xm;
445                         members.Push (xm);
446
447                         node_type = XamlNodeType.StartMember;
448                         inside_object_not_member = false;
449                         
450                         r.Read ();
451                 }
452                 
453                 void ReadEndType ()
454                 {
455                         r.Read ();
456                         SetEndOfObject ();
457                 }
458                 
459                 void SetEndOfObject ()
460                 {
461                         types.Pop ();
462                         current = null;
463                         node_type = XamlNodeType.EndObject;
464                         inside_object_not_member = false;
465                 }
466                 
467                 void ReadEndMember ()
468                 {
469                         r.Read ();
470
471                         current_member = members.Pop ();
472                         current = null;
473                         node_type = XamlNodeType.EndMember;
474                         inside_object_not_member = true;
475                 }
476
477                 void ReadValue ()
478                 {
479                         // FIXME: (probably) use ValueSerializer to deserialize the value to the expected type.
480                         current = r.Value;
481
482                         r.Read ();
483
484                         node_type = XamlNodeType.Value;
485                 }
486                 
487                 void SetGetObject ()
488                 {
489                         current = null; // do not clear current_member as it is reused in the next Read().
490                         node_type = XamlNodeType.GetObject;
491                         inside_object_not_member = true;
492                         types.Push (current_member.Type);
493                 }
494
495                 XamlDirective FindStandardDirective (string name, AllowedMemberLocations loc)
496                 {
497                         return XamlLanguage.AllDirectives.FirstOrDefault (dd => (dd.AllowedLocation & loc) != 0 && dd.Name == name);
498                 }
499
500                 // returns remaining attributes to be processed
501                 Dictionary<string,string> ProcessAttributes (List<Pair> members)
502                 {
503                         var l = members;
504
505                         // base
506                         string xmlbase = r.GetAttribute ("base", XamlLanguage.Xml1998Namespace) ?? r.BaseURI;
507                         if (types.Count == 0 && xmlbase != null) // top
508                                 l.Add (new Pair (XamlLanguage.Base, xmlbase));
509
510                         var atts = new Dictionary<string,string> ();
511
512                         if (r.MoveToFirstAttribute ()) {
513                                 do {
514                                         switch (r.NamespaceURI) {
515                                         case XamlLanguage.Xml1998Namespace:
516                                                 switch (r.LocalName) {
517                                                 case "base":
518                                                         continue; // already processed.
519                                                 case "lang":
520                                                         l.Add (new Pair (XamlLanguage.Lang, r.Value));
521                                                         continue;
522                                                 case "space":
523                                                         l.Add (new Pair (XamlLanguage.Space, r.Value));
524                                                         continue;
525                                                 }
526                                                 break;
527                                         case XamlLanguage.Xmlns2000Namespace:
528                                                 continue;
529                                         case XamlLanguage.Xaml2006Namespace:
530                                                 XamlDirective d = FindStandardDirective (r.LocalName, AllowedMemberLocations.Attribute);
531                                                 if (d != null) {
532                                                         l.Add (new Pair (d, r.Value));
533                                                         continue;
534                                                 }
535                                                 throw new NotSupportedException (String.Format ("Attribute '{0}' is not supported", r.Name));
536                                         default:
537                                                 if (r.NamespaceURI == String.Empty) {
538                                                         atts.Add (r.Name, r.Value);
539                                                         continue;
540                                                 }
541                                                 // Should we just ignore unknown attribute in XAML namespace or any other namespaces ?
542                                                 // Probably yes for compatibility with future version.
543                                                 break;
544                                         }
545                                 } while (r.MoveToNextAttribute ());
546                                 r.MoveToElement ();
547                         }
548                         return atts;
549                 }
550                 
551                 IEnumerator<KeyValuePair<XamlMember,object>> markup_extension_attr_members;
552                 IEnumerator<string> markup_extension_attr_values;
553
554                 bool MoveToNextMarkupExtensionAttributeMember ()
555                 {
556                         if (markup_extension_attr_members != null) {
557                                 switch (node_type) {
558                                 case XamlNodeType.StartObject:
559                                 case XamlNodeType.EndMember:
560                                         // -> next member or end object
561                                         if (!markup_extension_attr_members.MoveNext ()) {
562                                                 node_type = XamlNodeType.EndObject;
563                                         } else {
564                                                 current = current_member = markup_extension_attr_members.Current.Key;
565                                                 members.Push (current_member);
566                                                 node_type = XamlNodeType.StartMember;
567                                         }
568                                         return true;
569                                 case XamlNodeType.EndObject:
570                                         types.Pop ();
571                                         markup_extension_attr_members = null;
572                                         return false;
573                                 case XamlNodeType.StartMember:
574                                         node_type = XamlNodeType.Value;
575                                         current = markup_extension_attr_members.Current.Value;
576                                         if (current_member == XamlLanguage.PositionalParameters) {
577                                                 markup_extension_attr_values = ((List<string>) current).GetEnumerator ();
578                                                 goto case XamlNodeType.Value;
579                                         }
580                                         return true;
581                                 case XamlNodeType.Value:
582                                         if (markup_extension_attr_values != null) {
583                                                 if (markup_extension_attr_values.MoveNext ())
584                                                         current = markup_extension_attr_values.Current;
585                                                 else {
586                                                         node_type = XamlNodeType.EndMember;
587                                                         markup_extension_attr_values = null;
588                                                 }
589                                         }
590                                         else
591                                                 node_type = XamlNodeType.EndMember;
592                                         return true;
593                                 }
594                         }
595                         return false;
596                 }
597
598                 bool MoveToNextStoredMember ()
599                 {
600                         if (MoveToNextMarkupExtensionAttributeMember ())
601                                 return true;
602
603                         if (stored_member_enumerator != null) {
604                                 // FIXME: value might have to be deserialized.
605                                 switch (node_type) {
606                                 case XamlNodeType.StartObject:
607                                 case XamlNodeType.EndMember:
608                                         // -> StartMember
609                                         if (stored_member_enumerator.MoveNext ()) {
610                                                 current = current_member = stored_member_enumerator.Current.Key;
611                                                 node_type = XamlNodeType.StartMember;
612                                                 return true;
613                                         }
614                                         break;
615                                 case XamlNodeType.StartMember:
616                                         // -> Value or StartObject (of MarkupExtension)
617                                         var v = stored_member_enumerator.Current.Value;
618                                         current = v;
619                                         // Try markup extension
620                                         // FIXME: is this rule correct?
621                                         if (!String.IsNullOrEmpty (v) && v [0] == '{') {
622                                                 var pai = ParsedMarkupExtensionInfo.Parse (v, xaml_namespace_resolver, sctx);
623                                                 types.Push (pai.Type);
624                                                 current = pai.Type;
625                                                 node_type = XamlNodeType.StartObject;
626                                                 markup_extension_attr_members = pai.Arguments.GetEnumerator ();
627                                         }
628                                         else
629                                                 node_type = XamlNodeType.Value;
630                                         return true;
631                                 case XamlNodeType.EndObject: // of MarkupExtension
632                                 case XamlNodeType.Value:
633                                         // -> EndMember
634                                         current = null;
635                                         node_type = XamlNodeType.EndMember;
636                                         return true;
637                                 }
638                         }
639
640                         stored_member_enumerator = null;
641                         return false;
642                 }
643                 
644                 class NamespaceResolver : IXamlNamespaceResolver
645                 {
646                         IXmlNamespaceResolver source;
647
648                         public NamespaceResolver (IXmlNamespaceResolver source)
649                         {
650                                 this.source = source;
651                         }
652
653                         public string GetNamespace (string prefix)
654                         {
655                                 return source.LookupNamespace (prefix);
656                         }
657
658                         public IEnumerable<NamespaceDeclaration> GetNamespacePrefixes ()
659                         {
660                                 foreach (var p in source.GetNamespacesInScope (XmlNamespaceScope.All))
661                                         yield return new NamespaceDeclaration (p.Value, p.Key);
662                         }
663                 }
664         }
665 }
666