ResXResourceReader.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  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.
  19. //
  20. // Copyright (c) 2004-2005 Novell, Inc.
  21. //
  22. // Authors:
  23. // Duncan Mak [email protected]
  24. // Nick Drochak [email protected]
  25. // Paolo Molaro [email protected]
  26. // Peter Bartok [email protected]
  27. // Gert Driesen [email protected]
  28. // Olivier Dufour [email protected]
  29. // Gary Barnett [email protected]
  30. using System;
  31. using System.Collections;
  32. using System.Collections.Specialized;
  33. using System.ComponentModel;
  34. using System.ComponentModel.Design;
  35. using System.Globalization;
  36. using System.IO;
  37. using System.Resources;
  38. using System.Runtime.Serialization.Formatters.Binary;
  39. using System.Xml;
  40. using System.Reflection;
  41. using System.Drawing;
  42. using System.Runtime.Serialization;
  43. namespace System.Resources
  44. {
  45. #if INSIDE_SYSTEM_WEB
  46. internal
  47. #else
  48. public
  49. #endif
  50. class ResXResourceReader : IResourceReader, IDisposable
  51. {
  52. #region Local Variables
  53. private string fileName;
  54. private Stream stream;
  55. private TextReader reader;
  56. private OrderedDictionary hasht;
  57. private ITypeResolutionService typeresolver;
  58. private XmlTextReader xmlReader;
  59. private string basepath;
  60. private bool useResXDataNodes;
  61. private AssemblyName [] assemblyNames;
  62. private OrderedDictionary hashtm;
  63. #endregion // Local Variables
  64. #region Constructors & Destructor
  65. public ResXResourceReader (Stream stream)
  66. {
  67. if (stream == null)
  68. throw new ArgumentNullException ("stream");
  69. if (!stream.CanRead)
  70. throw new ArgumentException ("Stream was not readable.");
  71. this.stream = stream;
  72. }
  73. public ResXResourceReader (Stream stream, ITypeResolutionService typeResolver)
  74. : this (stream)
  75. {
  76. this.typeresolver = typeResolver;
  77. }
  78. public ResXResourceReader (string fileName)
  79. {
  80. this.fileName = fileName;
  81. }
  82. public ResXResourceReader (string fileName, ITypeResolutionService typeResolver)
  83. : this (fileName)
  84. {
  85. this.typeresolver = typeResolver;
  86. }
  87. public ResXResourceReader (TextReader reader)
  88. {
  89. this.reader = reader;
  90. }
  91. public ResXResourceReader (TextReader reader, ITypeResolutionService typeResolver)
  92. : this (reader)
  93. {
  94. this.typeresolver = typeResolver;
  95. }
  96. public ResXResourceReader (Stream stream, AssemblyName [] assemblyNames)
  97. : this (stream)
  98. {
  99. this.assemblyNames = assemblyNames;
  100. }
  101. public ResXResourceReader (string fileName, AssemblyName [] assemblyNames)
  102. : this (fileName)
  103. {
  104. this.assemblyNames = assemblyNames;
  105. }
  106. public ResXResourceReader (TextReader reader, AssemblyName [] assemblyNames)
  107. : this (reader)
  108. {
  109. this.assemblyNames = assemblyNames;
  110. }
  111. ~ResXResourceReader ()
  112. {
  113. Dispose (false);
  114. }
  115. #endregion // Constructors & Destructor
  116. public string BasePath {
  117. get { return basepath; }
  118. set { basepath = value; }
  119. }
  120. public bool UseResXDataNodes {
  121. get { return useResXDataNodes; }
  122. set {
  123. if (xmlReader != null)
  124. throw new InvalidOperationException ();
  125. useResXDataNodes = value;
  126. }
  127. }
  128. #region Private Methods
  129. private void LoadData ()
  130. {
  131. hasht = new OrderedDictionary ();
  132. hashtm = new OrderedDictionary ();
  133. if (fileName != null) {
  134. stream = File.OpenRead (fileName);
  135. }
  136. try {
  137. xmlReader = null;
  138. if (stream != null) {
  139. xmlReader = new XmlTextReader (stream);
  140. } else if (reader != null) {
  141. xmlReader = new XmlTextReader (reader);
  142. }
  143. if (xmlReader == null) {
  144. throw new InvalidOperationException ("ResourceReader is closed.");
  145. }
  146. xmlReader.WhitespaceHandling = WhitespaceHandling.None;
  147. ResXHeader header = new ResXHeader ();
  148. try {
  149. while (xmlReader.Read ()) {
  150. if (xmlReader.NodeType != XmlNodeType.Element)
  151. continue;
  152. switch (xmlReader.LocalName) {
  153. case "resheader":
  154. ParseHeaderNode (header);
  155. break;
  156. case "data":
  157. ParseDataNode (false);
  158. break;
  159. case "metadata":
  160. ParseDataNode (true);
  161. break;
  162. }
  163. }
  164. } catch (XmlException ex) {
  165. throw new ArgumentException ("Invalid ResX input.", ex);
  166. } catch (SerializationException ex) {
  167. throw ex;
  168. } catch (TargetInvocationException ex) {
  169. throw ex;
  170. } catch (Exception ex) {
  171. XmlException xex = new XmlException (ex.Message, ex,
  172. xmlReader.LineNumber, xmlReader.LinePosition);
  173. throw new ArgumentException ("Invalid ResX input.", xex);
  174. }
  175. header.Verify ();
  176. } finally {
  177. if (fileName != null) {
  178. stream.Close ();
  179. stream = null;
  180. }
  181. xmlReader = null;
  182. }
  183. }
  184. private void ParseHeaderNode (ResXHeader header)
  185. {
  186. string v = GetAttribute ("name");
  187. if (v == null)
  188. return;
  189. if (String.Compare (v, "resmimetype", true) == 0) {
  190. header.ResMimeType = GetHeaderValue ();
  191. } else if (String.Compare (v, "reader", true) == 0) {
  192. header.Reader = GetHeaderValue ();
  193. } else if (String.Compare (v, "version", true) == 0) {
  194. header.Version = GetHeaderValue ();
  195. } else if (String.Compare (v, "writer", true) == 0) {
  196. header.Writer = GetHeaderValue ();
  197. }
  198. }
  199. private string GetHeaderValue ()
  200. {
  201. string value = null;
  202. xmlReader.ReadStartElement ();
  203. if (xmlReader.NodeType == XmlNodeType.Element) {
  204. value = xmlReader.ReadElementString ();
  205. } else {
  206. value = xmlReader.Value.Trim ();
  207. }
  208. return value;
  209. }
  210. private string GetAttribute (string name)
  211. {
  212. if (!xmlReader.HasAttributes)
  213. return null;
  214. for (int i = 0; i < xmlReader.AttributeCount; i++) {
  215. xmlReader.MoveToAttribute (i);
  216. if (String.Compare (xmlReader.Name, name, true) == 0) {
  217. string v = xmlReader.Value;
  218. xmlReader.MoveToElement ();
  219. return v;
  220. }
  221. }
  222. xmlReader.MoveToElement ();
  223. return null;
  224. }
  225. private string GetDataValue (bool meta, out string comment)
  226. {
  227. string value = null;
  228. comment = null;
  229. while (xmlReader.Read ()) {
  230. if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
  231. break;
  232. if (xmlReader.NodeType == XmlNodeType.Element) {
  233. if (xmlReader.Name.Equals ("value")) {
  234. xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
  235. value = xmlReader.ReadString ();
  236. xmlReader.WhitespaceHandling = WhitespaceHandling.None;
  237. } else if (xmlReader.Name.Equals ("comment")) {
  238. xmlReader.WhitespaceHandling = WhitespaceHandling.Significant;
  239. comment = xmlReader.ReadString ();
  240. xmlReader.WhitespaceHandling = WhitespaceHandling.None;
  241. if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.LocalName == (meta ? "metadata" : "data"))
  242. break;
  243. }
  244. }
  245. else
  246. value = xmlReader.Value.Trim ();
  247. }
  248. return value;
  249. }
  250. private void ParseDataNode (bool meta)
  251. {
  252. OrderedDictionary hashtable = ((meta && ! useResXDataNodes) ? hashtm : hasht);
  253. Point pos = new Point (xmlReader.LineNumber, xmlReader.LinePosition);
  254. string name = GetAttribute ("name");
  255. string type_name = GetAttribute ("type");
  256. string mime_type = GetAttribute ("mimetype");
  257. string comment = null;
  258. string value = GetDataValue (meta, out comment);
  259. ResXDataNode node = new ResXDataNode (name, mime_type, type_name, value, comment, pos, BasePath);
  260. if (useResXDataNodes) {
  261. hashtable [name] = node;
  262. return;
  263. }
  264. if (name == null)
  265. throw new ArgumentException (string.Format (CultureInfo.CurrentCulture,
  266. "Could not find a name for a resource. The resource value was '{0}'.",
  267. node.GetValue ((AssemblyName []) null).ToString()));
  268. // useResXDataNodes is false, add to dictionary of values
  269. if (assemblyNames != null) {
  270. try {
  271. hashtable [name] = node.GetValue (assemblyNames);
  272. } catch (TypeLoadException ex) {
  273. // different error messages depending on type of resource, hacky solution
  274. if (node.handler is TypeConverterFromResXHandler)
  275. hashtable [name] = null;
  276. else
  277. throw ex;
  278. }
  279. } else { // there is a typeresolver or its null
  280. try {
  281. hashtable [name] = node.GetValue (typeresolver);
  282. } catch (TypeLoadException ex) {
  283. if (node.handler is TypeConverterFromResXHandler)
  284. hashtable [name] = null;
  285. else
  286. throw ex;
  287. }
  288. }
  289. }
  290. #endregion // Private Methods
  291. #region Public Methods
  292. public void Close ()
  293. {
  294. if (reader != null) {
  295. reader.Close ();
  296. reader = null;
  297. }
  298. }
  299. public IDictionaryEnumerator GetEnumerator ()
  300. {
  301. if (hasht == null) {
  302. LoadData ();
  303. }
  304. return hasht.GetEnumerator ();
  305. }
  306. IEnumerator IEnumerable.GetEnumerator ()
  307. {
  308. return ((IResourceReader) this).GetEnumerator ();
  309. }
  310. void IDisposable.Dispose ()
  311. {
  312. Dispose (true);
  313. GC.SuppressFinalize (this);
  314. }
  315. protected virtual void Dispose (bool disposing)
  316. {
  317. if (disposing) {
  318. Close ();
  319. }
  320. }
  321. public static ResXResourceReader FromFileContents (string fileContents)
  322. {
  323. return new ResXResourceReader (new StringReader (fileContents));
  324. }
  325. public static ResXResourceReader FromFileContents (string fileContents, ITypeResolutionService typeResolver)
  326. {
  327. return new ResXResourceReader (new StringReader (fileContents), typeResolver);
  328. }
  329. public static ResXResourceReader FromFileContents (string fileContents, AssemblyName [] assemblyNames)
  330. {
  331. return new ResXResourceReader (new StringReader (fileContents), assemblyNames);
  332. }
  333. public IDictionaryEnumerator GetMetadataEnumerator ()
  334. {
  335. if (hashtm == null)
  336. LoadData ();
  337. return hashtm.GetEnumerator ();
  338. }
  339. #endregion // Public Methods
  340. #region Internal Classes
  341. private class ResXHeader
  342. {
  343. private string resMimeType;
  344. private string reader;
  345. private string version;
  346. private string writer;
  347. public string ResMimeType
  348. {
  349. get { return resMimeType; }
  350. set { resMimeType = value; }
  351. }
  352. public string Reader {
  353. get { return reader; }
  354. set { reader = value; }
  355. }
  356. public string Version {
  357. get { return version; }
  358. set { version = value; }
  359. }
  360. public string Writer {
  361. get { return writer; }
  362. set { writer = value; }
  363. }
  364. public void Verify ()
  365. {
  366. if (!IsValid)
  367. throw new ArgumentException ("Invalid ResX input. Could "
  368. + "not find valid \"resheader\" tags for the ResX "
  369. + "reader & writer type names.");
  370. }
  371. public bool IsValid {
  372. get {
  373. if (string.Compare (ResMimeType, ResXResourceWriter.ResMimeType) != 0)
  374. return false;
  375. if (Reader == null || Writer == null)
  376. return false;
  377. string readerType = Reader.Split (',') [0].Trim ();
  378. if (readerType != typeof (ResXResourceReader).FullName)
  379. return false;
  380. string writerType = Writer.Split (',') [0].Trim ();
  381. if (writerType != typeof (ResXResourceWriter).FullName)
  382. return false;
  383. return true;
  384. }
  385. }
  386. }
  387. #endregion
  388. }
  389. }