Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[mono.git] / mcs / class / Managed.Windows.Forms / System.Resources / ResXResourceWriter.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Duncan Mak              duncan@ximian.com
24 //      Gonzalo Paniagua Javier gonzalo@ximian.com
25 //      Peter Bartok            pbartok@novell.com
26 //
27
28 // COMPLETE
29
30 using System.ComponentModel;
31 using System.IO;
32 using System.Runtime.Serialization.Formatters.Binary;
33 using System.Text;
34 using System.Xml;
35 using System.Reflection;
36
37 namespace System.Resources
38 {
39 #if INSIDE_SYSTEM_WEB
40         internal
41 #else
42         public
43 #endif
44         class ResXResourceWriter : IResourceWriter, IDisposable
45         {
46                 #region Local Variables
47                 private string          filename;
48                 private Stream          stream;
49                 private TextWriter      textwriter;
50                 private XmlTextWriter   writer;
51                 private bool            written;
52                 private string          base_path;
53                 #endregion      // Local Variables
54
55                 #region Static Fields
56                 public static readonly string BinSerializedObjectMimeType       = "application/x-microsoft.net.object.binary.base64";
57                 public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64";
58                 public static readonly string DefaultSerializedObjectMimeType   = BinSerializedObjectMimeType;
59                 public static readonly string ResMimeType                       = "text/microsoft-resx";
60                 public static readonly string ResourceSchema                    = schema;
61                 public static readonly string SoapSerializedObjectMimeType      = "application/x-microsoft.net.object.soap.base64";
62                 public static readonly string Version                           = "2.0";
63                 #endregion      // Static Fields
64
65                 #region Constructors & Destructor
66                 public ResXResourceWriter (Stream stream)
67                 {
68                         if (stream == null)
69                                 throw new ArgumentNullException ("stream");
70
71                         if (!stream.CanWrite)
72                                 throw new ArgumentException ("stream is not writable.", "stream");
73
74                         this.stream = stream;
75                 }
76
77                 public ResXResourceWriter (TextWriter textWriter)
78                 {
79                         if (textWriter == null)
80                                 throw new ArgumentNullException ("textWriter");
81
82                         this.textwriter = textWriter;
83                 }
84                 
85                 public ResXResourceWriter (string fileName)
86                 {
87                         if (fileName == null)
88                                 throw new ArgumentNullException ("fileName");
89
90                         this.filename = fileName;
91                 }
92
93                 ~ResXResourceWriter() {
94                         Dispose(false);
95                 }
96                 #endregion      // Constructors & Destructor
97
98                 void InitWriter ()
99                 {
100                         if (filename != null)
101                                 stream = File.OpenWrite (filename);
102                         if (textwriter == null)
103                                 textwriter = new StreamWriter (stream, Encoding.UTF8);
104
105                         writer = new XmlTextWriter (textwriter);
106                         writer.Formatting = Formatting.Indented;
107                         writer.WriteStartDocument ();
108                         writer.WriteStartElement ("root");
109                         writer.WriteRaw (schema);
110                         WriteHeader ("resmimetype", "text/microsoft-resx");
111                         WriteHeader ("version", "1.3");
112                         WriteHeader ("reader", typeof (ResXResourceReader).AssemblyQualifiedName);
113                         WriteHeader ("writer", typeof (ResXResourceWriter).AssemblyQualifiedName);
114                 }
115
116                 void WriteHeader (string name, string value)
117                 {
118                         writer.WriteStartElement ("resheader");
119                         writer.WriteAttributeString ("name", name);
120                         writer.WriteStartElement ("value");
121                         writer.WriteString (value);
122                         writer.WriteEndElement ();
123                         writer.WriteEndElement ();
124                 }
125
126                 void WriteNiceBase64(byte[] value, int offset, int length) {
127                         string          b64;
128                         StringBuilder   sb;
129                         int             pos;
130                         int             inc;
131                         string          ins;
132
133                         b64 = Convert.ToBase64String(value, offset, length);
134
135                         // Wild guess; two extra newlines, and one newline/tab pair for every 80 chars
136                         sb = new StringBuilder(b64, b64.Length + ((b64.Length + 160) / 80) * 3);
137                         pos = 0;
138                         inc = 80 + Environment.NewLine.Length + 1;
139                         ins = Environment.NewLine + "\t";
140                         while (pos < sb.Length) {
141                                 sb.Insert(pos, ins);
142                                 pos += inc;
143                         }
144                         sb.Insert(sb.Length, Environment.NewLine);
145                         writer.WriteString(sb.ToString());
146                 }
147                 void WriteBytes (string name, Type type, byte[] value, int offset, int length)
148                 {
149                         WriteBytes (name, type, value, offset, length, String.Empty);
150                 }
151
152                 void WriteBytes (string name, Type type, byte[] value, int offset, int length, string comment)
153                 {
154                         writer.WriteStartElement ("data");
155                         writer.WriteAttributeString ("name", name);
156
157                         if (type != null) {
158                                 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
159                                 // byte[] should never get a mimetype, otherwise MS.NET won't be able
160                                 // to parse the data.
161                                 if (type != typeof (byte[]))
162                                         writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
163                                 writer.WriteStartElement ("value");
164                                 WriteNiceBase64 (value, offset, length);
165                         } else {
166                                 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
167                                 writer.WriteStartElement ("value");
168                                 writer.WriteBase64 (value, offset, length);
169                         }
170
171                         writer.WriteEndElement ();
172
173                         if (!(comment == null || comment.Equals (String.Empty))) {
174                                 writer.WriteStartElement ("comment");
175                                 writer.WriteString (comment);
176                                 writer.WriteEndElement ();
177                         }
178                         
179                         writer.WriteEndElement ();
180                 }
181
182                 void WriteBytes (string name, Type type, byte [] value)
183                 {
184                         WriteBytes (name, type, value, 0, value.Length);
185                 }
186
187                 void WriteString (string name, string value)
188                 {
189                         WriteString (name, value, null);
190                 }
191                 void WriteString (string name, string value, Type type)
192                 {
193                         WriteString (name, value, type, String.Empty);
194                 }
195                 void WriteString (string name, string value, Type type, string comment)
196                 {
197                         writer.WriteStartElement ("data");
198                         writer.WriteAttributeString ("name", name);
199                         if (type != null)
200                                 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
201                         writer.WriteStartElement ("value");
202                         writer.WriteString (value);
203                         writer.WriteEndElement ();
204                         if (!(comment == null || comment.Equals (String.Empty))) {
205                                 writer.WriteStartElement ("comment");
206                                 writer.WriteString (comment);
207                                 writer.WriteEndElement ();
208                         }
209                         writer.WriteEndElement ();
210                         writer.WriteWhitespace ("\n  ");
211                 }
212
213                 public void AddResource (string name, byte [] value)
214                 {
215                         if (name == null)
216                                 throw new ArgumentNullException ("name");
217
218                         if (value == null)
219                                 throw new ArgumentNullException ("value");
220
221                         if (written)
222                                 throw new InvalidOperationException ("The resource is already generated.");
223
224                         if (writer == null)
225                                 InitWriter ();
226
227                         WriteBytes (name, value.GetType (), value);
228                 }
229
230                 public void AddResource (string name, object value)
231                 {
232                         AddResource (name, value, String.Empty);
233                 }
234
235                 private void AddResource (string name, object value, string comment)
236                 {
237                         if (value is string) {
238                                 AddResource (name, (string) value, comment);
239                                 return;
240                         }
241
242                         if (value is byte[]) {
243                                 AddResource (name, (byte[]) value);
244                                 return;
245                         }
246
247                         if (name == null)
248                                 throw new ArgumentNullException ("name");
249
250                         if (value == null)
251                                 throw new ArgumentNullException ("value");
252
253                         if (!value.GetType ().IsSerializable)
254                                         throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
255
256                         if (written)
257                                 throw new InvalidOperationException ("The resource is already generated.");
258
259                         if (writer == null)
260                                 InitWriter ();
261
262                         TypeConverter converter = TypeDescriptor.GetConverter (value);
263                         if (converter != null && converter.CanConvertTo (typeof (string)) && converter.CanConvertFrom (typeof (string))) {
264                                 string str = (string) converter.ConvertToInvariantString (value);
265                                 WriteString (name, str, value.GetType ());
266                                 return;
267                         }
268                         
269                         if (converter != null && converter.CanConvertTo (typeof (byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
270                                 byte[] b = (byte[]) converter.ConvertTo (value, typeof (byte[]));
271                                 WriteBytes (name, value.GetType (), b);
272                                 return;
273                         }
274                         
275                         MemoryStream ms = new MemoryStream ();
276                         BinaryFormatter fmt = new BinaryFormatter ();
277                         try {
278                                 fmt.Serialize (ms, value);
279                         } catch (Exception e) {
280                                 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
281                                                                      "because it cannot be serialized: " +
282                                                                      e.Message);
283                         }
284
285                         WriteBytes (name, null, ms.GetBuffer (), 0, (int) ms.Length, comment);
286                         ms.Close ();
287                 }
288                 
289                 public void AddResource (string name, string value)
290                 {
291                         AddResource (name, value, string.Empty);
292                 }
293
294                 private void AddResource (string name, string value, string comment)
295                 {
296                         if (name == null)
297                                 throw new ArgumentNullException ("name");
298
299                         if (value == null)
300                                 throw new ArgumentNullException ("value");
301
302                         if (written)
303                                 throw new InvalidOperationException ("The resource is already generated.");
304
305                         if (writer == null)
306                                 InitWriter ();
307
308                         WriteString (name, value, null, comment);
309                 }
310
311                 [MonoTODO ("Stub, not implemented")]
312                 public virtual void AddAlias (string aliasName, AssemblyName assemblyName)
313                 {
314                 }
315                 
316                 public void AddResource (ResXDataNode node)
317                 {
318                         AddResource (node.Name, node.Value, node.Comment);
319                 }
320                 
321                 public void AddMetadata (string name, string value)
322                 {
323                         if (name == null)
324                                 throw new ArgumentNullException ("name");
325
326                         if (value == null)
327                                 throw new ArgumentNullException ("value");
328
329                         if (written)
330                                 throw new InvalidOperationException ("The resource is already generated.");
331
332                         if (writer == null)
333                                 InitWriter ();
334
335                         writer.WriteStartElement ("metadata");
336                         writer.WriteAttributeString ("name", name);
337                         writer.WriteAttributeString ("xml:space", "preserve");
338                         
339                         writer.WriteElementString ("value", value);
340                         
341                         writer.WriteEndElement ();
342                 }
343
344                 public void AddMetadata (string name, byte[] value)
345                 {
346                         if (name == null)
347                                 throw new ArgumentNullException ("name");
348
349                         if (value == null)
350                                 throw new ArgumentNullException ("value");
351
352                         if (written)
353                                 throw new InvalidOperationException ("The resource is already generated.");
354
355                         if (writer == null)
356                                 InitWriter ();
357
358                         writer.WriteStartElement ("metadata");
359                         writer.WriteAttributeString ("name", name);
360
361                         writer.WriteAttributeString ("type", value.GetType ().AssemblyQualifiedName);
362                         
363                         writer.WriteStartElement ("value");
364                         WriteNiceBase64 (value, 0, value.Length);
365                         writer.WriteEndElement ();
366
367                         writer.WriteEndElement ();
368                 }
369                 
370                 public void AddMetadata (string name, object value)
371                 {
372                         if (value is string) {
373                                 AddMetadata (name, (string)value);
374                                 return;
375                         }
376
377                         if (value is byte[]) {
378                                 AddMetadata (name, (byte[])value);
379                                 return;
380                         }
381
382                         if (name == null)
383                                 throw new ArgumentNullException ("name");
384
385                         if (value == null)
386                                 throw new ArgumentNullException ("value");
387
388                         if (!value.GetType ().IsSerializable)
389                                 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
390
391                         if (written)
392                                 throw new InvalidOperationException ("The resource is already generated.");
393
394                         if (writer == null)
395                                 InitWriter ();
396
397                         Type type = value.GetType ();
398                         
399                         TypeConverter converter = TypeDescriptor.GetConverter (value);
400                         if (converter != null && converter.CanConvertTo (typeof (string)) && converter.CanConvertFrom (typeof (string))) {
401                                 string str = (string)converter.ConvertToInvariantString (value);
402                                 writer.WriteStartElement ("metadata");
403                                 writer.WriteAttributeString ("name", name);
404                                 if (type != null)
405                                         writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
406                                 writer.WriteStartElement ("value");
407                                 writer.WriteString (str);
408                                 writer.WriteEndElement ();
409                                 writer.WriteEndElement ();
410                                 writer.WriteWhitespace ("\n  ");
411                                 return;
412                         }
413
414                         if (converter != null && converter.CanConvertTo (typeof (byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
415                                 byte[] b = (byte[])converter.ConvertTo (value, typeof (byte[]));
416                                 writer.WriteStartElement ("metadata");
417                                 writer.WriteAttributeString ("name", name);
418
419                                 if (type != null) {
420                                         writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
421                                         writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
422                                         writer.WriteStartElement ("value");
423                                         WriteNiceBase64 (b, 0, b.Length);
424                                 } else {
425                                         writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
426                                         writer.WriteStartElement ("value");
427                                         writer.WriteBase64 (b, 0, b.Length);
428                                 }
429
430                                 writer.WriteEndElement ();
431                                 writer.WriteEndElement ();
432                                 return;
433                         }
434
435                         MemoryStream ms = new MemoryStream ();
436                         BinaryFormatter fmt = new BinaryFormatter ();
437                         try {
438                                 fmt.Serialize (ms, value);
439                         } catch (Exception e) {
440                                 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
441                                                                      "because it cannot be serialized: " +
442                                                                      e.Message);
443                         }
444
445                         writer.WriteStartElement ("metadata");
446                         writer.WriteAttributeString ("name", name);
447
448                         if (type != null) {
449                                 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
450                                 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
451                                 writer.WriteStartElement ("value");
452                                 WriteNiceBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
453                         } else {
454                                 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
455                                 writer.WriteStartElement ("value");
456                                 writer.WriteBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
457                         }
458
459                         writer.WriteEndElement ();
460                         writer.WriteEndElement ();
461                         ms.Close ();
462                 }
463
464                 public void Close ()
465                 {
466                         if (!written) {
467                                 Generate ();
468                         }
469
470                         if (writer != null) {
471                                 writer.Close ();
472                                 stream = null;
473                                 filename = null;
474                                 textwriter = null;
475                         }
476                 }
477                 
478                 public virtual void Dispose ()
479                 {
480                         Dispose(true);
481                         GC.SuppressFinalize(this);
482                 }
483
484                 public void Generate ()
485                 {
486                         if (written)
487                                 throw new InvalidOperationException ("The resource is already generated.");
488
489                         written = true;
490                         writer.WriteEndElement ();
491                         writer.Flush ();
492                 }
493
494                 protected virtual void Dispose (bool disposing)
495                 {
496                         if (disposing)
497                                 Close();
498                 }
499
500                 static string schema = @"
501   <xsd:schema id='root' xmlns='' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
502     <xsd:element name='root' msdata:IsDataSet='true'>
503       <xsd:complexType>
504         <xsd:choice maxOccurs='unbounded'>
505           <xsd:element name='data'>
506             <xsd:complexType>
507               <xsd:sequence>
508                 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
509                 <xsd:element name='comment' type='xsd:string' minOccurs='0' msdata:Ordinal='2' />
510               </xsd:sequence>
511               <xsd:attribute name='name' type='xsd:string' msdata:Ordinal='1' />
512               <xsd:attribute name='type' type='xsd:string' msdata:Ordinal='3' />
513               <xsd:attribute name='mimetype' type='xsd:string' msdata:Ordinal='4' />
514             </xsd:complexType>
515           </xsd:element>
516           <xsd:element name='resheader'>
517             <xsd:complexType>
518               <xsd:sequence>
519                 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
520               </xsd:sequence>
521               <xsd:attribute name='name' type='xsd:string' use='required' />
522             </xsd:complexType>
523           </xsd:element>
524         </xsd:choice>
525       </xsd:complexType>
526     </xsd:element>
527   </xsd:schema>
528 ".Replace ("'", "\"");
529
530                 #region Public Properties
531                 public string BasePath {
532                         get { return base_path; }
533                         set { base_path = value; }
534                 }
535                 #endregion
536         }
537 }