2002-11-12 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.UI / LosFormatter.cs
1 //
2 // System.Web.UI.LosFormatter
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (C) 2002 Ximian, Inc (http://www.ximian.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Drawing;
13 using System.IO;
14 using System.Runtime.Serialization.Formatters.Binary;
15 using System.Text;
16 using System.Web.UI;
17 using System.Web.Util;
18
19 namespace System.Web.UI
20 {
21         public sealed class LosFormatter
22         {
23                 delegate void WriteObject (LosFormatter formatter, TextWriter writer, object value);
24                 static char [] specialChars = new char [] {'<', '>', ';'};
25
26                 const char booleanID = 'o';
27                 const char stringID = 's';
28                 const char charID = 'c';
29                 const char int16ID = 'i';
30                 const char int32ID = 'I';
31                 const char int64ID = 'l';
32                 const char colorID = 'C';
33                 const char pairID = 'p';
34                 const char tripletID = 't';
35                 const char arrayListID = 'L';
36                 const char hashtableID = 'h';
37                 const char binaryID = 'b';
38                 const char arrayID = 'a';
39                 
40                 static Hashtable specialTypes;
41                 static Hashtable idToType;
42                 
43                 static LosFormatter ()
44                 {
45                         specialTypes = new Hashtable ();
46                         specialTypes.Add (typeof (Boolean), new WriteObject (WriteBoolean));
47                         specialTypes.Add (typeof (Pair), new WriteObject (WritePair));
48                         specialTypes.Add (typeof (Triplet), new WriteObject (WriteTriplet));
49                         specialTypes.Add (typeof (Color), new WriteObject (WriteColor));
50                         specialTypes.Add (typeof (ArrayList), new WriteObject (WriteArrayList));
51                         specialTypes.Add (typeof (Hashtable), new WriteObject (WriteHashtable));
52                         specialTypes.Add (typeof (Array), new WriteObject (WriteArray));
53
54                         idToType = new Hashtable ();
55                         idToType.Add (typeof (string), stringID);
56                         idToType.Add (typeof (char), charID);
57                         idToType.Add (typeof (Int16), int16ID);
58                         idToType.Add (typeof (Int32), int32ID);
59                         idToType.Add (typeof (Int64), int64ID);
60                         idToType.Add (typeof (Boolean), booleanID);
61                         idToType.Add (typeof (Pair), pairID);
62                         idToType.Add (typeof (Triplet), tripletID);
63                         idToType.Add (typeof (Color), colorID);
64                         idToType.Add (typeof (ArrayList), arrayListID);
65                         idToType.Add (typeof (Hashtable), hashtableID);
66                         idToType.Add (typeof (Array), arrayID);
67                 }
68                 
69                 public object Deserialize (Stream stream)
70                 {
71                         if (stream == null)
72                                 throw new ArgumentNullException ("stream");
73
74                         return Deserialize (new StreamReader (stream));
75                 }
76
77                 public object Deserialize (TextReader input)
78                 {
79                         if (input == null)
80                                 throw new ArgumentNullException ("input");
81
82                         return Deserialize (input.ReadToEnd ());
83                 }
84
85                 public object Deserialize (string input)
86                 {
87                         if (input == null)
88                                 throw new ArgumentNullException ("input");
89
90                         string real_input = WebEncoding.Encoding.GetString (Convert.FromBase64String (input));
91                         return DeserializeObject (real_input);
92                 }
93
94                 private static string UnEscapeSpecialChars (string str)
95                 {
96                         if (str.IndexOf ('\\') == -1)
97                                 return str;
98
99                         string result = str.Replace ("\\;", ";");
100                         result = result.Replace ("\\>", ">");
101                         result = result.Replace ("\\<", "<");
102                         result = result.Replace ("\\\\", "\\");
103                         return result;
104                 }
105                 
106                 private static string GetEnclosedString (string input)
107                 {
108                         if (input [0] != '<')
109                                 throw new ArgumentException (input);
110
111                         int count = 1;
112                         bool escaped = false;
113                         StringBuilder result = new StringBuilder ();
114                         for (int i = 1; count != 0 && i < input.Length; i++) {
115                                 char c = input [i];
116                                 if (escaped)
117                                         escaped = false;
118                                 else if (c == '\\')
119                                         escaped = true;
120                                 else if (c == '<')
121                                         count++;
122                                 else if (c == '>')
123                                         count--;
124
125                                 result.Append (c);
126                         }
127
128                         result.Length--;
129                         return result.ToString ();
130                 }
131                 
132                 private static string [] GetStringValues (string input)
133                 {
134                         if (input == null || input.Length == 0)
135                                 return new string [0];
136
137                         int length = input.Length;
138                         bool escaped = false;
139                         int opened = 0;
140                         ArrayList list = new ArrayList ();
141                         StringBuilder builder = new StringBuilder ();
142                         for (int i = 0; i < length; i++) {
143                                 char c = input [i];
144                                 if (escaped)
145                                         escaped = false;
146                                 else if (c == '\\')
147                                         escaped = true;
148                                 else if (c == '<')
149                                         opened++;
150                                 else if (c == '>')
151                                         opened--;
152                                 else if (c == ';' && opened == 0) {
153                                         list.Add (builder.ToString ());
154                                         builder = new StringBuilder ();
155                                         continue;
156                                 }
157
158                                 builder.Append (c);
159                         }
160
161                         list.Add (builder.ToString ());
162
163                         string [] result = new string [list.Count];
164                         list.CopyTo (result, 0);
165                         return result;
166                 }
167
168                 private object DeserializeObject (string input)
169                 {
170                         if (input == null || input.Length < 2)
171                                 return null;
172
173                         object obj;
174                         string enclosed = GetEnclosedString (input.Substring (1));
175                         string [] splitted;
176
177                         switch (input [0]) {
178                         case booleanID:
179                                 obj = enclosed.Length == 1;
180                                 break;
181                         case stringID:
182                                 obj = UnEscapeSpecialChars (enclosed);
183                                 break;
184                         case int16ID:
185                                 obj = Int16.Parse (enclosed);
186                                 break;
187                         case int32ID:
188                                 obj = Int32.Parse (enclosed);
189                                 break;
190                         case int64ID:
191                                 obj = Int64.Parse (enclosed);
192                                 break;
193                         case colorID:
194                                 obj = Color.FromArgb (Int32.Parse (enclosed));
195                                 break;
196                         case pairID:
197                                 Pair pair = new Pair ();
198                                 obj = pair;
199                                 splitted = GetStringValues (enclosed);
200                                 if (splitted.Length > 0) {
201                                         pair.First = DeserializeObject (splitted [0]);
202                                         if (splitted.Length > 1)
203                                                 pair.Second = DeserializeObject (splitted [1]);
204                                 }
205                                 break;
206                         case tripletID:
207                                 Triplet triplet = new Triplet ();
208                                 obj = triplet;
209                                 splitted = GetStringValues (enclosed);
210                                 if (splitted.Length == 0)
211                                         break;
212                                 triplet.First = DeserializeObject (splitted [0]);
213                                 if (splitted.Length < 1)
214                                         break;
215                                 triplet.Second = DeserializeObject (splitted [1]);
216                                 if (splitted.Length < 2)
217                                         break;
218                                 triplet.Third = DeserializeObject (splitted [2]);
219                                 break;
220                         case arrayListID:
221                         case arrayID:
222                                 ArrayList list = new ArrayList ();
223                                 obj = list;
224                                 splitted = GetStringValues (enclosed);
225                                 foreach (string s in splitted) {
226                                         object o = DeserializeObject (s);
227                                         list.Add (o);
228                                 }
229
230                                 if (input [0] == arrayID)
231                                         obj = list.ToArray (typeof (object));
232
233                                 break;
234                         case hashtableID:
235                                 object key;
236                                 object value;
237                                 Hashtable hash = new Hashtable ();
238                                 obj = hash;
239                                 splitted = GetStringValues (enclosed);
240                                 int length = splitted.Length;
241                                 for (int i = 0; i < length; i++) {
242                                         key = DeserializeObject (splitted [i++]);
243                                         if (i < length)
244                                                 value = DeserializeObject (splitted [i]);
245                                         else
246                                                 value = null;
247
248                                         hash.Add (key, value);
249                                 }
250                                 break;
251                         case binaryID:
252                                 byte [] buffer = WebEncoding.Encoding.GetBytes (enclosed);
253                                 MemoryStream ms = new MemoryStream (buffer);
254                                 BinaryFormatter fmt = new BinaryFormatter ();
255                                 obj = fmt.Deserialize (ms);
256                                 break;
257                         default:
258                                 throw new ArgumentException ("input");
259                         }
260
261                         return obj;
262                 }
263
264                 public void Serialize (Stream stream, object value)
265                 {
266                         if (stream == null)
267                                 throw new ArgumentNullException ("stream");
268
269                         if (value == null)
270                                 throw new ArgumentNullException ("value");
271
272                         StreamWriter writer = new StreamWriter (stream);
273                         Serialize (writer, value);
274                         writer.Flush ();
275                 }
276
277                 public void Serialize (TextWriter output, object value)
278                 {
279                         if (value == null)
280                                 return;
281
282                         if (output == null)
283                                 throw new ArgumentNullException ("output");
284
285                         StringBuilder builder = new StringBuilder ();
286                         StringWriter writer = new StringWriter (builder);
287                         SerializeObject (writer, value);
288                         byte [] bytes = WebEncoding.Encoding.GetBytes (builder.ToString ());
289                         output.Write (Convert.ToBase64String (bytes));
290                 }
291
292                 private static void WriteBoolean (LosFormatter formatter, TextWriter output, object value)
293                 {
294                         if (value == null)
295                                 return;
296                         
297                         output.Write (booleanID);
298                         bool b = (bool) value;
299                         output.Write (b ? "<t>" : "<>");
300                 }
301                 
302                 private static void WritePair (LosFormatter formatter, TextWriter output, object value)
303                 {
304                         if (value == null)
305                                 return;
306                         
307                         output.Write (pairID);
308                         Pair pair = (Pair) value;
309                         output.Write ('<');
310                         formatter.SerializeObject (output, pair.First);
311                         output.Write (';');
312                         formatter.SerializeObject (output, pair.Second);
313                         output.Write ('>');
314                 }
315
316                 private static void WriteTriplet (LosFormatter formatter, TextWriter output, object value)
317                 {
318                         if (value == null)
319                                 return;
320                         
321                         output.Write (tripletID);
322                         Triplet triplet = (Triplet) value;
323                         output.Write ('<');
324                         formatter.SerializeObject (output, triplet.First);
325                         output.Write (';');
326                         formatter.SerializeObject (output, triplet.Second);
327                         output.Write (';');
328                         formatter.SerializeObject (output, triplet.Third);
329                         output.Write ('>');
330                 }
331
332                 private static void WriteColor (LosFormatter formatter, TextWriter output, object value)
333                 {
334                         if (value == null)
335                                 return;
336                         
337                         Color c = (Color) value;
338                         output.Write (String.Format ("{0}<{1}>", colorID, c.ToArgb ()));
339                 }
340
341                 private static void WriteArrayList (LosFormatter formatter, TextWriter output, object value)
342                 {
343                         if (value == null)
344                                 return;
345                         
346                         output.Write (arrayListID);
347                         output.Write ('<');
348                         ArrayList list = (ArrayList) value;
349                         for (int i = 0; i < list.Count; i++) {
350                                 formatter.SerializeObject (output, list [i]);
351                                 if (i != list.Count - 1)
352                                         output.Write (';');
353                         }
354                         output.Write('>');
355                 }
356
357                 private static void WriteArray (LosFormatter formatter, TextWriter output, object value)
358                 {
359                         if (value == null)
360                                 return;
361                         
362                         output.Write (arrayID);
363                         output.Write ('<');
364                         Array array = (Array) value;
365                         for (int i = 0; i < array.Length; i++) {
366                                 formatter.SerializeObject (output, array.GetValue (i));
367                                 if (i != array.Length - 1)
368                                         output.Write (';');
369                         }
370                         output.Write('>');
371                 }
372
373                 private static void WriteHashtable (LosFormatter formatter, TextWriter output, object value)
374                 {
375                         if (value == null)
376                                 return;
377                         
378                         output.Write (hashtableID);
379                         output.Write ('<');
380                         Hashtable hash = (Hashtable) value;
381                         int i = 0;
382                         foreach (DictionaryEntry entry in hash) {
383                                 formatter.SerializeObject (output, entry.Key);
384                                 output.Write (';');
385                                 formatter.SerializeObject (output, entry.Value);
386                                 if (i != hash.Count - 1)
387                                         output.Write (';');
388                                 i++;
389                         }
390                         output.Write('>');
391                 }
392
393                 private static string EscapeSpecialChars (string str)
394                 {
395                         if (str.IndexOfAny (specialChars) == -1)
396                                 return str;
397
398                         string result = str.Replace ("\\", "\\\\");
399                         result = result.Replace ("<", "\\<");
400                         result = result.Replace (">", "\\>");
401                         result = result.Replace (";", "\\;");
402                         return result;
403                 }
404                 
405                 private void SerializeBinary (TextWriter output, object value)
406                 {
407                         WebTrace.PushContext ("LosFormatter.SerializeBinary");
408                         WebTrace.WriteLine ("not serializing value type: " + value.GetType ());
409                         /* This is just for debugging purposes */
410                         /*if (value is Array) {
411                                 Array array = (Array) value;
412                                 for (int i = 0; i < array.Length; i++) {
413                                         object o = array.GetValue (i);
414                                         if (o == null)
415                                                 WebTrace.WriteLine ("\t{0} is null", i);
416                                         else
417                                                 WebTrace.WriteLine ("\t{0} {1} {2}", i, o.GetType (), o);
418                                 }
419                         }
420                         */
421                         /*
422                         BinaryFormatter fmt = new BinaryFormatter ();
423                         MemoryStream stream = new MemoryStream ();
424
425                         fmt.Serialize (stream, value);
426                         output.Write (binaryID);
427                         output.Write ('<');
428                         byte [] buffer = stream.GetBuffer ();
429                         output.Write (Convert.ToBase64String (stream.GetBuffer ()));
430                         output.Write ('>');
431                         */
432                         WebTrace.PopContext ();
433                 }
434
435                 private void SerializeObject (TextWriter output, object value)
436                 {
437                         WebTrace.PushContext ("LosFormatter.SerializeObject");
438                         if (value == null) {
439                                 WebTrace.WriteLine ("value is null");
440                                 WebTrace.PopContext ();
441                                 return;
442                         }
443
444                         Type t = value.GetType ();
445                         if (t.IsArray)
446                                 t = typeof (Array);
447
448                         if (specialTypes.Contains (t)) {
449                                 WriteObject w = (WriteObject) specialTypes [t];
450                                 w (this, output, value);
451                                 WebTrace.WriteLine ("special type: {0}", value.GetType ());
452                                 WebTrace.PopContext ();
453                                 return;
454                         }
455
456                         if (idToType.Contains (t)) {
457                                 char c = (char) idToType [t];
458                                 string s = EscapeSpecialChars (value.ToString ());
459                                 output.Write (String.Format ("{0}<{1}>", c, value.ToString ()));
460                                 WebTrace.WriteLine ("regular type: {0}", value.GetType ());
461                                 WebTrace.PopContext ();
462                                 return;
463                         }
464
465                         SerializeBinary (output, value);
466                         WebTrace.PopContext ();
467                 }
468         }
469 }
470