JSONMapper
parent
8ded5ec2af
commit
9b2d2fe6b5
|
@ -1,15 +1,17 @@
|
||||||
<Properties StartupConfiguration="{49FFBD9F-655E-4C74-A078-99B5E09059C6}|Default">
|
<Properties StartupConfiguration="{49FFBD9F-655E-4C74-A078-99B5E09059C6}|Default">
|
||||||
<MonoDevelop.Ide.Workbench>
|
<MonoDevelop.Ide.Workbench ActiveDocument="json.test/Program.cs">
|
||||||
|
<Files>
|
||||||
|
<File FileName="json.test/Program.cs" Line="54" Column="9" />
|
||||||
|
</Files>
|
||||||
<Pads>
|
<Pads>
|
||||||
<Pad Id="ProjectPad">
|
<Pad Id="ProjectPad">
|
||||||
<State name="__root__">
|
<State name="__root__">
|
||||||
<Node name="sharp.json" expanded="True">
|
<Node name="sharp.json" expanded="True">
|
||||||
<Node name="json.test" expanded="True" />
|
<Node name="json.test" expanded="True">
|
||||||
<Node name="ln.json" expanded="True">
|
<Node name="Program.cs" selected="True" />
|
||||||
<Node name="JSONParser.cs" selected="True" />
|
|
||||||
</Node>
|
</Node>
|
||||||
<Node name="ln.types" expanded="True">
|
<Node name="ln.json" expanded="True">
|
||||||
<Node name="stream" expanded="True" />
|
<Node name="mapping" expanded="True" />
|
||||||
</Node>
|
</Node>
|
||||||
</Node>
|
</Node>
|
||||||
</State>
|
</State>
|
||||||
|
|
|
@ -9,6 +9,8 @@ namespace ln.json
|
||||||
public override IEnumerable<JSONValue> Children => Values;
|
public override IEnumerable<JSONValue> Children => Values;
|
||||||
public override bool HasChildren => true;
|
public override bool HasChildren => true;
|
||||||
|
|
||||||
|
public int Count => values.Count;
|
||||||
|
|
||||||
JSONValue[] Values
|
JSONValue[] Values
|
||||||
{
|
{
|
||||||
get => values.ToArray();
|
get => values.ToArray();
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace ln.json
|
||||||
{
|
{
|
||||||
public class JSONNumber : JSONValue
|
public class JSONNumber : JSONValue
|
||||||
{
|
{
|
||||||
|
public Decimal Decimal => decValue;
|
||||||
readonly decimal decValue;
|
readonly decimal decValue;
|
||||||
|
|
||||||
public JSONNumber(int i)
|
public JSONNumber(int i)
|
||||||
|
@ -16,6 +17,15 @@ namespace ln.json
|
||||||
decValue = new decimal(integer);
|
decValue = new decimal(integer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JSONNumber(uint i)
|
||||||
|
: this((ulong)i) { }
|
||||||
|
|
||||||
|
public JSONNumber(ulong integer)
|
||||||
|
: base(JSONValueType.NUMBER)
|
||||||
|
{
|
||||||
|
decValue = new decimal(integer);
|
||||||
|
}
|
||||||
|
|
||||||
public JSONNumber(double doubleValue)
|
public JSONNumber(double doubleValue)
|
||||||
: base(JSONValueType.NUMBER)
|
: base(JSONValueType.NUMBER)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace ln.json
|
||||||
public override IEnumerable<JSONValue> Children => values.Values;
|
public override IEnumerable<JSONValue> Children => values.Values;
|
||||||
public override bool HasChildren => true;
|
public override bool HasChildren => true;
|
||||||
|
|
||||||
|
public int Count => values.Count;
|
||||||
|
|
||||||
BTree<string, JSONValue> values = new BTree<string, JSONValue>();
|
BTree<string, JSONValue> values = new BTree<string, JSONValue>();
|
||||||
|
|
||||||
public JSONObject()
|
public JSONObject()
|
||||||
|
@ -32,6 +34,11 @@ namespace ln.json
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
return values.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using ln.json;
|
using ln.json;
|
||||||
|
using ln.json.mapping;
|
||||||
|
|
||||||
namespace json.test
|
namespace json.test
|
||||||
{
|
{
|
||||||
|
@ -37,6 +38,26 @@ namespace json.test
|
||||||
Console.WriteLine("");
|
Console.WriteLine("");
|
||||||
Console.WriteLine("test.json file:");
|
Console.WriteLine("test.json file:");
|
||||||
Console.WriteLine("PARSED: {0}",value.ToString());
|
Console.WriteLine("PARSED: {0}",value.ToString());
|
||||||
|
|
||||||
|
Person person = new Person()
|
||||||
|
{
|
||||||
|
firstname = "Harald",
|
||||||
|
lastname = "Wolff-Thobaben",
|
||||||
|
age = 39
|
||||||
|
};
|
||||||
|
|
||||||
|
JSONValue jsonPerson = JSONMapper.DefaultMapper.ToJson(person);
|
||||||
|
Console.WriteLine(jsonPerson.ToString());
|
||||||
|
|
||||||
|
Person p2 = JSONMapper.DefaultMapper.FromJson<Person>(jsonPerson);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
class Person
|
||||||
|
{
|
||||||
|
public string firstname;
|
||||||
|
public string lastname;
|
||||||
|
|
||||||
|
public int age;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,11 @@
|
||||||
<Compile Include="JSONNumber.cs" />
|
<Compile Include="JSONNumber.cs" />
|
||||||
<Compile Include="JSONValue.cs" />
|
<Compile Include="JSONValue.cs" />
|
||||||
<Compile Include="JSONParser.cs" />
|
<Compile Include="JSONParser.cs" />
|
||||||
|
<Compile Include="mapping\JSONMapper.cs" />
|
||||||
|
<Compile Include="mapping\JSONMapping.cs" />
|
||||||
|
<Compile Include="mapping\JSONArrayMapping.cs" />
|
||||||
|
<Compile Include="mapping\JSONObjectMapping.cs" />
|
||||||
|
<Compile Include="mapping\JSONEnumerableMapping.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="ln.types\ln.types.csproj">
|
<ProjectReference Include="ln.types\ln.types.csproj">
|
||||||
|
@ -45,6 +50,9 @@
|
||||||
<Name>ln.types</Name>
|
<Name>ln.types</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="mapping\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<MonoDevelop>
|
<MonoDevelop>
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
// /**
|
||||||
|
// * File: JSONArrayMapping.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
namespace ln.json.mapping
|
||||||
|
{
|
||||||
|
public class JSONArrayMapping : JSONMapping
|
||||||
|
{
|
||||||
|
Type elementType;
|
||||||
|
|
||||||
|
public JSONArrayMapping(Type type)
|
||||||
|
:base(type)
|
||||||
|
{
|
||||||
|
elementType = type.GetElementType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object FromJson(JSONMapper mapper, JSONValue json)
|
||||||
|
{
|
||||||
|
JSONArray jsonArray = (JSONArray)json;
|
||||||
|
Array array = Array.CreateInstance(elementType,jsonArray.Count);
|
||||||
|
|
||||||
|
for (int n = 0; n < array.Length; n++)
|
||||||
|
array.SetValue(mapper.FromJson(jsonArray[n],elementType),n);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
public override JSONValue ToJson(JSONMapper mapper, object value)
|
||||||
|
{
|
||||||
|
Array array = (Array)value;
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
|
||||||
|
foreach (object element in array)
|
||||||
|
jsonArray.Add(mapper.ToJson(element));
|
||||||
|
|
||||||
|
return jsonArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// /**
|
||||||
|
// * File: JSONArrayMapping.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace ln.json.mapping
|
||||||
|
{
|
||||||
|
public class JSONEnumerableMapping<T> : JSONMapping
|
||||||
|
{
|
||||||
|
Type elementType;
|
||||||
|
|
||||||
|
public JSONEnumerableMapping()
|
||||||
|
:base(typeof(T))
|
||||||
|
{
|
||||||
|
elementType = typeof(T).GetGenericArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object FromJson(JSONMapper mapper, JSONValue json)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("IEnumerable<> can not be set from JSON");
|
||||||
|
}
|
||||||
|
public override JSONValue ToJson(JSONMapper mapper, object value)
|
||||||
|
{
|
||||||
|
IEnumerable<T> ie = (IEnumerable<T>)value;
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
|
||||||
|
foreach (object element in ie)
|
||||||
|
jsonArray.Add(mapper.ToJson(element));
|
||||||
|
|
||||||
|
return jsonArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
// /**
|
||||||
|
// * File: JSONMapper.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
|
namespace ln.json.mapping
|
||||||
|
{
|
||||||
|
public class JSONMapper
|
||||||
|
{
|
||||||
|
public static JSONMapper DefaultMapper { get; set; } = new JSONMapper(true);
|
||||||
|
|
||||||
|
Dictionary<Type, JSONMapping> mappings = new Dictionary<Type, JSONMapping>();
|
||||||
|
|
||||||
|
public JSONMapper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
private JSONMapper(bool defaultMapper)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Integer
|
||||||
|
**/
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(byte),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((int)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToByte(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(short),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((int)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt16(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(int),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((int)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt32(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(long),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((long)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToInt64(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(ushort),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((uint)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt16(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(uint),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((uint)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt32(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(ulong),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((ulong)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToUInt64(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Float
|
||||||
|
**/
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(float),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((float)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => (float)Decimal.ToDouble(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(double),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONNumber((double)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => Decimal.ToDouble(((JSONNumber)arg2).Decimal)
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strings
|
||||||
|
**/
|
||||||
|
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(string),
|
||||||
|
(JSONMapper arg1, object arg2) => new JSONString((string)arg2),
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => ((JSONString)arg2).Value
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Others
|
||||||
|
**/
|
||||||
|
|
||||||
|
Add(new JSONMapping(
|
||||||
|
typeof(bool),
|
||||||
|
(JSONMapper arg1, object arg2) => ((bool)arg2) ? (JSONValue)JSONTrue.Instance : (JSONValue)JSONFalse.Instance,
|
||||||
|
(JSONMapper arg1, JSONValue arg2) => (arg2.ValueType == JSONValueType.TRUE) || (arg2.ValueType == JSONValueType.FALSE) ? false : throw new NotSupportedException()
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(JSONMapping mapping)
|
||||||
|
{
|
||||||
|
mappings[mapping.TargetType] = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONMapping FindMapping(Type targetType)
|
||||||
|
{
|
||||||
|
if (mappings.ContainsKey(targetType))
|
||||||
|
return mappings[targetType];
|
||||||
|
if (this != DefaultMapper)
|
||||||
|
return DefaultMapper.FindMapping(targetType);
|
||||||
|
|
||||||
|
if (!targetType.IsPrimitive && !targetType.IsValueType)
|
||||||
|
{
|
||||||
|
if (targetType.IsArray)
|
||||||
|
{
|
||||||
|
Add(new JSONArrayMapping(targetType));
|
||||||
|
} else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>)))
|
||||||
|
{
|
||||||
|
Add((JSONMapping)Activator.CreateInstance(typeof(JSONEnumerableMapping<>).MakeGenericType(targetType.GetGenericArguments()[0])));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Add(new JSONObjectMapping(targetType));
|
||||||
|
}
|
||||||
|
return mappings[targetType];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public T FromJson<T>(JSONValue json) => (T)FromJson(json, typeof(T));
|
||||||
|
public object FromJson(JSONValue json, Type targetType)
|
||||||
|
{
|
||||||
|
if (json.ValueType == JSONValueType.NULL)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return FindMapping(targetType).FromJson(this, json);
|
||||||
|
}
|
||||||
|
public JSONValue ToJson(object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return JSONNull.Instance;
|
||||||
|
|
||||||
|
Type sourceType = value.GetType();
|
||||||
|
return FindMapping(sourceType).ToJson(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// /**
|
||||||
|
// * File: JSONMapping.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
namespace ln.json.mapping
|
||||||
|
{
|
||||||
|
public class JSONMapping
|
||||||
|
{
|
||||||
|
public Type TargetType { get; }
|
||||||
|
|
||||||
|
Func<JSONMapper, object, JSONValue> tojson;
|
||||||
|
Func<JSONMapper, JSONValue, object> fromjson;
|
||||||
|
|
||||||
|
public JSONMapping(Type targetType,Func<JSONMapper, object, JSONValue> tojson, Func<JSONMapper, JSONValue, object> fromjson)
|
||||||
|
{
|
||||||
|
TargetType = targetType;
|
||||||
|
this.fromjson = fromjson;
|
||||||
|
this.tojson = tojson;
|
||||||
|
}
|
||||||
|
protected JSONMapping(Type targetType)
|
||||||
|
{
|
||||||
|
TargetType = targetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual object FromJson(JSONMapper mapper,JSONValue json)
|
||||||
|
{
|
||||||
|
return fromjson(mapper, json);
|
||||||
|
}
|
||||||
|
public virtual JSONValue ToJson(JSONMapper mapper,object value)
|
||||||
|
{
|
||||||
|
return tojson(mapper, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
// /**
|
||||||
|
// * File: JSONObjectMapping.cs
|
||||||
|
// * Author: haraldwolff
|
||||||
|
// *
|
||||||
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
||||||
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
||||||
|
// *
|
||||||
|
// *
|
||||||
|
// **/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
namespace ln.json.mapping
|
||||||
|
{
|
||||||
|
public class JSONObjectMapping : JSONMapping
|
||||||
|
{
|
||||||
|
Dictionary<string, Action<object,object>> setters = new Dictionary<string, Action<object,object>>();
|
||||||
|
Dictionary<string, Func<object, object>> getters = new Dictionary<string, Func<object, object>>();
|
||||||
|
Dictionary<string, Type> types = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
public JSONObjectMapping(Type type)
|
||||||
|
:base(type)
|
||||||
|
{
|
||||||
|
foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.Public))
|
||||||
|
{
|
||||||
|
setters.Add(fieldInfo.Name, (object arg1, object arg2) => fieldInfo.SetValue(arg1, arg2));
|
||||||
|
getters.Add(fieldInfo.Name, (object arg) => fieldInfo.GetValue(arg));
|
||||||
|
types.Add(fieldInfo.Name, fieldInfo.FieldType);
|
||||||
|
}
|
||||||
|
foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
|
||||||
|
{
|
||||||
|
setters.Add(propertyInfo.Name, (object arg1, object arg2) => propertyInfo.SetValue(arg1, arg2));
|
||||||
|
getters.Add(propertyInfo.Name, (object arg) => propertyInfo.GetValue(arg));
|
||||||
|
types.Add(propertyInfo.Name, propertyInfo.PropertyType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object FromJson(JSONMapper mapper, JSONValue json)
|
||||||
|
{
|
||||||
|
JSONObject jObject = (JSONObject)json;
|
||||||
|
object o = Activator.CreateInstance(TargetType);
|
||||||
|
|
||||||
|
foreach (string name in setters.Keys)
|
||||||
|
{
|
||||||
|
if (jObject.ContainsKey(name))
|
||||||
|
setters[name](o, mapper.FromJson(jObject[name], types[name]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override JSONValue ToJson(JSONMapper mapper, object value)
|
||||||
|
{
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
foreach (string name in getters.Keys)
|
||||||
|
{
|
||||||
|
json[name] = mapper.ToJson(getters[name](value));
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue