Merge pull request #3528 from BrzVlad/fix-sgen-check-before-collections
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / MessageHeader.cs
1 //
2 // System.ServiceModel.MessageHeader.cs
3 //
4 // Author: Duncan Mak (duncan@novell.com)
5 //         Atsushi Enomoto (atsushi@ximian.com)
6 //
7 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.IO;
31 using System.Runtime.Serialization;
32 using System.ServiceModel;
33 using System.ServiceModel.Channels;
34 using System.Text;
35 using System.Xml;
36
37 namespace System.ServiceModel.Channels
38 {
39         public abstract class MessageHeader : MessageHeaderInfo
40         {
41                 static readonly XmlWriterSettings writer_settings;
42
43                 static MessageHeader ()
44                 {
45                         writer_settings = new XmlWriterSettings ();
46                         writer_settings.OmitXmlDeclaration = true;
47                         writer_settings.Indent = true;
48                 }
49
50                 protected MessageHeader () {}
51
52                 static string default_actor = String.Empty;
53                 static bool default_is_ref = false;
54                 static bool default_must_understand = false;
55                 static bool default_relay = false;
56
57                 public static MessageHeader CreateHeader (string name, string ns, object value)
58                 {
59                         return CreateHeader (name, ns, value, default_must_understand);
60                 }
61
62                 public static MessageHeader CreateHeader (string name, string ns, object value, bool mustUnderstand)
63                 {
64                         return CreateHeader (name, ns, value, mustUnderstand, default_actor);
65                 }
66
67                 public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer serializer)
68                 {
69                         return CreateHeader (name, ns, value, serializer, default_must_understand, 
70                                              default_actor, default_relay);
71                 }
72
73                 public static MessageHeader CreateHeader (string name, string ns, object value, 
74                                                    bool mustUnderstand, string actor)
75                 {
76                         return CreateHeader (name, ns, value, mustUnderstand, actor, default_relay);
77                 }
78
79                 public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer serializer, 
80                                                    bool mustUnderstand)
81                 {
82                         return CreateHeader (name, ns, value, serializer, mustUnderstand, default_actor, default_relay);
83                 }
84                 
85                 public static MessageHeader CreateHeader (string name, string ns, object value, 
86                                                    bool mustUnderstand, string actor, bool relay)
87                 {
88                         return CreateHeader (name, ns, value, new DataContractSerializer (value.GetType ()),
89                                         mustUnderstand, actor, relay);
90                 }
91
92                 public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer serializer,
93                                                    bool mustUnderstand, string actor)
94                 {
95                         return CreateHeader (name, ns, value, serializer, mustUnderstand, actor, default_relay);
96                 }
97                 
98                 public static MessageHeader CreateHeader (string name, string ns, object value, XmlObjectSerializer serializer,
99                                                    bool mustUnderstand, string actor, bool relay)
100                 {
101                         // FIXME: how to get IsReferenceParameter ?
102                         return new DefaultMessageHeader (name, ns, value, serializer, default_is_ref, mustUnderstand, actor, relay);
103                 }
104
105                 public virtual bool IsMessageVersionSupported (MessageVersion messageVersion)
106                 {
107                         if (messageVersion.Envelope == EnvelopeVersion.Soap12)
108                                 if (Actor == EnvelopeVersion.Soap11.NextDestinationActorValue)
109                                         return false;
110
111                         if (messageVersion.Envelope == EnvelopeVersion.Soap11)
112                                 if (Actor == EnvelopeVersion.Soap12.NextDestinationActorValue ||
113                                     Actor == EnvelopeVersion.Soap12UltimateReceiver)
114                                         return false;
115
116                         // by default, it's always supported
117                         return true;
118                 }
119
120                 protected abstract void OnWriteHeaderContents (XmlDictionaryWriter writer, MessageVersion messageVersion);
121
122                 protected virtual void OnWriteStartHeader (XmlDictionaryWriter writer, MessageVersion messageVersion)
123                 {
124                         var dic = Constants.SoapDictionary;
125                         XmlDictionaryString name, ns;
126                         var prefix = Prefix ?? (Namespace.Length > 0 ? writer.LookupPrefix (Namespace) : String.Empty);
127                         if (dic.TryLookup (Name, out name) && dic.TryLookup (Namespace, out ns))
128                                 writer.WriteStartElement (prefix, name, ns);
129                         else
130                                 writer.WriteStartElement (prefix, this.Name, this.Namespace);
131                         WriteHeaderAttributes (writer, messageVersion);
132                 }
133
134                 public override string ToString ()
135                 {
136                         StringBuilder sb = new StringBuilder ();
137
138                         XmlWriter w = XmlWriter.Create (sb, writer_settings);
139
140                         WriteHeader (w, MessageVersion.Default);
141                         w.Close ();
142
143                         return sb.ToString ();
144                 }
145
146                 public void WriteHeader (XmlDictionaryWriter writer, MessageVersion messageVersion)
147                 {
148                         if (writer == null)
149                                 throw new ArgumentNullException ("writer is null.");
150
151                         if (messageVersion == null)
152                                 throw new ArgumentNullException ("messageVersion is null.");
153
154                         if (messageVersion.Envelope == EnvelopeVersion.None)
155                                 return;
156
157                         WriteStartHeader (writer, messageVersion);
158                         WriteHeaderContents (writer, messageVersion);
159
160                         writer.WriteEndElement ();
161                 }
162
163                 public void WriteHeader (XmlWriter writer, MessageVersion messageVersion)
164                 {
165                         WriteHeader (XmlDictionaryWriter.CreateDictionaryWriter (writer), messageVersion);
166                 }
167
168                 protected void WriteHeaderAttributes (XmlDictionaryWriter writer, MessageVersion messageVersion)
169                 {
170                         var dic = Constants.SoapDictionary;
171                         if (Id != null)
172                                 writer.WriteAttributeString ("u", dic.Add ("Id"), dic.Add (Constants.WsuNamespace), Id);
173                         if (!String.IsNullOrEmpty (Actor)) {
174                                 if (messageVersion.Envelope == EnvelopeVersion.Soap11) 
175                                         writer.WriteAttributeString ("s", dic.Add ("actor"), dic.Add (messageVersion.Envelope.Namespace), Actor);
176
177                                 if (messageVersion.Envelope == EnvelopeVersion.Soap12) 
178                                         writer.WriteAttributeString ("s", dic.Add ("role"), dic.Add (messageVersion.Envelope.Namespace), Actor);
179                         }
180
181                         // mustUnderstand is the same across SOAP 1.1 and 1.2
182                         if (MustUnderstand == true)
183                                 writer.WriteAttributeString ("s", dic.Add ("mustUnderstand"), dic.Add (messageVersion.Envelope.Namespace), "1");
184
185                         // relay is only available on SOAP 1.2
186                         if (Relay == true && messageVersion.Envelope == EnvelopeVersion.Soap12)
187                                 writer.WriteAttributeString ("s", dic.Add ("relay"), dic.Add (messageVersion.Envelope.Namespace), "true");
188                 }
189
190                 public void WriteHeaderContents (XmlDictionaryWriter writer, MessageVersion messageVersion)
191                 {
192                         this.OnWriteHeaderContents (writer, messageVersion);
193                 }
194
195                 public void WriteStartHeader (XmlDictionaryWriter writer, MessageVersion messageVersion)
196                 {
197                         this.OnWriteStartHeader (writer, messageVersion);
198                 }
199
200                 public override string Actor { get { return default_actor; }}
201
202                 public override bool IsReferenceParameter { get { return default_is_ref; }}
203
204                 public override bool MustUnderstand { get { return default_must_understand; }}
205
206                 public override bool Relay { get { return default_relay; }}
207
208                 internal class XmlMessageHeader : MessageHeader
209                 {
210                         bool is_ref, must_understand, relay;
211                         string actor;
212 #if MOBILE
213                         string body;
214 #else
215                         // This is required to completely clone body xml that 
216                         // does not introduce additional xmlns declarations that
217                         // blocks canonicalized copy of the input XML.
218                         XmlDocument body;
219 #endif
220                         string local_name;
221                         string namespace_uri;
222
223                         public XmlMessageHeader (XmlReader reader, MessageVersion version)
224                         {
225                                 var soapNS = version.Envelope.Namespace;
226                                 var addrNS = version.Addressing.Namespace;
227                                 Prefix = reader.Prefix;
228                                 Id = reader.GetAttribute ("Id", Constants.WsuNamespace);
229
230                                 string s = reader.GetAttribute ("relay", soapNS);
231                                 relay = s != null ? XmlConvert.ToBoolean (s) : false;
232                                 s = reader.GetAttribute ("mustUnderstand", soapNS);
233                                 must_understand = s != null ? XmlConvert.ToBoolean (s) : false;
234                                 actor = reader.GetAttribute ("actor", soapNS) ?? String.Empty;
235
236                                 s = reader.GetAttribute ("IsReferenceParameter", addrNS);
237                                 is_ref = s != null ? XmlConvert.ToBoolean (s) : false;
238
239                                 local_name = reader.LocalName;
240                                 namespace_uri = reader.NamespaceURI;
241 #if MOBILE
242                                 body = reader.ReadOuterXml ();
243 #else
244                                 body = new XmlDocument ();
245                                 var w = body.CreateNavigator ().AppendChild ();
246                                 w.WriteNode (reader, false);
247                                 w.Close ();
248 #endif
249                         }
250
251                         public XmlReader CreateReader ()
252                         {
253 #if MOBILE
254                                 var reader = XmlReader.Create (new StringReader (body));
255 #else
256                                 var reader = new XmlNodeReader (body);
257 #endif
258                                 reader.MoveToContent ();
259                                 return reader;
260                         }
261
262                         protected override void OnWriteHeaderContents (
263                                 XmlDictionaryWriter writer, MessageVersion version)
264                         {
265                                 var r = CreateReader ();
266                                 r.MoveToContent ();
267                                 if (r.IsEmptyElement)
268                                         return; // write nothing
269                                 for (r.Read (); r.NodeType != XmlNodeType.EndElement;)
270                                         writer.WriteNode (r, false);
271                         }
272
273                         public override string Actor { get { return actor; }}
274
275                         public override bool IsReferenceParameter { get { return is_ref; }}
276
277                         public override bool MustUnderstand { get { return must_understand; }}
278
279                         public override string Name { get { return local_name; }}
280
281                         public override string Namespace { get { return namespace_uri; }}
282
283                         public override bool Relay { get { return relay; }}
284                 }
285
286                 internal class DefaultMessageHeader : MessageHeader
287                 {
288                         string actor, name, ns;
289                         object value;
290                         XmlObjectSerializer formatter;
291                         bool is_ref, must_understand, relay;
292                         
293                         internal DefaultMessageHeader (string name, string ns, object value, XmlObjectSerializer formatter, 
294                                                        bool isReferenceParameter,
295                                                        bool mustUnderstand, string actor, bool relay)
296                         {
297                                 this.name = name;
298                                 this.ns = ns;
299                                 this.value = value;
300                                 this.formatter = formatter;
301                                 this.is_ref = isReferenceParameter;
302                                 this.must_understand = mustUnderstand;
303                                 this.actor = actor ?? String.Empty;
304                                 this.relay = relay;
305                         }
306
307                         protected override void OnWriteHeaderContents (XmlDictionaryWriter writer,
308                                                                        MessageVersion version)
309                         {
310                                 // FIXME: it's a nasty workaround just to avoid UniqueId output as a string, for bug #577139.
311                                 if (Value is UniqueId)
312                                         writer.WriteValue ((UniqueId) Value);
313                                 else
314                                         this.formatter.WriteObjectContent (writer, value);
315                         }
316
317                         public object Value { get { return value; } }
318
319                         public override string Actor { get { return actor; }}
320
321                         public override bool IsReferenceParameter { get { return is_ref; }}
322
323                         public override bool MustUnderstand { get { return must_understand; }}
324
325                         public override string Name { get { return name; }}
326
327                         public override string Namespace { get { return ns; }}
328
329                         public override bool Relay { get { return relay; }}
330                 }
331         }
332 }