Robert Jordan wrote:
Indeed, Mono's ResXResourceWriter uses ConvertTo (typeof (byte[]))
when a certain TypeConverter supports it, but fails to convert
it back, unless the patch I attached is applied.
But the real issue still remains: why does MS.NET not use ConvertTo
(typeof (byte[]))? Because the type is serializable? I'll run some
tests...
The attached patches fix this problem together with some other
issues I found while testing (see changelogs).
The test cases pass on MS.NET as well.
Robert
Index: System.Resources/ChangeLog
===================================================================
--- System.Resources/ChangeLog (revision 66181)
+++ System.Resources/ChangeLog (working copy)
@@ -1,3 +1,14 @@
+2006-10-03 Robert Jordan <[EMAIL PROTECTED]>
+
+ * ResXResourceReader.cs: Factored out parse_data_node () from load_data
()
+ to be able to correctly handle data nodes that occur before resheader.
+ Fixed handling of objects that have a byte[] converter.
+
+ * ResXResourceWriter.cs: Fixed AddResource (string, object) to support
+ only serializable type, matching MS.
+ Fixed WriteBytes to not emit the mimetype attribute when the
+ type is byte[], otherwise MS.NET won't parse correctly.
+
2006-10-02 Sebastien Pouliot <[EMAIL PROTECTED]>
* ResXResourceReader.cs: Handle empty mimetype just like a null
Index: System.Resources/ResXResourceReader.cs
===================================================================
--- System.Resources/ResXResourceReader.cs (revision 66181)
+++ System.Resources/ResXResourceReader.cs (working copy)
@@ -180,11 +180,7 @@
/* resheader apparently can appear
anywhere, so we collect
* the data even if we haven't
validated yet.
*/
- string n = get_attr (reader, "name");
- if (n != null) {
- string v = get_value (reader,
"value");
- hasht [n] = v;
- }
+ parse_data_node ();
}
}
return gotmime;
@@ -193,51 +189,66 @@
private void load_data ()
{
while (reader.Read ()) {
- if (reader.NodeType == XmlNodeType.Element &&
String.Compare (reader.Name, "data", true) == 0) {
- string n = get_attr (reader, "name");
- string t = get_attr (reader, "type");
- string mt = get_attr (reader,
"mimetype");
+ if (reader.NodeType == XmlNodeType.Element &&
String.Compare (reader.Name, "data", true) == 0)
+ parse_data_node ();
- Type tt = t == null ? null :
Type.GetType (t);
+ }
+ }
- if (t != null && tt == null) {
- throw new SystemException ("The
type `" + t +"' could not be resolved");
- }
- if (tt == typeof (ResXNullRef)) {
- hasht [n] = null;
- continue;
- }
- if (n != null) {
- object v = null;
- string val = get_value (reader,
"value");
+ void parse_data_node ()
+ {
+ string name = get_attr (reader, "name");
+ string type_name = get_attr (reader, "type");
+ string mime_type = get_attr (reader, "mimetype");
- if ((mt != null) && (mt.Length
> 0) && (tt != null)) {
- TypeConverter c =
TypeDescriptor.GetConverter (tt);
- v = c.ConvertFrom
(Convert.FromBase64String (val));
- } else if (tt != null) {
- // MS seems to handle
Byte[] without any mimetype :-(
- if
(t.StartsWith("System.Byte[], mscorlib")) {
- v =
Convert.FromBase64String(val);
- } else {
- TypeConverter c
= TypeDescriptor.GetConverter (tt);
- v =
c.ConvertFromInvariantString (val);
- }
- } else if ((mt != null) &&
(mt.Length > 0)) {
- byte [] data =
Convert.FromBase64String (val);
- BinaryFormatter f = new
BinaryFormatter ();
- using (MemoryStream s =
new MemoryStream (data)) {
- v =
f.Deserialize (s);
- }
- } else {
- v = val;
- }
- hasht [n] = v;
+ if (name == null)
+ return;
+
+ Type type = type_name == null ? null : ResolveType
(type_name);
+
+ if (type_name != null && type == null)
+ throw new ArgumentException (String.Format (
+ "The type '{0}' of the element '{1}'
could not be resolved.", type_name, name));
+
+ if (type == typeof (ResXNullRef)) {
+ hasht [name] = null;
+ return;
+ }
+
+ string value = get_value (reader, "value");
+ object obj = null;
+
+ if (type != null) {
+ TypeConverter c = TypeDescriptor.GetConverter
(type);
+
+ if (c == null) {
+ obj = null;
+ } else if (c.CanConvertFrom (typeof (string))) {
+ obj = c.ConvertFromInvariantString
(value);
+ } else if (c.CanConvertFrom (typeof (byte[]))) {
+ obj = c.ConvertFrom
(Convert.FromBase64String (value));
+ } else {
+ // the type must be a byte[]
+ obj = Convert.FromBase64String(value);
+ }
+ } else if (mime_type != null && mime_type !=
String.Empty) {
+ if (mime_type ==
ResXResourceWriter.BinSerializedObjectMimeType) {
+ byte [] data = Convert.FromBase64String
(value);
+ BinaryFormatter f = new BinaryFormatter
();
+ using (MemoryStream s = new
MemoryStream (data)) {
+ obj = f.Deserialize (s);
}
+ } else {
+ // invalid mime type
+ obj = null;
}
+ } else {
+ obj = value;
}
+ hasht [name] = obj;
}
- private Type GetType(string type) {
+ private Type ResolveType (string type) {
if (typeresolver == null) {
return Type.GetType(type);
} else {
Index: System.Resources/ResXResourceWriter.cs
===================================================================
--- System.Resources/ResXResourceWriter.cs (revision 66181)
+++ System.Resources/ResXResourceWriter.cs (working copy)
@@ -138,18 +138,21 @@
writer.WriteString(sb.ToString());
}
- void WriteBytes (string name, string typename, byte [] value,
int offset, int length)
+ void WriteBytes (string name, Type type, byte [] value, int
offset, int length)
{
writer.WriteStartElement ("data");
writer.WriteAttributeString ("name", name);
- if (typename != null) {
- writer.WriteAttributeString ("type", typename);
+ if (type != null) {
+ writer.WriteAttributeString ("type",
type.AssemblyQualifiedName);
+ // byte[] should never get a mimetype,
otherwise MS.NET won't be able
+ // to parse the data.
+ if (type != typeof (byte[]))
+ writer.WriteAttributeString ("mimetype",
ByteArraySerializedObjectMimeType);
writer.WriteStartElement ("value");
WriteNiceBase64(value, offset, length);
} else {
- writer.WriteAttributeString ("mimetype",
-
"application/x-microsoft.net.object.binary.base64");
+ writer.WriteAttributeString ("mimetype",
BinSerializedObjectMimeType);
writer.WriteStartElement ("value");
writer.WriteBase64 (value, offset, length);
}
@@ -158,9 +161,9 @@
writer.WriteEndElement ();
}
- void WriteBytes (string name, string typename, byte [] value)
+ void WriteBytes (string name, Type type, byte [] value)
{
- WriteBytes (name, typename, value, 0, value.Length);
+ WriteBytes (name, type, value, 0, value.Length);
}
void WriteString (string name, string value)
@@ -168,12 +171,12 @@
WriteString (name, value, null);
}
- void WriteString (string name, string value, string typename)
+ void WriteString (string name, string value, Type type)
{
writer.WriteStartElement ("data");
writer.WriteAttributeString ("name", name);
- if (typename != null)
- writer.WriteAttributeString ("type", typename);
+ if (type != null)
+ writer.WriteAttributeString ("type",
type.AssemblyQualifiedName);
writer.WriteStartElement ("value");
writer.WriteString (value);
writer.WriteEndElement ();
@@ -195,7 +198,7 @@
if (writer == null)
InitWriter ();
- WriteBytes (name, value.GetType
().AssemblyQualifiedName, value);
+ WriteBytes (name, value.GetType (), value);
}
public void AddResource (string name, object value)
@@ -216,6 +219,9 @@
if (value == null)
throw new ArgumentNullException ("value");
+ if (!value.GetType ().IsSerializable)
+ throw new InvalidOperationException
(String.Format ("The element '{0}' of type '{1}' is not serializable.", name,
value.GetType ().Name));
+
if (written)
throw new InvalidOperationException ("The
resource is already generated.");
@@ -225,13 +231,13 @@
TypeConverter converter = TypeDescriptor.GetConverter
(value);
if (converter != null && converter.CanConvertTo (typeof
(string)) && converter.CanConvertFrom (typeof (string))) {
string str = (string)
converter.ConvertToInvariantString (value);
- WriteString (name, str, value.GetType
().AssemblyQualifiedName);
+ WriteString (name, str, value.GetType ());
return;
}
if (converter != null && converter.CanConvertTo (typeof
(byte[])) && converter.CanConvertFrom (typeof (byte[]))) {
byte[] b = (byte[]) converter.ConvertTo (value,
typeof (byte[]));
- WriteBytes (name,
value.GetType().AssemblyQualifiedName, b);
+ WriteBytes (name, value.GetType (), b);
return;
}
Index: Test/System.Resources/WriterTest.cs
===================================================================
--- Test/System.Resources/WriterTest.cs (revision 66185)
+++ Test/System.Resources/WriterTest.cs (working copy)
@@ -7,9 +7,12 @@
using System;
using System.Collections;
+using System.ComponentModel;
using System.Drawing;
+using System.Globalization;
using System.IO;
using System.Resources;
+using System.Text;
using NUnit.Framework;
namespace MonoTests.System.Resources
@@ -33,6 +36,16 @@
w.AddResource ("ByteArray2", (object) new byte[] {15,
16, 17});
w.AddResource ("IntArray", new int[] {1012, 1013,
1014});
w.AddResource ("StringArray", new string[] {"hello",
"world"});
+ w.AddResource ("Image", new Bitmap (1, 1));
+ w.AddResource ("StrType", new MyStrType ("hello"));
+ w.AddResource ("BinType", new MyBinType ("world"));
+
+ try {
+ w.AddResource ("NonSerType", new
MyNonSerializableType ());
+ Assert.Fail ("#0");
+ } catch (InvalidOperationException) {
+ }
+
w.Generate ();
w.Close ();
@@ -53,8 +66,141 @@
Assert.AreEqual (16, ((byte[]) h["ByteArray2"])[1],
"#8");
Assert.AreEqual (1013, ((int[]) h["IntArray"])[1],
"#9");
Assert.AreEqual ("world", ((string[])
h["StringArray"])[1], "#10");
+ Assert.AreEqual (typeof (Bitmap), h["Image"].GetType
(), "#11");
+ Assert.AreEqual ("hello", ((MyStrType)
h["StrType"]).Value, "#12");
+ Assert.AreEqual ("world", ((MyBinType)
h["BinType"]).Value, "#13");
File.Delete (fileName);
}
}
+
+ [Serializable]
+ [TypeConverter(typeof(MyStrTypeConverter))]
+ public class MyStrType
+ {
+ public string Value;
+
+ public MyStrType (string s)
+ {
+ Value = s;
+ }
+ }
+
+ public class MyStrTypeConverter : TypeConverter
+ {
+ public override bool CanConvertTo(ITypeDescriptorContext
context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ return true;
+ return base.CanConvertTo (context, destinationType);
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext
context, Type sourceType)
+ {
+ if (sourceType == typeof(string))
+ return true;
+ return base.CanConvertFrom (context, sourceType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext
context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ return ((MyStrType) value).Value;
+ return base.ConvertTo (context, culture, value,
destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext
context, CultureInfo culture, object value)
+ {
+ if (value.GetType() == typeof(string))
+ return new MyStrType((string) value);
+ return base.ConvertFrom (context, culture, value);
+ }
+
+ }
+
+
+ [Serializable]
+ [TypeConverter(typeof(MyBinTypeConverter))]
+ public class MyBinType
+ {
+ public string Value;
+
+ public MyBinType (string s)
+ {
+ Value = s;
+ }
+ }
+
+ public class MyBinTypeConverter : TypeConverter
+ {
+ public override bool CanConvertTo(ITypeDescriptorContext
context, Type destinationType)
+ {
+ if (destinationType == typeof(byte[]))
+ return true;
+ return base.CanConvertTo (context, destinationType);
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext
context, Type sourceType)
+ {
+ if (sourceType == typeof(byte[]))
+ return true;
+ return base.CanConvertFrom (context, sourceType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext
context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(byte[]))
+ return Encoding.Default.GetBytes (((MyBinType)
value).Value);
+ return base.ConvertTo (context, culture, value,
destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext
context, CultureInfo culture, object value)
+ {
+ if (value.GetType() == typeof(byte[]))
+ return new MyBinType
(Encoding.Default.GetString ((byte[]) value));
+ return base.ConvertFrom (context, culture, value);
+ }
+
+ }
+
+
+ [TypeConverter(typeof(MyNonSerializableTypeConverter))]
+ public class MyNonSerializableType
+ {
+ public MyNonSerializableType ()
+ {
+ }
+ }
+
+ public class MyNonSerializableTypeConverter : TypeConverter
+ {
+ public override bool CanConvertTo(ITypeDescriptorContext
context, Type destinationType)
+ {
+ if (destinationType == typeof(byte[]))
+ return true;
+ return base.CanConvertTo (context, destinationType);
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext
context, Type sourceType)
+ {
+ if (sourceType == typeof(byte[]))
+ return true;
+ return base.CanConvertFrom (context, sourceType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext
context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == typeof(byte[]))
+ return new byte[] {0, 1, 2, 3};
+ return base.ConvertTo (context, culture, value,
destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext
context, CultureInfo culture, object value)
+ {
+ if (value.GetType() == typeof(byte[]))
+ return new MyNonSerializableType();
+ return base.ConvertFrom (context, culture, value);
+ }
+
+ }
}
Index: Test/System.Resources/compat_1_1.resx
===================================================================
--- Test/System.Resources/compat_1_1.resx (revision 66185)
+++ Test/System.Resources/compat_1_1.resx (working copy)
@@ -78,4 +78,16 @@
<data name="StringArray"
mimetype="application/x-microsoft.net.object.binary.base64">
<value>AAEAAAD/////AQAAAAAAAAARAQAAAAIAAAAGAgAAAAVoZWxsbwYDAAAABXdvcmxkCw==</value>
</data>
+ <data name="InvalidMimeType" mimetype="foo">
+
<value>AAEAAAD/////AQAAAAAAAAARAQAAAAIAAAAGAgAAAAVoZWxsbwYDAAAABXdvcmxkCw==</value>
+ </data>
+ <data name="Image" type="System.Drawing.Bitmap, System.Drawing,
Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+
YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAtJREFUGFdjYAAC
+
AAAFAAGq1chRAAAAAElFTkSuQmCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAA==
+ </value>
+ </data>
</root>
Index: Test/System.Resources/compat_2_0.resx
===================================================================
--- Test/System.Resources/compat_2_0.resx (revision 66185)
+++ Test/System.Resources/compat_2_0.resx (working copy)
@@ -97,4 +97,16 @@
<data name="StringArray"
mimetype="application/x-microsoft.net.object.binary.base64">
<value>AAEAAAD/////AQAAAAAAAAARAQAAAAIAAAAGAgAAAAVoZWxsbwYDAAAABXdvcmxkCw==</value>
</data>
+ <data name="InvalidMimeType" mimetype="foo">
+
<value>AAEAAAD/////AQAAAAAAAAARAQAAAAIAAAAGAgAAAAVoZWxsbwYDAAAABXdvcmxkCw==</value>
+ </data>
+ <data name="Image" type="System.Drawing.Bitmap, System.Drawing,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>
+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+
YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAtJREFUGFdjYAAC
+
AAAFAAGq1chRAAAAAElFTkSuQmCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAA==
+ </value>
+ </data>
</root>
Index: Test/System.Resources/ChangeLog
===================================================================
--- Test/System.Resources/ChangeLog (revision 66185)
+++ Test/System.Resources/ChangeLog (working copy)
@@ -1,3 +1,12 @@
+2006-10-03 Robert Jordan <[EMAIL PROTECTED]>
+
+ * compat_2_0.resx, compat_1_1.resx: Added an Image element to test the
+ type converter. Added InvalidMimeType, an element with an invalid
mimetype
+ attribute.
+ * CompatTest.cs: Added assert for InvalidMimeType and Image element.
+ Fixed test for the 2.0 profile.
+ * WriterTest.cs: Added type converter tests.
+
2006-01-14 Robert Jordan <[EMAIL PROTECTED]>
* compat_2_0.resx: Added a CDATA element as a test for bug #77253.
Index: Test/System.Resources/CompatTest.cs
===================================================================
--- Test/System.Resources/CompatTest.cs (revision 66185)
+++ Test/System.Resources/CompatTest.cs (working copy)
@@ -38,6 +38,9 @@
Assert.AreEqual (16, ((byte[])
h["ByteArray2"])[1], fileName + "#8");
Assert.AreEqual (1013, ((int[])
h["IntArray"])[1], fileName + "#9");
Assert.AreEqual ("world", ((string[])
h["StringArray"])[1], fileName + "#10");
+ Assert.IsNull (h["InvalidMimeType"]);
+ Assert.IsNotNull (h["Image"], fileName +
"#11");
+ Assert.AreEqual (typeof (Bitmap),
h["Image"].GetType (), fileName + "#12");
}
}
@@ -45,7 +48,14 @@
public void TestReader ()
{
Helper.TestReader
("Test/System.Resources/compat_1_1.resx");
+ }
+
+#if NET_2_0
+ [Test]
+ public void TestReader_2_0 ()
+ {
Helper.TestReader
("Test/System.Resources/compat_2_0.resx");
}
+#endif
}
}
_______________________________________________
Mono-winforms-list maillist - [email protected]
http://lists.ximian.com/mailman/listinfo/mono-winforms-list