| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- //
- // System.Resources.ResourceWriter.cs
- //
- // Authors:
- // Duncan Mak <[email protected]>
- // Dick Porter <[email protected]>
- //
- // (C) 2001, 2002 Ximian, Inc. http://www.ximian.com
- //
- using System.IO;
- using System.Collections;
- using System.Text;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- namespace System.Resources
- {
- public sealed class ResourceWriter : IResourceWriter, IDisposable
- {
- Hashtable resources;
- Stream stream;
-
- public ResourceWriter (Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException ("stream is null");
- if (stream.CanWrite == false)
- throw new ArgumentException ("stream is not writable.");
- this.stream=stream;
- resources=new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
- }
-
- public ResourceWriter (String fileName)
- {
- if (fileName == null)
- throw new ArgumentNullException ("fileName is null.");
- stream=new FileStream(fileName, FileMode.Create, FileAccess.Write);
- resources=new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
- }
-
- public void AddResource (string name, byte[] value)
- {
- if (name == null) {
- throw new ArgumentNullException ("name is null");
- }
- if (value == null) {
- throw new ArgumentNullException ("value is null");
- }
- if(resources==null) {
- throw new InvalidOperationException ("ResourceWriter has been closed");
- }
- if(resources[name]!=null) {
- throw new ArgumentException ("Resource already present: " + name);
- }
- resources.Add(name, value);
- }
-
- public void AddResource (string name, object value)
- {
- if (name == null) {
- throw new ArgumentNullException ("name is null");
- }
- if (value == null) {
- throw new ArgumentNullException ("value is null");
- }
- if(resources==null) {
- throw new InvalidOperationException ("ResourceWriter has been closed");
- }
- if(resources[name]!=null) {
- throw new ArgumentException ("Resource already present: " + name);
- }
- resources.Add(name, value);
- }
-
- public void AddResource (string name, string value)
- {
- if (name == null) {
- throw new ArgumentNullException ("name is null");
- }
- if (value == null) {
- throw new ArgumentNullException ("value is null");
- }
- if(resources==null) {
- throw new InvalidOperationException ("ResourceWriter has been closed");
- }
- if(resources[name]!=null) {
- throw new ArgumentException ("Resource already present: " + name);
- }
- resources.Add(name, value);
- }
- public void Close () {
- Dispose(true);
- }
-
- public void Dispose ()
- {
- Dispose(true);
- }
- private void Dispose (bool disposing)
- {
- if(disposing) {
- if(resources.Count>0 && generated==false) {
- Generate();
- }
- if(stream!=null) {
- stream.Close();
- }
- }
- resources=null;
- stream=null;
- }
-
- private bool generated=false;
-
- public void Generate () {
- BinaryWriter writer;
- IFormatter formatter;
- if(resources==null) {
- throw new InvalidOperationException ("ResourceWriter has been closed");
- }
- if(generated) {
- throw new InvalidOperationException ("ResourceWriter can only Generate() once");
- }
- generated=true;
-
- writer=new BinaryWriter(stream, Encoding.UTF8);
- formatter=new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
- /* The ResourceManager header */
-
- writer.Write(ResourceManager.MagicNumber);
- writer.Write(ResourceManager.HeaderVersionNumber);
-
- /* Build the rest of the ResourceManager
- * header in memory, because we need to know
- * how long it is in advance
- */
- MemoryStream resman_stream=new MemoryStream();
- BinaryWriter resman=new BinaryWriter(resman_stream,
- Encoding.UTF8);
- resman.Write(typeof(ResourceReader).AssemblyQualifiedName);
- resman.Write(typeof(ResourceSet).AssemblyQualifiedName);
- /* Only space for 32 bits of header len in the
- * resource file format
- */
- int resman_len=(int)resman_stream.Length;
- writer.Write(resman_len);
- writer.Write(resman_stream.GetBuffer(), 0, resman_len);
- /* We need to build the ResourceReader name
- * and data sections simultaneously
- */
- MemoryStream res_name_stream=new MemoryStream();
- BinaryWriter res_name=new BinaryWriter(res_name_stream, Encoding.Unicode);
- MemoryStream res_data_stream=new MemoryStream();
- BinaryWriter res_data=new BinaryWriter(res_data_stream,
- Encoding.UTF8);
- /* Not sure if this is the best collection to
- * use, I just want an unordered list of
- * objects with fast lookup, but without
- * needing a key. (I suppose a hashtable with
- * key==value would work, but I need to find
- * the index of each item later)
- */
- ArrayList types=new ArrayList();
- int[] hashes=new int[resources.Count];
- int[] name_offsets=new int[resources.Count];
- int count=0;
-
- IDictionaryEnumerator res_enum=resources.GetEnumerator();
- while(res_enum.MoveNext()) {
- Type type=res_enum.Value.GetType();
- /* Keep a list of unique types */
- if(!types.Contains(type)) {
- types.Add(type);
- }
- /* Hash the name */
- hashes[count]=GetHash((string)res_enum.Key);
- /* Record the offsets */
- name_offsets[count]=(int)res_name.BaseStream.Position;
- /* Write the name section */
- res_name.Write((string)res_enum.Key);
- res_name.Write((int)res_data.BaseStream.Position);
- /* Write the data section */
- Write7BitEncodedInt(res_data, types.IndexOf(type));
- /* Strangely, Char is serialized
- * rather than just written out
- */
- if(type==typeof(Byte)) {
- res_data.Write((Byte)res_enum.Value);
- } else if (type==typeof(Decimal)) {
- res_data.Write((Decimal)res_enum.Value);
- } else if (type==typeof(DateTime)) {
- res_data.Write(((DateTime)res_enum.Value).Ticks);
- } else if (type==typeof(Double)) {
- res_data.Write((Double)res_enum.Value);
- } else if (type==typeof(Int16)) {
- res_data.Write((Int16)res_enum.Value);
- } else if (type==typeof(Int32)) {
- res_data.Write((Int32)res_enum.Value);
- } else if (type==typeof(Int64)) {
- res_data.Write((Int64)res_enum.Value);
- } else if (type==typeof(SByte)) {
- res_data.Write((SByte)res_enum.Value);
- } else if (type==typeof(Single)) {
- res_data.Write((Single)res_enum.Value);
- } else if (type==typeof(String)) {
- res_data.Write((String)res_enum.Value);
- } else if (type==typeof(TimeSpan)) {
- res_data.Write(((TimeSpan)res_enum.Value).Ticks);
- } else if (type==typeof(UInt16)) {
- res_data.Write((UInt16)res_enum.Value);
- } else if (type==typeof(UInt32)) {
- res_data.Write((UInt32)res_enum.Value);
- } else if (type==typeof(UInt64)) {
- res_data.Write((UInt64)res_enum.Value);
- } else {
- /* non-intrinsic types are
- * serialized
- */
- formatter.Serialize(res_data.BaseStream, res_enum.Value);
- }
- count++;
- }
- /* Sort the hashes, keep the name offsets
- * matching up
- */
- Array.Sort(hashes, name_offsets);
-
- /* now do the ResourceReader header */
- writer.Write(1);
- writer.Write(resources.Count);
- writer.Write(types.Count);
- /* Write all of the unique types */
- foreach(Type type in types) {
- writer.Write(type.AssemblyQualifiedName);
- }
- /* Pad the next fields (hash values) on an 8
- * byte boundary, using the letters "PAD"
- */
- int pad_align=(int)(writer.BaseStream.Position & 7);
- int pad_chars=0;
- if(pad_align!=0) {
- pad_chars=8-pad_align;
- }
- for(int i=0; i<pad_chars; i++) {
- writer.Write((byte)"PAD"[i%3]);
- }
- /* Write the hashes */
- for(int i=0; i<resources.Count; i++) {
- writer.Write(hashes[i]);
- }
- /* and the name offsets */
- for(int i=0; i<resources.Count; i++) {
- writer.Write(name_offsets[i]);
- }
- /* Write the data section offset */
- int data_offset=(int)writer.BaseStream.Position +
- (int)res_name_stream.Length + 4;
- writer.Write(data_offset);
- /* The name section goes next */
- writer.Write(res_name_stream.GetBuffer(), 0,
- (int)res_name_stream.Length);
- /* The data section is last */
- writer.Write(res_data_stream.GetBuffer(), 0,
- (int)res_data_stream.Length);
- res_name.Close();
- res_data.Close();
- /* Don't close writer, according to the spec */
- writer.Flush();
- }
- private int GetHash(string name)
- {
- uint hash=5381;
- for(int i=0; i<name.Length; i++) {
- hash=((hash<<5)+hash)^name[i];
- }
-
- return((int)hash);
- }
- /* Cut and pasted from BinaryWriter, because it's
- * 'protected' there.
- */
- private void Write7BitEncodedInt(BinaryWriter writer,
- int value)
- {
- do {
- int high = (value >> 7) & 0x01ffffff;
- byte b = (byte)(value & 0x7f);
- if (high != 0) {
- b = (byte)(b | 0x80);
- }
- writer.Write(b);
- value = high;
- } while(value != 0);
- }
- }
- }
|