mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-04-17 03:33:06 +00:00
872 lines
24 KiB
C#
872 lines
24 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using System.Xml.Serialization;
|
|
using SabreTools.Data.Models.Metadata;
|
|
|
|
namespace SabreTools.Metadata.Filter
|
|
{
|
|
/// <summary>
|
|
/// Represents a single filter key
|
|
/// </summary>
|
|
public class FilterKey
|
|
{
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Item name associated with the filter
|
|
/// </summary>
|
|
public readonly string ItemName;
|
|
|
|
/// <summary>
|
|
/// Field name associated with the filter
|
|
/// </summary>
|
|
public readonly string FieldName;
|
|
|
|
#endregion
|
|
|
|
#region Constants
|
|
|
|
/// <summary>
|
|
/// Cached item type names for filter selection
|
|
/// </summary>
|
|
#if NET5_0_OR_GREATER
|
|
private static readonly string[] _datItemTypeNames = Enum.GetNames<ItemType>();
|
|
#else
|
|
private static readonly string[] _datItemTypeNames = Enum.GetNames(typeof(ItemType));
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Known keys for Adjuster
|
|
/// </summary>
|
|
private static readonly string[] _adjusterKeys =
|
|
[
|
|
"default",
|
|
"name"
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Analog
|
|
/// </summary>
|
|
private static readonly string[] _analogKeys =
|
|
[
|
|
"mask"
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Archive
|
|
/// </summary>
|
|
private static readonly string[] _archiveKeys =
|
|
[
|
|
"additional",
|
|
"adult",
|
|
"alt",
|
|
"bios",
|
|
"categories",
|
|
"clone",
|
|
"clonetag",
|
|
"complete",
|
|
"dat",
|
|
"datternote",
|
|
"description",
|
|
"devstatus",
|
|
"gameid1",
|
|
"gameid2",
|
|
"langchecked",
|
|
"languages",
|
|
"licensed",
|
|
"listed",
|
|
"mergeof",
|
|
"mergename",
|
|
"name",
|
|
"namealt",
|
|
"number",
|
|
"physical",
|
|
"pirate",
|
|
"private",
|
|
"region",
|
|
"regparent",
|
|
"showlang",
|
|
"special1",
|
|
"special2",
|
|
"stickynote",
|
|
"version1",
|
|
"version2",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for BiosSet
|
|
/// </summary>
|
|
private static readonly string[] _biossetKeys =
|
|
[
|
|
"default",
|
|
"description",
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Chip
|
|
/// </summary>
|
|
private static readonly string[] _chipKeys =
|
|
[
|
|
"chiptype",
|
|
"clock",
|
|
"flags",
|
|
"name",
|
|
"soundonly",
|
|
"tag",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Condition
|
|
/// </summary>
|
|
private static readonly string[] _conditionKeys =
|
|
[
|
|
"mask",
|
|
"relation",
|
|
"tag",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Configuration
|
|
/// </summary>
|
|
private static readonly string[] _configurationKeys =
|
|
[
|
|
"mask",
|
|
"name",
|
|
"tag",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for ConfLocation
|
|
/// </summary>
|
|
private static readonly string[] _confLocationKeys =
|
|
[
|
|
"inverted",
|
|
"name",
|
|
"number",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for ConfSetting
|
|
/// </summary>
|
|
private static readonly string[] _confSettingKeys =
|
|
[
|
|
"default",
|
|
"name",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Control
|
|
/// </summary>
|
|
private static readonly string[] _controlKeys =
|
|
[
|
|
"buttons",
|
|
"controltype",
|
|
"keydelta",
|
|
"maximum",
|
|
"minimum",
|
|
"player",
|
|
"reqbuttons",
|
|
"reverse",
|
|
"sensitivity",
|
|
"ways",
|
|
"ways2",
|
|
"ways3",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DataArea
|
|
/// </summary>
|
|
private static readonly string[] _dataAreaKeys =
|
|
[
|
|
"endianness",
|
|
"name",
|
|
"size",
|
|
"width",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Device
|
|
/// </summary>
|
|
private static readonly string[] _deviceKeys =
|
|
[
|
|
"devicetype",
|
|
"fixedimage",
|
|
"interface",
|
|
"mandatory",
|
|
"tag",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DeviceRef
|
|
/// </summary>
|
|
private static readonly string[] _deviceRefKeys =
|
|
[
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DipLocation
|
|
/// </summary>
|
|
private static readonly string[] _dipLocationKeys =
|
|
[
|
|
"inverted",
|
|
"name",
|
|
"number",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DipSwitch
|
|
/// </summary>
|
|
private static readonly string[] _dipSwitchKeys =
|
|
[
|
|
"default",
|
|
"mask",
|
|
"name",
|
|
"tag",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DipSwitch
|
|
/// </summary>
|
|
private static readonly string[] _dipValueKeys =
|
|
[
|
|
"default",
|
|
"name",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Disk
|
|
/// </summary>
|
|
private static readonly string[] _diskKeys =
|
|
[
|
|
"flags",
|
|
"index",
|
|
"md5",
|
|
"merge",
|
|
"name",
|
|
"optional",
|
|
"region",
|
|
"sha1",
|
|
"status",
|
|
"writable",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for DiskArea
|
|
/// </summary>
|
|
private static readonly string[] _diskAreaKeys =
|
|
[
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Display
|
|
/// </summary>
|
|
private static readonly string[] _displayKeys =
|
|
[
|
|
"aspectx",
|
|
"aspecty",
|
|
"displaytype",
|
|
"flipx",
|
|
"freq",
|
|
"hbend",
|
|
"hbstart",
|
|
"height",
|
|
"htotal",
|
|
"orientation",
|
|
"pixclock",
|
|
"refresh",
|
|
"rotate",
|
|
"screen",
|
|
"tag",
|
|
"vbend",
|
|
"vbstart",
|
|
"vtotal",
|
|
"width",
|
|
"x",
|
|
"y",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Driver
|
|
/// </summary>
|
|
private static readonly string[] _driverKeys =
|
|
[
|
|
"blit",
|
|
"cocktail",
|
|
"color",
|
|
"emulation",
|
|
"incomplete",
|
|
"nosoundhardware",
|
|
"palettesize",
|
|
"requiresartwork",
|
|
"savestate",
|
|
"sound",
|
|
"status",
|
|
"unofficial",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Extension
|
|
/// </summary>
|
|
private static readonly string[] _extensionKeys =
|
|
[
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Feature/PartFeature
|
|
/// </summary>
|
|
private static readonly string[] _featureKeys =
|
|
[
|
|
"featuretype",
|
|
"name",
|
|
"overall",
|
|
"status",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Header
|
|
/// </summary>
|
|
private static readonly string[] _headerKeys =
|
|
[
|
|
"author",
|
|
"biosmode",
|
|
"build",
|
|
"category",
|
|
"comment",
|
|
"date",
|
|
"datversion",
|
|
"debug",
|
|
"description",
|
|
"email",
|
|
"emulatorversion",
|
|
"filename",
|
|
"forcemerging",
|
|
"forcenodump",
|
|
"forcepacking",
|
|
"forcezipping",
|
|
"header",
|
|
"headerskipper",
|
|
"homepage",
|
|
"id",
|
|
"imfolder",
|
|
"lockbiosmode",
|
|
"lockrommode",
|
|
"locksamplemode",
|
|
"mameconfig",
|
|
"name",
|
|
"notes",
|
|
"plugin",
|
|
"refname",
|
|
"rommode",
|
|
"romtitle",
|
|
"rootdir",
|
|
"samplemode",
|
|
"schemalocation",
|
|
"screenshotsheight",
|
|
"screenshotswidth",
|
|
"skipper",
|
|
"system",
|
|
"timestamp",
|
|
"type",
|
|
"url",
|
|
"version",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Info
|
|
/// </summary>
|
|
private static readonly string[] _infoKeys =
|
|
[
|
|
"name",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Input
|
|
/// </summary>
|
|
private static readonly string[] _inputKeys =
|
|
[
|
|
"buttons",
|
|
"coins",
|
|
"control",
|
|
"controlattr",
|
|
"players",
|
|
"service",
|
|
"tilt",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Instance
|
|
/// </summary>
|
|
private static readonly string[] _instanceKeys =
|
|
[
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Machine
|
|
/// </summary>
|
|
private static readonly string[] _machineKeys =
|
|
[
|
|
"board",
|
|
"buttons",
|
|
"category",
|
|
"cloneof",
|
|
"comment",
|
|
"company",
|
|
"control",
|
|
"crc",
|
|
"country",
|
|
"description",
|
|
"developer",
|
|
"dirname",
|
|
"displaycount",
|
|
"displaytype",
|
|
"duplicateid",
|
|
"emulator",
|
|
"enabled",
|
|
"extra",
|
|
"favorite",
|
|
"genmsxid",
|
|
"genre",
|
|
"hash",
|
|
"history",
|
|
"id",
|
|
"im1crc",
|
|
"im2crc",
|
|
"imagenumber",
|
|
"isbios",
|
|
"isdevice",
|
|
"ismechanical",
|
|
"language",
|
|
"location",
|
|
"manufacturer",
|
|
"name",
|
|
"notes",
|
|
"playedcount",
|
|
"playedtime",
|
|
"players",
|
|
"publisher",
|
|
"ratings",
|
|
"rebuildto",
|
|
"relatedto",
|
|
"releasenumber",
|
|
"romof",
|
|
"rotation",
|
|
"runnable",
|
|
"sampleof",
|
|
"savetype",
|
|
"score",
|
|
"source",
|
|
"sourcefile",
|
|
"sourcerom",
|
|
"status",
|
|
"subgenre",
|
|
"supported",
|
|
"system",
|
|
"tags",
|
|
"titleid",
|
|
"type",
|
|
"url",
|
|
"year",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Media
|
|
/// </summary>
|
|
private static readonly string[] _mediaKeys =
|
|
[
|
|
"md5",
|
|
"name",
|
|
"sha1",
|
|
"sha256",
|
|
"spamsum",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Original
|
|
/// </summary>
|
|
private static readonly string[] _originalKeys =
|
|
[
|
|
"content",
|
|
"value",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Part
|
|
/// </summary>
|
|
private static readonly string[] _partKeys =
|
|
[
|
|
"interface",
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Port
|
|
/// </summary>
|
|
private static readonly string[] _portKeys =
|
|
[
|
|
"tag",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for RamOption
|
|
/// </summary>
|
|
private static readonly string[] _ramOptionKeys =
|
|
[
|
|
"content",
|
|
"default",
|
|
"name",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys for Release
|
|
/// </summary>
|
|
private static readonly string[] _releaseKeys =
|
|
[
|
|
"date",
|
|
"default",
|
|
"language",
|
|
"name",
|
|
"region",
|
|
];
|
|
|
|
/// <summary>
|
|
/// Known keys ReleaseDetails Release
|
|
/// </summary>
|
|
private static readonly string[] _releaseDetailsKeys =
|
|
[
|
|
"appendtonumber",
|
|
"archivename",
|
|
"category",
|
|
"comment",
|
|
"date",
|
|
"dirname",
|
|
"group",
|
|
"id",
|
|
"nfocrc",
|
|
"nfoname",
|
|
"nfosize",
|
|
"origin",
|
|
"originalformat",
|
|
"region",
|
|
"rominfo",
|
|
"tool",
|
|
];
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Validating combined key constructor
|
|
/// </summary>
|
|
public FilterKey(string? key)
|
|
{
|
|
if (!ParseFilterId(key, out string itemName, out string fieldName))
|
|
throw new ArgumentException($"{nameof(key)} could not be parsed", nameof(key));
|
|
|
|
ItemName = itemName;
|
|
FieldName = fieldName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validating discrete value constructor
|
|
/// </summary>
|
|
public FilterKey(string itemName, string fieldName)
|
|
{
|
|
if (!ParseFilterId(ref itemName, ref fieldName))
|
|
throw new ArgumentException($"{nameof(itemName)} was not recognized", nameof(itemName));
|
|
|
|
ItemName = itemName;
|
|
FieldName = fieldName;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override string ToString() => $"{ItemName}.{FieldName}";
|
|
|
|
/// <summary>
|
|
/// Parse a filter ID string into the item name and field name, if possible
|
|
/// </summary>
|
|
private static bool ParseFilterId(string? itemFieldString, out string itemName, out string fieldName)
|
|
{
|
|
// Set default values
|
|
itemName = string.Empty; fieldName = string.Empty;
|
|
|
|
// If we don't have a filter ID, we can't do anything
|
|
if (string.IsNullOrEmpty(itemFieldString))
|
|
return false;
|
|
|
|
// If we only have one part, we can't do anything
|
|
string[] splitFilter = itemFieldString!.Split('.');
|
|
if (splitFilter.Length != 2)
|
|
return false;
|
|
|
|
// Set and sanitize the filter ID
|
|
itemName = splitFilter[0];
|
|
fieldName = splitFilter[1];
|
|
return ParseFilterId(ref itemName, ref fieldName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a filter ID string into the item name and field name, if possible
|
|
/// </summary>
|
|
private static bool ParseFilterId(ref string itemName, ref string fieldName)
|
|
{
|
|
// If we don't have a filter ID, we can't do anything
|
|
if (string.IsNullOrEmpty(itemName) || string.IsNullOrEmpty(fieldName))
|
|
return false;
|
|
|
|
// Return santized values based on the split ID
|
|
return itemName.ToLowerInvariant() switch
|
|
{
|
|
// Header
|
|
"header" => ParseHeaderFilterId(ref itemName, ref fieldName),
|
|
|
|
// Machine
|
|
"game" => ParseMachineFilterId(ref itemName, ref fieldName),
|
|
"machine" => ParseMachineFilterId(ref itemName, ref fieldName),
|
|
"resource" => ParseMachineFilterId(ref itemName, ref fieldName),
|
|
"set" => ParseMachineFilterId(ref itemName, ref fieldName),
|
|
|
|
// DatItem
|
|
"datitem" => ParseDatItemFilterId(ref itemName, ref fieldName),
|
|
"item" => ParseDatItemFilterId(ref itemName, ref fieldName),
|
|
_ => ParseDatItemFilterId(ref itemName, ref fieldName),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse and validate header fields
|
|
/// </summary>
|
|
private static bool ParseHeaderFilterId(ref string itemName, ref string fieldName)
|
|
{
|
|
// Get if there's a match to a property
|
|
string localFieldName = fieldName;
|
|
string? propertyMatch = Array.Find(_headerKeys, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
|
|
if (propertyMatch is null)
|
|
return false;
|
|
|
|
// Return the sanitized ID
|
|
itemName = "header";
|
|
fieldName = propertyMatch.ToLowerInvariant();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse and validate machine/game fields
|
|
/// </summary>
|
|
private static bool ParseMachineFilterId(ref string itemName, ref string fieldName)
|
|
{
|
|
// Get if there's a match to a property
|
|
string localFieldName = fieldName;
|
|
string? propertyMatch = Array.Find(_machineKeys, c => string.Equals(c, localFieldName, StringComparison.OrdinalIgnoreCase));
|
|
if (propertyMatch is null)
|
|
return false;
|
|
|
|
// Return the sanitized ID
|
|
itemName = "machine";
|
|
fieldName = propertyMatch.ToLowerInvariant();
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse and validate item fields
|
|
/// </summary>
|
|
private static bool ParseDatItemFilterId(ref string itemName, ref string fieldName)
|
|
{
|
|
// Special case if the item name is reserved
|
|
if (string.Equals(itemName, "datitem", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(itemName, "item", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Handle item type
|
|
if (string.Equals(fieldName, "type", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
itemName = "item";
|
|
fieldName = "type";
|
|
return true;
|
|
}
|
|
|
|
// If we get any matches
|
|
string localFieldName = fieldName;
|
|
string? matchedType = Array.Find(_datItemTypeNames, t => DatItemContainsField(t, localFieldName));
|
|
if (matchedType is not null)
|
|
{
|
|
// Check for a matching field
|
|
string? matchedField = GetMatchingField(matchedType, fieldName);
|
|
if (matchedField is null)
|
|
return false;
|
|
|
|
itemName = "item";
|
|
fieldName = matchedField;
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check for a matching field
|
|
string? matchedField = GetMatchingField(itemName, fieldName);
|
|
if (matchedField is null)
|
|
return false;
|
|
|
|
itemName = itemName.ToLowerInvariant();
|
|
fieldName = matchedField;
|
|
return true;
|
|
}
|
|
|
|
// Nothing was found
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determine if an item type contains a field
|
|
/// </summary>
|
|
private static bool DatItemContainsField(string itemName, string fieldName)
|
|
=> GetMatchingField(itemName, fieldName) is not null;
|
|
|
|
/// <summary>
|
|
/// Determine if an item type contains a field
|
|
/// </summary>
|
|
private static string? GetMatchingField(string itemName, string fieldName)
|
|
{
|
|
// Get the set of properties
|
|
string[]? properties = itemName.ToLowerInvariant() switch
|
|
{
|
|
"adjuster" => _adjusterKeys,
|
|
"analog" => _analogKeys,
|
|
"archive" => _archiveKeys,
|
|
"biosset" => _biossetKeys,
|
|
"chip" => _chipKeys,
|
|
"condition" => _conditionKeys,
|
|
"configuration" => _configurationKeys,
|
|
"conflocation" => _confLocationKeys,
|
|
"confsetting" => _confSettingKeys,
|
|
"control" => _controlKeys,
|
|
"dataarea" => _dataAreaKeys,
|
|
"device" => _deviceKeys,
|
|
"deviceref" => _deviceRefKeys,
|
|
"diplocation" => _dipLocationKeys,
|
|
"dipswitch" => _dipSwitchKeys,
|
|
"dipvalue" => _dipValueKeys,
|
|
"disk" => _diskKeys,
|
|
"diskarea" => _diskAreaKeys,
|
|
"display" => _displayKeys,
|
|
"driver" => _driverKeys,
|
|
"extension" => _extensionKeys,
|
|
"feature" or "partfeature" => _featureKeys,
|
|
"game" or "machine" or "resource" or "set" => _machineKeys,
|
|
"header" => _headerKeys,
|
|
"info" => _infoKeys,
|
|
"input" => _inputKeys,
|
|
"instance" => _instanceKeys,
|
|
"media" => _mediaKeys,
|
|
"original" => _originalKeys,
|
|
"part" => _partKeys,
|
|
"port" => _portKeys,
|
|
"ramoption" => _ramOptionKeys,
|
|
"release" => _releaseKeys,
|
|
"releasedetails" => _releaseDetailsKeys,
|
|
_ => null,
|
|
};
|
|
|
|
// TODO: Remove this fallback path
|
|
if (properties is null)
|
|
{
|
|
// Get the correct item type
|
|
var itemType = GetDatItemType(itemName.ToLowerInvariant());
|
|
if (itemType is null)
|
|
return null;
|
|
|
|
properties = GetProperties(itemType);
|
|
|
|
// Special cases for mismatched names
|
|
if (properties is not null && itemType == typeof(Rom))
|
|
properties = [.. properties, "crc"];
|
|
}
|
|
|
|
if (properties is null)
|
|
return null;
|
|
|
|
// Get if there's a match to a property
|
|
string? propertyMatch = Array.Find(properties, c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase));
|
|
return propertyMatch?.ToLowerInvariant();
|
|
}
|
|
|
|
#region Reflection-based Helpers
|
|
|
|
/// <summary>
|
|
/// Attempt to get the DatItem type from the name
|
|
/// </summary>
|
|
private static Type? GetDatItemType(string? itemType)
|
|
{
|
|
if (string.IsNullOrEmpty(itemType))
|
|
return null;
|
|
|
|
// Loop through all loaded assemblies
|
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
{
|
|
// If not all types can be loaded, use the ones that could be
|
|
Type?[] assemblyTypes = [];
|
|
try
|
|
{
|
|
assemblyTypes = assembly.GetTypes();
|
|
}
|
|
catch (ReflectionTypeLoadException rtle)
|
|
{
|
|
assemblyTypes = Array.FindAll(rtle.Types ?? [], t => t is not null);
|
|
}
|
|
|
|
// Loop through all types
|
|
foreach (Type? type in assemblyTypes)
|
|
{
|
|
// If the type is invalid
|
|
if (type is null)
|
|
continue;
|
|
|
|
// If the type isn't a class or doesn't implement the interface
|
|
if (!type.IsClass || !typeof(DatItem).IsAssignableFrom(type))
|
|
continue;
|
|
|
|
// Get the XML type name
|
|
#if NET20 || NET35 || NET40
|
|
string? elementName = (Attribute.GetCustomAttribute(type, typeof(XmlRootAttribute)) as XmlRootAttribute)!.ElementName;
|
|
#else
|
|
string? elementName = type.GetCustomAttribute<XmlRootAttribute>()?.ElementName;
|
|
#endif
|
|
if (elementName is null)
|
|
continue;
|
|
|
|
// If the name matches
|
|
if (string.Equals(elementName, itemType, StringComparison.OrdinalIgnoreCase))
|
|
return type;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get property names for the given type, if possible
|
|
/// </summary>
|
|
private static string[]? GetProperties(Type? type)
|
|
{
|
|
if (type is null)
|
|
return null;
|
|
|
|
var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
|
|
if (properties is null)
|
|
return null;
|
|
|
|
string[] propertyNames = Array.ConvertAll(properties, f => f.Name);
|
|
return Array.FindAll(propertyNames, s => s.Length > 0);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|