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 string basepath;
54 #endregion // Local Variables
56 #region Constructors & Destructor
57 public ResXResourceReader (Stream stream)
60 throw new ArgumentNullException ("Value cannot be null.");
63 throw new ArgumentException ("Stream was not readable.");
68 public ResXResourceReader (Stream stream, ITypeResolutionService typeresolver)
71 this.typeresolver = typeresolver;
74 public ResXResourceReader (string fileName)
76 this.fileName = fileName;
79 public ResXResourceReader (string fileName, ITypeResolutionService typeresolver)
82 this.typeresolver = typeresolver;
85 public ResXResourceReader (TextReader reader)
90 public ResXResourceReader (TextReader reader, ITypeResolutionService typeresolver)
93 this.typeresolver = typeresolver;
96 ~ResXResourceReader ()
100 #endregion // Constructors & Destructor
103 public string BasePath {
104 get { return basepath; }
105 set { basepath = value; }
109 #region Private Methods
111 static string get_attr (XmlTextReader xtr, string name)
113 if (!xtr.HasAttributes)
115 for (int i = 0; i < xtr.AttributeCount; i++) {
116 xtr.MoveToAttribute (i);
117 if (String.Compare (xtr.Name, name, true) == 0) {
118 string v = xtr.Value;
119 xtr.MoveToElement ();
123 xtr.MoveToElement ();
127 private void load_data ()
129 if (fileName != null) {
130 stream = File.OpenRead (fileName);
134 XmlTextReader xtr = null;
135 if (stream != null) {
136 xtr = new XmlTextReader (stream);
137 } else if (reader != null) {
138 xtr = new XmlTextReader (reader);
142 throw new InvalidOperationException ("ResourceReader is closed.");
145 xtr.WhitespaceHandling = WhitespaceHandling.None;
147 ResXHeader header = new ResXHeader ();
149 while (xtr.Read ()) {
150 if (xtr.NodeType != XmlNodeType.Element)
153 switch (xtr.LocalName) {
155 parse_header_node (xtr, header);
158 parse_data_node (xtr);
163 } catch (XmlException ex) {
164 throw new ArgumentException ("Invalid ResX input.", ex);
165 } catch (Exception ex) {
166 XmlException xex = new XmlException (ex.Message, ex,
167 xtr.LineNumber, xtr.LinePosition);
168 throw new ArgumentException ("Invalid ResX input.", xex);
171 } catch (Exception ex) {
172 throw new ArgumentException ("Invalid ResX input.", ex);
178 if (fileName != null) {
185 void parse_header_node (XmlTextReader xtr, ResXHeader header)
187 string v = get_attr (xtr, "name");
191 if (String.Compare (v, "resmimetype", true) == 0) {
192 header.ResMimeType = get_header_value (xtr);
193 } else if (String.Compare (v, "reader", true) == 0) {
194 header.Reader = get_header_value (xtr);
195 } else if (String.Compare (v, "version", true) == 0) {
196 header.Version = get_header_value (xtr);
197 } else if (String.Compare (v, "writer", true) == 0) {
198 header.Writer = get_header_value (xtr);
202 string get_header_value (XmlTextReader xtr)
205 xtr.ReadStartElement ();
206 if (xtr.NodeType == XmlNodeType.Element) {
207 value = xtr.ReadElementString ();
209 value = xtr.Value.Trim ();
214 void parse_data_node (XmlTextReader xtr)
216 string name = get_attr (xtr, "name");
217 string type_name = get_attr (xtr, "type");
218 string mime_type = get_attr (xtr, "mimetype");
220 Type type = type_name == null ? null : ResolveType (type_name);
222 if (type_name != null && type == null)
223 throw new ArgumentException (String.Format (
224 "The type '{0}' of the element '{1}' could not be resolved.", type_name, name));
226 if (type == typeof (ResXNullRef)) {
231 string value = get_data_value (xtr);
235 TypeConverter c = TypeDescriptor.GetConverter (type);
239 } else if (c.CanConvertFrom (typeof (string))) {
241 if (BasePath != null && type == typeof (ResXFileRef)) {
242 string [] parts = ResXFileRef.Parse (value);
243 parts [0] = Path.Combine (BasePath, parts [0]);
244 obj = c.ConvertFromInvariantString (string.Join (";", parts));
246 obj = c.ConvertFromInvariantString (value);
249 obj = c.ConvertFromInvariantString (value);
251 } else if (c.CanConvertFrom (typeof (byte []))) {
252 obj = c.ConvertFrom (Convert.FromBase64String (value));
254 // the type must be a byte[]
255 obj = Convert.FromBase64String (value);
257 } else if (mime_type != null && mime_type != String.Empty) {
258 if (mime_type == ResXResourceWriter.BinSerializedObjectMimeType) {
259 byte [] data = Convert.FromBase64String (value);
260 BinaryFormatter f = new BinaryFormatter ();
261 using (MemoryStream s = new MemoryStream (data)) {
262 obj = f.Deserialize (s);
277 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
278 "Could not find a name for a resource. The resource value "
279 + "was '{0}'.", obj));
285 // Returns the value of the next XML element with the specified
286 // name from the reader. canBeCdata == true specifies that
287 // the element may be a CDATA node as well.
288 static string get_data_value (XmlTextReader xtr)
292 while (xtr.Read ()) {
293 if (xtr.NodeType == XmlNodeType.EndElement && xtr.LocalName == "data")
295 if (xtr.NodeType == XmlNodeType.Element) {
296 if (xtr.Name == "value") {
297 value = xtr.ReadString ();
301 value = xtr.Value.Trim ();
305 if (xtr.NodeType == XmlNodeType.Element) {
306 value = xtr.ReadElementString ();
308 value = xtr.Value.Trim ();
312 value = string.Empty;
317 private Type ResolveType (string type)
319 if (typeresolver == null) {
320 return Type.GetType (type);
322 return typeresolver.GetType (type);
325 #endregion // Private Methods
327 #region Public Methods
330 if (reader != null) {
336 public IDictionaryEnumerator GetEnumerator ()
339 hasht = new Hashtable ();
342 return hasht.GetEnumerator ();
345 IEnumerator IEnumerable.GetEnumerator ()
347 return ((IResourceReader) this).GetEnumerator ();
350 void IDisposable.Dispose ()
353 GC.SuppressFinalize (this);
356 protected virtual void Dispose (bool disposing)
363 public static ResXResourceReader FromFileContents (string fileContents)
365 return new ResXResourceReader (new StringReader (fileContents));
368 public static ResXResourceReader FromFileContents (string fileContents, ITypeResolutionService typeResolver)
370 return new ResXResourceReader (new StringReader (fileContents), typeResolver);
373 #endregion // Public Methods
375 private class ResXHeader
377 public string ResMimeType {
378 get { return _resMimeType; }
379 set { _resMimeType = value; }
382 public string Reader {
383 get { return _reader; }
384 set { _reader = value; }
387 public string Version {
388 get { return _version; }
389 set { _version = value; }
392 public string Writer {
393 get { return _writer; }
394 set { _writer = value; }
397 public void Verify ()
400 throw new ArgumentException ("Invalid ResX input. Could "
401 + "not find valid \"resheader\" tags for the ResX "
402 + "reader & writer type names.");
405 public bool IsValid {
407 if (string.Compare (ResMimeType, ResXResourceWriter.ResMimeType) != 0)
409 if (Reader == null || Writer == null)
411 string readerType = Reader.Split (',') [0].Trim ();
412 if (readerType != typeof (ResXResourceReader).FullName)
414 string writerType = Writer.Split (',') [0].Trim ();
415 if (writerType != typeof (ResXResourceWriter).FullName)
421 private string _resMimeType;
422 private string _reader;
423 private string _version;
424 private string _writer;