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;
36 namespace System.Resources
38 public class ResXResourceWriter : IResourceWriter, IDisposable
40 #region Local Variables
41 private string filename;
42 private Stream stream;
43 private TextWriter textwriter;
44 private XmlTextWriter writer;
46 #endregion // Local Variables
49 public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64";
50 public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64";
51 public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType;
52 public static readonly string ResMimeType = "text/microsoft-resx";
53 public static readonly string ResourceSchema = schema;
54 public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64";
55 public static readonly string Version = "1.3";
56 #endregion // Static Fields
58 #region Constructors & Destructor
59 public ResXResourceWriter (Stream stream)
62 throw new ArgumentNullException ("stream");
64 if (stream.CanWrite == false)
65 throw new ArgumentException ("stream is not writable.", "stream");
70 public ResXResourceWriter (TextWriter textwriter)
72 if (textwriter == null)
73 throw new ArgumentNullException ("textwriter");
75 this.textwriter = textwriter;
78 public ResXResourceWriter (string fileName)
81 throw new ArgumentNullException ("fileName");
83 this.filename = fileName;
86 ~ResXResourceWriter() {
89 #endregion // Constructors & Destructor
94 stream = File.OpenWrite (filename);
95 if (textwriter == null)
96 textwriter = new StreamWriter (stream, Encoding.UTF8);
98 writer = new XmlTextWriter (textwriter);
99 writer.Formatting = Formatting.Indented;
100 writer.WriteStartDocument ();
101 writer.WriteStartElement ("root");
102 writer.WriteRaw (schema);
103 WriteHeader ("resmimetype", "text/microsoft-resx");
104 WriteHeader ("version", "1.3");
105 WriteHeader ("reader", typeof (ResXResourceReader).AssemblyQualifiedName);
106 WriteHeader ("writer", typeof (ResXResourceWriter).AssemblyQualifiedName);
109 void WriteHeader (string name, string value)
111 writer.WriteStartElement ("resheader");
112 writer.WriteAttributeString ("name", name);
113 writer.WriteStartElement ("value");
114 writer.WriteString (value);
115 writer.WriteEndElement ();
116 writer.WriteEndElement ();
119 void WriteNiceBase64(byte[] value, int offset, int length) {
126 b64 = Convert.ToBase64String(value, offset, length);
128 // Wild guess; two extra newlines, and one newline/tab pair for every 80 chars
129 sb = new StringBuilder(b64, b64.Length + ((b64.Length + 160) / 80) * 3);
131 inc = 80 + Environment.NewLine.Length + 1;
132 ins = Environment.NewLine + "\t";
133 while (pos < sb.Length) {
137 sb.Insert(sb.Length, Environment.NewLine);
138 writer.WriteString(sb.ToString());
140 void WriteBytes (string name, Type type, byte[] value, int offset, int length)
142 WriteBytes (name, type, value, offset, length, String.Empty);
145 void WriteBytes (string name, Type type, byte[] value, int offset, int length, string comment)
147 writer.WriteStartElement ("data");
148 writer.WriteAttributeString ("name", name);
151 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
152 // byte[] should never get a mimetype, otherwise MS.NET won't be able
153 // to parse the data.
154 if (type != typeof (byte[]))
155 writer.WriteAttributeString ("mimetype", ByteArraySerializedObjectMimeType);
156 writer.WriteStartElement ("value");
157 WriteNiceBase64 (value, offset, length);
159 writer.WriteAttributeString ("mimetype", BinSerializedObjectMimeType);
160 writer.WriteStartElement ("value");
161 writer.WriteBase64 (value, offset, length);
164 writer.WriteEndElement ();
166 if (!(comment == null || comment.Equals (String.Empty))) {
167 writer.WriteStartElement ("comment");
168 writer.WriteString (comment);
169 writer.WriteEndElement ();
172 writer.WriteEndElement ();
175 void WriteBytes (string name, Type type, byte [] value)
177 WriteBytes (name, type, value, 0, value.Length);
180 void WriteString (string name, string value)
182 WriteString (name, value, null);
184 void WriteString (string name, string value, Type type)
186 WriteString (name, value, type, String.Empty);
188 void WriteString (string name, string value, Type type, string comment)
190 writer.WriteStartElement ("data");
191 writer.WriteAttributeString ("name", name);
193 writer.WriteAttributeString ("type", type.AssemblyQualifiedName);
194 writer.WriteStartElement ("value");
195 writer.WriteString (value);
196 writer.WriteEndElement ();
197 if (!(comment == null || comment.Equals (String.Empty))) {
198 writer.WriteStartElement ("comment");
199 writer.WriteString (comment);
200 writer.WriteEndElement ();
202 writer.WriteEndElement ();
203 writer.WriteWhitespace ("\n ");
206 public void AddResource (string name, byte [] value)
209 throw new ArgumentNullException ("name");
212 throw new ArgumentNullException ("value");
215 throw new InvalidOperationException ("The resource is already generated.");
220 WriteBytes (name, value.GetType (), value);
223 public void AddResource (string name, object value)
225 AddResource (name, value, String.Empty);
228 private void AddResource (string name, object value, string comment)
230 if (value is string) {
231 AddResource (name, (string) value);
235 if (value is byte[]) {
236 AddResource (name, (byte[]) value);
241 throw new ArgumentNullException ("name");
244 throw new ArgumentNullException ("value");
246 if (!value.GetType ().IsSerializable)
247 throw new InvalidOperationException (String.Format ("The element '{0}' of type '{1}' is not serializable.", name, value.GetType ().Name));
250 throw new InvalidOperationException ("The resource is already generated.");
255 TypeConverter converter = TypeDescriptor.GetConverter (value);
256 if (converter != null && converter.CanConvertTo (typeof (string)) && converter.CanConvertFrom (typeof (string))) {
257 string str = (string) converter.ConvertToInvariantString (value);
258 WriteString (name, str, value.GetType ());
262 if (converter != null && converter.CanConvertTo (typeof (byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
263 byte[] b = (byte[]) converter.ConvertTo (value, typeof (byte[]));
264 WriteBytes (name, value.GetType (), b);
268 MemoryStream ms = new MemoryStream ();
269 BinaryFormatter fmt = new BinaryFormatter ();
271 fmt.Serialize (ms, value);
272 } catch (Exception e) {
273 throw new InvalidOperationException ("Cannot add a " + value.GetType () +
274 "because it cannot be serialized: " +
278 WriteBytes (name, null, ms.GetBuffer (), 0, (int) ms.Length, comment);
282 public void AddResource (string name, string value)
285 throw new ArgumentNullException ("name");
288 throw new ArgumentNullException ("value");
291 throw new InvalidOperationException ("The resource is already generated.");
296 WriteString (name, value);
300 public void AddResource (ResXDataNode node)
302 AddResource (node.Name, node.Value, node.Comment);
312 if (writer != null) {
320 public virtual void Dispose ()
323 GC.SuppressFinalize(this);
326 public void Generate ()
329 throw new InvalidOperationException ("The resource is already generated.");
332 writer.WriteEndElement ();
336 protected virtual void Dispose(bool disposing) {
342 static string schema = @"
343 <xsd:schema id='root' xmlns='' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
344 <xsd:element name='root' msdata:IsDataSet='true'>
346 <xsd:choice maxOccurs='unbounded'>
347 <xsd:element name='data'>
350 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
351 <xsd:element name='comment' type='xsd:string' minOccurs='0' msdata:Ordinal='2' />
353 <xsd:attribute name='name' type='xsd:string' msdata:Ordinal='1' />
354 <xsd:attribute name='type' type='xsd:string' msdata:Ordinal='3' />
355 <xsd:attribute name='mimetype' type='xsd:string' msdata:Ordinal='4' />
358 <xsd:element name='resheader'>
361 <xsd:element name='value' type='xsd:string' minOccurs='0' msdata:Ordinal='1' />
363 <xsd:attribute name='name' type='xsd:string' use='required' />
370 ".Replace ("'", "\"");