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
27 // Gert Driesen drieseng@users.sourceforge.net
31 using System.Collections;
32 using System.Collections.Specialized;
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 string fileName;
47 private Stream stream;
48 private TextReader reader;
49 private Hashtable hasht;
50 private ITypeResolutionService typeresolver;
52 private XmlTextReader xmlReader;
55 private string basepath;
57 #endregion // Local Variables
59 #region Constructors & Destructor
60 public ResXResourceReader (Stream stream)
63 throw new ArgumentNullException ("Value cannot be null.");
66 throw new ArgumentException ("Stream was not readable.");
71 public ResXResourceReader (Stream stream, ITypeResolutionService typeresolver)
74 this.typeresolver = typeresolver;
77 public ResXResourceReader (string fileName)
79 this.fileName = fileName;
82 public ResXResourceReader (string fileName, ITypeResolutionService typeresolver)
85 this.typeresolver = typeresolver;
88 public ResXResourceReader (TextReader reader)
93 public ResXResourceReader (TextReader reader, ITypeResolutionService typeresolver)
96 this.typeresolver = typeresolver;
99 ~ResXResourceReader ()
103 #endregion // Constructors & Destructor
106 public string BasePath {
107 get { return basepath; }
108 set { basepath = value; }
112 #region Private Methods
113 private void LoadData ()
115 if (fileName != null) {
116 stream = File.OpenRead (fileName);
121 if (stream != null) {
122 xmlReader = new XmlTextReader (stream);
123 } else if (reader != null) {
124 xmlReader = new XmlTextReader (reader);
127 if (xmlReader == null) {
128 throw new InvalidOperationException ("ResourceReader is closed.");
131 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
133 ResXHeader header = new ResXHeader ();
135 while (xmlReader.Read ()) {
136 if (xmlReader.NodeType != XmlNodeType.Element)
139 switch (xmlReader.LocalName) {
141 ParseHeaderNode (header);
149 } catch (XmlException ex) {
150 throw new ArgumentException ("Invalid ResX input.", ex);
151 } catch (Exception ex) {
152 XmlException xex = new XmlException (ex.Message, ex,
153 xmlReader.LineNumber, xmlReader.LinePosition);
154 throw new ArgumentException ("Invalid ResX input.", xex);
157 } catch (Exception ex) {
158 throw new ArgumentException ("Invalid ResX input.", ex);
164 if (fileName != null) {
171 private void ParseHeaderNode (ResXHeader header)
173 string v = GetAttribute ("name");
177 if (String.Compare (v, "resmimetype", true) == 0) {
178 header.ResMimeType = GetHeaderValue ();
179 } else if (String.Compare (v, "reader", true) == 0) {
180 header.Reader = GetHeaderValue ();
181 } else if (String.Compare (v, "version", true) == 0) {
182 header.Version = GetHeaderValue ();
183 } else if (String.Compare (v, "writer", true) == 0) {
184 header.Writer = GetHeaderValue ();
188 private string GetHeaderValue ()
191 xmlReader.ReadStartElement ();
192 if (xmlReader.NodeType == XmlNodeType.Element) {
193 value = xmlReader.ReadElementString ();
195 value = xmlReader.Value.Trim ();
200 private string GetAttribute (string name)
202 if (!xmlReader.HasAttributes)
204 for (int i = 0; i < xmlReader.AttributeCount; i++) {
205 xmlReader.MoveToAttribute (i);
206 if (String.Compare (xmlReader.Name, name, true) == 0) {
207 string v = xmlReader.Value;
208 xmlReader.MoveToElement ();
212 xmlReader.MoveToElement ();
216 private string GetDataValue ()
220 while (xmlReader.Read ()) {
221 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == "data")
224 if (xmlReader.NodeType == XmlNodeType.Element) {
225 if (xmlReader.Name.Equals ("value")) {
226 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
227 value = xmlReader.ReadString ();
228 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
229 } else if (xmlReader.Name.Equals ("comment")) {
231 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == "data")
236 value = xmlReader.Value.Trim ();
240 if (xmlReader.NodeType == XmlNodeType.Element) {
241 value = xmlReader.ReadElementString ();
243 value = xmlReader.Value.Trim ();
247 value = string.Empty;
252 private void ParseDataNode ()
254 string name = GetAttribute ("name");
255 string type_name = GetAttribute ("type");
256 string mime_type = GetAttribute ("mimetype");
258 Type type = type_name == null ? null : ResolveType (type_name);
260 if (type_name != null && type == null)
261 throw new ArgumentException (String.Format (
262 "The type '{0}' of the element '{1}' could not be resolved.", type_name, name));
264 if (type == typeof (ResXNullRef)) {
269 string value = GetDataValue ();
273 TypeConverter c = TypeDescriptor.GetConverter (type);
277 } else if (c.CanConvertFrom (typeof (string))) {
279 if (BasePath != null && type == typeof (ResXFileRef)) {
280 string [] parts = ResXFileRef.Parse (value);
281 parts [0] = Path.Combine (BasePath, parts [0]);
282 obj = c.ConvertFromInvariantString (string.Join (";", parts));
284 obj = c.ConvertFromInvariantString (value);
287 obj = c.ConvertFromInvariantString (value);
289 } else if (c.CanConvertFrom (typeof (byte []))) {
290 obj = c.ConvertFrom (Convert.FromBase64String (value));
292 // the type must be a byte[]
293 obj = Convert.FromBase64String (value);
295 } else if (mime_type != null && mime_type != String.Empty) {
296 if (mime_type == ResXResourceWriter.BinSerializedObjectMimeType) {
297 byte [] data = Convert.FromBase64String (value);
298 BinaryFormatter f = new BinaryFormatter ();
299 using (MemoryStream s = new MemoryStream (data)) {
300 obj = f.Deserialize (s);
315 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
316 "Could not find a name for a resource. The resource value "
317 + "was '{0}'.", obj));
322 private Type ResolveType (string type)
324 if (typeresolver == null) {
325 return Type.GetType (type);
327 return typeresolver.GetType (type);
330 #endregion // Private Methods
332 #region Public Methods
335 if (reader != null) {
341 public IDictionaryEnumerator GetEnumerator ()
344 hasht = new Hashtable ();
347 return hasht.GetEnumerator ();
350 IEnumerator IEnumerable.GetEnumerator ()
352 return ((IResourceReader) this).GetEnumerator ();
355 void IDisposable.Dispose ()
358 GC.SuppressFinalize (this);
361 protected virtual void Dispose (bool disposing)
368 public static ResXResourceReader FromFileContents (string fileContents)
370 return new ResXResourceReader (new StringReader (fileContents));
373 public static ResXResourceReader FromFileContents (string fileContents, ITypeResolutionService typeResolver)
375 return new ResXResourceReader (new StringReader (fileContents), typeResolver);
378 #endregion // Public Methods
380 #region Internal Classes
381 private class ResXHeader
383 private string resMimeType;
384 private string reader;
385 private string version;
386 private string writer;
388 public string ResMimeType
390 get { return resMimeType; }
391 set { resMimeType = value; }
394 public string Reader {
395 get { return reader; }
396 set { reader = value; }
399 public string Version {
400 get { return version; }
401 set { version = value; }
404 public string Writer {
405 get { return writer; }
406 set { writer = value; }
409 public void Verify ()
412 throw new ArgumentException ("Invalid ResX input. Could "
413 + "not find valid \"resheader\" tags for the ResX "
414 + "reader & writer type names.");
417 public bool IsValid {
419 if (string.Compare (ResMimeType, ResXResourceWriter.ResMimeType) != 0)
421 if (Reader == null || Writer == null)
423 string readerType = Reader.Split (',') [0].Trim ();
424 if (readerType != typeof (ResXResourceReader).FullName)
426 string writerType = Writer.Split (',') [0].Trim ();
427 if (writerType != typeof (ResXResourceWriter).FullName)