Merge pull request #1573 from akoeplinger/msbuild-import-empty
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / Message.cs
1 //
2 // Message.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006,2010 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 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Runtime.Serialization;
32 using System.Xml;
33 using System.Xml.Schema;
34 using Mono.Xml.XPath;
35
36 namespace System.ServiceModel.Channels
37 {
38         public abstract class Message : IDisposable
39         {
40                 bool disposed;
41                 string body_id;
42                 Message copied_message;
43                 string string_cache;
44
45                 protected Message () {
46                         State = MessageState.Created;
47                 }
48
49                 public abstract MessageHeaders Headers { get; }
50
51                 public virtual bool IsEmpty {
52                         get { return false; }
53                 }
54
55                 public virtual bool IsFault {
56                         get { return false; }
57                 }
58
59                 public abstract MessageProperties Properties { get; }
60
61                 public MessageState State { get; private set; }
62
63                 public abstract MessageVersion Version { get; }
64
65                 protected bool IsDisposed {
66                         get { return disposed; }
67                 }
68
69                 public void Close ()
70                 {
71                         if (!disposed)
72                                 OnClose ();
73                         State = MessageState.Closed;
74                         disposed = true;
75                 }
76
77                 public MessageBuffer CreateBufferedCopy (int maxBufferSize)
78                 {
79                         if (State != MessageState.Created)
80                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
81
82                         if (copied_message != null)
83                                 return copied_message.CreateBufferedCopy (maxBufferSize);
84
85                         try {
86                                 return OnCreateBufferedCopy (maxBufferSize);
87                         } finally {
88                                 State = MessageState.Copied;
89                         }
90                 }
91
92                 void IDisposable.Dispose ()
93                 {
94                         Close ();
95                 }
96
97                 public T GetBody<T> ()
98                 {
99                         return OnGetBody<T> (GetReaderAtBodyContents ());
100                 }
101
102                 public T GetBody<T> (XmlObjectSerializer xmlFormatter)
103                 {
104                         // FIXME: Somehow use OnGetBody() here as well?
105                         return (T)xmlFormatter.ReadObject (GetReaderAtBodyContents ());
106                 }
107
108                 protected virtual T OnGetBody<T> (XmlDictionaryReader reader)
109                 {
110                         var xmlFormatter = new DataContractSerializer (typeof (T));
111                         return (T)xmlFormatter.ReadObject (reader);
112                 }
113
114                 public string GetBodyAttribute (string localName, string ns)
115                 {
116                         return OnGetBodyAttribute (localName, ns);
117                 }
118
119                 public XmlDictionaryReader GetReaderAtBodyContents ()
120                 {
121                         if (copied_message != null)
122                                 return copied_message.GetReaderAtBodyContents ();
123
124                         return OnGetReaderAtBodyContents ();
125                 }
126
127                 public override string ToString ()
128                 {
129                         if (string_cache != null)
130                                 return string_cache;
131
132                         StringWriter sw = new StringWriter ();
133                         XmlWriterSettings settings = new XmlWriterSettings ();
134                         settings.Indent = true;
135                         settings.OmitXmlDeclaration = true;
136
137                         using (XmlWriter w = XmlWriter.Create (sw, settings)) {
138                                 OnBodyToString (XmlDictionaryWriter.CreateDictionaryWriter (w));
139                         }
140                         string_cache = sw.ToString ();
141                         return string_cache;
142                 }
143
144                 void WriteXsiNil (XmlDictionaryWriter writer)
145                 {
146                         var dic = Constants.SoapDictionary;
147                         writer.WriteStartElement ("z", dic.Add ("anyType"), dic.Add (Constants.MSSerialization));
148                         writer.WriteAttributeString ("i", dic.Add ("nil"), dic.Add ("http://www.w3.org/2001/XMLSchema-instance"), "true");
149                         writer.WriteEndElement ();
150                 }
151
152                 public void WriteBody (XmlDictionaryWriter writer)
153                 {
154                         if (Version.Envelope != EnvelopeVersion.None)
155                                 WriteStartBody (writer);
156                         WriteBodyContents (writer);
157                         if (Version.Envelope != EnvelopeVersion.None)
158                                 writer.WriteEndElement ();
159                 }
160
161                 public void WriteBody (XmlWriter writer)
162                 {
163                         WriteBody (XmlDictionaryWriter.CreateDictionaryWriter (writer));
164                 }
165
166                 public void WriteBodyContents (XmlDictionaryWriter writer)
167                 {
168                         if (!IsEmpty) {
169                                 if (copied_message != null)
170                                         copied_message.WriteBodyContents (writer);
171                                 else
172                                         OnWriteBodyContents (writer);
173                         }
174                         else if (Version.Envelope == EnvelopeVersion.None)
175                                 WriteXsiNil (writer);
176                         State = MessageState.Written;
177                 }
178
179                 public void WriteMessage (XmlDictionaryWriter writer)
180                 {
181                         if (State != MessageState.Created)
182                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
183
184                         OnWriteMessage (writer);
185                 }
186
187                 public void WriteMessage (XmlWriter writer)
188                 {
189                         WriteMessage (XmlDictionaryWriter.CreateDictionaryWriter (writer));
190                 }
191
192                 public void WriteStartBody (XmlDictionaryWriter writer)
193                 {
194                         if (State != MessageState.Created)
195                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
196
197                         OnWriteStartBody (writer);
198                 }
199
200                 public void WriteStartBody (XmlWriter writer)
201                 {
202                         WriteStartBody (
203                                 XmlDictionaryWriter.CreateDictionaryWriter (writer));
204                 }
205
206                 public void WriteStartEnvelope (XmlDictionaryWriter writer)
207                 {
208                         if (State != MessageState.Created)
209                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
210
211                         OnWriteStartEnvelope (writer);
212                 }
213
214                 protected virtual void OnBodyToString (
215                         XmlDictionaryWriter writer)
216                 {
217                         MessageState tempState = State;
218                         try {
219                                 var mb = CreateBufferedCopy (int.MaxValue);
220                                 copied_message = mb.CreateMessage ();
221                                 var msg = mb.CreateMessage ();
222                                 msg.WriteMessage (writer);
223                         }
224                         finally {
225                                 State = tempState;
226                         }
227                 }
228
229                 protected virtual void OnClose ()
230                 {
231                 }
232
233                 protected virtual MessageBuffer OnCreateBufferedCopy (
234                         int maxBufferSize)
235                 {
236                         var s = new XmlWriterSettings ();
237                         s.OmitXmlDeclaration = true;
238                         s.ConformanceLevel = ConformanceLevel.Auto;
239                         StringWriter sw = new StringWriter ();
240                         using (XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (XmlWriter.Create (sw, s)))
241                                 WriteBodyContents (w);
242                         var headers = new MessageHeaders (Headers);
243                         var props = new MessageProperties (Properties);
244                         return new DefaultMessageBuffer (maxBufferSize, headers, props, new XmlReaderBodyWriter (sw.ToString (), maxBufferSize, null), false, new AttributeCollection ());
245                 }
246
247                 protected virtual string OnGetBodyAttribute (
248                         string localName, string ns)
249                 {
250                         return null;
251                 }
252
253                 protected virtual XmlDictionaryReader OnGetReaderAtBodyContents ()
254                 {
255                         var ws = new XmlWriterSettings ();
256                         ws.ConformanceLevel = ConformanceLevel.Auto;
257                         StringWriter sw = new StringWriter ();
258                         using (XmlDictionaryWriter body = XmlDictionaryWriter.CreateDictionaryWriter (XmlWriter.Create (sw, ws))) {
259                                 WriteBodyContents (body);
260                         }
261
262                         var rs = new XmlReaderSettings ();
263                         rs.ConformanceLevel = ConformanceLevel.Auto;
264                         return XmlDictionaryReader.CreateDictionaryReader (XmlReader.Create (new StringReader (sw.ToString ()), rs));
265                 }
266
267                 protected abstract void OnWriteBodyContents (
268                         XmlDictionaryWriter writer);
269
270                 protected virtual void OnWriteMessage (
271                         XmlDictionaryWriter writer)
272                 {
273                         if (Version.Envelope != EnvelopeVersion.None) {
274                                 WriteStartEnvelope (writer);
275                                 if (Headers.Count > 0) {
276                                         OnWriteStartHeaders (writer);
277                                         for (int i = 0, count = Headers.Count; i < count; i++)
278                                                 Headers.WriteHeader (i, writer);
279                                         writer.WriteEndElement ();
280                                 }
281                         }
282                         WriteBody (writer);
283                         if (Version.Envelope != EnvelopeVersion.None)
284                                 writer.WriteEndElement ();
285                 }
286
287                 protected virtual void OnWriteStartBody (
288                         XmlDictionaryWriter writer)
289                 {
290                         var dic = Constants.SoapDictionary;
291                         writer.WriteStartElement ("s", dic.Add ("Body"), dic.Add (Version.Envelope.Namespace));
292                 }
293
294                 protected virtual void OnWriteStartEnvelope (
295                         XmlDictionaryWriter writer)
296                 {
297                         var dic = Constants.SoapDictionary;
298                         writer.WriteStartElement ("s", dic.Add ("Envelope"), dic.Add (Version.Envelope.Namespace));
299                         if (Headers.Action != null && Version.Addressing.Namespace != MessageVersion.None.Addressing.Namespace)
300                                 writer.WriteXmlnsAttribute ("a", dic.Add (Version.Addressing.Namespace));
301                         foreach (MessageHeaderInfo h in Headers)
302                                 if (h.Id != null && writer.LookupPrefix (Constants.WsuNamespace) != "u") {
303                                         writer.WriteXmlnsAttribute ("u", dic.Add (Constants.WsuNamespace));
304                                         break;
305                                 }
306                 }
307
308                 protected virtual void OnWriteStartHeaders (
309                         XmlDictionaryWriter writer)
310                 {
311                         var dic = Constants.SoapDictionary;
312                         writer.WriteStartElement ("s", dic.Add ("Header"), dic.Add (Version.Envelope.Namespace));
313                 }
314
315                 #region factory methods
316
317                 // 1) version, code, reason, action -> 3
318                 // 2) version, code, reason, detail, action -> 3
319                 // 3) version, fault, action -> SimpleMessage
320                 // 4) version, action, body -> 10 or 5
321                 // 5) version, action, body, formatter -> 10 or 9
322                 // 6) version, action, xmlReader -> 7
323                 // 7) version, action, reader -> 9
324                 // 8) xmlReader, maxSizeOfHeaders, version -> 11
325                 // 9) version, action, body -> SimpleMessage
326                 // 10) version, action -> EmptyMessage
327                 // 11) reader, maxSizeOfHeaders, version -> XmlReaderMessage
328
329                 // 1)
330                 public static Message CreateMessage (MessageVersion version,
331                         FaultCode code, string reason, string action)
332                 {
333                         MessageFault fault = MessageFault.CreateFault (code, reason);
334                         return CreateMessage (version, fault, action);
335                 }
336
337                 // 2)
338                 public static Message CreateMessage (MessageVersion version,
339                         FaultCode code, string reason, object detail,
340                         string action)
341                 {
342                         MessageFault fault = MessageFault.CreateFault (
343                                 code, new FaultReason (reason), detail);
344                         return CreateMessage (version, fault, action);
345                 }
346
347                 // 3)
348                 public static Message CreateMessage (MessageVersion version,
349                         MessageFault fault, string action)
350                 {
351                         return new SimpleMessage (version, action,
352                                 new MessageFaultBodyWriter (fault, version), true, empty_attributes);
353                 }
354
355                 // 4)
356                 public static Message CreateMessage (MessageVersion version,
357                         string action, object body)
358                 {
359                         return body == null ?
360                                 CreateMessage (version, action) :
361                                 CreateMessage (version, action, body, new DataContractSerializer (body.GetType ()));
362                 }
363
364                 // 5)
365                 public static Message CreateMessage (MessageVersion version,
366                         string action, object body, XmlObjectSerializer xmlFormatter)
367                 {
368                         return body == null ?
369                                 CreateMessage (version, action) :
370                                 CreateMessage (
371                                         version, action,
372                                         new XmlObjectSerializerBodyWriter (body, xmlFormatter));
373                 }
374
375                 // 6)
376                 public static Message CreateMessage (MessageVersion version,
377                         string action, XmlReader body)
378                 {
379                         return CreateMessage (version, action,
380                                 XmlDictionaryReader.CreateDictionaryReader (body));
381                 }
382
383                 // 7)
384                 public static Message CreateMessage (MessageVersion version,
385                         string action, XmlDictionaryReader body)
386                 {
387                         return CreateMessage (version, action,
388                                 new XmlReaderBodyWriter (body));
389                 }
390
391                 // 8)
392                 public static Message CreateMessage (XmlReader envelopeReader,
393                         int maxSizeOfHeaders, MessageVersion version)
394                 {
395                         return CreateMessage (
396                                 XmlDictionaryReader.CreateDictionaryReader (envelopeReader),
397                                 maxSizeOfHeaders,
398                                 version);
399                 }
400
401                 // Core implementations of CreateMessage.
402
403                 static readonly AttributeCollection empty_attributes = new AttributeCollection ();
404
405                 // 9)
406                 public static Message CreateMessage (MessageVersion version,
407                         string action, BodyWriter body)
408                 {
409                         if (version == null)
410                                 throw new ArgumentNullException ("version");
411                         if (body == null)
412                                 throw new ArgumentNullException ("body");
413                         return new SimpleMessage (version, action, body, false, empty_attributes);
414                 }
415
416                 // 10)
417                 public static Message CreateMessage (MessageVersion version,
418                         string action)
419                 {
420                         if (version == null)
421                                 throw new ArgumentNullException ("version");
422                         return new EmptyMessage (version, action);
423                 }
424
425                 // 11)
426                 public static Message CreateMessage (
427                         XmlDictionaryReader envelopeReader,
428                         int maxSizeOfHeaders,
429                         MessageVersion version)
430                 {
431                         if (envelopeReader == null)
432                                 throw new ArgumentNullException ("envelopeReader");
433                         if (version == null)
434                                 throw new ArgumentNullException ("version");
435                         return new XmlReaderMessage (version,
436                                 envelopeReader, maxSizeOfHeaders);
437                 }
438
439                 #endregion
440         }
441 }