Merge branch 'xml-fixes' of https://github.com/myeisha/mono into myeisha-xml-fixes
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlXmlWriter.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
24 // To use this under .NET, compile sources as:
25 //
26 //      dmcs -d:DOTNET -r:System.Xaml -debug System.Xaml/XamlXmlWriter.cs System.Xaml/TypeExtensionMethods.cs System.Xaml/XamlWriterStateManager.cs System.Xaml/XamlNameResolver.cs System.Xaml/PrefixLookup.cs System.Xaml/ValueSerializerContext.cs ../../build/common/MonoTODOAttribute.cs Test/System.Xaml/TestedTypes.cs
27
28 using System;
29 using System.Collections.Generic;
30 using System.ComponentModel;
31 using System.Globalization;
32 using System.IO;
33 using System.Linq;
34 using System.Reflection;
35 using System.Text;
36 using System.Windows.Markup;
37 using System.Xaml;
38 using System.Xaml.Schema;
39 using System.Xml;
40
41 //
42 // XamlWriter expects write operations in premised orders.
43 // The most basic one is:
44 //
45 //      [NamespaceDeclaration]* -> StartObject -> [ StartMember -> Value | StartObject ... EndObject -> EndMember ]* -> EndObject
46 //
47 // For collections:
48 //      [NamespaceDeclaration]* -> StartObject -> (members)* -> StartMember XamlLanguage.Items -> [ StartObject ... EndObject ]* -> EndMember -> EndObject
49 //
50 // For MarkupExtension with PositionalParameters:
51 //
52 //      [NamespaceDeclaration]* -> StartObject -> StartMember XamlLanguage.PositionalParameters -> [Value]* -> EndMember -> ... -> EndObject
53 //
54
55 #if DOTNET
56 namespace Mono.Xaml
57 #else
58 namespace System.Xaml
59 #endif
60 {
61         public class XamlXmlWriter : XamlWriter
62         {
63                 public XamlXmlWriter (Stream stream, XamlSchemaContext schemaContext)
64                         : this (stream, schemaContext, null)
65                 {
66                 }
67                 
68                 public XamlXmlWriter (Stream stream, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
69                         : this (XmlWriter.Create (stream), schemaContext, null)
70                 {
71                 }
72                 
73                 public XamlXmlWriter (TextWriter textWriter, XamlSchemaContext schemaContext)
74                         : this (XmlWriter.Create (textWriter), schemaContext, null)
75                 {
76                 }
77                 
78                 public XamlXmlWriter (TextWriter textWriter, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
79                         : this (XmlWriter.Create (textWriter), schemaContext, null)
80                 {
81                 }
82                 
83                 public XamlXmlWriter (XmlWriter xmlWriter, XamlSchemaContext schemaContext)
84                         : this (xmlWriter, schemaContext, null)
85                 {
86                 }
87                 
88                 public XamlXmlWriter (XmlWriter xmlWriter, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
89                 {
90                         if (xmlWriter == null)
91                                 throw new ArgumentNullException ("xmlWriter");
92                         if (schemaContext == null)
93                                 throw new ArgumentNullException ("schemaContext");
94                         this.w = xmlWriter;
95                         this.sctx = schemaContext;
96                         this.settings = settings ?? new XamlXmlWriterSettings ();
97                         var manager = new XamlWriterStateManager<XamlXmlWriterException, InvalidOperationException> (true);
98                         intl = new XamlXmlWriterInternal (xmlWriter, sctx, manager);
99                 }
100
101                 XmlWriter w;
102                 XamlSchemaContext sctx;
103                 XamlXmlWriterSettings settings;
104
105                 XamlXmlWriterInternal intl;
106
107                 public override XamlSchemaContext SchemaContext {
108                         get { return sctx; }
109                 }
110
111                 public XamlXmlWriterSettings Settings {
112                         get { return settings; }
113                 }
114
115                 protected override void Dispose (bool disposing)
116                 {
117                         if (!disposing)
118                                 return;
119
120                         intl.CloseAll ();
121
122                         if (settings.CloseOutput)
123                                 w.Close ();
124                 }
125
126                 public void Flush ()
127                 {
128                         w.Flush ();
129                 }
130
131                 public override void WriteGetObject ()
132                 {
133                         intl.WriteGetObject ();
134                 }
135
136                 public override void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
137                 {
138                         intl.WriteNamespace (namespaceDeclaration);
139                 }
140
141                 public override void WriteStartObject (XamlType xamlType)
142                 {
143                         intl.WriteStartObject (xamlType);
144                 }
145                 
146                 public override void WriteValue (object value)
147                 {
148                         if (value != null && !(value is string))
149                                 throw new ArgumentException ("Non-string value cannot be written.");
150
151                         intl.WriteValue (value);
152                 }
153                 
154                 public override void WriteStartMember (XamlMember property)
155                 {
156                         intl.WriteStartMember (property);
157                 }
158                 
159                 public override void WriteEndObject ()
160                 {
161                         intl.WriteEndObject ();
162                 }
163
164                 public override void WriteEndMember ()
165                 {
166                         intl.WriteEndMember ();
167                 }
168         }
169
170         internal abstract class XamlWriterInternalBase
171         {
172                 public XamlWriterInternalBase (XamlSchemaContext schemaContext, XamlWriterStateManager manager)
173                 {
174                         this.sctx = schemaContext;
175                         this.manager = manager;
176                         var p = new PrefixLookup (sctx) { IsCollectingNamespaces = true }; // it does not raise unknown namespace error.
177                         service_provider = new ValueSerializerContext (p, schemaContext);
178                 }
179
180                 XamlSchemaContext sctx;
181                 XamlWriterStateManager manager;
182
183                 internal IValueSerializerContext service_provider;
184
185                 internal Stack<ObjectState> object_states = new Stack<ObjectState> ();
186                 internal PrefixLookup prefix_lookup {
187                         get { return (PrefixLookup) service_provider.GetService (typeof (INamespacePrefixLookup)); }
188                 }
189
190                 List<NamespaceDeclaration> namespaces {
191                         get { return prefix_lookup.Namespaces; }
192                 }
193
194                 internal class ObjectState
195                 {
196                         public XamlType Type;
197                         public object Value;
198                         public List<object> Contents = new List<object> ();
199                         public List<MemberAndValue> WrittenProperties = new List<MemberAndValue> ();
200                         public bool IsInstantiated;
201                         public bool IsGetObject;
202                         public int PositionalParameterIndex = -1;
203
204                         public string FactoryMethod;
205                         public List<object> Arguments = new List<object> ();
206                 }
207                 
208                 internal class MemberAndValue
209                 {
210                         public MemberAndValue (XamlMember xm)
211                         {
212                                 Member = xm;
213                         }
214
215                         public XamlMember Member;
216                         public object Value;
217                         public object KeyValue;
218                         public AllowedMemberLocations OccuredAs = AllowedMemberLocations.None;
219                 }
220
221                 public void CloseAll ()
222                 {
223                         while (object_states.Count > 0) {
224                                 switch (manager.State) {
225                                 case XamlWriteState.MemberDone:
226                                 case XamlWriteState.ObjectStarted: // StartObject without member
227                                         WriteEndObject ();
228                                         break;
229                                 case XamlWriteState.ValueWritten:
230                                 case XamlWriteState.ObjectWritten:
231                                 case XamlWriteState.MemberStarted: // StartMember without content
232                                         manager.OnClosingItem ();
233                                         WriteEndMember ();
234                                         break;
235                                 default:
236                                         throw new NotImplementedException (manager.State.ToString ()); // there shouldn't be anything though
237                                 }
238                         }
239                 }
240
241                 internal string GetPrefix (string ns)
242                 {
243                         foreach (var nd in namespaces)
244                                 if (nd.Namespace == ns)
245                                         return nd.Prefix;
246                         return null;
247                 }
248
249                 protected MemberAndValue CurrentMemberState {
250                         get { return object_states.Count > 0 ? object_states.Peek ().WrittenProperties.LastOrDefault () : null; }
251                 }
252
253                 protected XamlMember CurrentMember {
254                         get {
255                                 var mv = CurrentMemberState;
256                                 return mv != null ? mv.Member : null;
257                         }
258                 }
259
260                 public void WriteGetObject ()
261                 {
262                         manager.GetObject ();
263
264                         var xm = CurrentMember;
265
266                         if (!xm.Type.IsCollection)
267                                 throw new InvalidOperationException (String.Format ("WriteGetObject method can be invoked only when current member '{0}' is of collection type", xm.Name));
268
269                         var state = new ObjectState () {Type = xm.Type, IsGetObject = true};
270
271                         object_states.Push (state);
272
273                         OnWriteGetObject ();
274                 }
275
276                 public void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
277                 {
278                         if (namespaceDeclaration == null)
279                                 throw new ArgumentNullException ("namespaceDeclaration");
280
281                         manager.Namespace ();
282
283                         namespaces.Add (namespaceDeclaration);
284                         OnWriteNamespace (namespaceDeclaration);
285                 }
286
287                 public void WriteStartObject (XamlType xamlType)
288                 {
289                         if (xamlType == null)
290                                 throw new ArgumentNullException ("xamlType");
291
292                         manager.StartObject ();
293
294                         OnWriteStartObject (xamlType);
295
296                         var cstate = new ObjectState () {Type = xamlType, IsInstantiated = false};
297                         object_states.Push (cstate);
298                 }
299                 
300                 public void WriteValue (object value)
301                 {
302                         if (value != null && !(value is string))
303                                 throw new ArgumentException ("Non-string value cannot be written.");
304
305                         manager.Value ();
306
307                         OnWriteValue (value);
308                 }
309                 
310                 public void WriteStartMember (XamlMember property)
311                 {
312                         if (property == null)
313                                 throw new ArgumentNullException ("property");
314
315                         manager.StartMember ();
316                         if (property == XamlLanguage.PositionalParameters)
317                                 // this is an exception that indicates the state manager to accept more than values within this member.
318                                 manager.AcceptMultipleValues = true;
319
320                         var state = object_states.Peek ();
321                         var wpl = state.WrittenProperties;
322                         if (wpl.Any (wp => wp.Member == property))
323                                 throw new XamlDuplicateMemberException (String.Format ("Property '{0}' is already set to this '{1}' object", property, object_states.Peek ().Type));
324                         wpl.Add (new MemberAndValue (property));
325                         if (property == XamlLanguage.PositionalParameters)
326                                 state.PositionalParameterIndex = 0;
327
328                         OnWriteStartMember (property);
329                 }
330                 
331                 public void WriteEndObject ()
332                 {
333                         manager.EndObject (object_states.Count > 1);
334
335                         OnWriteEndObject ();
336
337                         object_states.Pop ();
338                 }
339
340                 public void WriteEndMember ()
341                 {
342                         manager.EndMember ();
343
344                         OnWriteEndMember ();
345                         
346                         var state = object_states.Peek ();
347                         if (CurrentMember == XamlLanguage.PositionalParameters) {
348                                 manager.AcceptMultipleValues = false;
349                                 state.PositionalParameterIndex = -1;
350                         }
351                         var contents = state.Contents;
352
353                         contents.Clear ();
354                 }
355
356                 protected abstract void OnWriteEndObject ();
357
358                 protected abstract void OnWriteEndMember ();
359
360                 protected abstract void OnWriteStartObject (XamlType xamlType);
361
362                 protected abstract void OnWriteGetObject ();
363
364                 protected abstract void OnWriteStartMember (XamlMember xm);
365
366                 protected abstract void OnWriteValue (object value);
367
368                 protected abstract void OnWriteNamespace (NamespaceDeclaration nd);
369
370                 bool IsAllowedType (XamlType xt, object value)
371                 {
372                         // FIXME: not sure if it is correct
373                         if (value is string)
374                                 return true;
375
376                         return  xt == null ||
377                                 xt.UnderlyingType == null ||
378                                 xt.UnderlyingType.IsInstanceOfType (value) ||
379                                 value == null && xt == XamlLanguage.Null ||
380                                 xt.IsMarkupExtension && IsAllowedType (xt.MarkupExtensionReturnType, value);
381                 }
382                 
383                 protected string GetValueString (XamlMember xm, object value)
384                 {
385                         var xt = value == null ? XamlLanguage.Null : sctx.GetXamlType (value.GetType ());
386                         var vs = xm.ValueSerializer ?? xt.ValueSerializer;
387                         if (vs != null)
388                                 return vs.ConverterInstance.ConvertToString (value, service_provider);
389                         else
390                                 throw new XamlXmlWriterException (String.Format ("Value type is '{0}' but it must be either string or any type that is convertible to string indicated by TypeConverterAttribute.", value != null ? value.GetType () : null));
391                 }
392         }
393         
394         // specific implementation
395         class XamlXmlWriterInternal : XamlWriterInternalBase
396         {
397                 const string Xmlns2000Namespace = "http://www.w3.org/2000/xmlns/";
398
399                 public XamlXmlWriterInternal (XmlWriter w, XamlSchemaContext schemaContext, XamlWriterStateManager manager)
400                         : base (schemaContext, manager)
401                 {
402                         this.w = w;
403                         this.sctx = schemaContext;
404                 }
405                 
406                 XmlWriter w;
407                 XamlSchemaContext sctx;
408                 
409                 // Here's a complication.
410                 // - local_nss holds namespace declarations that are written *before* current element.
411                 // - local_nss2 holds namespace declarations that are wrtten *after* current element.
412                 //   (current element == StartObject or StartMember)
413                 // - When the next element or content is being written, local_nss items are written *within* current element, BUT after all attribute members are written. Hence I had to preserve all those nsdecls at such late.
414                 // - When current *start* element is closed, then copy local_nss2 items into local_nss.
415                 // - When there was no children i.e. end element immediately occurs, local_nss should be written at this stage too, and local_nss2 are *ignored*.
416                 List<NamespaceDeclaration> local_nss = new List<NamespaceDeclaration> ();
417                 List<NamespaceDeclaration> local_nss2 = new List<NamespaceDeclaration> ();
418                 bool inside_toplevel_positional_parameter;
419                 bool inside_attribute_object;
420
421                 protected override void OnWriteEndObject ()
422                 {
423                         WritePendingStartMember (XamlNodeType.EndObject);
424
425                         var state = object_states.Count > 0 ? object_states.Peek () : null;
426                         if (state != null && state.IsGetObject) {
427                                 // do nothing
428                                 state.IsGetObject = false;
429                         } else if (w.WriteState == WriteState.Attribute) {
430                                 w.WriteString ("}");
431                                 inside_attribute_object = false;
432                         } else {
433                                 WritePendingNamespaces ();
434                                 w.WriteEndElement ();
435                         }
436                 }
437
438                 protected override void OnWriteEndMember ()
439                 {
440                         WritePendingStartMember (XamlNodeType.EndMember);
441
442                         var member = CurrentMember;
443                         if (member == XamlLanguage.Initialization)
444                                 return;
445                         if (member == XamlLanguage.Items)
446                                 return;
447                         if (member.Type.IsCollection && member.IsReadOnly)
448                                 return;
449                         if (member.DeclaringType != null && member == member.DeclaringType.ContentProperty)
450                                 return;
451
452                         if (inside_toplevel_positional_parameter) {
453                                 w.WriteEndAttribute ();
454                                 inside_toplevel_positional_parameter = false;
455                         } else if (inside_attribute_object) {
456                                 // do nothing. It didn't open this attribute.
457                         } else {
458                                 switch (CurrentMemberState.OccuredAs) {
459                                 case AllowedMemberLocations.Attribute:
460                                         w.WriteEndAttribute ();
461                                         break;
462                                 case AllowedMemberLocations.MemberElement:
463                                         WritePendingNamespaces ();
464                                         w.WriteEndElement ();
465                                         break;
466                                 }
467                         }
468                 }
469                 
470                 protected override void OnWriteStartObject (XamlType xamlType)
471                 {
472                         WritePendingStartMember (XamlNodeType.StartObject);
473
474                         string ns = xamlType.PreferredXamlNamespace;
475                         string prefix = GetPrefix (ns); // null prefix is not rejected...
476
477                         if (w.WriteState == WriteState.Attribute) {
478                                 // MarkupExtension
479                                 w.WriteString ("{");
480                                 if (!String.IsNullOrEmpty (prefix)) {
481                                         w.WriteString (prefix);
482                                         w.WriteString (":");
483                                 }
484                                 string name = ns == XamlLanguage.Xaml2006Namespace ? xamlType.GetInternalXmlName () : xamlType.Name;
485                                 w.WriteString (name);
486                                 // space between type and first member (if any).
487                                 if (xamlType.IsMarkupExtension && xamlType.GetSortedConstructorArguments ().GetEnumerator ().MoveNext ())
488                                         w.WriteString (" ");
489                         } else {
490                                 WritePendingNamespaces ();
491                                 w.WriteStartElement (prefix, xamlType.GetInternalXmlName (), xamlType.PreferredXamlNamespace);
492                                 var l = xamlType.TypeArguments;
493                                 if (l != null) {
494                                         w.WriteStartAttribute ("x", "TypeArguments", XamlLanguage.Xaml2006Namespace);
495                                         for (int i = 0; i < l.Count; i++) {
496                                                 if (i > 0)
497                                                         w.WriteString (", ");
498                                                 w.WriteString (new XamlTypeName (l [i]).ToString (prefix_lookup));
499                                         }
500                                         w.WriteEndAttribute ();
501                                 }
502                         }
503                 }
504
505                 protected override void OnWriteGetObject ()
506                 {
507                         WritePendingStartMember (XamlNodeType.GetObject);
508
509                         // Other than above, nothing to do.
510                 }
511                 
512                 void WritePendingStartMember (XamlNodeType nodeType)
513                 {
514                         var cm = CurrentMemberState;
515                         if (cm == null || cm.OccuredAs != AllowedMemberLocations.Any)
516                                 return;
517
518                         var state = object_states.Peek ();
519                         if (nodeType == XamlNodeType.Value)
520                                 OnWriteStartMemberAttribute (state.Type, CurrentMember);
521                         else
522                                 OnWriteStartMemberElement (state.Type, CurrentMember);
523                 }
524                 
525                 protected override void OnWriteStartMember (XamlMember member)
526                 {
527                         if (member == XamlLanguage.Initialization)
528                                 return;
529                         if (member == XamlLanguage.Items)
530                                 return;
531                         if (member.Type.IsCollection && member.IsReadOnly)
532                                 return;
533                         if (member.DeclaringType != null && member == member.DeclaringType.ContentProperty)
534                                 return;
535
536                         var state = object_states.Peek ();
537                         
538                         // Top-level positional parameters are somehow special.
539                         // - If it has only one parameter, it is written as an
540                         //   attribute using the actual argument's member name.
541                         // - If there are more than one, then it is an error at
542                         //   the second constructor argument.
543                         // (Here "top-level" means an object that involves
544                         //  StartObject i.e. the root or a collection item.)
545                         var posprms = member == XamlLanguage.PositionalParameters && IsAtTopLevelObject () && object_states.Peek ().Type.HasPositionalParameters (service_provider) ? state.Type.GetSortedConstructorArguments ().GetEnumerator () : null;
546                         if (posprms != null) {
547                                 posprms.MoveNext ();
548                                 var arg = posprms.Current;
549                                 w.WriteStartAttribute (arg.Name);
550                                 inside_toplevel_positional_parameter = true;
551                         }
552                         else if (w.WriteState == WriteState.Attribute)
553                                 inside_attribute_object = true;
554
555                         if (w.WriteState == WriteState.Attribute) {
556                                 if (state.PositionalParameterIndex < 0) {
557                                         w.WriteString (" ");
558                                         w.WriteString (member.Name);
559                                         w.WriteString ("=");
560                                 }
561                         } else {
562                                 switch (IsAttribute (state.Type, member)) {
563                                 case AllowedMemberLocations.Attribute:
564                                         OnWriteStartMemberAttribute (state.Type, member);
565                                         break;
566                                 case AllowedMemberLocations.MemberElement:
567                                         OnWriteStartMemberElement (state.Type, member);
568                                         break;
569                                 default: // otherwise - pending output
570                                         CurrentMemberState.OccuredAs = AllowedMemberLocations.Any; // differentiate from .None
571                                         break;
572                                 }
573                         }
574                 }
575
576                 bool IsAtTopLevelObject ()
577                 {
578                         if (object_states.Count == 1)
579                                 return true;
580                         var tmp = object_states.Pop ();
581                         var parentMember = object_states.Peek ().WrittenProperties.LastOrDefault ().Member;
582                         object_states.Push (tmp);
583
584                         return parentMember == XamlLanguage.Items;
585                 }
586
587                 AllowedMemberLocations IsAttribute (XamlType ownerType, XamlMember xm)
588                 {
589                         var xt = ownerType;
590                         var mt = xm.Type;
591                         if (xm == XamlLanguage.Key) {
592                                 var tmp = object_states.Pop ();
593                                 mt = object_states.Peek ().Type.KeyType;
594                                 object_states.Push (tmp);
595                         }
596
597                         if (xm == XamlLanguage.Initialization)
598                                 return AllowedMemberLocations.MemberElement;
599                         if (mt.HasPositionalParameters (service_provider))
600                                 return AllowedMemberLocations.Attribute;
601                         if (w.WriteState == WriteState.Content)
602                                 return AllowedMemberLocations.MemberElement;
603                         if (xt.IsDictionary && xm != XamlLanguage.Key)
604                                 return AllowedMemberLocations.MemberElement; // as each item holds a key.
605
606                         var xd = xm as XamlDirective;
607                         if (xd != null && (xd.AllowedLocation & AllowedMemberLocations.Attribute) == 0)
608                                 return AllowedMemberLocations.MemberElement;
609
610                         // surprisingly, WriteNamespace() can affect this.
611                         if (local_nss2.Count > 0)
612                                 return AllowedMemberLocations.MemberElement;
613
614                         // Somehow such a "stranger" is processed as an element.
615                         if (xd == null && !xt.GetAllMembers ().Contains (xm))
616                                 return AllowedMemberLocations.None;
617
618                         if (xm.IsContentValue (service_provider) || xt.IsContentValue (service_provider))
619                                 return AllowedMemberLocations.Attribute;
620
621                         return AllowedMemberLocations.MemberElement;
622                 }
623
624                 void OnWriteStartMemberElement (XamlType xt, XamlMember xm)
625                 {
626                         CurrentMemberState.OccuredAs = AllowedMemberLocations.MemberElement;
627                         string prefix = GetPrefix (xm.PreferredXamlNamespace);
628                         string name = xm.IsDirective ? xm.Name : String.Concat (xt.GetInternalXmlName (), ".", xm.Name);
629                         WritePendingNamespaces ();
630                         w.WriteStartElement (prefix, name, xm.PreferredXamlNamespace);
631                 }
632                 
633                 void OnWriteStartMemberAttribute (XamlType xt, XamlMember xm)
634                 {
635                         CurrentMemberState.OccuredAs = AllowedMemberLocations.Attribute;
636                         if (xt.PreferredXamlNamespace == xm.PreferredXamlNamespace &&
637                             !(xm is XamlDirective)) // e.g. x:Key inside x:Int should not be written as Key.
638                                 w.WriteStartAttribute (xm.Name);
639                         else {
640                                 string prefix = GetPrefix (xm.PreferredXamlNamespace);
641                                 w.WriteStartAttribute (prefix, xm.Name, xm.PreferredXamlNamespace);
642                         }
643                 }
644
645                 protected override void OnWriteValue (object value)
646                 {
647                         XamlMember xm = CurrentMember;
648                         WritePendingStartMember (XamlNodeType.Value);
649
650                         if (w.WriteState != WriteState.Attribute)
651                                 WritePendingNamespaces ();
652
653                         string s = GetValueString (xm, value);
654
655                         var state = object_states.Peek ();
656                         switch (state.PositionalParameterIndex) {
657                         case -1:
658                                 break;
659                         case 0:
660                                 state.PositionalParameterIndex++;
661                                 break;
662                         default:
663                                 if (inside_toplevel_positional_parameter)
664                                         throw new XamlXmlWriterException (String.Format ("The XAML reader input has more than one positional parameter values within a top-level object {0} because it tries to write all of the argument values as an attribute value of the first argument. While XamlObjectReader can read such an object, XamlXmlWriter cannot write such an object to XML.", state.Type));
665
666                                 state.PositionalParameterIndex++;
667                                 w.WriteString (", ");
668                                 break;
669                         }
670                         w.WriteString (s);
671                 }
672
673                 protected override void OnWriteNamespace (NamespaceDeclaration nd)
674                 {
675                         local_nss2.Add (nd);
676                 }
677                 
678                 void WritePendingNamespaces ()
679                 {
680                         foreach (var nd in local_nss) {
681                                 if (String.IsNullOrEmpty (nd.Prefix))
682                                         w.WriteAttributeString ("xmlns", nd.Namespace);
683                                 else
684                                         w.WriteAttributeString ("xmlns", nd.Prefix, Xmlns2000Namespace, nd.Namespace);
685                         }
686                         local_nss.Clear ();
687
688                         local_nss.AddRange (local_nss2);
689                         local_nss2.Clear ();
690                 }
691         }
692
693 #if DOTNET
694         internal static class TypeExtensionMethods2
695         {
696                 static TypeExtensionMethods2 ()
697                 {
698                         SpecialNames = new SpecialTypeNameList ();
699                 }
700
701                 public static string GetInternalXmlName (this XamlType type)
702                 {
703                         if (type.IsMarkupExtension && type.Name.EndsWith ("Extension", StringComparison.Ordinal))
704                                 return type.Name.Substring (0, type.Name.Length - 9);
705                         var stn = SpecialNames.FirstOrDefault (s => s.Type == type);
706                         return stn != null ? stn.Name : type.Name;
707                 }
708
709                 // FIXME: I'm not sure if these "special names" should be resolved like this. I couldn't find any rule so far.
710                 internal static readonly SpecialTypeNameList SpecialNames;
711
712                 internal class SpecialTypeNameList : List<SpecialTypeName>
713                 {
714                         internal SpecialTypeNameList ()
715                         {
716                                 Add (new SpecialTypeName ("Member", XamlLanguage.Member));
717                                 Add (new SpecialTypeName ("Property", XamlLanguage.Property));
718                         }
719
720                         public XamlType Find (string name, string ns)
721                         {
722                                 if (ns != XamlLanguage.Xaml2006Namespace)
723                                         return null;
724                                 var stn = this.FirstOrDefault (s => s.Name == name);
725                                 return stn != null ? stn.Type : null;
726                         }
727                 }
728
729                 internal class SpecialTypeName
730                 {
731                         public SpecialTypeName (string name, XamlType type)
732                         {
733                                 Name = name;
734                                 Type = type;
735                         }
736                         
737                         public string Name { get; private set; }
738                         public XamlType Type { get; private set; }
739                 }
740         }
741 #endif
742 }