(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / System.Resources / ResourceReader.cs
blob3f5d3046ae3f70d3f97636d244f1598a00ba5e2a
1 //
2 // System.Resources.ResourceReader.cs
3 //
4 // Authors:
5 // Duncan Mak <duncan@ximian.com>
6 // Nick Drochak <ndrochak@gol.com>
7 // Dick Porter <dick@ximian.com>
8 //
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:
22 //
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 //
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;
37 using System.IO;
38 using System.Text;
39 using System.Runtime.Serialization;
40 using System.Runtime.Serialization.Formatters.Binary;
42 namespace System.Resources
44 public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
46 BinaryReader reader;
47 IFormatter formatter;
48 internal int resourceCount = 0;
49 int typeCount = 0;
50 Type[] types;
51 int[] hashes;
52 long[] positions;
53 int dataSectionOffset;
54 long nameSectionOffset;
56 // Constructors
57 public ResourceReader (Stream stream)
59 if (stream == null)
60 throw new ArgumentNullException ("Value cannot be null.");
62 if (!stream.CanRead)
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));
68 ReadHeaders();
71 public ResourceReader (string fileName)
73 if (fileName == null)
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));
82 ReadHeaders();
85 /* Read the ResourceManager header and the
86 * ResourceReader header.
88 private void ReadHeaders()
90 try {
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);
105 } else {
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
133 * here?
135 types[i]=Type.GetType(type_name);
136 if(types[i]==null) {
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
146 * boundary.
149 int pad_align=(int)(reader.BaseStream.Position & 7);
150 int pad_chars=0;
152 if(pad_align!=0) {
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
178 * each resource name
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
193 * 'protected' there
195 private int Read7BitEncodedInt() {
196 int ret = 0;
197 int shift = 0;
198 byte b;
200 do {
201 b = reader.ReadByte();
203 ret = ret | ((b & 0x7f) << shift);
204 shift += 7;
205 } while ((b & 0x80) == 0x80);
207 return ret;
210 private string ResourceName(int index)
212 lock(this)
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)
228 lock(this)
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
237 * section
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());
277 } else {
278 /* non-intrinsic types are
279 * serialized
281 object obj=formatter.Deserialize(reader.BaseStream);
282 if(obj.GetType() != type) {
283 /* We got a bogus
284 * object. This
285 * exception is the
286 * best match I could
287 * find. (.net seems
288 * to throw
289 * BadImageFormatException,
290 * which the docs
291 * state is used when
292 * file or dll images
293 * cant be loaded by
294 * the runtime.)
296 throw new InvalidOperationException("Deserialized object is wrong type");
299 return(obj);
304 public void Close ()
306 Dispose(true);
309 public IDictionaryEnumerator GetEnumerator () {
310 if (reader == null){
311 throw new InvalidOperationException("ResourceReader is closed.");
313 else {
314 return new ResourceEnumerator (this);
318 IEnumerator IEnumerable.GetEnumerator ()
320 return ((IResourceReader) this).GetEnumerator();
323 void IDisposable.Dispose ()
325 Dispose(true);
328 private void Dispose (bool disposing)
330 if(disposing) {
331 if(reader!=null) {
332 reader.Close();
336 reader=null;
337 hashes=null;
338 positions=null;
339 types=null;
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
354 get {
355 if (reader.reader == null)
356 throw new InvalidOperationException("ResourceReader is closed.");
357 if (index < 0)
358 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
360 DictionaryEntry entry = new DictionaryEntry();
361 entry.Key = Key;
362 entry.Value = Value;
363 return entry;
367 public virtual object Key
369 get {
370 if (reader.reader == null)
371 throw new InvalidOperationException("ResourceReader is closed.");
372 if (index < 0)
373 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
374 return (reader.ResourceName(index));
378 public virtual object Value
380 get {
381 if (reader.reader == null)
382 throw new InvalidOperationException("ResourceReader is closed.");
383 if (index < 0)
384 throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
385 return(reader.ResourceValue(index));
389 public virtual object Current
391 get {
392 /* Entry does the checking, no
393 * need to repeat it here
395 return Entry;
399 public virtual bool MoveNext ()
401 if (reader.reader == null)
402 throw new InvalidOperationException("ResourceReader is closed.");
403 if (finished) {
404 return false;
407 if (++index < reader.resourceCount){
408 return true;
410 else {
411 finished=true;
412 return false;
416 public void Reset () {
417 if (reader.reader == null)
418 throw new InvalidOperationException("ResourceReader is closed.");
419 index = -1;
420 finished = false;
422 } // internal class ResourceEnumerator
423 } // public sealed class ResourceReader
424 } // namespace System.Resources