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;
28 using System.Xaml.Schema;
30 using Pair = System.Collections.Generic.KeyValuePair<System.Xaml.XamlMember,string>;
34 public class XamlXmlReader : XamlReader, IXamlLineInfo
38 public XamlXmlReader (Stream stream)
39 : this (stream, (XamlXmlReaderSettings) null)
43 public XamlXmlReader (string fileName)
44 : this (fileName, (XamlXmlReaderSettings) null)
48 public XamlXmlReader (TextReader textReader)
49 : this (textReader, (XamlXmlReaderSettings) null)
53 public XamlXmlReader (XmlReader xmlReader)
54 : this (xmlReader, (XamlXmlReaderSettings) null)
58 public XamlXmlReader (Stream stream, XamlSchemaContext schemaContext)
59 : this (stream, schemaContext, null)
63 public XamlXmlReader (Stream stream, XamlXmlReaderSettings settings)
64 : this (stream, new XamlSchemaContext (null, null), settings)
68 public XamlXmlReader (string fileName, XamlSchemaContext schemaContext)
69 : this (fileName, schemaContext, null)
73 public XamlXmlReader (string fileName, XamlXmlReaderSettings settings)
74 : this (fileName, new XamlSchemaContext (null, null), settings)
78 public XamlXmlReader (TextReader textReader, XamlSchemaContext schemaContext)
79 : this (textReader, schemaContext, null)
83 public XamlXmlReader (TextReader textReader, XamlXmlReaderSettings settings)
84 : this (textReader, new XamlSchemaContext (null, null), settings)
88 public XamlXmlReader (XmlReader xmlReader, XamlSchemaContext schemaContext)
89 : this (xmlReader, schemaContext, null)
93 public XamlXmlReader (XmlReader xmlReader, XamlXmlReaderSettings settings)
94 : this (xmlReader, new XamlSchemaContext (null, null), settings)
98 public XamlXmlReader (Stream stream, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
99 : this (XmlReader.Create (stream), schemaContext, settings)
103 static readonly XmlReaderSettings file_reader_settings = new XmlReaderSettings () { CloseInput =true };
105 public XamlXmlReader (string fileName, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
106 : this (XmlReader.Create (fileName, file_reader_settings), schemaContext, settings)
110 public XamlXmlReader (TextReader textReader, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
111 : this (XmlReader.Create (textReader), schemaContext, settings)
115 public XamlXmlReader (XmlReader xmlReader, XamlSchemaContext schemaContext, XamlXmlReaderSettings settings)
117 if (xmlReader == null)
118 throw new ArgumentNullException ("xmlReader");
119 if (schemaContext == null)
120 throw new ArgumentNullException ("schemaContext");
122 sctx = schemaContext;
123 this.settings = settings ?? new XamlXmlReaderSettings ();
125 // filter out some nodes.
126 var xrs = new XmlReaderSettings () {
127 CloseInput = this.settings.CloseInput,
128 IgnoreComments = true,
129 IgnoreProcessingInstructions = true,
130 IgnoreWhitespace = true };
132 r = XmlReader.Create (xmlReader, xrs);
133 line_info = r as IXmlLineInfo;
134 xaml_namespace_resolver = new NamespaceResolver (r as IXmlNamespaceResolver);
140 IXmlLineInfo line_info;
141 XamlSchemaContext sctx;
142 XamlXmlReaderSettings settings;
144 XamlNodeType node_type;
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;
152 IEnumerator<Pair> stored_member_enumerator;
153 IXamlNamespaceResolver xaml_namespace_resolver;
155 public bool HasLineInfo {
156 get { return line_info != null && line_info.HasLineInfo (); }
159 public override bool IsEof {
160 get { return is_eof; }
163 public int LineNumber {
164 get { return line_info != null ? line_info.LineNumber : 0; }
167 public int LinePosition {
168 get { return line_info != null ? line_info.LinePosition : 0; }
171 public override XamlMember Member {
172 get { return current as XamlMember; }
174 public override NamespaceDeclaration Namespace {
175 get { return current as NamespaceDeclaration; }
178 public override XamlNodeType NodeType {
179 get { return node_type; }
182 public override XamlSchemaContext SchemaContext {
186 public override XamlType Type {
187 get { return current as XamlType; }
190 public override object Value {
191 get { return NodeType == XamlNodeType.Value ? current : null; }
194 public override bool Read ()
197 throw new ObjectDisposedException ("reader");
201 // check this before is_empty_* so that they aren't ignored.
202 if (MoveToNextStoredMember ())
210 if (is_empty_object) {
211 is_empty_object = false;
215 if (is_empty_member) {
216 is_empty_member = false;
221 bool attrIterated = false;
222 if (r.NodeType == XmlNodeType.Attribute) {
224 if (r.MoveToNextAttribute ())
225 if (CheckNextNamespace ())
236 switch (r.NodeType) {
237 case XmlNodeType.Element:
239 // could be: StartObject, StartMember, optionally preceding NamespaceDeclarations
240 if (!attrIterated && r.MoveToFirstAttribute ())
241 if (CheckNextNamespace ())
245 if (inside_object_not_member) {
246 if (!ReadExtraStartMember ())
249 if (node_type == XamlNodeType.StartMember && current_member != null && !current_member.IsWritePublic) {
250 if (current_member.Type.IsXData)
252 else if (current_member.Type.IsCollection)
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 };
262 case XmlNodeType.EndElement:
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)
273 if (!ReadExtraEndMember ())
280 // could be: normal property Value (Initialization and ContentProperty are handled at ReadStartType()).
286 // returns an optional member without xml node.
287 XamlMember GetExtraMember (XamlType xt)
289 if (xt.IsCollection || xt.IsDictionary)
290 return XamlLanguage.Items;
291 if (xt.ContentProperty != null) // e.g. Array.Items
292 return xt.ContentProperty;
296 bool ReadExtraStartMember ()
298 var xm = GetExtraMember (types.Peek ());
300 inside_object_not_member = false;
301 current = current_member = xm;
303 node_type = XamlNodeType.StartMember;
309 bool ReadExtraEndMember ()
311 var xm = GetExtraMember (types.Peek ());
313 inside_object_not_member = true;
314 current_member = members.Pop ();
315 node_type = XamlNodeType.EndMember;
321 bool CheckNextNamespace ()
324 if (r.NamespaceURI == XamlLanguage.Xmlns2000Namespace) {
325 current = new NamespaceDeclaration (r.Value, r.Prefix == "xmlns" ? r.LocalName : String.Empty);
326 node_type = XamlNodeType.NamespaceDeclaration;
329 } while (r.MoveToNextAttribute ());
333 void ReadStartType ()
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);
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;
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);
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);
363 if (!r.IsEmptyElement) {
367 switch (r.NodeType) {
368 case XmlNodeType.Element:
369 // FIXME: parse type arguments etc.
370 case XmlNodeType.EndElement:
373 // this value is for Initialization, or Content property value
374 if (xt.ContentProperty != null)
375 members.Add (new Pair (xt.ContentProperty, r.Value));
377 members.Add (new Pair (XamlLanguage.Initialization, r.Value));
385 is_empty_object = true;
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 ('.');
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));
399 members.Add (new Pair (am, p.Value));
400 // ignore unknown attribute
402 var xm = xt.GetMember (aname);
404 members.Add (new Pair (xm, p.Value));
405 // ignore unknown attribute
408 node_type = XamlNodeType.StartObject;
409 inside_object_not_member = true;
411 // The next Read() results are likely directives.
412 stored_member_enumerator = members.GetEnumerator ();
415 void ReadStartXData ()
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 ();
423 node_type = XamlNodeType.StartObject;
424 inside_object_not_member = true;
428 void ReadStartMember ()
430 var xt = types.Peek ();
431 var name = r.LocalName;
432 int idx = name.IndexOf ('.');
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);
440 var xm = (XamlMember) FindStandardDirective (name, AllowedMemberLocations.MemberElement) ?? xt.GetAttachableMember (name) ?? xt.GetMember (name);
442 // create unknown member.
443 xm = new XamlMember (name, xt, false); // FIXME: not sure if isAttachable is always false.
444 current = current_member = xm;
447 node_type = XamlNodeType.StartMember;
448 inside_object_not_member = false;
459 void SetEndOfObject ()
463 node_type = XamlNodeType.EndObject;
464 inside_object_not_member = false;
467 void ReadEndMember ()
471 current_member = members.Pop ();
473 node_type = XamlNodeType.EndMember;
474 inside_object_not_member = true;
479 // FIXME: (probably) use ValueSerializer to deserialize the value to the expected type.
484 node_type = XamlNodeType.Value;
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);
495 XamlDirective FindStandardDirective (string name, AllowedMemberLocations loc)
497 return XamlLanguage.AllDirectives.FirstOrDefault (dd => (dd.AllowedLocation & loc) != 0 && dd.Name == name);
500 // returns remaining attributes to be processed
501 Dictionary<string,string> ProcessAttributes (List<Pair> members)
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));
510 var atts = new Dictionary<string,string> ();
512 if (r.MoveToFirstAttribute ()) {
514 switch (r.NamespaceURI) {
515 case XamlLanguage.Xml1998Namespace:
516 switch (r.LocalName) {
518 continue; // already processed.
520 l.Add (new Pair (XamlLanguage.Lang, r.Value));
523 l.Add (new Pair (XamlLanguage.Space, r.Value));
527 case XamlLanguage.Xmlns2000Namespace:
529 case XamlLanguage.Xaml2006Namespace:
530 XamlDirective d = FindStandardDirective (r.LocalName, AllowedMemberLocations.Attribute);
532 l.Add (new Pair (d, r.Value));
535 throw new NotSupportedException (String.Format ("Attribute '{0}' is not supported", r.Name));
537 if (r.NamespaceURI == String.Empty) {
538 atts.Add (r.Name, r.Value);
541 // Should we just ignore unknown attribute in XAML namespace or any other namespaces ?
542 // Probably yes for compatibility with future version.
545 } while (r.MoveToNextAttribute ());
551 IEnumerator<KeyValuePair<XamlMember,object>> markup_extension_attr_members;
552 IEnumerator<string> markup_extension_attr_values;
554 bool MoveToNextMarkupExtensionAttributeMember ()
556 if (markup_extension_attr_members != null) {
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;
564 current = current_member = markup_extension_attr_members.Current.Key;
565 members.Push (current_member);
566 node_type = XamlNodeType.StartMember;
569 case XamlNodeType.EndObject:
571 markup_extension_attr_members = null;
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;
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;
586 node_type = XamlNodeType.EndMember;
587 markup_extension_attr_values = null;
591 node_type = XamlNodeType.EndMember;
598 bool MoveToNextStoredMember ()
600 if (MoveToNextMarkupExtensionAttributeMember ())
603 if (stored_member_enumerator != null) {
604 // FIXME: value might have to be deserialized.
606 case XamlNodeType.StartObject:
607 case XamlNodeType.EndMember:
609 if (stored_member_enumerator.MoveNext ()) {
610 current = current_member = stored_member_enumerator.Current.Key;
611 node_type = XamlNodeType.StartMember;
615 case XamlNodeType.StartMember:
616 // -> Value or StartObject (of MarkupExtension)
617 var v = stored_member_enumerator.Current.Value;
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);
625 node_type = XamlNodeType.StartObject;
626 markup_extension_attr_members = pai.Arguments.GetEnumerator ();
629 node_type = XamlNodeType.Value;
631 case XamlNodeType.EndObject: // of MarkupExtension
632 case XamlNodeType.Value:
635 node_type = XamlNodeType.EndMember;
640 stored_member_enumerator = null;
644 class NamespaceResolver : IXamlNamespaceResolver
646 IXmlNamespaceResolver source;
648 public NamespaceResolver (IXmlNamespaceResolver source)
650 this.source = source;
653 public string GetNamespace (string prefix)
655 return source.LookupNamespace (prefix);
658 public IEnumerable<NamespaceDeclaration> GetNamespacePrefixes ()
660 foreach (var p in source.GetNamespacesInScope (XmlNamespaceScope.All))
661 yield return new NamespaceDeclaration (p.Value, p.Key);