Skip to content

Commit 4b9afab

Browse files
committed
finish reflection based deserialisation (add for text codec)
1 parent c3dfe42 commit 4b9afab

5 files changed

Lines changed: 201 additions & 76 deletions

File tree

Codecs/Binary.cs

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
392392

393393
if(matchedType && reflectionParams.AttemptReflection)
394394
{
395-
var isElementDerived = IsElementDerived(classType);
395+
var isElementDerived = Element.IsElementDerived(classType);
396396
if (isElementDerived && classType.Name == type)
397397
{
398398
Type derivedType = classType;
@@ -425,7 +425,8 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
425425
// read attributes (or not, if we're deferred)
426426
foreach (var elem in dm.AllElements.ToArray())
427427
{
428-
System.Diagnostics.Debug.Assert(!elem.Stub);
428+
// assert if stub
429+
Debug.Assert(!elem.Stub);
429430

430431
var num_attrs = Reader.ReadInt32();
431432

@@ -798,28 +799,6 @@ void WriteAttribute(object value, bool in_array)
798799
}
799800
}
800801

801-
bool IsElementDerived(Type type)
802-
{
803-
var elementType = typeof(Element);
804-
805-
while (type.BaseType != elementType)
806-
{
807-
var baseType = type.BaseType;
808-
809-
if (baseType != null)
810-
{
811-
type = baseType;
812-
}
813-
else
814-
{
815-
return type == elementType ? true : false;
816-
}
817-
818-
}
819-
820-
return type.BaseType == elementType ? true : false;
821-
}
822-
823802
class DmxBinaryWriter : BinaryWriter
824803
{
825804
public DmxBinaryWriter(Stream output)

Codecs/KeyValues2.cs

Lines changed: 150 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System.Runtime.CompilerServices;
88
using System.Globalization;
99
using System.Reflection;
10+
using System.Xml.Linq;
11+
using System.Collections;
1012

1113
namespace Datamodel.Codecs
1214
{
@@ -292,7 +294,7 @@ void WriteAttribute(string name, Type type, object value, bool in_array)
292294
else if (type == typeof(Matrix4x4))
293295
{
294296
var castValue = (Matrix4x4)value;
295-
var matrixString =
297+
var matrixString =
296298
$"{castValue.M11} {castValue.M12} {castValue.M13} {castValue.M14}" +
297299
$" {castValue.M21} {castValue.M22} {castValue.M23} {castValue.M24}" +
298300
$" {castValue.M31} {castValue.M32} {castValue.M33} {castValue.M34}" +
@@ -397,6 +399,44 @@ public void Encode(Datamodel dm, string encoding, int encoding_version, Stream s
397399
#endregion
398400

399401
#region Decode
402+
403+
private class IntermediateData
404+
{
405+
// these store element refs while we process the elements, once were done
406+
// we can go trough these and actually create the attributes
407+
// and add the elements to lists
408+
public Dictionary<Element, List<(string, Guid)>> PropertiesToAdd = new();
409+
public Dictionary<IList, List<Guid>> ListRefs = new();
410+
411+
public void HandleElementProp(Element element, string attrName, Guid id)
412+
{
413+
414+
PropertiesToAdd.TryGetValue(element, out var attrList);
415+
416+
if (attrList == null)
417+
{
418+
attrList = new List<(string, Guid)>();
419+
PropertiesToAdd.Add(element, attrList);
420+
}
421+
422+
attrList.Add((attrName, id));
423+
424+
}
425+
426+
public void HandleListRefs(IList list, Guid id)
427+
{
428+
ListRefs.TryGetValue(list, out var guidList);
429+
430+
if (guidList == null)
431+
{
432+
guidList = new List<Guid>();
433+
ListRefs.Add(list, guidList);
434+
}
435+
436+
guidList.Add(id);
437+
}
438+
}
439+
400440
readonly StringBuilder TokenBuilder = new();
401441
int Line = 0;
402442
string Decode_NextToken()
@@ -441,31 +481,17 @@ string Decode_NextToken()
441481
}
442482
}
443483

444-
Element Decode_ParseElementId()
445-
{
446-
Element elem;
447-
var id_s = Decode_NextToken();
448-
449-
if (string.IsNullOrEmpty(id_s))
450-
elem = null;
451-
else
452-
{
453-
Guid id = new(id_s);
454-
elem = DM.AllElements[id];
455-
elem ??= new Element(DM, id);
456-
}
457-
return elem;
458-
}
459-
460-
Element Decode_ParseElement(string class_name)
484+
Element Decode_ParseElement(string class_name, ReflectionParams reflectionParams, IntermediateData intermediateData)
461485
{
462486
string elem_class = class_name ?? Decode_NextToken();
463487
string elem_name = null;
464488
string elem_id = null;
465489
Element elem = null;
466490

491+
var types = CodecUtilities.GetReflectionTypes(reflectionParams);
492+
467493
string next = Decode_NextToken();
468-
if (next != "{") throw new CodecException(String.Format("Expected Element opener, got '{0}'.", next));
494+
if (next != "{") throw new CodecException($"Expected Element opener, got '{next}'.");
469495
while (true)
470496
{
471497
next = Decode_NextToken();
@@ -479,16 +505,41 @@ Element Decode_ParseElement(string class_name)
479505
{
480506
elem_id = Decode_NextToken();
481507
var id = new Guid(elem_id);
482-
var local_element = DM.AllElements[id];
483-
if (local_element != null)
508+
if (elem_class != "$prefix_element$")
484509
{
485-
elem = local_element;
486-
elem.Name = elem_name;
487-
elem.ClassName = elem_class;
488-
elem.Stub = false;
510+
var matchedType = types.TryGetValue(elem_class, out var classType);
511+
512+
if (matchedType && reflectionParams.AttemptReflection)
513+
{
514+
var isElementDerived = Element.IsElementDerived(classType);
515+
if (isElementDerived && classType.Name == elem_class)
516+
{
517+
Type derivedType = classType;
518+
519+
ConstructorInfo? constructor = typeof(Element).GetConstructor(
520+
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
521+
null,
522+
new Type[] { typeof(Datamodel), typeof(string), typeof(Guid), typeof(string) },
523+
null
524+
);
525+
526+
if (constructor == null)
527+
{
528+
throw new InvalidOperationException("Failed to get constructor while attemption reflection based deserialisation");
529+
}
530+
531+
object uninitializedObject = RuntimeHelpers.GetUninitializedObject(derivedType);
532+
constructor.Invoke(uninitializedObject, new object[] { DM, elem_name, new Guid(elem_id), elem_class });
533+
534+
elem = (Element?)uninitializedObject;
535+
}
536+
}
537+
538+
if (elem == null)
539+
{
540+
elem = new Element(DM, elem_name, new Guid(elem_id), elem_class);
541+
}
489542
}
490-
else if (elem_class != "$prefix_element$")
491-
elem = new Element(DM, elem_name, new Guid(elem_id), elem_class);
492543

493544
continue;
494545
}
@@ -501,19 +552,21 @@ Element Decode_ParseElement(string class_name)
501552
continue;
502553
}
503554

504-
if (elem == null)
505-
continue;
506-
507555
if (attr_type_s == "element")
508556
{
509-
elem.Add(attr_name, Decode_ParseElementId());
557+
var id_s = Decode_NextToken();
558+
559+
if (!string.IsNullOrEmpty(id_s))
560+
{
561+
intermediateData.HandleElementProp(elem, attr_name, new Guid(id_s));
562+
}
510563
continue;
511564
}
512565

513566
object attr_value = null;
514567

515568
if (attr_type == null)
516-
attr_value = Decode_ParseElement(attr_type_s);
569+
attr_value = Decode_ParseElement(attr_type_s, reflectionParams, intermediateData);
517570
else if (attr_type_s.EndsWith("_array"))
518571
{
519572
var array = CodecUtilities.MakeList(attr_type, 5); // assume 5 items
@@ -527,15 +580,28 @@ Element Decode_ParseElement(string class_name)
527580
if (next == "]") break;
528581

529582
if (next == "element") // Element ID reference
530-
array.Add(Decode_ParseElementId());
531-
else if (attr_type == typeof(Element)) // inline Element
532-
array.Add(Decode_ParseElement(next));
533-
else // normal value
534-
array.Add(Decode_ParseValue(attr_type, next));
583+
{
584+
var id_s = Decode_NextToken();
585+
586+
if (!string.IsNullOrEmpty(id_s))
587+
{
588+
intermediateData.HandleListRefs(array, new Guid(id_s));
589+
}
590+
}
591+
// inline Element
592+
else if (attr_type == typeof(Element))
593+
{
594+
array.Add(Decode_ParseElement(next, reflectionParams, intermediateData));
595+
}
596+
// normal value
597+
else
598+
{
599+
array.Add(Decode_ParseValue(attr_type, next, reflectionParams, intermediateData));
600+
}
535601
}
536602
}
537603
else
538-
attr_value = Decode_ParseValue(attr_type, Decode_NextToken());
604+
attr_value = Decode_ParseValue(attr_type, Decode_NextToken(), reflectionParams, intermediateData);
539605

540606
if (elem != null)
541607
elem.Add(attr_name, attr_value);
@@ -545,15 +611,15 @@ Element Decode_ParseElement(string class_name)
545611
return elem;
546612
}
547613

548-
object Decode_ParseValue(Type type, string value)
614+
object Decode_ParseValue(Type type, string value, ReflectionParams reflectionParams, IntermediateData intermediateData)
549615
{
550616
if (type == typeof(string))
551617
return value;
552618

553619
value = value.Trim();
554620

555621
if (type == typeof(Element))
556-
return Decode_ParseElement(value);
622+
return Decode_ParseElement(value, reflectionParams, intermediateData);
557623
if (type == typeof(int))
558624
return int.Parse(value, CultureInfo.InvariantCulture);
559625
else if (type == typeof(float))
@@ -562,11 +628,34 @@ object Decode_ParseValue(Type type, string value)
562628
return byte.Parse(value, CultureInfo.InvariantCulture) == 1;
563629
else if (type == typeof(byte[]))
564630
{
631+
// need to sanitise input here because for example when exporting map as txt,
632+
// hammer will format the binary data to fit nicer on the screen by inserting two tabs
633+
var sb = new StringBuilder(value.Length);
634+
foreach (char c in value)
635+
{
636+
switch (c)
637+
{
638+
case '\\':
639+
case '\r':
640+
case '\n':
641+
case '\t':
642+
case ' ':
643+
continue;
644+
default:
645+
sb.Append(c);
646+
break;
647+
}
648+
}
649+
value = sb.ToString();
650+
565651
byte[] result = new byte[value.Length / 2];
652+
566653
for (int i = 0; i * 2 < value.Length; i++)
567654
{
568-
result[i] = byte.Parse(value.AsSpan(i * 2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture);
655+
var slice = value.AsSpan(i * 2, 2);
656+
result[i] = byte.Parse(slice, System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture);
569657
}
658+
570659
return result;
571660
}
572661
else if (type == typeof(TimeSpan))
@@ -611,6 +700,8 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
611700
Line = 1;
612701
string next;
613702

703+
var intermediateData = new IntermediateData();
704+
614705
while (true)
615706
{
616707
try
@@ -619,11 +710,28 @@ public Datamodel Decode(string encoding, int encoding_version, string format, in
619710
{ break; }
620711

621712
try
622-
{ Decode_ParseElement(next); }
713+
{ Decode_ParseElement(next, reflectionParams, intermediateData); }
623714
catch (Exception err)
624715
{ throw new CodecException($"KeyValues2 decode failed on line {Line}:\n\n{err.Message}", err); }
625716
}
626717

718+
foreach (var propList in intermediateData.PropertiesToAdd)
719+
{
720+
foreach (var prop in propList.Value)
721+
{
722+
propList.Key.Add(prop.Item1, DM.AllElements[prop.Item2]);
723+
}
724+
725+
}
726+
727+
foreach (var list in intermediateData.ListRefs)
728+
{
729+
foreach (var id in list.Value)
730+
{
731+
list.Key.Add(DM.AllElements[id]);
732+
}
733+
}
734+
627735
return DM;
628736
}
629737
#endregion

Datamodel.ElementList.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Datamodel
1010
{
11-
public partial class Datamodel
11+
public partial class Datamodel
1212
{
1313
/// <summary>
1414
/// A collection of <see cref="Element"/>s owned by a single <see cref="Datamodel"/>.

Element.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,27 @@ public bool Equals(Element other)
381381
{
382382
return other != null && ID == other.ID;
383383
}
384+
385+
public static bool IsElementDerived(Type type)
386+
{
387+
var elementType = typeof(Element);
388+
389+
while (type.BaseType != elementType)
390+
{
391+
var baseType = type.BaseType;
392+
393+
if (baseType != null)
394+
{
395+
type = baseType;
396+
}
397+
else
398+
{
399+
return type == elementType ? true : false;
400+
}
401+
}
402+
403+
return type.BaseType == elementType ? true : false;
404+
}
384405
}
385406

386407
namespace TypeConverters

0 commit comments

Comments
 (0)