// Authors:
// Duncan Mak <duncan@ximian.com>
// Nick Drochak <ndrochak@gol.com>
+// Dick Porter <dick@ximian.com>
+// Marek Safar <marek.safar@seznam.cz>
+// Atsushi Enomoto <atsushi@ximian.com>
+// Larry Ewing <lewing@novell.com>
//
-// 2001 (C) Ximian Inc, http://www.ximian.com
+// (C) 2001, 2002 Ximian Inc, http://www.ximian.com
+// Copyright (C) 2004-2005,2007-2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-// TODO: Finish this
using System.Collections;
using System.Resources;
using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Security.Permissions;
+using System.Collections.Generic;
namespace System.Resources
{
- class MonoTODO : Attribute {}\r
- public sealed class ResourceReader : IResourceReader, IDisposable
+ internal enum PredefinedResourceType
+ {
+ Null = 0,
+ String = 1,
+ Bool = 2,
+ Char = 3,
+ Byte = 4,
+ SByte = 5,
+ Int16 = 6,
+ UInt16 = 7,
+ Int32 = 8,
+ UInt32 = 9,
+ Int64 = 10,
+ UInt64 = 11,
+ Single = 12,
+ Double = 13,
+ Decimal = 14,
+ DateTime = 15,
+ TimeSpan = 16,
+ ByteArray = 32,
+ Stream = 33,
+ FistCustom = 64
+ }
+
+ [System.Runtime.InteropServices.ComVisible (true)]
+ public sealed class ResourceReader : IResourceReader, IEnumerable, IDisposable
{
- Stream stream;
- internal ArrayList resourceNames = null;
- internal ArrayList resourceValues = null;
- BinaryReader binaryReader;
+ struct ResourceInfo
+ {
+ public readonly long ValuePosition;
+ public readonly string ResourceName;
+ public readonly int TypeIndex;
+
+ public ResourceInfo (string resourceName, long valuePosition, int type_index)
+ {
+ ValuePosition = valuePosition;
+ ResourceName = resourceName;
+ TypeIndex = type_index;
+ }
+ }
+
+ struct ResourceCacheItem
+ {
+ public readonly string ResourceName;
+ public readonly object ResourceValue;
+
+ public ResourceCacheItem (string name, object value)
+ {
+ ResourceName = name;
+ ResourceValue = value;
+ }
+ }
+
+ BinaryReader reader;
+ object readerLock = new object ();
+ IFormatter formatter;
internal int resourceCount = 0;
int typeCount = 0;
- ArrayList typeArray = new ArrayList();
- ArrayList hashes = new ArrayList();
- ArrayList positions = new ArrayList();
+ string[] typeNames;
+ int[] hashes;
+ ResourceInfo[] infos;
int dataSectionOffset;
-
+ long nameSectionOffset;
+ int resource_ver;
+ ResourceCacheItem[] cache;
+ object cache_lock = new object ();
+
// Constructors
+ [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)]
public ResourceReader (Stream stream)
{
if (stream == null)
- throw new ArgumentNullException ("Value cannot be null.");
+ throw new ArgumentNullException ("stream");
if (!stream.CanRead)
throw new ArgumentException ("Stream was not readable.");
- this.stream = stream;
+ reader = new BinaryReader(stream, Encoding.UTF8);
+ formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
- if (!IsStreamValid()){
- throw new ArgumentException("Stream is not a valid .resources file! It was possibly truncated.");
- }
+ ReadHeaders();
}
public ResourceReader (string fileName)
{
- if (fileName == null)
- throw new ArgumentException ("Path cannot be null.");
-
- if (String.Empty == fileName)
- throw new ArgumentException("Empty path name is not legal.");
-
- if (!System.IO.File.Exists (fileName))
- throw new FileNotFoundException ("Could not find file " + Path.GetFullPath(fileName));
-
- stream = new FileStream (fileName, FileMode.Open);
+ reader = new BinaryReader (new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read));
+ formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File|StreamingContextStates.Persistence));
- if (!IsStreamValid()){
- throw new ArgumentException("Stream is not a valid .resources file! It was possibly truncated.");
- }
+ ReadHeaders();
}
- [MonoTODO]
- private bool IsStreamValid() {
- // not sure how much to check to determine if it's valid,
- // but look at magic number, version numbers and class names
- string readerClass;
- string resourceSetClass;
+ /* Read the ResourceManager header and the
+ * ResourceReader header.
+ */
+ private void ReadHeaders()
+ {
try {
- binaryReader = new BinaryReader(stream);
- int magicNumber = binaryReader.ReadInt32();
- if (-1091581234 != magicNumber) {
- return false;
- }
- int versionNumber = binaryReader.ReadInt32();
- if (1 != versionNumber){
- return false;
- }
- // Ignore next 32bits. they contain the length of the class name strings
- binaryReader.ReadInt32();
+ int manager_magic = reader.ReadInt32();
- readerClass = binaryReader.ReadString();
- if (!readerClass.StartsWith("System.Resources.ResourceReader")){
- return false;
+ if(manager_magic != ResourceManager.MagicNumber)
+ throw new ArgumentException(String.Format ("Stream is not a valid .resources file, magic=0x{0:x}", manager_magic));
+
+ int manager_ver = reader.ReadInt32();
+ int manager_len = reader.ReadInt32();
+
+ /* We know how long the header is, even if
+ * the version number is too new
+ */
+ if(manager_ver > ResourceManager.HeaderVersionNumber) {
+ reader.BaseStream.Seek(manager_len, SeekOrigin.Current);
+ } else {
+ string reader_class=reader.ReadString();
+ if(!reader_class.StartsWith("System.Resources.ResourceReader")) {
+ throw new NotSupportedException("This .resources file requires reader class " + reader_class);
+ }
+
+ string set_class=reader.ReadString();
+ if(!set_class.StartsWith(typeof(ResourceSet).FullName) && !set_class.StartsWith("System.Resources.RuntimeResourceSet")) {
+ throw new NotSupportedException("This .resources file requires set class " + set_class);
+ }
}
- resourceSetClass = binaryReader.ReadString();
- if (!resourceSetClass.StartsWith("System.Resources.RuntimeResourceSet")){
- return false;
+
+ /* Now read the ResourceReader header */
+ resource_ver = reader.ReadInt32();
+
+ if(resource_ver != 1 && resource_ver != 2) {
+ throw new NotSupportedException("This .resources file requires unsupported set class version: " + resource_ver.ToString());
}
- int versionNumber2 = binaryReader.ReadInt32();
- if (1 != versionNumber2){
- return false;
+
+ resourceCount = reader.ReadInt32();
+ typeCount = reader.ReadInt32();
+
+ typeNames=new string[typeCount];
+
+ for(int i=0; i<typeCount; i++) {
+ typeNames[i]=reader.ReadString();
}
- resourceCount = binaryReader.ReadInt32();
- typeCount = binaryReader.ReadInt32();
+ /* There are between 0 and 7 bytes of
+ * padding here, consisting of the
+ * letters PAD. The next item (Hash
+ * values for each resource name) need
+ * to be aligned on an 8-byte
+ * boundary.
+ */
+
+ int pad_align=(int)(reader.BaseStream.Position & 7);
+ int pad_chars=0;
- for (int i = 0; i < typeCount; i++) {
- typeArray.Add(binaryReader.ReadString());
+ if(pad_align!=0) {
+ pad_chars=8-pad_align;
}
- for (int i = 0; i < resourceCount; i++) {
- hashes.Add(binaryReader.ReadInt32());
+
+ for(int i=0; i<pad_chars; i++) {
+ byte pad_byte=reader.ReadByte();
+ if(pad_byte!="PAD"[i%3]) {
+ throw new ArgumentException("Malformed .resources file (padding values incorrect)");
+ }
}
- for (int i = 0; i < resourceCount; i++) {
- positions.Add(binaryReader.ReadInt32());
+ /* Read in the hash values for each
+ * resource name. These can be used
+ * by ResourceSet (calling internal
+ * methods) to do a fast compare on
+ * resource names without doing
+ * expensive string compares (but we
+ * dont do that yet, so far we only
+ * implement the Enumerator interface)
+ */
+ hashes=new int[resourceCount];
+ for(int i=0; i<resourceCount; i++) {
+ hashes[i]=reader.ReadInt32();
}
+
+ /* Read in the virtual offsets for
+ * each resource name
+ */
+ long[] positions = new long [resourceCount];
+ for(int i = 0; i < resourceCount; i++)
+ positions [i] = reader.ReadInt32();
+
+ dataSectionOffset = reader.ReadInt32();
+ nameSectionOffset = reader.BaseStream.Position;
+
+ long origPosition = reader.BaseStream.Position;
+ infos = new ResourceInfo [resourceCount];
+ for (int i = 0; i < resourceCount; i++)
+ CreateResourceInfo (positions [i], ref infos [i]);
+ reader.BaseStream.Seek (origPosition, SeekOrigin.Begin);
+
+ positions = null;
+ } catch(EndOfStreamException e) {
+ throw new ArgumentException("Stream is not a valid .resources file! It was possibly truncated.", e);
+ }
+ }
+
+ void CreateResourceInfo (long position, ref ResourceInfo info)
+ {
+ long pos = position + nameSectionOffset;
+
+ reader.BaseStream.Seek (pos, SeekOrigin.Begin);
+
+ // Resource name
+ int len = Read7BitEncodedInt ();
+ byte[] str = new byte [len];
+
+ reader.Read (str, 0, len);
+ string resourceName = Encoding.Unicode.GetString (str);
+
+ long data_offset = reader.ReadInt32 () + dataSectionOffset;
+ reader.BaseStream.Seek (data_offset, SeekOrigin.Begin);
+ int type_index = Read7BitEncodedInt ();
+
+ info = new ResourceInfo (resourceName, reader.BaseStream.Position, type_index);
+ }
+
+ /* Cut and pasted from BinaryReader, because it's
+ * 'protected' there
+ */
+ private int Read7BitEncodedInt() {
+ int ret = 0;
+ int shift = 0;
+ byte b;
+
+ do {
+ b = reader.ReadByte();
+
+ ret = ret | ((b & 0x7f) << shift);
+ shift += 7;
+ } while ((b & 0x80) == 0x80);
+
+ return ret;
+ }
+
+ object ReadValueVer2 (int type_index)
+ {
+ switch ((PredefinedResourceType)type_index)
+ {
+ case PredefinedResourceType.Null:
+ return null;
+
+ case PredefinedResourceType.String:
+ return reader.ReadString();
+
+ case PredefinedResourceType.Bool:
+ return reader.ReadBoolean ();
+
+ case PredefinedResourceType.Char:
+ return (char)reader.ReadUInt16();
+
+ case PredefinedResourceType.Byte:
+ return reader.ReadByte();
+
+ case PredefinedResourceType.SByte:
+ return reader.ReadSByte();
+
+ case PredefinedResourceType.Int16:
+ return reader.ReadInt16();
+
+ case PredefinedResourceType.UInt16:
+ return reader.ReadUInt16();
+
+ case PredefinedResourceType.Int32:
+ return reader.ReadInt32();
+
+ case PredefinedResourceType.UInt32:
+ return reader.ReadUInt32();
+
+ case PredefinedResourceType.Int64:
+ return reader.ReadInt64();
+
+ case PredefinedResourceType.UInt64:
+ return reader.ReadUInt64();
+
+ case PredefinedResourceType.Single:
+ return reader.ReadSingle();
+
+ case PredefinedResourceType.Double:
+ return reader.ReadDouble();
+
+ case PredefinedResourceType.Decimal:
+ return reader.ReadDecimal();
+
+ case PredefinedResourceType.DateTime:
+ return new DateTime(reader.ReadInt64());
- dataSectionOffset = binaryReader.ReadInt32();
+ case PredefinedResourceType.TimeSpan:
+ return new TimeSpan(reader.ReadInt64());
- // LAMESPEC: what is the next Int32 here?
- binaryReader.ReadInt32();
+ case PredefinedResourceType.ByteArray:
+ return reader.ReadBytes (reader.ReadInt32 ());
+
+ case PredefinedResourceType.Stream:
+ // FIXME: create pinned UnmanagedMemoryStream for efficiency.
+ byte [] bytes = new byte [reader.ReadUInt32 ()];
+ reader.Read (bytes, 0, bytes.Length);
+ return new MemoryStream (bytes);
}
- catch{
- return false;
+
+ type_index -= (int)PredefinedResourceType.FistCustom;
+ return ReadNonPredefinedValue (Type.GetType (typeNames[type_index], true));
+ }
+
+ object ReadValueVer1 (Type type)
+ {
+ // The most common first
+ if (type==typeof(String))
+ return reader.ReadString();
+ if (type==typeof(Int32))
+ return reader.ReadInt32();
+ if (type==typeof(Byte))
+ return(reader.ReadByte());
+ if (type==typeof(Double))
+ return(reader.ReadDouble());
+ if (type==typeof(Int16))
+ return(reader.ReadInt16());
+ if (type==typeof(Int64))
+ return(reader.ReadInt64());
+ if (type==typeof(SByte))
+ return(reader.ReadSByte());
+ if (type==typeof(Single))
+ return(reader.ReadSingle());
+ if (type==typeof(TimeSpan))
+ return(new TimeSpan(reader.ReadInt64()));
+ if (type==typeof(UInt16))
+ return(reader.ReadUInt16());
+ if (type==typeof(UInt32))
+ return(reader.ReadUInt32());
+ if (type==typeof(UInt64))
+ return(reader.ReadUInt64());
+ if (type==typeof(Decimal))
+ return(reader.ReadDecimal());
+ if (type==typeof(DateTime))
+ return(new DateTime(reader.ReadInt64()));
+
+ return ReadNonPredefinedValue(type);
+ }
+
+ // TODO: Add security checks
+ object ReadNonPredefinedValue (Type exp_type)
+ {
+ object obj=formatter.Deserialize(reader.BaseStream);
+ if(obj.GetType() != exp_type) {
+ /* We got a bogus
+ * object. This
+ * exception is the
+ * best match I could
+ * find. (.net seems
+ * to throw
+ * BadImageFormatException,
+ * which the docs
+ * state is used when
+ * file or dll images
+ * cant be loaded by
+ * the runtime.)
+ */
+ throw new InvalidOperationException("Deserialized object is wrong type");
+ }
+ return obj;
+ }
+
+ void LoadResourceValues (ResourceCacheItem[] store)
+ {
+ ResourceInfo ri;
+ object value;
+
+ lock (readerLock) {
+ for (int i = 0; i < resourceCount; i++) {
+ ri = infos [i];
+ if (ri.TypeIndex == -1) {
+ store [i] = new ResourceCacheItem (ri.ResourceName, null);
+ continue;
+ }
+
+ reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
+ if (resource_ver == 2)
+ value = ReadValueVer2 (ri.TypeIndex);
+ else
+ value = ReadValueVer1 (Type.GetType (typeNames [ri.TypeIndex], true));
+
+ store [i] = new ResourceCacheItem (ri.ResourceName, value);
+ }
}
- return true;
}
+
+ internal UnmanagedMemoryStream ResourceValueAsStream (string name, int index)
+ {
+ ResourceInfo ri = infos [index];
+ if ((PredefinedResourceType)ri.TypeIndex != PredefinedResourceType.Stream)
+ throw new InvalidOperationException (String.Format ("Resource '{0}' was not a Stream. Use GetObject() instead.", name));
+
+ lock (readerLock) {
+ reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
+
+ // here we return a Stream from exactly
+ // current position so that the returned
+ // Stream represents a single object stream.
+ long slen = reader.ReadInt32();
+ UnmanagedMemoryStream basePtrStream = reader.BaseStream as UnmanagedMemoryStream;
+ unsafe {
+ if (basePtrStream != null) {
+ return new UnmanagedMemoryStream (basePtrStream.PositionPointer, slen);
+ } else {
+ IntPtr ptr = Marshal.AllocHGlobal ((int) slen);
+ byte* addr = (byte*) ptr.ToPointer ();
+ UnmanagedMemoryStream ms = new UnmanagedMemoryStream (addr, slen, slen, FileAccess.ReadWrite);
+ // The memory resource must be freed
+ // when the stream is disposed.
+ ms.Closed += delegate (object o, EventArgs e) {
+ Marshal.FreeHGlobal (ptr);
+ };
+
+ byte [] bytes = new byte [slen < 1024 ? slen : 1024];
+ while (slen > 0 ) {
+ int x = reader.Read (bytes, 0, (int)Math.Min (bytes.Length, slen));
+
+ if (x == 0)
+ throw new FormatException ("The resource data is corrupt. Resource stream ended");
- private string ReadString(BinaryReader br) {
- return br.ReadString();
+ ms.Write (bytes, 0, x);
+ slen -= x;
+ }
+ ms.Seek (0, SeekOrigin.Begin);
+ return ms;
+ }
+ }
+ }
}
public void Close ()
{
- stream.Close ();
- stream = null;
+ Dispose(true);
+ }
+
+#if NET_4_0
+ public void Dispose ()
+#else
+ void IDisposable.Dispose ()
+#endif
+ {
+ Dispose(true);
}
public IDictionaryEnumerator GetEnumerator () {
- if (null == stream){
+ if (reader == null){
throw new InvalidOperationException("ResourceReader is closed.");
}
else {
- // STRATEGY: if this is the first enumerator requested, fill the hash.
- // delaying in this way seems ok since there's not much you can do with just
- // a reader except close it. And if you close it, you cannot get the enumerator.
- // So, create the hash for the first enumerator, and re-use it for all others.
- if (null == resourceNames) {
- FillResources();
- }
return new ResourceEnumerator (this);
}
}
- internal struct NameOffsetPair {
- public string name;
- public int offset;
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return ((IResourceReader) this).GetEnumerator();
}
- [MonoTODO]
- private void FillResources(){
- NameOffsetPair pair;
- resourceNames = new ArrayList();
- resourceValues = new ArrayList();
- BinaryReader unicodeReader =
- new BinaryReader(binaryReader.BaseStream, System.Text.Encoding.Unicode);
- // TODO: need to put these in an array and work out when to get the values.
- // also need to figure out the hash and how/if to use it.
- for (int index=0; index < resourceCount; index++){
- pair = new NameOffsetPair();
- pair.name = unicodeReader.ReadString();
- pair.offset = binaryReader.ReadInt32();
- resourceNames.Add(pair);
- }
- for (int index=0; index < resourceCount; index++){
- // LAMESPEC: what the heck is this byte here? always 0? just a separator?
- binaryReader.ReadByte();
- resourceValues.Add(binaryReader.ReadString());
- }
+ public void GetResourceData (string resourceName, out string resourceType, out byte [] resourceData)
+ {
+ if (resourceName == null)
+ throw new ArgumentNullException ("resourceName");
+ ResourceEnumerator en = new ResourceEnumerator (this);
+ while (en.MoveNext ())
+ if ((string) en.Key == resourceName) {
+ GetResourceDataAt (en.Index, out resourceType, out resourceData);
+ return;
+ }
+ throw new ArgumentException (String.Format ("Specified resource not found: {0}", resourceName));
}
- IEnumerator IEnumerable.GetEnumerator ()
+ private void GetResourceDataAt (int index, out string resourceType, out byte [] data)
{
- return ((IResourceReader) this).GetEnumerator();
+ ResourceInfo ri = infos [index];
+ int type_index = ri.TypeIndex;
+ if (type_index == -1)
+ throw new FormatException ("The resource data is corrupt");
+
+ lock (readerLock) {
+ reader.BaseStream.Seek (ri.ValuePosition, SeekOrigin.Begin);
+ long pos2 = reader.BaseStream.Position;
+
+ // Simply read data, and seek back to the original position
+ if (resource_ver == 2) {
+ if (type_index >= (int) PredefinedResourceType.FistCustom) {
+ int typenameidx = type_index - (int)PredefinedResourceType.FistCustom;
+ if (typenameidx >= typeNames.Length)
+ throw new FormatException ("The resource data is corrupt. Invalid index to types");
+ resourceType = typeNames[typenameidx];
+ }
+ else
+ resourceType = "ResourceTypeCode." + (PredefinedResourceType) type_index;
+ ReadValueVer2 (type_index);
+ } else {
+ // resource ver 1 == untyped
+ resourceType = "ResourceTypeCode.Null";
+ ReadValueVer1 (Type.GetType (typeNames [type_index], true));
+ }
+
+ // FIXME: the data size is wrong.
+ int datalen = (int) (reader.BaseStream.Position - pos2);
+ reader.BaseStream.Seek (-datalen, SeekOrigin.Current);
+ data = new byte [datalen];
+ reader.BaseStream.Read (data, 0, datalen);
+ }
}
-
- [MonoTODO]
- void IDisposable.Dispose ()
+
+ private void Dispose (bool disposing)
{
- // FIXME: is this all we need to do?
- Close();
+ if(disposing) {
+ if(reader!=null) {
+ reader.Close();
+ }
+ }
+
+ reader=null;
+ hashes=null;
+ infos=null;
+ typeNames=null;
+ cache = null;
}
- internal class ResourceEnumerator : IDictionaryEnumerator
+ internal sealed class ResourceEnumerator : IDictionaryEnumerator
{
- protected ResourceReader reader;
- protected int index = -1;
-
+ private ResourceReader reader;
+ private int index = -1;
+ private bool finished;
- public ResourceEnumerator(ResourceReader readerToEnumerate){
+ internal ResourceEnumerator(ResourceReader readerToEnumerate)
+ {
reader = readerToEnumerate;
+ FillCache ();
}
- public DictionaryEntry Entry
+ public int Index
{
+ get { return index; }
+ }
+
+ public DictionaryEntry Entry {
get {
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
if (index < 0)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
- DictionaryEntry entry = new DictionaryEntry();
- entry.Key = Key;
- entry.Value = Value;
- return entry;
+ return new DictionaryEntry(Key, Value);
}
}
public object Key
{
get {
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
if (index < 0)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
- return ((NameOffsetPair)(reader.resourceNames[index])).name;
+
+ return reader.cache [index].ResourceName;
}
}
public object Value
{
get {
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
if (index < 0)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
- return reader.resourceValues[index];
+ return reader.cache [index].ResourceValue;
}
}
- public object Current
+ public UnmanagedMemoryStream ValueAsStream
{
get {
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
if (index < 0)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
+ return(reader.ResourceValueAsStream((string) Key, index));
+ }
+ }
+
+ public object Current
+ {
+ get {
+ /* Entry does the checking, no
+ * need to repeat it here
+ */
return Entry;
}
}
public bool MoveNext ()
{
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
+ if (finished) {
+ return false;
+ }
+
if (++index < reader.resourceCount){
return true;
}
- else {
- --index;
- return false;
- }
+
+ finished=true;
+ return false;
}
public void Reset () {
- if (null == reader.stream)
+ if (reader.reader == null)
throw new InvalidOperationException("ResourceReader is closed.");
index = -1;
+ finished = false;
+ }
+
+ void FillCache ()
+ {
+ if (reader.cache != null)
+ return;
+
+ lock (reader.cache_lock) {
+ if (reader.cache != null)
+ return;
+
+ ResourceCacheItem[] resources = new ResourceCacheItem [reader.resourceCount];
+ reader.LoadResourceValues (resources);
+ reader.cache = resources;
+ }
}
} // internal class ResourceEnumerator
} // public sealed class ResourceReader