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
28 // Olivier Dufour olivier.duff@gmail.com
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;
40 using System.Reflection;
43 namespace System.Resources
50 class ResXResourceReader : IResourceReader, IDisposable
52 #region Local Variables
53 private string fileName;
54 private Stream stream;
55 private TextReader reader;
56 private Hashtable hasht;
57 private ITypeResolutionService typeresolver;
58 private XmlTextReader xmlReader;
61 private string basepath;
62 private bool useResXDataNodes;
63 private AssemblyName [] assemblyNames;
64 private Hashtable hashtm;
66 #endregion // Local Variables
68 #region Constructors & Destructor
69 public ResXResourceReader (Stream stream)
72 throw new ArgumentNullException ("stream");
75 throw new ArgumentException ("Stream was not readable.");
80 public ResXResourceReader (Stream stream, ITypeResolutionService typeResolver)
83 this.typeresolver = typeResolver;
86 public ResXResourceReader (string fileName)
88 this.fileName = fileName;
91 public ResXResourceReader (string fileName, ITypeResolutionService typeResolver)
94 this.typeresolver = typeResolver;
97 public ResXResourceReader (TextReader reader)
102 public ResXResourceReader (TextReader reader, ITypeResolutionService typeResolver)
105 this.typeresolver = typeResolver;
110 public ResXResourceReader (Stream stream, AssemblyName [] assemblyNames)
113 this.assemblyNames = assemblyNames;
116 public ResXResourceReader (string fileName, AssemblyName [] assemblyNames)
119 this.assemblyNames = assemblyNames;
122 public ResXResourceReader (TextReader reader, AssemblyName [] assemblyNames)
125 this.assemblyNames = assemblyNames;
130 ~ResXResourceReader ()
134 #endregion // Constructors & Destructor
137 public string BasePath {
138 get { return basepath; }
139 set { basepath = value; }
142 public bool UseResXDataNodes {
143 get { return useResXDataNodes; }
145 if (xmlReader != null)
146 throw new InvalidOperationException ();
147 useResXDataNodes = value;
152 #region Private Methods
153 private void LoadData ()
155 hasht = new Hashtable ();
157 hashtm = new Hashtable ();
159 if (fileName != null) {
160 stream = File.OpenRead (fileName);
165 if (stream != null) {
166 xmlReader = new XmlTextReader (stream);
167 } else if (reader != null) {
168 xmlReader = new XmlTextReader (reader);
171 if (xmlReader == null) {
172 throw new InvalidOperationException ("ResourceReader is closed.");
175 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
177 ResXHeader header = new ResXHeader ();
179 while (xmlReader.Read ()) {
180 if (xmlReader.NodeType != XmlNodeType.Element)
183 switch (xmlReader.LocalName) {
185 ParseHeaderNode (header);
188 ParseDataNode (false);
192 ParseDataNode (true);
198 } catch (XmlException ex) {
199 throw new ArgumentException ("Invalid ResX input.", ex);
200 } catch (Exception ex) {
201 XmlException xex = new XmlException (ex.Message, ex,
202 xmlReader.LineNumber, xmlReader.LinePosition);
203 throw new ArgumentException ("Invalid ResX input.", xex);
206 } catch (Exception ex) {
207 throw new ArgumentException ("Invalid ResX input.", ex);
212 if (fileName != null) {
220 private void ParseHeaderNode (ResXHeader header)
222 string v = GetAttribute ("name");
226 if (String.Compare (v, "resmimetype", true) == 0) {
227 header.ResMimeType = GetHeaderValue ();
228 } else if (String.Compare (v, "reader", true) == 0) {
229 header.Reader = GetHeaderValue ();
230 } else if (String.Compare (v, "version", true) == 0) {
231 header.Version = GetHeaderValue ();
232 } else if (String.Compare (v, "writer", true) == 0) {
233 header.Writer = GetHeaderValue ();
237 private string GetHeaderValue ()
240 xmlReader.ReadStartElement ();
241 if (xmlReader.NodeType == XmlNodeType.Element) {
242 value = xmlReader.ReadElementString ();
244 value = xmlReader.Value.Trim ();
249 private string GetAttribute (string name)
251 if (!xmlReader.HasAttributes)
253 for (int i = 0; i < xmlReader.AttributeCount; i++) {
254 xmlReader.MoveToAttribute (i);
255 if (String.Compare (xmlReader.Name, name, true) == 0) {
256 string v = xmlReader.Value;
257 xmlReader.MoveToElement ();
261 xmlReader.MoveToElement ();
265 private string GetDataValue (bool meta, out string comment)
270 while (xmlReader.Read ()) {
271 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
274 if (xmlReader.NodeType == XmlNodeType.Element) {
275 if (xmlReader.Name.Equals ("value")) {
276 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
277 value = xmlReader.ReadString ();
278 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
279 } else if (xmlReader.Name.Equals ("comment")) {
280 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
281 comment = xmlReader.ReadString ();
282 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
283 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
288 value = xmlReader.Value.Trim ();
292 if (xmlReader.NodeType == XmlNodeType.Element) {
293 value = xmlReader.ReadElementString ();
295 value = xmlReader.Value.Trim ();
299 value = string.Empty;
304 private void ParseDataNode (bool meta)
307 Hashtable hashtable = ((meta && ! useResXDataNodes) ? hashtm : hasht);
308 Point pos = new Point (xmlReader.LineNumber, xmlReader.LinePosition);
310 Hashtable hashtable = hasht;
312 string name = GetAttribute ("name");
313 string type_name = GetAttribute ("type");
314 string mime_type = GetAttribute ("mimetype");
317 Type type = type_name == null ? null : ResolveType (type_name);
319 if (type_name != null && type == null)
320 throw new ArgumentException (String.Format (
321 "The type '{0}' of the element '{1}' could not be resolved.", type_name, name));
323 if (type == typeof (ResXNullRef)) {
326 if (useResXDataNodes)
327 hashtable [name] = new ResXDataNode (name, null, pos);
330 hashtable [name] = null;
334 string comment = null;
335 string value = GetDataValue (meta, out comment);
338 if (mime_type != null && mime_type.Length > 0) {
339 if (mime_type == ResXResourceWriter.BinSerializedObjectMimeType) {
340 byte [] data = Convert.FromBase64String (value);
341 BinaryFormatter f = new BinaryFormatter ();
342 using (MemoryStream s = new MemoryStream (data)) {
343 obj = f.Deserialize (s);
345 } else if (mime_type == ResXResourceWriter.ByteArraySerializedObjectMimeType) {
347 TypeConverter c = TypeDescriptor.GetConverter (type);
348 if (c.CanConvertFrom (typeof (byte [])))
349 obj = c.ConvertFrom (Convert.FromBase64String (value));
352 } else if (type != null) {
353 if (type == typeof (byte [])) {
354 obj = Convert.FromBase64String (value);
356 TypeConverter c = TypeDescriptor.GetConverter (type);
357 if (c.CanConvertFrom (typeof (string))) {
359 if (BasePath != null && type == typeof (ResXFileRef)) {
360 string [] parts = ResXFileRef.Parse (value);
361 parts [0] = Path.Combine (BasePath, parts [0]);
362 obj = c.ConvertFromInvariantString (string.Join (";", parts));
364 obj = c.ConvertFromInvariantString (value);
367 obj = c.ConvertFromInvariantString (value);
381 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
382 "Could not find a name for a resource. The resource value "
383 + "was '{0}'.", obj));
385 if (useResXDataNodes)
387 ResXDataNode dataNode = new ResXDataNode(name, obj, pos);
388 dataNode.Comment = comment;
389 hashtable [name] = dataNode;
394 hashtable [name] = obj;
397 private Type ResolveType (string type)
399 if (typeresolver != null) {
400 return typeresolver.GetType (type);
403 if (assemblyNames != null) {
405 foreach (AssemblyName assem in assemblyNames) {
406 Assembly myAssembly = Assembly.Load (assem);
407 result = myAssembly.GetType (type, false);
412 //if type not found on assembly list we return null or we get from current assembly?
413 //=> unit test needed
416 return Type.GetType (type);
419 #endregion // Private Methods
421 #region Public Methods
424 if (reader != null) {
430 public IDictionaryEnumerator GetEnumerator ()
435 return hasht.GetEnumerator ();
438 IEnumerator IEnumerable.GetEnumerator ()
440 return ((IResourceReader) this).GetEnumerator ();
443 void IDisposable.Dispose ()
446 GC.SuppressFinalize (this);
449 protected virtual void Dispose (bool disposing)
456 public static ResXResourceReader FromFileContents (string fileContents)
458 return new ResXResourceReader (new StringReader (fileContents));
461 public static ResXResourceReader FromFileContents (string fileContents, ITypeResolutionService typeResolver)
463 return new ResXResourceReader (new StringReader (fileContents), typeResolver);
466 public static ResXResourceReader FromFileContents (string fileContents, AssemblyName [] assemblyNames)
468 return new ResXResourceReader (new StringReader (fileContents), assemblyNames);
471 public IDictionaryEnumerator GetMetadataEnumerator ()
475 return hashtm.GetEnumerator ();
478 #endregion // Public Methods
480 #region Internal Classes
481 private class ResXHeader
483 private string resMimeType;
484 private string reader;
485 private string version;
486 private string writer;
488 public string ResMimeType
490 get { return resMimeType; }
491 set { resMimeType = value; }
494 public string Reader {
495 get { return reader; }
496 set { reader = value; }
499 public string Version {
500 get { return version; }
501 set { version = value; }
504 public string Writer {
505 get { return writer; }
506 set { writer = value; }
509 public void Verify ()
512 throw new ArgumentException ("Invalid ResX input. Could "
513 + "not find valid \"resheader\" tags for the ResX "
514 + "reader & writer type names.");
517 public bool IsValid {
519 if (string.Compare (ResMimeType, ResXResourceWriter.ResMimeType) != 0)
521 if (Reader == null || Writer == null)
523 string readerType = Reader.Split (',') [0].Trim ();
524 if (readerType != typeof (ResXResourceReader).FullName)
526 string writerType = Writer.Split (',') [0].Trim ();
527 if (writerType != typeof (ResXResourceWriter).FullName)