Fix to UriTemplate.Match to properly handle query parameters without a value. No...
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlWriterInternalBase.cs
1 //
2 // Copyright (C) 2010 Novell Inc. http://novell.com
3 // Copyright (C) 2012 Xamarin Inc. http://xamarin.com
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 // 
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 // 
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24
25 using System;
26 using System.Collections.Generic;
27 using System.ComponentModel;
28 using System.Globalization;
29 using System.IO;
30 using System.Linq;
31 using System.Reflection;
32 using System.Text;
33 using System.Windows.Markup;
34 using System.Xaml;
35 using System.Xaml.Schema;
36 using System.Xml;
37
38 #if DOTNET
39 namespace Mono.Xaml
40 #else
41 namespace System.Xaml
42 #endif
43 {
44         internal abstract class XamlWriterInternalBase
45         {
46                 public XamlWriterInternalBase (XamlSchemaContext schemaContext, XamlWriterStateManager manager)
47                 {
48                         this.sctx = schemaContext;
49                         this.manager = manager;
50                         var p = new PrefixLookup (sctx) { IsCollectingNamespaces = true }; // it does not raise unknown namespace error.
51                         service_provider = new ValueSerializerContext (p, schemaContext, AmbientProvider);
52                 }
53
54                 XamlSchemaContext sctx;
55                 XamlWriterStateManager manager;
56
57                 internal IValueSerializerContext service_provider;
58
59                 internal ObjectState root_state;
60                 internal Stack<ObjectState> object_states = new Stack<ObjectState> ();
61                 internal PrefixLookup prefix_lookup {
62                         get { return (PrefixLookup) service_provider.GetService (typeof (INamespacePrefixLookup)); }
63                 }
64
65                 List<NamespaceDeclaration> namespaces {
66                         get { return prefix_lookup.Namespaces; }
67                 }
68
69                 internal virtual IAmbientProvider AmbientProvider {
70                         get { return null; }
71                 }
72
73                 internal class ObjectState
74                 {
75                         public XamlType Type;
76                         public bool IsGetObject;
77                         public int PositionalParameterIndex = -1;
78
79                         public string FactoryMethod;
80                         public object Value;
81                         public object KeyValue;
82                         public List<MemberAndValue> WrittenProperties = new List<MemberAndValue> ();
83                         public bool IsInstantiated;
84                         public bool IsXamlWriterCreated; // affects AfterProperties() calls.
85                 }
86                 
87                 internal class MemberAndValue
88                 {
89                         public MemberAndValue (XamlMember xm)
90                         {
91                                 Member = xm;
92                         }
93
94                         public XamlMember Member;
95                         public object Value;
96                         public AllowedMemberLocations OccuredAs = AllowedMemberLocations.None;
97                 }
98
99                 public void CloseAll ()
100                 {
101                         while (object_states.Count > 0) {
102                                 switch (manager.State) {
103                                 case XamlWriteState.MemberDone:
104                                 case XamlWriteState.ObjectStarted: // StartObject without member
105                                         WriteEndObject ();
106                                         break;
107                                 case XamlWriteState.ValueWritten:
108                                 case XamlWriteState.ObjectWritten:
109                                 case XamlWriteState.MemberStarted: // StartMember without content
110                                         manager.OnClosingItem ();
111                                         WriteEndMember ();
112                                         break;
113                                 default:
114                                         throw new NotImplementedException (manager.State.ToString ()); // there shouldn't be anything though
115                                 }
116                         }
117                 }
118
119                 internal string GetPrefix (string ns)
120                 {
121                         foreach (var nd in namespaces)
122                                 if (nd.Namespace == ns)
123                                         return nd.Prefix;
124                         return null;
125                 }
126
127                 protected MemberAndValue CurrentMemberState {
128                         get { return object_states.Count > 0 ? object_states.Peek ().WrittenProperties.LastOrDefault () : null; }
129                 }
130
131                 protected XamlMember CurrentMember {
132                         get {
133                                 var mv = CurrentMemberState;
134                                 return mv != null ? mv.Member : null;
135                         }
136                 }
137
138                 public void WriteGetObject ()
139                 {
140                         manager.GetObject ();
141
142                         var xm = CurrentMember;
143
144                         var state = new ObjectState () {Type = xm.Type, IsGetObject = true};
145
146                         object_states.Push (state);
147
148                         OnWriteGetObject ();
149                 }
150
151                 public void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
152                 {
153                         if (namespaceDeclaration == null)
154                                 throw new ArgumentNullException ("namespaceDeclaration");
155
156                         manager.Namespace ();
157
158                         namespaces.Add (namespaceDeclaration);
159                         OnWriteNamespace (namespaceDeclaration);
160                 }
161
162                 public void WriteStartObject (XamlType xamlType)
163                 {
164                         if (xamlType == null)
165                                 throw new ArgumentNullException ("xamlType");
166
167                         manager.StartObject ();
168
169                         var cstate = new ObjectState () {Type = xamlType};
170                         object_states.Push (cstate);
171
172                         OnWriteStartObject ();
173                 }
174                 
175                 public void WriteValue (object value)
176                 {
177                         manager.Value ();
178
179                         OnWriteValue (value);
180                 }
181                 
182                 public void WriteStartMember (XamlMember property)
183                 {
184                         if (property == null)
185                                 throw new ArgumentNullException ("property");
186
187                         manager.StartMember ();
188                         if (property == XamlLanguage.PositionalParameters)
189                                 // this is an exception that indicates the state manager to accept more than values within this member.
190                                 manager.AcceptMultipleValues = true;
191
192                         var state = object_states.Peek ();
193                         var wpl = state.WrittenProperties;
194                         if (wpl.Any (wp => wp.Member == property))
195                                 throw new XamlDuplicateMemberException (String.Format ("Property '{0}' is already set to this '{1}' object", property, object_states.Peek ().Type));
196                         wpl.Add (new MemberAndValue (property));
197                         if (property == XamlLanguage.PositionalParameters)
198                                 state.PositionalParameterIndex = 0;
199
200                         OnWriteStartMember (property);
201                 }
202                 
203                 public void WriteEndObject ()
204                 {
205                         manager.EndObject (object_states.Count > 1);
206
207                         OnWriteEndObject ();
208
209                         object_states.Pop ();
210                 }
211
212                 public void WriteEndMember ()
213                 {
214                         manager.EndMember ();
215
216                         OnWriteEndMember ();
217                         
218                         var state = object_states.Peek ();
219                         if (CurrentMember == XamlLanguage.PositionalParameters) {
220                                 manager.AcceptMultipleValues = false;
221                                 state.PositionalParameterIndex = -1;
222                         }
223                 }
224
225                 protected abstract void OnWriteEndObject ();
226
227                 protected abstract void OnWriteEndMember ();
228
229                 protected abstract void OnWriteStartObject ();
230
231                 protected abstract void OnWriteGetObject ();
232
233                 protected abstract void OnWriteStartMember (XamlMember xm);
234
235                 protected abstract void OnWriteValue (object value);
236
237                 protected abstract void OnWriteNamespace (NamespaceDeclaration nd);
238                 
239                 protected string GetValueString (XamlMember xm, object value)
240                 {
241                         // change XamlXmlReader too if we change here.
242                         if ((value as string) == String.Empty) // FIXME: there could be some escape syntax.
243                                 return "\"\"";
244
245                         var xt = value == null ? XamlLanguage.Null : sctx.GetXamlType (value.GetType ());
246                         var vs = xm.ValueSerializer ?? xt.ValueSerializer;
247                         if (vs != null)
248                                 return vs.ConverterInstance.ConvertToString (value, service_provider);
249                         else
250                                 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));
251                 }
252         }
253 }