mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Make custom types optional
This commit is contained in:
parent
67876c6532
commit
8c9068cc97
21 changed files with 189 additions and 59 deletions
|
@ -73,12 +73,12 @@ public class Program
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ICustomTypeDefinition ResolveCustomType(string name)
|
private static Optional<ICustomTypeDefinition> ResolveCustomType(string name)
|
||||||
{
|
{
|
||||||
ICustomTypeDefinition[] allDefinedTypes =
|
ICustomTypeDefinition[] allDefinedTypes =
|
||||||
[
|
[
|
||||||
new CustomClassDefinition() { Name = "a" },
|
new CustomClassDefinition() { Name = "a" },
|
||||||
];
|
];
|
||||||
return allDefinedTypes.FirstOrDefault(type => type.Name == name) ?? throw new InvalidOperationException();
|
return allDefinedTypes.FirstOrDefault(ctd => ctd.Name == name) is ICustomTypeDefinition ctd ? new Optional<ICustomTypeDefinition>(ctd) : Optional<ICustomTypeDefinition>.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DotTiled.Serialization;
|
using DotTiled.Serialization;
|
||||||
|
@ -57,12 +56,12 @@ public partial class MapParser : Node2D
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ICustomTypeDefinition ResolveCustomType(string name)
|
private static Optional<ICustomTypeDefinition> ResolveCustomType(string name)
|
||||||
{
|
{
|
||||||
ICustomTypeDefinition[] allDefinedTypes =
|
ICustomTypeDefinition[] allDefinedTypes =
|
||||||
[
|
[
|
||||||
new CustomClassDefinition() { Name = "a" },
|
new CustomClassDefinition() { Name = "a" },
|
||||||
];
|
];
|
||||||
return allDefinedTypes.FirstOrDefault(type => type.Name == name) ?? throw new InvalidOperationException();
|
return allDefinedTypes.FirstOrDefault(ctd => ctd.Name == name) is ICustomTypeDefinition ctd ? new Optional<ICustomTypeDefinition>(ctd) : Optional<ICustomTypeDefinition>.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,7 +246,7 @@ public class LoaderTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void LoadMap_MapHasClassAndLoaderHasNoCustomTypes_ThrowsException()
|
public void LoadMap_MapHasClassAndLoaderHasNoCustomTypes_ReturnsMapWithEmptyProperties()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var resourceReader = Substitute.For<IResourceReader>();
|
var resourceReader = Substitute.For<IResourceReader>();
|
||||||
|
@ -270,8 +270,11 @@ public class LoaderTests
|
||||||
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
var customTypeDefinitions = Enumerable.Empty<ICustomTypeDefinition>();
|
||||||
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
var loader = new Loader(resourceReader, resourceCache, customTypeDefinitions);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
Assert.Throws<KeyNotFoundException>(() => loader.LoadMap("map.tmx"));
|
var result = loader.LoadMap("map.tmx");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
DotTiledAssert.AssertProperties([], result.Properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
@ -32,9 +32,14 @@ public partial class MapReaderTests
|
||||||
using var tilesetReader = new TilesetReader(tilesetString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var tilesetReader = new TilesetReader(tilesetString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
ICustomTypeDefinition ResolveCustomType(string name)
|
Optional<ICustomTypeDefinition> ResolveCustomType(string name)
|
||||||
{
|
{
|
||||||
return customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name)!;
|
if (customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name) is ICustomTypeDefinition ctd)
|
||||||
|
{
|
||||||
|
return new Optional<ICustomTypeDefinition>(ctd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional<ICustomTypeDefinition>.Empty;
|
||||||
}
|
}
|
||||||
using var mapReader = new MapReader(mapString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var mapReader = new MapReader(mapString, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,14 @@ public partial class TmjMapReaderTests
|
||||||
using var tilesetReader = new TsjTilesetReader(tilesetJson, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var tilesetReader = new TsjTilesetReader(tilesetJson, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
ICustomTypeDefinition ResolveCustomType(string name)
|
Optional<ICustomTypeDefinition> ResolveCustomType(string name)
|
||||||
{
|
{
|
||||||
return customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name)!;
|
if (customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name) is ICustomTypeDefinition ctd)
|
||||||
|
{
|
||||||
|
return new Optional<ICustomTypeDefinition>(ctd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional<ICustomTypeDefinition>.Empty;
|
||||||
}
|
}
|
||||||
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var mapReader = new TmjMapReader(json, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,14 @@ public partial class TmxMapReaderTests
|
||||||
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var tilesetReader = new TsxTilesetReader(xmlTilesetReader, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
return tilesetReader.ReadTileset();
|
return tilesetReader.ReadTileset();
|
||||||
}
|
}
|
||||||
ICustomTypeDefinition ResolveCustomType(string name)
|
Optional<ICustomTypeDefinition> ResolveCustomType(string name)
|
||||||
{
|
{
|
||||||
return customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name)!;
|
if (customTypeDefinitions.FirstOrDefault(ctd => ctd.Name == name) is ICustomTypeDefinition ctd)
|
||||||
|
{
|
||||||
|
return new Optional<ICustomTypeDefinition>(ctd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional<ICustomTypeDefinition>.Empty;
|
||||||
}
|
}
|
||||||
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
using var mapReader = new TmxMapReader(reader, ResolveTileset, ResolveTemplate, ResolveCustomType);
|
||||||
|
|
||||||
|
|
|
@ -86,13 +86,16 @@ internal static partial class Helpers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static List<IProperty> ResolveClassProperties(string className, Func<string, ICustomTypeDefinition> customTypeResolver)
|
internal static List<IProperty> ResolveClassProperties(string className, Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(className))
|
if (string.IsNullOrWhiteSpace(className))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var customType = customTypeResolver(className) ?? throw new InvalidOperationException($"Could not resolve custom type '{className}'.");
|
var customType = customTypeResolver(className) ?? throw new InvalidOperationException($"Could not resolve custom type '{className}'.");
|
||||||
if (customType is not CustomClassDefinition ccd)
|
if (!customType.HasValue)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (customType.Value is not CustomClassDefinition ccd)
|
||||||
throw new InvalidOperationException($"Custom type '{className}' is not a class.");
|
throw new InvalidOperationException($"Custom type '{className}' is not a class.");
|
||||||
|
|
||||||
return CreateInstanceOfCustomClass(ccd, customTypeResolver);
|
return CreateInstanceOfCustomClass(ccd, customTypeResolver);
|
||||||
|
@ -100,17 +103,31 @@ internal static partial class Helpers
|
||||||
|
|
||||||
internal static List<IProperty> CreateInstanceOfCustomClass(
|
internal static List<IProperty> CreateInstanceOfCustomClass(
|
||||||
CustomClassDefinition customClassDefinition,
|
CustomClassDefinition customClassDefinition,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
return customClassDefinition.Members.Select(x =>
|
return customClassDefinition.Members.Select(x =>
|
||||||
{
|
{
|
||||||
if (x is ClassProperty cp)
|
if (x is ClassProperty cp)
|
||||||
|
{
|
||||||
|
var resolvedType = customTypeResolver(cp.PropertyType);
|
||||||
|
if (!resolvedType.HasValue)
|
||||||
{
|
{
|
||||||
return new ClassProperty
|
return new ClassProperty
|
||||||
{
|
{
|
||||||
Name = cp.Name,
|
Name = cp.Name,
|
||||||
PropertyType = cp.PropertyType,
|
PropertyType = cp.PropertyType,
|
||||||
Value = CreateInstanceOfCustomClass((CustomClassDefinition)customTypeResolver(cp.PropertyType), customTypeResolver)
|
Value = []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedType.Value is not CustomClassDefinition ccd)
|
||||||
|
throw new InvalidOperationException($"Custom type '{cp.PropertyType}' is not a class.");
|
||||||
|
|
||||||
|
return new ClassProperty
|
||||||
|
{
|
||||||
|
Name = cp.Name,
|
||||||
|
PropertyType = cp.PropertyType,
|
||||||
|
Value = CreateInstanceOfCustomClass(ccd, customTypeResolver)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ public class Loader
|
||||||
{
|
{
|
||||||
private readonly IResourceReader _resourceReader;
|
private readonly IResourceReader _resourceReader;
|
||||||
private readonly IResourceCache _resourceCache;
|
private readonly IResourceCache _resourceCache;
|
||||||
private readonly IDictionary<string, ICustomTypeDefinition> _customTypeDefinitions;
|
private readonly Dictionary<string, ICustomTypeDefinition> _customTypeDefinitions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Loader"/> class with the given <paramref name="resourceReader"/>, <paramref name="resourceCache"/>, and <paramref name="customTypeDefinitions"/>.
|
/// Initializes a new instance of the <see cref="Loader"/> class with the given <paramref name="resourceReader"/>, <paramref name="resourceCache"/>, and <paramref name="customTypeDefinitions"/>.
|
||||||
|
@ -114,5 +114,11 @@ public class Loader
|
||||||
return templateReader.ReadTemplate();
|
return templateReader.ReadTemplate();
|
||||||
});
|
});
|
||||||
|
|
||||||
private ICustomTypeDefinition CustomTypeResolver(string name) => _customTypeDefinitions[name];
|
private Optional<ICustomTypeDefinition> CustomTypeResolver(string name)
|
||||||
|
{
|
||||||
|
if (_customTypeDefinitions.TryGetValue(name, out var customTypeDefinition))
|
||||||
|
return new Optional<ICustomTypeDefinition>(customTypeDefinition);
|
||||||
|
|
||||||
|
return Optional<ICustomTypeDefinition>.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class MapReader : IMapReader
|
||||||
// External resolvers
|
// External resolvers
|
||||||
private readonly Func<string, Tileset> _externalTilesetResolver;
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
private readonly Func<string, Template> _externalTemplateResolver;
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
private readonly Func<string, ICustomTypeDefinition> _customTypeResolver;
|
private readonly Func<string, Optional<ICustomTypeDefinition>> _customTypeResolver;
|
||||||
|
|
||||||
private readonly StringReader _mapStringReader;
|
private readonly StringReader _mapStringReader;
|
||||||
private readonly XmlReader _xmlReader;
|
private readonly XmlReader _xmlReader;
|
||||||
|
@ -33,7 +33,7 @@ public class MapReader : IMapReader
|
||||||
string map,
|
string map,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class TemplateReader : ITemplateReader
|
||||||
// External resolvers
|
// External resolvers
|
||||||
private readonly Func<string, Tileset> _externalTilesetResolver;
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
private readonly Func<string, Template> _externalTemplateResolver;
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
private readonly Func<string, ICustomTypeDefinition> _customTypeResolver;
|
private readonly Func<string, Optional<ICustomTypeDefinition>> _customTypeResolver;
|
||||||
|
|
||||||
private readonly StringReader _templateStringReader;
|
private readonly StringReader _templateStringReader;
|
||||||
private readonly XmlReader _xmlReader;
|
private readonly XmlReader _xmlReader;
|
||||||
|
@ -33,7 +33,7 @@ public class TemplateReader : ITemplateReader
|
||||||
string template,
|
string template,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class TilesetReader : ITilesetReader
|
||||||
// External resolvers
|
// External resolvers
|
||||||
private readonly Func<string, Tileset> _externalTilesetResolver;
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
private readonly Func<string, Template> _externalTemplateResolver;
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
private readonly Func<string, ICustomTypeDefinition> _customTypeResolver;
|
private readonly Func<string, Optional<ICustomTypeDefinition>> _customTypeResolver;
|
||||||
|
|
||||||
private readonly StringReader _tilesetStringReader;
|
private readonly StringReader _tilesetStringReader;
|
||||||
private readonly XmlReader _xmlReader;
|
private readonly XmlReader _xmlReader;
|
||||||
|
@ -33,7 +33,7 @@ public class TilesetReader : ITilesetReader
|
||||||
string tileset,
|
string tileset,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
_externalTemplateResolver = externalTemplateResolver ?? throw new ArgumentNullException(nameof(externalTemplateResolver));
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class TjTemplateReader : TmjReaderBase, ITemplateReader
|
||||||
string jsonString,
|
string jsonString,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class TmjMapReader : TmjReaderBase, IMapReader
|
||||||
string jsonString,
|
string jsonString,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -63,8 +64,28 @@ public abstract partial class TmjReaderBase
|
||||||
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
||||||
var customTypeDef = _customTypeResolver(propertyType);
|
var customTypeDef = _customTypeResolver(propertyType);
|
||||||
|
|
||||||
if (customTypeDef is CustomClassDefinition ccd)
|
// If the custom class definition is not found,
|
||||||
|
// we assume an empty class definition.
|
||||||
|
if (!customTypeDef.HasValue)
|
||||||
{
|
{
|
||||||
|
if (!element.TryGetProperty("value", out var valueElement))
|
||||||
|
return new ClassProperty { Name = name, PropertyType = propertyType, Value = [] };
|
||||||
|
|
||||||
|
return new ClassProperty
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
PropertyType = propertyType,
|
||||||
|
Value = ReadPropertiesInsideClass(valueElement, new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = propertyType,
|
||||||
|
Members = []
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customTypeDef.Value is not CustomClassDefinition ccd)
|
||||||
|
throw new JsonException($"Custom type {propertyType} is not a class.");
|
||||||
|
|
||||||
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd, _customTypeResolver);
|
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd, _customTypeResolver);
|
||||||
var props = element.GetOptionalPropertyCustom<List<IProperty>>("value", e => ReadPropertiesInsideClass(e, ccd)).GetValueOr([]);
|
var props = element.GetOptionalPropertyCustom<List<IProperty>>("value", e => ReadPropertiesInsideClass(e, ccd)).GetValueOr([]);
|
||||||
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||||
|
@ -77,9 +98,6 @@ public abstract partial class TmjReaderBase
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new JsonException($"Unknown custom class '{propertyType}'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
internal List<IProperty> ReadPropertiesInsideClass(
|
internal List<IProperty> ReadPropertiesInsideClass(
|
||||||
JsonElement element,
|
JsonElement element,
|
||||||
CustomClassDefinition customClassDefinition)
|
CustomClassDefinition customClassDefinition)
|
||||||
|
@ -91,6 +109,33 @@ public abstract partial class TmjReaderBase
|
||||||
if (!element.TryGetProperty(prop.Name, out var propElement))
|
if (!element.TryGetProperty(prop.Name, out var propElement))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (prop is ClassProperty classProp)
|
||||||
|
{
|
||||||
|
var resolvedCustomType = _customTypeResolver(classProp.PropertyType);
|
||||||
|
if (!resolvedCustomType.HasValue)
|
||||||
|
{
|
||||||
|
resultingProps.Add(new ClassProperty
|
||||||
|
{
|
||||||
|
Name = classProp.Name,
|
||||||
|
PropertyType = classProp.PropertyType,
|
||||||
|
Value = []
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedCustomType.Value is not CustomClassDefinition ccd)
|
||||||
|
throw new JsonException($"Custom type '{classProp.PropertyType}' is not a class.");
|
||||||
|
|
||||||
|
var readProps = ReadPropertiesInsideClass(propElement, ccd);
|
||||||
|
resultingProps.Add(new ClassProperty
|
||||||
|
{
|
||||||
|
Name = classProp.Name,
|
||||||
|
PropertyType = classProp.PropertyType,
|
||||||
|
Value = readProps
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
IProperty property = prop.Type switch
|
IProperty property = prop.Type switch
|
||||||
{
|
{
|
||||||
PropertyType.String => new StringProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
PropertyType.String => new StringProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
||||||
|
@ -100,8 +145,8 @@ public abstract partial class TmjReaderBase
|
||||||
PropertyType.Color => new ColorProperty { Name = prop.Name, Value = Color.Parse(propElement.GetValueAs<string>(), CultureInfo.InvariantCulture) },
|
PropertyType.Color => new ColorProperty { Name = prop.Name, Value = Color.Parse(propElement.GetValueAs<string>(), CultureInfo.InvariantCulture) },
|
||||||
PropertyType.File => new FileProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
PropertyType.File => new FileProperty { Name = prop.Name, Value = propElement.GetValueAs<string>() },
|
||||||
PropertyType.Object => new ObjectProperty { Name = prop.Name, Value = propElement.GetValueAs<uint>() },
|
PropertyType.Object => new ObjectProperty { Name = prop.Name, Value = propElement.GetValueAs<uint>() },
|
||||||
PropertyType.Class => new ClassProperty { Name = prop.Name, PropertyType = ((ClassProperty)prop).PropertyType, Value = ReadPropertiesInsideClass(propElement, (CustomClassDefinition)_customTypeResolver(((ClassProperty)prop).PropertyType)) },
|
|
||||||
PropertyType.Enum => ReadEnumProperty(propElement),
|
PropertyType.Enum => ReadEnumProperty(propElement),
|
||||||
|
PropertyType.Class => throw new NotImplementedException("Class properties should be handled elsewhere"),
|
||||||
_ => throw new JsonException("Invalid property type")
|
_ => throw new JsonException("Invalid property type")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,7 +160,7 @@ public abstract partial class TmjReaderBase
|
||||||
{
|
{
|
||||||
var name = element.GetRequiredProperty<string>("name");
|
var name = element.GetRequiredProperty<string>("name");
|
||||||
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
var propertyType = element.GetRequiredProperty<string>("propertytype");
|
||||||
var typeInXml = element.GetOptionalPropertyParseable<PropertyType>("type", (s) => s switch
|
var typeInJson = element.GetOptionalPropertyParseable<PropertyType>("type", (s) => s switch
|
||||||
{
|
{
|
||||||
"string" => PropertyType.String,
|
"string" => PropertyType.String,
|
||||||
"int" => PropertyType.Int,
|
"int" => PropertyType.Int,
|
||||||
|
@ -123,8 +168,24 @@ public abstract partial class TmjReaderBase
|
||||||
}).GetValueOr(PropertyType.String);
|
}).GetValueOr(PropertyType.String);
|
||||||
var customTypeDef = _customTypeResolver(propertyType);
|
var customTypeDef = _customTypeResolver(propertyType);
|
||||||
|
|
||||||
if (customTypeDef is not CustomEnumDefinition ced)
|
if (!customTypeDef.HasValue)
|
||||||
throw new JsonException($"Unknown custom enum '{propertyType}'. Enums must be defined");
|
{
|
||||||
|
if (typeInJson == PropertyType.String)
|
||||||
|
{
|
||||||
|
var value = element.GetRequiredProperty<string>("value");
|
||||||
|
var values = value.Split(',').Select(v => v.Trim()).ToHashSet();
|
||||||
|
return new EnumProperty { Name = name, PropertyType = propertyType, Value = values };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var value = element.GetRequiredProperty<int>("value");
|
||||||
|
var values = new HashSet<string> { value.ToString(CultureInfo.InvariantCulture) };
|
||||||
|
return new EnumProperty { Name = name, PropertyType = propertyType, Value = values };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customTypeDef.Value is not CustomEnumDefinition ced)
|
||||||
|
throw new JsonException($"Custom type '{propertyType}' is not an enum.");
|
||||||
|
|
||||||
if (ced.StorageType == CustomEnumStorageType.String)
|
if (ced.StorageType == CustomEnumStorageType.String)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@ public abstract partial class TmjReaderBase : IDisposable
|
||||||
// External resolvers
|
// External resolvers
|
||||||
private readonly Func<string, Tileset> _externalTilesetResolver;
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
private readonly Func<string, Template> _externalTemplateResolver;
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
private readonly Func<string, ICustomTypeDefinition> _customTypeResolver;
|
private readonly Func<string, Optional<ICustomTypeDefinition>> _customTypeResolver;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The root element of the JSON document being read.
|
/// The root element of the JSON document being read.
|
||||||
|
@ -34,7 +34,7 @@ public abstract partial class TmjReaderBase : IDisposable
|
||||||
string jsonString,
|
string jsonString,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
RootElement = JsonDocument.Parse(jsonString ?? throw new ArgumentNullException(nameof(jsonString))).RootElement;
|
RootElement = JsonDocument.Parse(jsonString ?? throw new ArgumentNullException(nameof(jsonString))).RootElement;
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class TsjTilesetReader : TmjReaderBase, ITilesetReader
|
||||||
string jsonString,
|
string jsonString,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
jsonString, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class TmxMapReader : TmxReaderBase, IMapReader
|
||||||
XmlReader reader,
|
XmlReader reader,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
|
@ -66,25 +67,35 @@ public abstract partial class TmxReaderBase
|
||||||
var propertyType = _reader.GetRequiredAttribute("propertytype");
|
var propertyType = _reader.GetRequiredAttribute("propertytype");
|
||||||
var customTypeDef = _customTypeResolver(propertyType);
|
var customTypeDef = _customTypeResolver(propertyType);
|
||||||
|
|
||||||
if (customTypeDef is CustomClassDefinition ccd)
|
// If the custom class definition is not found,
|
||||||
|
// we assume an empty class definition.
|
||||||
|
if (!customTypeDef.HasValue)
|
||||||
{
|
{
|
||||||
if (!_reader.IsEmptyElement)
|
if (!_reader.IsEmptyElement)
|
||||||
{
|
{
|
||||||
_reader.ReadStartElement("property");
|
_reader.ReadStartElement("property");
|
||||||
|
var props = ReadProperties();
|
||||||
|
_reader.ReadEndElement();
|
||||||
|
return new ClassProperty { Name = name, PropertyType = propertyType, Value = props };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClassProperty { Name = name, PropertyType = propertyType, Value = [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customTypeDef.Value is not CustomClassDefinition ccd)
|
||||||
|
throw new XmlException($"Custom type {propertyType} is not a class.");
|
||||||
|
|
||||||
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd, _customTypeResolver);
|
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd, _customTypeResolver);
|
||||||
|
if (!_reader.IsEmptyElement)
|
||||||
|
{
|
||||||
|
_reader.ReadStartElement("property");
|
||||||
var props = ReadProperties();
|
var props = ReadProperties();
|
||||||
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
var mergedProps = Helpers.MergeProperties(propsInType, props);
|
||||||
_reader.ReadEndElement();
|
_reader.ReadEndElement();
|
||||||
return new ClassProperty { Name = name, PropertyType = propertyType, Value = mergedProps };
|
return new ClassProperty { Name = name, PropertyType = propertyType, Value = mergedProps };
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var propsInType = Helpers.CreateInstanceOfCustomClass(ccd, _customTypeResolver);
|
|
||||||
return new ClassProperty { Name = name, PropertyType = propertyType, Value = propsInType };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new XmlException($"Unkonwn custom class definition: {propertyType}");
|
return new ClassProperty { Name = name, PropertyType = propertyType, Value = propsInType };
|
||||||
}
|
}
|
||||||
|
|
||||||
internal EnumProperty ReadEnumProperty()
|
internal EnumProperty ReadEnumProperty()
|
||||||
|
@ -99,8 +110,26 @@ public abstract partial class TmxReaderBase
|
||||||
}) ?? PropertyType.String;
|
}) ?? PropertyType.String;
|
||||||
var customTypeDef = _customTypeResolver(propertyType);
|
var customTypeDef = _customTypeResolver(propertyType);
|
||||||
|
|
||||||
if (customTypeDef is not CustomEnumDefinition ced)
|
// If the custom enum definition is not found,
|
||||||
throw new XmlException($"Unknown custom enum definition: {propertyType}. Enums must be defined");
|
// we assume an empty enum definition.
|
||||||
|
if (!customTypeDef.HasValue)
|
||||||
|
{
|
||||||
|
if (typeInXml == PropertyType.String)
|
||||||
|
{
|
||||||
|
var value = _reader.GetRequiredAttribute("value");
|
||||||
|
var values = value.Split(',').Select(v => v.Trim()).ToHashSet();
|
||||||
|
return new EnumProperty { Name = name, PropertyType = propertyType, Value = values };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var value = _reader.GetRequiredAttributeParseable<int>("value");
|
||||||
|
var values = new HashSet<string> { value.ToString(CultureInfo.InvariantCulture) };
|
||||||
|
return new EnumProperty { Name = name, PropertyType = propertyType, Value = values };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customTypeDef.Value is not CustomEnumDefinition ced)
|
||||||
|
throw new XmlException($"Custom defined type {propertyType} is not an enum.");
|
||||||
|
|
||||||
if (ced.StorageType == CustomEnumStorageType.String)
|
if (ced.StorageType == CustomEnumStorageType.String)
|
||||||
{
|
{
|
||||||
|
@ -144,6 +173,6 @@ public abstract partial class TmxReaderBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new XmlException($"Unknown custom enum storage type: {ced.StorageType}");
|
throw new XmlException($"Unable to read enum property {name} with type {propertyType}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ public abstract partial class TmxReaderBase : IDisposable
|
||||||
// External resolvers
|
// External resolvers
|
||||||
private readonly Func<string, Tileset> _externalTilesetResolver;
|
private readonly Func<string, Tileset> _externalTilesetResolver;
|
||||||
private readonly Func<string, Template> _externalTemplateResolver;
|
private readonly Func<string, Template> _externalTemplateResolver;
|
||||||
private readonly Func<string, ICustomTypeDefinition> _customTypeResolver;
|
private readonly Func<string, Optional<ICustomTypeDefinition>> _customTypeResolver;
|
||||||
|
|
||||||
private readonly XmlReader _reader;
|
private readonly XmlReader _reader;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
|
@ -28,7 +28,7 @@ public abstract partial class TmxReaderBase : IDisposable
|
||||||
XmlReader reader,
|
XmlReader reader,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver)
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver)
|
||||||
{
|
{
|
||||||
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
_reader = reader ?? throw new ArgumentNullException(nameof(reader));
|
||||||
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
_externalTilesetResolver = externalTilesetResolver ?? throw new ArgumentNullException(nameof(externalTilesetResolver));
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class TsxTilesetReader : TmxReaderBase, ITilesetReader
|
||||||
XmlReader reader,
|
XmlReader reader,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class TxTemplateReader : TmxReaderBase, ITemplateReader
|
||||||
XmlReader reader,
|
XmlReader reader,
|
||||||
Func<string, Tileset> externalTilesetResolver,
|
Func<string, Tileset> externalTilesetResolver,
|
||||||
Func<string, Template> externalTemplateResolver,
|
Func<string, Template> externalTemplateResolver,
|
||||||
Func<string, ICustomTypeDefinition> customTypeResolver) : base(
|
Func<string, Optional<ICustomTypeDefinition>> customTypeResolver) : base(
|
||||||
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
reader, externalTilesetResolver, externalTemplateResolver, customTypeResolver)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue