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