12b24fa5cebfc73646e767d3772d340c3d3c0266
[mono.git] / mcs / class / Managed.Windows.Forms / System.Resources / ResXResourceReader.cs
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      duncan@ximian.com
24 //      Nick Drochak    ndrochak@gol.com
25 //      Paolo Molaro    lupus@ximian.com
26 //      Peter Bartok    pbartok@novell.com
27 //
28
29 // COMPLETE
30
31 using System;
32 using System.Collections;
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
41 namespace System.Resources
42 {
43         public class ResXResourceReader : IResourceReader, IDisposable
44         {
45                 #region Local Variables
46                 private Stream                  stream;
47                 private XmlTextReader           reader;
48                 private Hashtable               hasht;
49                 private ITypeResolutionService  typeresolver;
50                 #endregion      // Local Variables
51
52                 #region Constructors & Destructor
53                 public ResXResourceReader (Stream stream)
54                 {
55                         if (stream == null)
56                                 throw new ArgumentNullException ("Value cannot be null.");
57                         
58                         if (!stream.CanRead)
59                                 throw new ArgumentException ("Stream was not readable.");
60
61                         this.stream = stream;
62                         basic_setup ();
63                 }
64
65                 public ResXResourceReader (Stream stream, ITypeResolutionService typeresolver) : this(stream) {
66                         this.typeresolver = typeresolver;
67                 }
68                 
69                 public ResXResourceReader (string fileName)
70                 {
71                         stream = File.OpenRead (fileName);
72                         basic_setup ();
73                 }
74
75                 public ResXResourceReader (string fileName, ITypeResolutionService typeresolver) : this(fileName) {
76                         this.typeresolver = typeresolver;
77                 }
78
79                 public ResXResourceReader(TextReader reader) {
80                         this.reader = new XmlTextReader(reader);
81                         this.hasht = new Hashtable();
82
83                         load_data();
84                 }
85
86                 public ResXResourceReader (TextReader reader, ITypeResolutionService typeresolver) : this(reader) {
87                         this.typeresolver = typeresolver;
88                 }
89
90                 ~ResXResourceReader() {
91                         Dispose(false);
92                 }
93                 #endregion      // Constructors & Destructor
94
95
96                 #region Private Methods
97                 void basic_setup () {
98                         reader = new XmlTextReader (stream);
99                         hasht = new Hashtable ();
100
101                         if (!IsStreamValid()){
102                                 throw new ArgumentException("Stream is not a valid .resx file!  It was possibly truncated.");
103                         }
104                         load_data ();
105                 }
106                 
107                 static string get_attr (XmlTextReader reader, string name) {
108                         if (!reader.HasAttributes)
109                                 return null;
110                         for (int i = 0; i < reader.AttributeCount; i++) {
111                                 reader.MoveToAttribute (i);
112                                 if (String.Compare (reader.Name, name, true) == 0) {
113                                         string v = reader.Value;
114                                         reader.MoveToElement ();
115                                         return v;
116                                 }
117                         }
118                         reader.MoveToElement ();
119                         return null;
120                 }
121
122
123                 static string get_value (XmlTextReader reader, string name)
124                 {
125                         return get_value (reader, name, true);
126                 }
127
128                 // Returns the value of the next XML element with the specified
129                 // name from the reader. canBeCdata == true specifies that
130                 // the element may be a CDATA node as well.
131                 static string get_value (XmlTextReader reader, string name, bool canBeCdata) {
132                         bool gotelement = false;
133                         while (reader.Read ()) {
134                                 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, name, true) == 0) {
135                                         gotelement = true;
136                                         break;
137                                 }
138                                 if (canBeCdata && reader.NodeType == XmlNodeType.CDATA)
139                                    return reader.Value;
140                         }
141                         if (!gotelement)
142                                 return null;
143                         while (reader.Read ()) {
144                                 if (reader.NodeType == XmlNodeType.Text || reader.NodeType == XmlNodeType.CDATA) {
145                                         string v = reader.Value;
146                                         return v;
147                                 }
148                                 else if (reader.NodeType == XmlNodeType.EndElement && reader.Value == string.Empty)
149                                 {
150                                         string v = reader.Value;
151                                         return v;
152                                 }
153                         }
154                         return null;
155                 }
156
157                 private bool IsStreamValid() {
158                         bool gotroot = false;
159                         bool gotmime = false;
160                         
161                         while (reader.Read ()) {
162                                 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "root", true) == 0) {
163                                         gotroot = true;
164                                         break;
165                                 }
166                         }
167                         if (!gotroot)
168                                 return false;
169                         while (reader.Read ()) {
170                                 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "resheader", true) == 0) {
171                                         string v = get_attr (reader, "name");
172                                         if (v != null && String.Compare (v, "resmimetype", true) == 0) {
173                                                 v = get_value (reader, "value");
174                                                 if (String.Compare (v, "text/microsoft-resx", true) == 0) {
175                                                         gotmime = true;
176                                                         break;
177                                                 }
178                                         }
179                                 } else if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "data", true) == 0) {
180                                         /* resheader apparently can appear anywhere, so we collect
181                                          * the data even if we haven't validated yet.
182                                          */
183                                         string n = get_attr (reader, "name");
184                                         if (n != null) {
185                                                 string v = get_value (reader, "value");
186                                                 hasht [n] = v;
187                                         }
188                                 }
189                         }
190                         return gotmime;
191                 }
192
193                 private void load_data ()
194                 {
195                         while (reader.Read ()) {
196                                 if (reader.NodeType == XmlNodeType.Element && String.Compare (reader.Name, "data", true) == 0) {
197                                         string n = get_attr (reader, "name");
198                                         string t = get_attr (reader, "type");
199                                         string mt = get_attr (reader, "mimetype");
200
201                                         Type tt = t == null ? null : Type.GetType (t);
202
203                                         if (t != null && tt == null) {
204                                                 throw new SystemException ("The type `" + t +"' could not be resolved");
205                                         }
206                                         if (tt == typeof (ResXNullRef)) {
207                                                 hasht [n] = null;
208                                                 continue;
209                                         }
210                                         if (n != null) {
211                                                 object v = null;
212                                                 string val = get_value (reader, "value");
213
214                                                 if ((mt != null) && (mt.Length > 0) && (tt != null)) {
215                                                         TypeConverter c = TypeDescriptor.GetConverter (tt);
216                                                         v = c.ConvertFrom (Convert.FromBase64String (val));
217                                                 } else if (tt != null) {
218                                                         // MS seems to handle Byte[] without any mimetype :-(
219                                                         if (t.StartsWith("System.Byte[], mscorlib")) {
220                                                                 v = Convert.FromBase64String(val);
221                                                         } else {
222                                                                 TypeConverter c = TypeDescriptor.GetConverter (tt);
223                                                                 v = c.ConvertFromInvariantString (val);
224                                                         }
225                                                 } else if ((mt != null) && (mt.Length > 0)) {
226                                                         byte [] data = Convert.FromBase64String (val);
227                                                         BinaryFormatter f = new BinaryFormatter ();
228                                                         using (MemoryStream s = new MemoryStream (data)) {
229                                                                 v = f.Deserialize (s);
230                                                         }
231                                                 } else {
232                                                         v = val;
233                                                 }
234                                                 hasht [n] = v;
235                                         }
236                                 }
237                         }
238                 }
239
240                 private Type GetType(string type) {
241                         if (typeresolver == null) {
242                                 return Type.GetType(type);
243                         } else {
244                                 return typeresolver.GetType(type);
245                         }
246                 }
247                 #endregion      // Private Methods
248
249                 #region Public Methods
250                 public void Close ()
251                 {
252                         if (stream != null) {
253                                 stream.Close ();
254                                 stream = null;
255                         }
256
257                         if (reader != null) {
258                                 reader.Close();
259                                 reader = null;
260                         }
261                 }
262                 
263                 public IDictionaryEnumerator GetEnumerator () {
264                         if (null == stream){
265                                 throw new InvalidOperationException("ResourceReader is closed.");
266                         }
267                         else {
268                                 return hasht.GetEnumerator ();
269                         }
270                 }
271                 
272                 IEnumerator IEnumerable.GetEnumerator ()
273                 {
274                         return ((IResourceReader) this).GetEnumerator();
275                 }
276                 
277                 void IDisposable.Dispose ()
278                 {
279                         Dispose(true);
280                         GC.SuppressFinalize(this);
281                 }
282
283                 protected virtual void Dispose(bool disposing) {
284                         if (disposing) {
285                                 Close();
286                         }
287                 }
288
289                 public static ResXResourceReader FromFileContents(string fileContents) {
290                         return new ResXResourceReader(new StringReader(fileContents));
291                 }
292
293                 public static ResXResourceReader FromFileContents(string fileContents, ITypeResolutionService typeResolver) {
294                         return new ResXResourceReader(new StringReader(fileContents), typeResolver);
295                 }
296
297                 #endregion      // Public Methods
298                 
299         }  // public sealed class ResXResourceReader
300 } // namespace System.Resources