2 // System.Resources.ResourceWriter.cs
5 // Duncan Mak <duncan@ximian.com>
6 // Dick Porter <dick@ximian.com>
7 // Gert Driesen <drieseng@users.sourceforge.net>
9 // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System.Collections;
38 using System.Runtime.Serialization;
39 using System.Runtime.Serialization.Formatters.Binary;
41 namespace System.Resources
44 [System.Runtime.InteropServices.ComVisible (true)]
46 public sealed class ResourceWriter : IResourceWriter, IDisposable
48 class TypeByNameObject
50 public readonly string TypeName;
51 public readonly byte [] Value;
53 public TypeByNameObject (string typeName, byte [] value)
56 Value = (byte []) value.Clone ();
61 SortedList resources = new SortedList (StringComparer.OrdinalIgnoreCase);
63 Hashtable resources = new Hashtable (CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
67 public ResourceWriter (Stream stream)
70 throw new ArgumentNullException ("stream");
72 throw new ArgumentException ("Stream was not writable.");
77 public ResourceWriter (String fileName)
80 throw new ArgumentNullException ("fileName");
82 stream = new FileStream(fileName, FileMode.Create,
86 public void AddResource (string name, byte[] value)
89 throw new ArgumentNullException ("name");
90 if (resources == null)
91 throw new InvalidOperationException ("The resource writer has already been closed and cannot be edited");
92 if (resources [name] != null)
93 throw new ArgumentException ("Resource already present: " + name);
95 resources.Add(name, value);
98 public void AddResource (string name, object value)
101 throw new ArgumentNullException ("name");
102 if (resources == null)
103 throw new InvalidOperationException ("The resource writer has already been closed and cannot be edited");
104 if (resources[name] != null)
105 throw new ArgumentException ("Resource already present: " + name);
107 resources.Add(name, value);
110 public void AddResource (string name, string value)
113 throw new ArgumentNullException ("name");
114 if (resources == null)
115 throw new InvalidOperationException ("The resource writer has already been closed and cannot be edited");
116 if (resources [name] != null)
117 throw new ArgumentException ("Resource already present: " + name);
119 resources.Add(name, value);
127 public void Dispose ()
132 private void Dispose (bool disposing)
135 if (resources != null)
139 GC.SuppressFinalize (this);
146 public void AddResourceData (string name, string typeName, byte [] serializedData)
149 throw new ArgumentNullException ("name");
150 if (typeName == null)
151 throw new ArgumentNullException ("typeName");
152 if (serializedData == null)
153 throw new ArgumentNullException ("serializedData");
156 AddResource (name, new TypeByNameObject (typeName, serializedData));
160 public void Generate ()
163 IFormatter formatter;
165 if (resources == null)
166 throw new InvalidOperationException ("The resource writer has already been closed and cannot be edited");
168 writer = new BinaryWriter (stream, Encoding.UTF8);
169 formatter = new BinaryFormatter (null, new StreamingContext (StreamingContextStates.File | StreamingContextStates.Persistence));
171 /* The ResourceManager header */
173 writer.Write (ResourceManager.MagicNumber);
174 writer.Write (ResourceManager.HeaderVersionNumber);
176 /* Build the rest of the ResourceManager
177 * header in memory, because we need to know
178 * how long it is in advance
180 MemoryStream resman_stream = new MemoryStream ();
181 BinaryWriter resman = new BinaryWriter (resman_stream,
184 resman.Write (typeof (ResourceReader).AssemblyQualifiedName);
186 resman.Write (typeof (RuntimeResourceSet).FullName);
188 resman.Write (typeof (ResourceSet).AssemblyQualifiedName);
191 /* Only space for 32 bits of header len in the
192 * resource file format
194 int resman_len = (int) resman_stream.Length;
195 writer.Write (resman_len);
196 writer.Write (resman_stream.GetBuffer (), 0, resman_len);
198 /* We need to build the ResourceReader name
199 * and data sections simultaneously
201 MemoryStream res_name_stream = new MemoryStream ();
202 BinaryWriter res_name = new BinaryWriter (res_name_stream, Encoding.Unicode);
204 MemoryStream res_data_stream = new MemoryStream ();
205 BinaryWriter res_data = new BinaryWriter (res_data_stream,
208 /* Not sure if this is the best collection to
209 * use, I just want an unordered list of
210 * objects with fast lookup, but without
211 * needing a key. (I suppose a hashtable with
212 * key==value would work, but I need to find
213 * the index of each item later)
215 ArrayList types = new ArrayList ();
216 int [] hashes = new int [resources.Count];
217 int [] name_offsets = new int [resources.Count];
220 IDictionaryEnumerator res_enum = resources.GetEnumerator ();
221 while (res_enum.MoveNext()) {
223 hashes [count] = GetHash ((string) res_enum.Key);
225 /* Record the offsets */
226 name_offsets [count] = (int) res_name.BaseStream.Position;
228 /* Write the name section */
229 res_name.Write ((string) res_enum.Key);
230 res_name.Write ((int) res_data.BaseStream.Position);
232 if (res_enum.Value == null) {
233 Write7BitEncodedInt (res_data, -1);
237 // implementation note: TypeByNameObject is
238 // not used in 1.x profile.
239 TypeByNameObject tbn = res_enum.Value as TypeByNameObject;
240 Type type = tbn != null ? null : res_enum.Value.GetType();
241 object typeObj = tbn != null ? (object) tbn.TypeName : type;
243 /* Keep a list of unique types */
245 // do not output predefined ones.
246 switch ((type != null && !type.IsEnum) ? Type.GetTypeCode (type) : TypeCode.Empty) {
247 case TypeCode.Decimal:
248 case TypeCode.Single:
249 case TypeCode.Double:
255 case TypeCode.UInt16:
256 case TypeCode.UInt32:
257 case TypeCode.UInt64:
258 case TypeCode.DateTime:
259 case TypeCode.String:
262 if (type == typeof (TimeSpan))
264 if (type == typeof (MemoryStream))
266 if (type==typeof(byte[]))
269 if (!types.Contains (typeObj))
271 /* Write the data section */
272 Write7BitEncodedInt(res_data, (int) PredefinedResourceType.FistCustom + types.IndexOf(typeObj));
276 if (!types.Contains (typeObj))
278 /* Write the data section */
279 Write7BitEncodedInt(res_data, types.IndexOf(type));
282 /* Strangely, Char is serialized
283 * rather than just written out
287 res_data.Write((byte []) tbn.Value);
288 else if (type == typeof (Byte)) {
289 res_data.Write ((byte) PredefinedResourceType.Byte);
290 res_data.Write ((Byte) res_enum.Value);
291 } else if (type == typeof (Decimal)) {
292 res_data.Write ((byte) PredefinedResourceType.Decimal);
293 res_data.Write ((Decimal) res_enum.Value);
294 } else if (type == typeof (DateTime)) {
295 res_data.Write ((byte) PredefinedResourceType.DateTime);
296 res_data.Write (((DateTime) res_enum.Value).Ticks);
297 } else if (type == typeof (Double)) {
298 res_data.Write ((byte) PredefinedResourceType.Double);
299 res_data.Write ((Double) res_enum.Value);
300 } else if (type == typeof (Int16)) {
301 res_data.Write ((byte) PredefinedResourceType.Int16);
302 res_data.Write ((Int16) res_enum.Value);
303 } else if (type == typeof (Int32)) {
304 res_data.Write ((byte) PredefinedResourceType.Int32);
305 res_data.Write ((Int32) res_enum.Value);
306 } else if (type == typeof (Int64)) {
307 res_data.Write ((byte) PredefinedResourceType.Int64);
308 res_data.Write ((Int64) res_enum.Value);
309 } else if (type == typeof (SByte)) {
310 res_data.Write ((byte) PredefinedResourceType.SByte);
311 res_data.Write ((SByte) res_enum.Value);
312 } else if (type == typeof (Single)) {
313 res_data.Write ((byte) PredefinedResourceType.Single);
314 res_data.Write ((Single) res_enum.Value);
315 } else if (type == typeof (String)) {
316 res_data.Write ((byte) PredefinedResourceType.String);
317 res_data.Write ((String) res_enum.Value);
318 } else if (type == typeof (TimeSpan)) {
319 res_data.Write ((byte) PredefinedResourceType.TimeSpan);
320 res_data.Write (((TimeSpan) res_enum.Value).Ticks);
321 } else if (type == typeof (UInt16)) {
322 res_data.Write ((byte) PredefinedResourceType.UInt16);
323 res_data.Write ((UInt16) res_enum.Value);
324 } else if (type == typeof (UInt32)) {
325 res_data.Write ((byte) PredefinedResourceType.UInt32);
326 res_data.Write ((UInt32) res_enum.Value);
327 } else if (type == typeof (UInt64)) {
328 res_data.Write ((byte) PredefinedResourceType.UInt64);
329 res_data.Write ((UInt64) res_enum.Value);
330 } else if (type == typeof (byte[])) {
331 res_data.Write ((byte) PredefinedResourceType.ByteArray);
332 byte [] data = (byte[]) res_enum.Value;
333 res_data.Write ((uint) data.Length);
334 res_data.Write (data, 0, data.Length);
335 } else if (type == typeof (MemoryStream)) {
336 res_data.Write ((byte) PredefinedResourceType.Stream);
337 byte [] data = ((MemoryStream) res_enum.Value).ToArray ();
338 res_data.Write ((uint) data.Length);
339 res_data.Write (data, 0, data.Length);
341 /* non-intrinsic types are
344 formatter.Serialize (res_data.BaseStream, res_enum.Value);
347 if (type == typeof (Byte)) {
348 res_data.Write ((Byte) res_enum.Value);
349 } else if (type == typeof (Decimal)) {
350 res_data.Write ((Decimal) res_enum.Value);
351 } else if (type == typeof (DateTime)) {
352 res_data.Write (((DateTime) res_enum.Value).Ticks);
353 } else if (type == typeof (Double)) {
354 res_data.Write ((Double) res_enum.Value);
355 } else if (type == typeof (Int16)) {
356 res_data.Write ((Int16) res_enum.Value);
357 } else if (type == typeof (Int32)) {
358 res_data.Write ((Int32) res_enum.Value);
359 } else if (type == typeof (Int64)) {
360 res_data.Write ((Int64) res_enum.Value);
361 } else if (type == typeof (SByte)) {
362 res_data.Write ((SByte) res_enum.Value);
363 } else if (type == typeof (Single)) {
364 res_data.Write ((Single) res_enum.Value);
365 } else if (type == typeof (String)) {
366 res_data.Write ((String) res_enum.Value);
367 } else if (type == typeof (TimeSpan)) {
368 res_data.Write (((TimeSpan) res_enum.Value).Ticks);
369 } else if (type == typeof (UInt16)) {
370 res_data.Write ((UInt16) res_enum.Value);
371 } else if (type == typeof (UInt32)) {
372 res_data.Write ((UInt32) res_enum.Value);
373 } else if (type == typeof (UInt64)) {
374 res_data.Write ((UInt64) res_enum.Value);
376 /* non-intrinsic types are
379 formatter.Serialize (res_data.BaseStream, res_enum.Value);
386 /* Sort the hashes, keep the name offsets
389 Array.Sort (hashes, name_offsets);
391 /* now do the ResourceReader header */
398 writer.Write (resources.Count);
399 writer.Write (types.Count);
401 /* Write all of the unique types */
402 foreach (object type in types) {
404 writer.Write(((Type) type).AssemblyQualifiedName);
406 writer.Write((string) type);
409 /* Pad the next fields (hash values) on an 8
410 * byte boundary, using the letters "PAD"
412 int pad_align = (int) (writer.BaseStream.Position & 7);
416 pad_chars = 8 - pad_align;
418 for (int i = 0; i < pad_chars; i++)
419 writer.Write ((byte) "PAD" [i % 3]);
421 /* Write the hashes */
422 for (int i = 0; i < resources.Count; i++)
423 writer.Write (hashes[i]);
425 /* and the name offsets */
426 for (int i = 0; i < resources.Count; i++)
427 writer.Write (name_offsets [i]);
429 /* Write the data section offset */
430 int data_offset= (int) writer.BaseStream.Position +
431 (int) res_name_stream.Length + 4;
432 writer.Write (data_offset);
434 /* The name section goes next */
435 writer.Write (res_name_stream.GetBuffer(), 0,
436 (int) res_name_stream.Length);
437 /* The data section is last */
438 writer.Write (res_data_stream.GetBuffer(), 0,
439 (int) res_data_stream.Length);
444 /* Don't close writer, according to the spec */
447 // ResourceWriter is no longer editable
451 // looks like it is (similar to) DJB hash
452 int GetHash (string name)
456 for (int i=0; i<name.Length; i++)
457 hash = ((hash << 5) + hash) ^ name [i];
462 /* Cut and pasted from BinaryWriter, because it's
465 void Write7BitEncodedInt (BinaryWriter writer, int value)
468 int high = (value >> 7) & 0x01ffffff;
469 byte b = (byte) (value & 0x7f);
472 b = (byte) (b | 0x80);
476 } while (value != 0);
479 internal Stream Stream {