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;
53 private string base_path;
55 #endregion // Local Variables
58 public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64";
59 public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64";
60 public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType;
61 public static readonly string ResMimeType = "text/microsoft-resx";
62 public static readonly string ResourceSchema = schema;
63 public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64";
65 public static readonly string Version = "2.0";
67 public static readonly string Version = "1.3";
69 #endregion // Static Fields
71 #region Constructors & Destructor
72 public ResXResourceWriter (Stream stream)
75 throw new ArgumentNullException ("stream");
78 throw new ArgumentException ("stream is not writable.", "stream");
83 public ResXResourceWriter (TextWriter textWriter)
85 if (textWriter == null)
86 throw new ArgumentNullException ("textWriter");
88 this.textwriter = textWriter;
91 public ResXResourceWriter (string fileName)
94 throw new ArgumentNullException ("fileName");
96 this.filename = fileName;
99 ~ResXResourceWriter() {
102 #endregion // Constructors & Destructor
106 if (filename != null)
107 stream = File.OpenWrite (filename);
108 if (textwriter == null)
109 textwriter = new StreamWriter (stream, Encoding.UTF8);
111 writer = new XmlTextWriter (textwriter);
112 writer.Formatting = Formatting.Indented;
113 writer.WriteStartDocument ();
114 writer.WriteStartElement ("root");
115 writer.WriteRaw (schema);
116 WriteHeader ("resmimetype", "text/microsoft-resx");
117 WriteHeader ("version", "1.3");
118 WriteHeader ("reader", typeof (ResXResourceReader).AssemblyQualifiedName);
119 WriteHeader ("writer", typeof (ResXResourceWriter).AssemblyQualifiedName);
122 void WriteHeader (string name, string value)
124 writer.WriteStartElement ("resheader");
125 writer.WriteAttributeString ("name", name);
126 writer.WriteStartElement ("value");
127 writer.WriteString (value);
128 writer.WriteEndElement ();
129 writer.WriteEndElement ();
132 void WriteNiceBase64(byte[] value, int offset, int length) {
139 b64 = Convert.ToBase64String(value, offset, length);
141 // Wild guess; two extra newlines, and one newline/tab pair for every 80 chars
142 sb = new StringBuilder(b64, b64.Length + ((b64.Length + 160) / 80) * 3);
144 inc = 80 + Environment.NewLine.Length + 1;
145 ins = Environment.NewLine + "\t";
146 while (pos < sb.Length) {
150 sb.Insert(sb.Length, Environment.NewLine);
151 writer.WriteString(sb.ToString());
153 void WriteBytes (string name, Type type, byte[] value, int offset, int length)
155 WriteBytes (name, type, value, offset, length, String.Empty);
158 void WriteBytes (string name, Type type, byte[] value, int offset, int length, string comment)
160 writer.WriteStartElement ("data");
161 writer.WriteAttributeString ("name", name);
164 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
165 // byte[] should never get a mimetype, otherwise MS.NET won't be able
166 // to parse the data.
167 if (type != typeof (byte[]))
168 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
169 writer.WriteStartElement ("value");
170 WriteNiceBase64 (value, offset, length);
172 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
173 writer.WriteStartElement ("value");
174 writer.WriteBase64 (value, offset, length);
177 writer.WriteEndElement ();
179 if (!(comment == null || comment.Equals (String.Empty))) {
180 writer.WriteStartElement ("comment");
181 writer.WriteString (comment);
182 writer.WriteEndElement ();
185 writer.WriteEndElement ();
188 void WriteBytes (string name, Type type, byte [] value)
190 WriteBytes (name, type, value, 0, value.Length);
193 void WriteString (string name, string value)
195 WriteString (name, value, null);
197 void WriteString (string name, string value, Type type)
199 WriteString (name, value, type, String.Empty);
201 void WriteString (string name, string value, Type type, string comment)
203 writer.WriteStartElement ("data");
204 writer.WriteAttributeString ("name", name);
206 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
207 writer.WriteStartElement ("value");
208 writer.WriteString (value);
209 writer.WriteEndElement ();
210 if (!(comment == null || comment.Equals (String.Empty))) {
211 writer.WriteStartElement ("comment");
212 writer.WriteString (comment);
213 writer.WriteEndElement ();
215 writer.WriteEndElement ();
216 writer.WriteWhitespace ("\n ");
219 public void AddResource (string name, byte [] value)
222 throw new ArgumentNullException ("name");
225 throw new ArgumentNullException ("value");
228 throw new InvalidOperationException ("The resource is already generated.");
233 WriteBytes (name, value.GetType (), value);
236 public void AddResource (string name, object value)
238 AddResource (name, value, String.Empty);
241 private void AddResource (string name, object value, string comment)
243 if (value is string) {
244 AddResource (name, (string) value, comment);
248 if (value is byte[]) {
249 AddResource (name, (byte[]) value);
254 throw new ArgumentNullException ("name");
257 throw new ArgumentNullException ("value");
259 if (!value.GetType ().IsSerializable)
260 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
263 throw new InvalidOperationException ("The resource is already generated.");
268 TypeConverter converter = TypeDescriptor.GetConverter (value);
269 if (converter != null && converter.CanConvertTo (typeof (string)) && converter.CanConvertFrom (typeof (string))) {
270 string str = (string) converter.ConvertToInvariantString (value);
271 WriteString (name, str, value.GetType ());
275 if (converter != null && converter.CanConvertTo (typeof (byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
276 byte[] b = (byte[]) converter.ConvertTo (value, typeof (byte[]));
277 WriteBytes (name, value.GetType (), b);
281 MemoryStream ms = new MemoryStream ();
282 BinaryFormatter fmt = new BinaryFormatter ();
284 fmt.Serialize (ms, value);
285 } catch (Exception e) {
286 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
287 "because it cannot be serialized: " +
291 WriteBytes (name, null, ms.GetBuffer (), 0, (int) ms.Length, comment);
295 public void AddResource (string name, string value)
297 AddResource (name, value, string.Empty);
300 private void AddResource (string name, string value, string comment)
303 throw new ArgumentNullException ("name");
306 throw new ArgumentNullException ("value");
309 throw new InvalidOperationException ("The resource is already generated.");
314 WriteString (name, value, null, comment);
318 [MonoTODO ("Stub, not implemented")]
319 public virtual void AddAlias (string aliasName, AssemblyName assemblyName)
323 public void AddResource (ResXDataNode node)
325 AddResource (node.Name, node.Value, node.Comment);
328 public void AddMetadata (string name, string value)
331 throw new ArgumentNullException ("name");
334 throw new ArgumentNullException ("value");
337 throw new InvalidOperationException ("The resource is already generated.");
342 writer.WriteStartElement ("metadata");
343 writer.WriteAttributeString ("name", name);
344 writer.WriteAttributeString ("xml:space", "preserve");
346 writer.WriteElementString ("value", value);
348 writer.WriteEndElement ();
351 public void AddMetadata (string name, byte[] value)
354 throw new ArgumentNullException ("name");
357 throw new ArgumentNullException ("value");
360 throw new InvalidOperationException ("The resource is already generated.");
365 writer.WriteStartElement ("metadata");
366 writer.WriteAttributeString ("name", name);
368 writer.WriteAttributeString ("type", value.GetType ().AssemblyQualifiedName);
370 writer.WriteStartElement ("value");
371 WriteNiceBase64 (value, 0, value.Length);
372 writer.WriteEndElement ();
374 writer.WriteEndElement ();
377 public void AddMetadata (string name, object value)
379 if (value is string) {
380 AddMetadata (name, (string)value);
384 if (value is byte[]) {
385 AddMetadata (name, (byte[])value);
390 throw new ArgumentNullException ("name");
393 throw new ArgumentNullException ("value");
395 if (!value.GetType ().IsSerializable)
396 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
399 throw new InvalidOperationException ("The resource is already generated.");
404 Type type = value.GetType ();
406 TypeConverter converter = TypeDescriptor.GetConverter (value);
407 if (converter != null && converter.CanConvertTo (typeof (string)) && converter.CanConvertFrom (typeof (string))) {
408 string str = (string)converter.ConvertToInvariantString (value);
409 writer.WriteStartElement ("metadata");
410 writer.WriteAttributeString ("name", name);
412 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
413 writer.WriteStartElement ("value");
414 writer.WriteString (str);
415 writer.WriteEndElement ();
416 writer.WriteEndElement ();
417 writer.WriteWhitespace ("\n ");
421 if (converter != null && converter.CanConvertTo (typeof (byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
422 byte[] b = (byte[])converter.ConvertTo (value, typeof (byte[]));
423 writer.WriteStartElement ("metadata");
424 writer.WriteAttributeString ("name", name);
427 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
428 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
429 writer.WriteStartElement ("value");
430 WriteNiceBase64 (b, 0, b.Length);
432 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
433 writer.WriteStartElement ("value");
434 writer.WriteBase64 (b, 0, b.Length);
437 writer.WriteEndElement ();
438 writer.WriteEndElement ();
442 MemoryStream ms = new MemoryStream ();
443 BinaryFormatter fmt = new BinaryFormatter ();
445 fmt.Serialize (ms, value);
446 } catch (Exception e) {
447 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
448 "because it cannot be serialized: " +
452 writer.WriteStartElement ("metadata");
453 writer.WriteAttributeString ("name", name);
456 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
457 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
458 writer.WriteStartElement ("value");
459 WriteNiceBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
461 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
462 writer.WriteStartElement ("value");
463 writer.WriteBase64 (ms.GetBuffer (), 0, ms.GetBuffer ().Length);
466 writer.WriteEndElement ();
467 writer.WriteEndElement ();
478 if (writer != null) {
486 public virtual void Dispose ()
489 GC.SuppressFinalize(this);
492 public void Generate ()
495 throw new InvalidOperationException ("The resource is already generated.");
498 writer.WriteEndElement ();
502 protected virtual void Dispose (bool disposing)
508 static string schema = @"
509 <xsd:schema id='root' xmlns='' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
510 <xsd:element name='root' msdata:IsDataSet='true'>
512 <xsd:choice maxOccurs='unbounded'>
513 <xsd:element name='data'>
516 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
517 <xsd:element name='comment' type='xsd:string' minOccurs='0' msdata:Ordinal='2' />
519 <xsd:attribute name='name' type='xsd:string' msdata:Ordinal='1' />
520 <xsd:attribute name='type' type='xsd:string' msdata:Ordinal='3' />
521 <xsd:attribute name='mimetype' type='xsd:string' msdata:Ordinal='4' />
524 <xsd:element name='resheader'>
527 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
529 <xsd:attribute name='name' type='xsd:string' use='required' />
536 ".Replace ("'", "\"");
538 #region Public Properties
540 public string BasePath {
541 get { return base_path; }
542 set { base_path = value; }