using System;
|
using System.Collections;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.Globalization;
|
using System.Linq;
|
using System.Threading.Tasks;
|
using System.Xml;
|
using System.Xml.Linq;
|
|
namespace OpenTap.Addin.Reader
|
{
|
public abstract class IObjectReader
|
{
|
internal const string TypeAttrName = "type";
|
|
public abstract object ReadObject(Type type, XElement element, Action<object> postAction);
|
}
|
|
public class DefaultObjectReader
|
{
|
static readonly Dictionary<Type, IObjectReader> SpecialReader = new Dictionary<Type, IObjectReader>();
|
|
static DefaultObjectReader()
|
{
|
SpecialReader.Add(typeof(TestStepList), new TestStepListReader());
|
}
|
|
static bool tryConvertNumber(Type type, string valueString, out object value)
|
{
|
switch (Type.GetTypeCode(type))
|
{
|
case TypeCode.Byte:
|
{
|
byte oval;
|
bool ok = byte.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.SByte:
|
{
|
SByte oval;
|
bool ok = SByte.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.UInt16:
|
{
|
UInt16 oval;
|
bool ok = UInt16.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.UInt32:
|
{
|
UInt32 oval;
|
bool ok = UInt32.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.UInt64:
|
{
|
UInt64 oval;
|
bool ok = UInt64.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
|
case TypeCode.Int16:
|
{
|
Int16 oval;
|
bool ok = Int16.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.Int32:
|
{
|
Int32 oval;
|
bool ok = Int32.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.Int64:
|
{
|
Int64 oval;
|
bool ok = Int64.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.Decimal:
|
{
|
value = BigFloat.Convert(valueString, CultureInfo.InvariantCulture).ConvertTo(typeof(decimal));
|
return true;
|
}
|
case TypeCode.Double:
|
{
|
Double oval;
|
bool ok = Double.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
case TypeCode.Single:
|
{
|
Single oval;
|
bool ok = Single.TryParse(valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out oval);
|
value = oval;
|
return ok;
|
}
|
default:
|
value = null;
|
return false;
|
}
|
}
|
|
static bool readContentInternal(Type propType, Func<string> getvalueString, XElement elem, out object outvalue)
|
{
|
|
object value = null;
|
bool ok;
|
|
if (propType.IsEnum || propType.IsPrimitive || propType == typeof(string) || propType.IsValueType || propType == typeof(Type))
|
{
|
ok = true;
|
string valueString = getvalueString()?.Trim();
|
|
if (propType.IsEnum)
|
{
|
if (!string.IsNullOrEmpty(valueString))
|
{
|
// legacy support: A flagged enum did not have ','s, but just spaces between.
|
if (!valueString.Contains(','))
|
{
|
var splitted = valueString.Split(' ');
|
value = Enum.Parse(propType, string.Join(",", splitted));
|
}
|
else
|
value = Enum.Parse(propType, valueString);
|
}
|
}
|
|
else if (propType == typeof(String) || propType == typeof(char))
|
{
|
if (elem.HasElements)
|
{
|
// string contains Base64 if it has invalid XML chars.
|
// elem.Element("Base64") fails if the attribute "xmlns" is set on the document.
|
// In this case, "Base64" must be prepended with the value of xmlns in brackets.
|
// If the namespace is not set, ns.GetName("Base64") will just evaluate to "Base64"
|
var ns = elem.GetDefaultNamespace();
|
var encode = elem.Element(ns.GetName("Base64"));
|
if (encode != null)
|
{
|
try
|
{
|
value = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encode.Value));
|
}
|
catch
|
{
|
value = encode.Value;
|
}
|
}
|
}
|
if (value == null)
|
value = valueString;
|
if (propType == typeof(char))
|
value = ((string)value)[0];
|
}
|
else if (propType == typeof(TimeSpan) || propType == typeof(TimeSpan?))
|
{
|
value = TimeSpan.Parse(valueString, CultureInfo.InvariantCulture);
|
}
|
else if (propType == typeof(Guid) || propType == typeof(Guid?))
|
{
|
value = Guid.Parse(valueString);
|
}
|
else if (propType == typeof(Type))
|
{
|
value = PluginManager.LocateType(valueString.Split(',').First());
|
}
|
else if (tryConvertNumber(propType, valueString, out value))
|
{
|
// Conversions done in tryConvertNumber
|
}
|
else if (typeof(IConvertible).IsAssignableFrom(propType))
|
{
|
value = Convert.ChangeType(valueString, propType, CultureInfo.InvariantCulture);
|
}
|
else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
{
|
// For nullable<T> we recurse focusing on the underlying non-nullable type.
|
if (valueString == null)
|
{
|
outvalue = null;
|
return true; // null is a valid value for nullable types.
|
}
|
|
return readContentInternal(Nullable.GetUnderlyingType(propType), getvalueString, elem, out outvalue);
|
}
|
else
|
{
|
ok = false;
|
}
|
}
|
else
|
{
|
ok = false;
|
}
|
|
outvalue = value;
|
return ok;
|
}
|
|
public static object ReadObject(Type type, XElement element, Action<object> postAction)
|
{
|
//var timer = Stopwatch.StartNew();
|
if (type == null || element == null)
|
return null;
|
object obj = null;
|
if (SpecialReader.ContainsKey(type))
|
{
|
obj = SpecialReader[type].ReadObject(type, element, postAction);
|
//TestPlanReader.Log.Debug(timer, $"Create {obj}");
|
return obj;
|
}
|
else
|
{
|
var typeAttr = element.Attribute(IObjectReader.TypeAttrName);
|
type = typeAttr == null ? type : Type.GetType(typeAttr.Value);
|
if (type == null)
|
{
|
var td = TypeData.GetTypeData(typeAttr.Value);
|
obj = td.CreateInstance();
|
type = obj.GetType();
|
}
|
|
if (!readContentInternal(type, () => element.Value, element, out obj))
|
{
|
try
|
{
|
obj = obj ?? Activator.CreateInstance(type);
|
}
|
catch (Exception ex)
|
{
|
throw ex;
|
}
|
|
if (typeof(IList).IsAssignableFrom(type) && type.IsGenericType)
|
{
|
var list = (IList)obj;
|
var eles = element.Elements().ToArray();
|
var values = new object[eles.Length];
|
|
Parallel.For(0, eles.Length, i => values[i] = ReadObject(type.GetGenericArguments().First(), eles[i], postAction));
|
|
//var values = element.Elements().AsParallel().AsOrdered().Select(childElemennt =>
|
//{
|
// return ;
|
//}).ToArray();
|
|
foreach (var val in values)
|
{
|
list.Add(val);
|
}
|
|
//foreach (var childElemennt in element.Elements())
|
//{
|
// var value = ReadObject(type.GetGenericArguments().First(), childElemennt, postAction);
|
// list.Add(value);
|
//}
|
}
|
else
|
{
|
var props = PropertyInfoCache.GetProps(type);
|
|
foreach (var childElemennt in element.Elements())
|
{
|
string childName = XmlConvert.DecodeName(childElemennt.Name.LocalName);
|
if (!props.ContainsKey(childName))
|
{
|
continue;
|
}
|
var prop = props[childName];
|
var value = ReadObject(prop.PropertyType, childElemennt, postAction);
|
prop.SetValue(obj, value);
|
}
|
|
//List<Task> tasks = new List<Task>();
|
//foreach (var childElemennt in element.Elements())
|
//{
|
// string childName = XmlConvert.DecodeName(childElemennt.Name.LocalName);
|
// if (!props.ContainsKey(childName))
|
// {
|
// continue;
|
// }
|
// tasks.Add(Task.Run(() =>
|
// {
|
//
|
|
// }));
|
//}
|
//Task.WaitAll(tasks.ToArray());
|
|
//var eles = element.Elements().ToArray();
|
//Parallel.For(0, eles.Length, i =>
|
//{
|
// string childName = XmlConvert.DecodeName(eles[i].Name.LocalName);
|
// if (!props.ContainsKey(childName))
|
// {
|
// return;
|
// }
|
// var prop = props[childName];
|
// var value = ReadObject(prop.PropertyType, eles[i], postAction);
|
// prop.SetValue(obj, value);
|
//});
|
}
|
//TestPlanReader.Log.Debug(timer, $"Create {obj}");
|
}
|
if (postAction != null) postAction(obj);
|
return obj;
|
}
|
}
|
}
|
}
|