1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004-2005 Novell, Inc.
23 // Duncan Mak duncan@ximian.com
24 // Nick Drochak ndrochak@gol.com
25 // Paolo Molaro lupus@ximian.com
26 // Peter Bartok pbartok@novell.com
32 using System.Collections;
33 using System.ComponentModel;
34 using System.ComponentModel.Design;
35 using System.Globalization;
37 using System.Resources;
38 using System.Runtime.Serialization.Formatters.Binary;
41 namespace System.Resources
43 public class ResXResourceReader : IResourceReader, IDisposable
45 #region Local Variables
46 private Stream stream;
47 private XmlTextReader reader;
48 private Hashtable hasht;
49 private ITypeResolutionService typeresolver;
50 #endregion // Local Variables
52 #region Constructors & Destructor
53 public ResXResourceReader (Stream stream)
56 throw new ArgumentNullException ("Value cannot be null.");
59 throw new ArgumentException ("Stream was not readable.");
65 public ResXResourceReader (Stream stream, ITypeResolutionService typeresolver) : this(stream) {
66 this.typeresolver = typeresolver;
69 public ResXResourceReader (string fileName)
71 stream = File.OpenRead (fileName);
75 public ResXResourceReader (string fileName, ITypeResolutionService typeresolver) : this(fileName) {
76 this.typeresolver = typeresolver;
79 public ResXResourceReader(TextReader reader) {
80 this.reader = new XmlTextReader(reader);
81 this.hasht = new Hashtable();
86 public ResXResourceReader (TextReader reader, ITypeResolutionService typeresolver) : this(reader) {
87 this.typeresolver = typeresolver;
90 ~ResXResourceReader() {
93 #endregion // Constructors & Destructor
96 #region Private Methods
98 reader = new XmlTextReader (stream);
99 hasht = new Hashtable ();
101 if (!IsStreamValid()){
102 throw new ArgumentException("Stream is not a valid .resx file! It was possibly truncated.");
107 static string get_attr (XmlTextReader reader, string name) {
108 if (!reader.HasAttributes)
110 for (int i = 0; i < reader.AttributeCount; i++) {
111 reader.MoveToAttribute (i);
112 if (String.Compare (reader.Name, name, true) == 0) {
113 string v = reader.Value;
114 reader.MoveToElement ();
118 reader.MoveToElement ();
123 static string get_value (XmlTextReader reader, string name)
125 return get_value (reader, name, true);
128 // Returns the value of the next XML element with the specified
129 // name from the reader. canBeCdata == true specifies that
130 // the element may be a CDATA node as well.
131 static string get_value (XmlTextReader reader, string name, bool canBeCdata) {
132 bool gotelement = false;
133 while (reader.Read ()) {
134 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, name, true) == 0) {
138 if (canBeCdata && reader.NodeType == XmlNodeType.CDATA)
143 while (reader.Read ()) {
144 if (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA) {
145 string v = reader.Value;
148 else if (reader.NodeType == XmlNodeType.EndElement && reader.Value == string.Empty)
150 string v = reader.Value;
157 private bool IsStreamValid() {
158 bool gotroot = false;
159 bool gotmime = false;
161 while (reader.Read ()) {
162 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "root", true) == 0) {
169 while (reader.Read ()) {
170 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "resheader", true) == 0) {
171 string v = get_attr (reader, "name");
172 if (v != null && String.Compare (v, "resmimetype", true) == 0) {
173 v = get_value (reader, "value");
174 if (String.Compare (v, "text/microsoft-resx", true) == 0) {
179 } else if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "data", true) == 0) {
180 /* resheader apparently can appear anywhere, so we collect
181 * the data even if we haven't validated yet.
183 string n = get_attr (reader, "name");
185 string v = get_value (reader, "value");
193 private void load_data ()
195 while (reader.Read ()) {
196 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "data", true) == 0) {
197 string n = get_attr (reader, "name");
198 string t = get_attr (reader, "type");
199 string mt = get_attr (reader, "mimetype");
201 Type tt = t == null ? null : Type.GetType (t);
203 if (t != null && tt == null) {
204 throw new SystemException ("The type `" + t +"' could not be resolved");
206 if (tt == typeof (ResXNullRef)) {
212 string val = get_value (reader, "value");
214 if ((mt != null) && (mt.Length > 0) && (tt != null)) {
215 TypeConverter c = TypeDescriptor.GetConverter (tt);
216 v = c.ConvertFrom (Convert.FromBase64String (val));
217 } else if (tt != null) {
218 // MS seems to handle Byte[] without any mimetype :-(
219 if (t.StartsWith("System.Byte[], mscorlib")) {
220 v = Convert.FromBase64String(val);
222 TypeConverter c = TypeDescriptor.GetConverter (tt);
223 v = c.ConvertFromInvariantString (val);
225 } else if ((mt != null) && (mt.Length > 0)) {
226 byte [] data = Convert.FromBase64String (val);
227 BinaryFormatter f = new BinaryFormatter ();
228 using (MemoryStream s = new MemoryStream (data)) {
229 v = f.Deserialize (s);
240 private Type GetType(string type) {
241 if (typeresolver == null) {
242 return Type.GetType(type);
244 return typeresolver.GetType(type);
247 #endregion // Private Methods
249 #region Public Methods
252 if (stream != null) {
257 if (reader != null) {
263 public IDictionaryEnumerator GetEnumerator () {
265 throw new InvalidOperationException("ResourceReader is closed.");
268 return hasht.GetEnumerator ();
272 IEnumerator IEnumerable.GetEnumerator ()
274 return ((IResourceReader) this).GetEnumerator();
277 void IDisposable.Dispose ()
280 GC.SuppressFinalize(this);
283 protected virtual void Dispose(bool disposing) {
289 public static ResXResourceReader FromFileContents(string fileContents) {
290 return new ResXResourceReader(new StringReader(fileContents));
293 public static ResXResourceReader FromFileContents(string fileContents, ITypeResolutionService typeResolver) {
294 return new ResXResourceReader(new StringReader(fileContents), typeResolver);
297 #endregion // Public Methods
299 } // public sealed class ResXResourceReader
300 } // namespace System.Resources