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
32 using System.Collections;
33 using System.Collections.Specialized;
34 using System.ComponentModel;
35 using System.ComponentModel.Design;
36 using System.Globalization;
38 using System.Resources;
39 using System.Runtime.Serialization.Formatters.Binary;
41 using System.Reflection;
43 using System.Runtime.Serialization;
45 namespace System.Resources
52 class ResXResourceReader : IResourceReader, IDisposable
54 #region Local Variables
55 private string fileName;
56 private Stream stream;
57 private TextReader reader;
58 private Hashtable hasht;
59 private ITypeResolutionService typeresolver;
60 private XmlTextReader xmlReader;
61 private string basepath;
62 private bool useResXDataNodes;
63 private AssemblyName [] assemblyNames;
64 private Hashtable hashtm;
65 #endregion // Local Variables
67 #region Constructors & Destructor
68 public ResXResourceReader (Stream stream)
71 throw new ArgumentNullException ("stream");
74 throw new ArgumentException ("Stream was not readable.");
79 public ResXResourceReader (Stream stream, ITypeResolutionService typeResolver)
82 this.typeresolver = typeResolver;
85 public ResXResourceReader (string fileName)
87 this.fileName = fileName;
90 public ResXResourceReader (string fileName, ITypeResolutionService typeResolver)
93 this.typeresolver = typeResolver;
96 public ResXResourceReader (TextReader reader)
101 public ResXResourceReader (TextReader reader, ITypeResolutionService typeResolver)
104 this.typeresolver = typeResolver;
107 public ResXResourceReader (Stream stream, AssemblyName [] assemblyNames)
110 this.assemblyNames = assemblyNames;
113 public ResXResourceReader (string fileName, AssemblyName [] assemblyNames)
116 this.assemblyNames = assemblyNames;
119 public ResXResourceReader (TextReader reader, AssemblyName [] assemblyNames)
122 this.assemblyNames = assemblyNames;
125 ~ResXResourceReader ()
129 #endregion // Constructors & Destructor
131 public string BasePath {
132 get { return basepath; }
133 set { basepath = value; }
136 public bool UseResXDataNodes {
137 get { return useResXDataNodes; }
139 if (xmlReader != null)
140 throw new InvalidOperationException ();
141 useResXDataNodes = value;
145 #region Private Methods
146 private void LoadData ()
148 hasht = new Hashtable ();
149 hashtm = new Hashtable ();
150 if (fileName != null) {
151 stream = File.OpenRead (fileName);
156 if (stream != null) {
157 xmlReader = new XmlTextReader (stream);
158 } else if (reader != null) {
159 xmlReader = new XmlTextReader (reader);
162 if (xmlReader == null) {
163 throw new InvalidOperationException ("ResourceReader is closed.");
166 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
168 ResXHeader header = new ResXHeader ();
170 while (xmlReader.Read ()) {
171 if (xmlReader.NodeType != XmlNodeType.Element)
174 switch (xmlReader.LocalName) {
176 ParseHeaderNode (header);
179 ParseDataNode (false);
182 ParseDataNode (true);
186 } catch (XmlException ex) {
187 throw new ArgumentException ("Invalid ResX input.", ex);
188 } catch (SerializationException ex) {
190 } catch (TargetInvocationException ex) {
192 } catch (Exception ex) {
193 XmlException xex = new XmlException (ex.Message, ex,
194 xmlReader.LineNumber, xmlReader.LinePosition);
195 throw new ArgumentException ("Invalid ResX input.", xex);
199 if (fileName != null) {
207 private void ParseHeaderNode (ResXHeader header)
209 string v = GetAttribute ("name");
213 if (String.Compare (v, "resmimetype", true) == 0) {
214 header.ResMimeType = GetHeaderValue ();
215 } else if (String.Compare (v, "reader", true) == 0) {
216 header.Reader = GetHeaderValue ();
217 } else if (String.Compare (v, "version", true) == 0) {
218 header.Version = GetHeaderValue ();
219 } else if (String.Compare (v, "writer", true) == 0) {
220 header.Writer = GetHeaderValue ();
224 private string GetHeaderValue ()
227 xmlReader.ReadStartElement ();
228 if (xmlReader.NodeType == XmlNodeType.Element) {
229 value = xmlReader.ReadElementString ();
231 value = xmlReader.Value.Trim ();
236 private string GetAttribute (string name)
238 if (!xmlReader.HasAttributes)
240 for (int i = 0; i < xmlReader.AttributeCount; i++) {
241 xmlReader.MoveToAttribute (i);
242 if (String.Compare (xmlReader.Name, name, true) == 0) {
243 string v = xmlReader.Value;
244 xmlReader.MoveToElement ();
248 xmlReader.MoveToElement ();
252 private string GetDataValue (bool meta, out string comment)
256 while (xmlReader.Read ()) {
257 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
260 if (xmlReader.NodeType == XmlNodeType.Element) {
261 if (xmlReader.Name.Equals ("value")) {
262 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
263 value = xmlReader.ReadString ();
264 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
265 } else if (xmlReader.Name.Equals ("comment")) {
266 xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
267 comment = xmlReader.ReadString ();
268 xmlReader.WhitespaceHandling = WhitespaceHandling.None;
269 if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
274 value = xmlReader.Value.Trim ();
279 private void ParseDataNode (bool meta)
281 Hashtable hashtable = ((meta && ! useResXDataNodes) ? hashtm : hasht);
282 Point pos = new Point (xmlReader.LineNumber, xmlReader.LinePosition);
283 string name = GetAttribute ("name");
284 string type_name = GetAttribute ("type");
285 string mime_type = GetAttribute ("mimetype");
288 string comment = null;
289 string value = GetDataValue (meta, out comment);
291 ResXDataNode node = new ResXDataNode (name, mime_type, type_name, value, comment, pos, BasePath);
293 if (useResXDataNodes) {
294 hashtable [name] = node;
299 throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
300 "Could not find a name for a resource. The resource value was '{0}'.",
301 node.GetValue ((AssemblyName []) null).ToString()));
303 // useResXDataNodes is false, add to dictionary of values
304 if (assemblyNames != null) {
306 hashtable [name] = node.GetValue (assemblyNames);
307 } catch (TypeLoadException ex) {
308 // different error messages depending on type of resource, hacky solution
309 if (node.handler is TypeConverterFromResXHandler)
310 hashtable [name] = null;
314 } else { // there is a typeresolver or its null
316 hashtable [name] = node.GetValue (typeresolver);
317 } catch (TypeLoadException ex) {
318 if (node.handler is TypeConverterFromResXHandler)
319 hashtable [name] = null;
327 #endregion // Private Methods
329 #region Public Methods
332 if (reader != null) {
338 public IDictionaryEnumerator GetEnumerator ()
343 return hasht.GetEnumerator ();
346 IEnumerator IEnumerable.GetEnumerator ()
348 return ((IResourceReader) this).GetEnumerator ();
351 void IDisposable.Dispose ()
354 GC.SuppressFinalize (this);
357 protected virtual void Dispose (bool disposing)
364 public static ResXResourceReader FromFileContents (string fileContents)
366 return new ResXResourceReader (new StringReader (fileContents));
369 public static ResXResourceReader FromFileContents (string fileContents, ITypeResolutionService typeResolver)
371 return new ResXResourceReader (new StringReader (fileContents), typeResolver);
374 public static ResXResourceReader FromFileContents (string fileContents, AssemblyName [] assemblyNames)
376 return new ResXResourceReader (new StringReader (fileContents), assemblyNames);
379 public IDictionaryEnumerator GetMetadataEnumerator ()
383 return hashtm.GetEnumerator ();
386 #endregion // Public Methods
388 #region Internal Classes
389 private class ResXHeader
391 private string resMimeType;
392 private string reader;
393 private string version;
394 private string writer;
396 public string ResMimeType
398 get { return resMimeType; }
399 set { resMimeType = value; }
402 public string Reader {
403 get { return reader; }
404 set { reader = value; }
407 public string Version {
408 get { return version; }
409 set { version = value; }
412 public string Writer {
413 get { return writer; }
414 set { writer = value; }
417 public void Verify ()
420 throw new ArgumentException ("Invalid ResX input. Could "
421 + "not find valid \"resheader\" tags for the ResX "
422 + "reader & writer type names.");
425 public bool IsValid {
427 if (string.Compare (ResMimeType, ResXResourceWriter.ResMimeType) != 0)
429 if (Reader == null || Writer == null)
431 string readerType = Reader.Split (',') [0].Trim ();
432 if (readerType != typeof (ResXResourceReader).FullName)
434 string writerType = Writer.Split (',') [0].Trim ();
435 if (writerType != typeof (ResXResourceWriter).FullName)