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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // Duncan Mak duncan@ximian.com
24 // Gonzalo Paniagua Javier gonzalo@ximian.com
25 // Peter Bartok pbartok@novell.com
30 using System.ComponentModel;
32 using System.Runtime.Serialization.Formatters.Binary;
35 using System.Reflection;
37 namespace System.Resources
44 class ResXResourceWriter : IResourceWriter, IDisposable
46 #region Local Variables
47 private string filename;
48 private Stream stream;
49 private TextWriter textwriter;
50 private XmlTextWriter writer;
52 private string base_path;
53 #endregion // Local Variables
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
65 #region Constructors & Destructor
66 public ResXResourceWriter (Stream stream)
69 throw new ArgumentNullException ("stream");
72 throw new ArgumentException ("stream is not writable.", "stream");
77 public ResXResourceWriter (TextWriter textWriter)
79 if (textWriter == null)
80 throw new ArgumentNullException ("textWriter");
82 this.textwriter = textWriter;
85 public ResXResourceWriter (string fileName)
88 throw new ArgumentNullException ("fileName");
90 this.filename = fileName;
93 ~ResXResourceWriter() {
96 #endregion // Constructors & Destructor
100 if (filename != null)
101 stream = File.OpenWrite (filename);
102 if (textwriter == null)
103 textwriter = new StreamWriter (stream, Encoding.UTF8);
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);
116 void WriteHeader (string name, string value)
118 writer.WriteStartElement ("resheader");
119 writer.WriteAttributeString ("name", name);
120 writer.WriteStartElement ("value");
121 writer.WriteString (value);
122 writer.WriteEndElement ();
123 writer.WriteEndElement ();
126 void WriteNiceBase64(byte[] value, int offset, int length) {
133 b64 = Convert.ToBase64String(value, offset, length);
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);
138 inc = 80 + Environment.NewLine.Length + 1;
139 ins = Environment.NewLine + "\t";
140 while (pos < sb.Length) {
144 sb.Insert(sb.Length, Environment.NewLine);
145 writer.WriteString(sb.ToString());
147 void WriteBytes (string name, Type type, byte[] value, int offset, int length)
149 WriteBytes (name, type, value, offset, length, String.Empty);
152 void WriteBytes (string name, Type type, byte[] value, int offset, int length, string comment)
154 writer.WriteStartElement ("data");
155 writer.WriteAttributeString ("name", name);
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);
166 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
167 writer.WriteStartElement ("value");
168 writer.WriteBase64 (value, offset, length);
171 writer.WriteEndElement ();
173 if (!(comment == null || comment.Equals (String.Empty))) {
174 writer.WriteStartElement ("comment");
175 writer.WriteString (comment);
176 writer.WriteEndElement ();
179 writer.WriteEndElement ();
182 void WriteBytes (string name, Type type, byte [] value)
184 WriteBytes (name, type, value, 0, value.Length);
187 void WriteString (string name, string value)
189 WriteString (name, value, null);
191 void WriteString (string name, string value, Type type)
193 WriteString (name, value, type, String.Empty);
195 void WriteString (string name, string value, Type type, string comment)
197 writer.WriteStartElement ("data");
198 writer.WriteAttributeString ("name", name);
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 ();
209 writer.WriteEndElement ();
210 writer.WriteWhitespace ("\n ");
213 public void AddResource (string name, byte [] value)
216 throw new ArgumentNullException ("name");
219 throw new ArgumentNullException ("value");
222 throw new InvalidOperationException ("The resource is already generated.");
227 WriteBytes (name, value.GetType (), value);
230 public void AddResource (string name, object value)
232 AddResource (name, value, String.Empty);
235 private void AddResource (string name, object value, string comment)
237 if (value is string) {
238 AddResource (name, (string) value, comment);
242 if (value is byte[]) {
243 AddResource (name, (byte[]) value);
248 throw new ArgumentNullException ("name");
251 throw new ArgumentNullException ("value");
253 if (!value.GetType ().IsSerializable)
254 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
257 throw new InvalidOperationException ("The resource is already generated.");
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 ());
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);
275 MemoryStream ms = new MemoryStream ();
276 BinaryFormatter fmt = new BinaryFormatter ();
278 fmt.Serialize (ms, value);
279 } catch (Exception e) {
280 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
281 "because it cannot be serialized: " +
285 WriteBytes (name, null, ms.GetBuffer (), 0, (int) ms.Length, comment);
289 public void AddResource (string name, string value)
291 AddResource (name, value, string.Empty);
294 private void AddResource (string name, string value, string comment)
297 throw new ArgumentNullException ("name");
300 throw new ArgumentNullException ("value");
303 throw new InvalidOperationException ("The resource is already generated.");
308 WriteString (name, value, null, comment);
311 [MonoTODO ("Stub, not implemented")]
312 public virtual void AddAlias (string aliasName, AssemblyName assemblyName)
316 public void AddResource (ResXDataNode node)
318 AddResource (node.Name, node.Value, node.Comment);
321 public void AddMetadata (string name, string value)
324 throw new ArgumentNullException ("name");
327 throw new ArgumentNullException ("value");
330 throw new InvalidOperationException ("The resource is already generated.");
335 writer.WriteStartElement ("metadata");
336 writer.WriteAttributeString ("name", name);
337 writer.WriteAttributeString ("xml:space", "preserve");
339 writer.WriteElementString ("value", value);
341 writer.WriteEndElement ();
344 public void AddMetadata (string name, byte[] value)
347 throw new ArgumentNullException ("name");
350 throw new ArgumentNullException ("value");
353 throw new InvalidOperationException ("The resource is already generated.");
358 writer.WriteStartElement ("metadata");
359 writer.WriteAttributeString ("name", name);
361 writer.WriteAttributeString ("type", value.GetType ().AssemblyQualifiedName);
363 writer.WriteStartElement ("value");
364 WriteNiceBase64 (value, 0, value.Length);
365 writer.WriteEndElement ();
367 writer.WriteEndElement ();
370 public void AddMetadata (string name, object value)
372 if (value is string) {
373 AddMetadata (name, (string)value);
377 if (value is byte[]) {
378 AddMetadata (name, (byte[])value);
383 throw new ArgumentNullException ("name");
386 throw new ArgumentNullException ("value");
388 if (!value.GetType ().IsSerializable)
389 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
392 throw new InvalidOperationException ("The resource is already generated.");
397 Type type = value.GetType ();
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);
405 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
406 writer.WriteStartElement ("value");
407 writer.WriteString (str);
408 writer.WriteEndElement ();
409 writer.WriteEndElement ();
410 writer.WriteWhitespace ("\n ");
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);
420 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
421 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
422 writer.WriteStartElement ("value");
423 WriteNiceBase64 (b, 0, b.Length);
425 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
426 writer.WriteStartElement ("value");
427 writer.WriteBase64 (b, 0, b.Length);
430 writer.WriteEndElement ();
431 writer.WriteEndElement ();
435 MemoryStream ms = new MemoryStream ();
436 BinaryFormatter fmt = new BinaryFormatter ();
438 fmt.Serialize (ms, value);
439 } catch (Exception e) {
440 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
441 "because it cannot be serialized: " +
445 writer.WriteStartElement ("metadata");
446 writer.WriteAttributeString ("name", name);
449 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
450 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
451 writer.WriteStartElement ("value");
452 WriteNiceBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
454 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
455 writer.WriteStartElement ("value");
456 writer.WriteBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
459 writer.WriteEndElement ();
460 writer.WriteEndElement ();
470 if (writer != null) {
478 public virtual void Dispose ()
481 GC.SuppressFinalize(this);
484 public void Generate ()
487 throw new InvalidOperationException ("The resource is already generated.");
490 writer.WriteEndElement ();
494 protected virtual void Dispose (bool disposing)
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'>
504 <xsd:choice maxOccurs='unbounded'>
505 <xsd:element name='data'>
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' />
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' />
516 <xsd:element name='resheader'>
519 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
521 <xsd:attribute name='name' type='xsd:string' use='required' />
528 ".Replace ("'", "\"");
530 #region Public Properties
531 public string BasePath {
532 get { return base_path; }
533 set { base_path = value; }