2 // System.Resources.ResourceReader.cs
5 // Duncan Mak <duncan@ximian.com>
6 // Nick Drochak <ndrochak@gol.com>
7 // Dick Porter <dick@ximian.com>
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.
35 using System
.Collections
;
36 using System
.Resources
;
39 using System
.Runtime
.Serialization
;
40 using System
.Runtime
.Serialization
.Formatters
.Binary
;
42 namespace System
.Resources
44 public sealed class ResourceReader
: IResourceReader
, IEnumerable
, IDisposable
48 internal int resourceCount
= 0;
53 int dataSectionOffset
;
54 long nameSectionOffset
;
57 public ResourceReader (Stream stream
)
60 throw new ArgumentNullException ("Value cannot be null.");
63 throw new ArgumentException ("Stream was not readable.");
65 reader
= new BinaryReader(stream
, Encoding
.UTF8
);
66 formatter
= new BinaryFormatter(null, new StreamingContext(StreamingContextStates
.File
|StreamingContextStates
.Persistence
));
71 public ResourceReader (string fileName
)
74 throw new ArgumentNullException ("Path cannot be null.");
76 if (!System
.IO
.File
.Exists (fileName
))
77 throw new FileNotFoundException ("Could not find file " + Path
.GetFullPath(fileName
));
79 reader
= new BinaryReader (new FileStream(fileName
, FileMode
.Open
, FileAccess
.Read
, FileShare
.Read
));
80 formatter
= new BinaryFormatter(null, new StreamingContext(StreamingContextStates
.File
|StreamingContextStates
.Persistence
));
85 /* Read the ResourceManager header and the
86 * ResourceReader header.
88 private void ReadHeaders()
91 int manager_magic
= reader
.ReadInt32();
93 if(manager_magic
!= ResourceManager
.MagicNumber
) {
94 throw new ArgumentException("Stream is not a valid .resources file!");
97 int manager_ver
= reader
.ReadInt32();
98 int manager_len
= reader
.ReadInt32();
100 /* We know how long the header is, even if
101 * the version number is too new
103 if(manager_ver
> ResourceManager
.HeaderVersionNumber
) {
104 reader
.BaseStream
.Seek(manager_len
, SeekOrigin
.Current
);
106 string reader_class
=reader
.ReadString();
107 if(!reader_class
.StartsWith("System.Resources.ResourceReader")) {
108 throw new NotSupportedException("This .resources file requires reader class " + reader_class
);
111 string set_class
=reader
.ReadString();
112 if(!set_class
.StartsWith(typeof(ResourceSet
).FullName
) && !set_class
.StartsWith("System.Resources.RuntimeResourceSet")) {
113 throw new NotSupportedException("This .resources file requires set class " + set_class
);
117 /* Now read the ResourceReader header */
118 int reader_ver
= reader
.ReadInt32();
120 if(reader_ver
!= 1) {
121 throw new NotSupportedException("This .resources file requires unsupported set class version: " + reader_ver
.ToString());
124 resourceCount
= reader
.ReadInt32();
125 typeCount
= reader
.ReadInt32();
127 types
=new Type
[typeCount
];
128 for(int i
=0; i
<typeCount
; i
++) {
129 string type_name
=reader
.ReadString();
131 /* FIXME: Should we ask for
132 * type loading exceptions
135 types
[i
]=Type
.GetType(type_name
);
137 throw new ArgumentException("Could not load type {0}", type_name
);
141 /* There are between 0 and 7 bytes of
142 * padding here, consisting of the
143 * letters PAD. The next item (Hash
144 * values for each resource name) need
145 * to be aligned on an 8-byte
149 int pad_align
=(int)(reader
.BaseStream
.Position
& 7);
153 pad_chars
=8-pad_align
;
156 for(int i
=0; i
<pad_chars
; i
++) {
157 byte pad_byte
=reader
.ReadByte();
158 if(pad_byte
!="PAD"[i
%3]) {
159 throw new ArgumentException("Malformed .resources file (padding values incorrect)");
163 /* Read in the hash values for each
164 * resource name. These can be used
165 * by ResourceSet (calling internal
166 * methods) to do a fast compare on
167 * resource names without doing
168 * expensive string compares (but we
169 * dont do that yet, so far we only
170 * implement the Enumerator interface)
172 hashes
=new int[resourceCount
];
173 for(int i
=0; i
<resourceCount
; i
++) {
174 hashes
[i
]=reader
.ReadInt32();
177 /* Read in the virtual offsets for
180 positions
=new long[resourceCount
];
181 for(int i
=0; i
<resourceCount
; i
++) {
182 positions
[i
]=reader
.ReadInt32();
185 dataSectionOffset
= reader
.ReadInt32();
186 nameSectionOffset
= reader
.BaseStream
.Position
;
187 } catch(EndOfStreamException e
) {
188 throw new ArgumentException("Stream is not a valied .resources file! It was possibly truncated.", e
);
192 /* Cut and pasted from BinaryReader, because it's
195 private int Read7BitEncodedInt() {
201 b
= reader
.ReadByte();
203 ret
= ret
| ((b
& 0x7f) << shift
);
205 } while ((b
& 0x80) == 0x80);
210 private string ResourceName(int index
)
214 long pos
=positions
[index
]+nameSectionOffset
;
215 reader
.BaseStream
.Seek(pos
, SeekOrigin
.Begin
);
217 /* Read a 7-bit encoded byte length field */
218 int len
=Read7BitEncodedInt();
219 byte[] str
=new byte[len
];
221 reader
.Read(str
, 0, len
);
222 return Encoding
.Unicode
.GetString(str
);
226 private object ResourceValue(int index
)
230 long pos
=positions
[index
]+nameSectionOffset
;
231 reader
.BaseStream
.Seek(pos
, SeekOrigin
.Begin
);
233 /* Read a 7-bit encoded byte length field */
234 long len
=Read7BitEncodedInt();
235 /* ... and skip that data to the info
236 * we want, the offset into the data
239 reader
.BaseStream
.Seek(len
, SeekOrigin
.Current
);
241 long data_offset
=reader
.ReadInt32();
242 reader
.BaseStream
.Seek(data_offset
+dataSectionOffset
, SeekOrigin
.Begin
);
243 int type_index
=Read7BitEncodedInt();
244 Type type
=types
[type_index
];
246 if (type
==typeof(Byte
)) {
247 return(reader
.ReadByte());
248 /* for some reason Char is serialized */
249 /*} else if (type==typeof(Char)) {
250 return(reader.ReadChar());*/
251 } else if (type
==typeof(Decimal
)) {
252 return(reader
.ReadDecimal());
253 } else if (type
==typeof(DateTime
)) {
254 return(new DateTime(reader
.ReadInt64()));
255 } else if (type
==typeof(Double
)) {
256 return(reader
.ReadDouble());
257 } else if (type
==typeof(Int16
)) {
258 return(reader
.ReadInt16());
259 } else if (type
==typeof(Int32
)) {
260 return(reader
.ReadInt32());
261 } else if (type
==typeof(Int64
)) {
262 return(reader
.ReadInt64());
263 } else if (type
==typeof(SByte
)) {
264 return(reader
.ReadSByte());
265 } else if (type
==typeof(Single
)) {
266 return(reader
.ReadSingle());
267 } else if (type
==typeof(String
)) {
268 return(reader
.ReadString());
269 } else if (type
==typeof(TimeSpan
)) {
270 return(new TimeSpan(reader
.ReadInt64()));
271 } else if (type
==typeof(UInt16
)) {
272 return(reader
.ReadUInt16());
273 } else if (type
==typeof(UInt32
)) {
274 return(reader
.ReadUInt32());
275 } else if (type
==typeof(UInt64
)) {
276 return(reader
.ReadUInt64());
278 /* non-intrinsic types are
281 object obj
=formatter
.Deserialize(reader
.BaseStream
);
282 if(obj
.GetType() != type
) {
289 * BadImageFormatException,
296 throw new InvalidOperationException("Deserialized object is wrong type");
309 public IDictionaryEnumerator
GetEnumerator () {
311 throw new InvalidOperationException("ResourceReader is closed.");
314 return new ResourceEnumerator (this);
318 IEnumerator IEnumerable
.GetEnumerator ()
320 return ((IResourceReader
) this).GetEnumerator();
323 void IDisposable
.Dispose ()
328 private void Dispose (bool disposing
)
342 internal class ResourceEnumerator
: IDictionaryEnumerator
344 private ResourceReader reader
;
345 private int index
= -1;
346 private bool finished
= false;
348 internal ResourceEnumerator(ResourceReader readerToEnumerate
){
349 reader
= readerToEnumerate
;
352 public virtual DictionaryEntry Entry
355 if (reader
.reader
== null)
356 throw new InvalidOperationException("ResourceReader is closed.");
358 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
360 DictionaryEntry entry
= new DictionaryEntry();
367 public virtual object Key
370 if (reader
.reader
== null)
371 throw new InvalidOperationException("ResourceReader is closed.");
373 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
374 return (reader
.ResourceName(index
));
378 public virtual object Value
381 if (reader
.reader
== null)
382 throw new InvalidOperationException("ResourceReader is closed.");
384 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
385 return(reader
.ResourceValue(index
));
389 public virtual object Current
392 /* Entry does the checking, no
393 * need to repeat it here
399 public virtual bool MoveNext ()
401 if (reader
.reader
== null)
402 throw new InvalidOperationException("ResourceReader is closed.");
407 if (++index
< reader
.resourceCount
){
416 public void Reset () {
417 if (reader
.reader
== null)
418 throw new InvalidOperationException("ResourceReader is closed.");
422 } // internal class ResourceEnumerator
423 } // public sealed class ResourceReader
424 } // namespace System.Resources