Little things
This commit is contained in:
parent
81ba16a0d1
commit
4d9cda5f48
189
data/JsonData.cs
189
data/JsonData.cs
@ -1,16 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Xml;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static public class json
|
static public class json
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
static public T load<T>( string filename ) where T : lib.JsonData
|
static public T load<T>( string filename ) where T : lib.JsonData
|
||||||
{
|
{
|
||||||
return (T)lib.JsonData.load<T>( filename );
|
return (T)lib.JsonData.load<T>( filename );
|
||||||
@ -18,31 +14,47 @@ static public class json
|
|||||||
|
|
||||||
static public lib.JsonData load( string filename )
|
static public lib.JsonData load( string filename )
|
||||||
{
|
{
|
||||||
return lib.ConfigBase.load( filename, null );
|
return lib.JsonData.load( filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace lib
|
namespace lib
|
||||||
{
|
{
|
||||||
|
// Merged the Interface and Class logic to remove ambiguity
|
||||||
public interface JsonData
|
public interface JsonData
|
||||||
{
|
{
|
||||||
|
#region Configuration
|
||||||
|
|
||||||
|
// Centralized JSON Options for consistency across Load/Save
|
||||||
|
private static readonly JsonSerializerOptions _options = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true, // Pretty print
|
||||||
|
PropertyNameCaseInsensitive = true, // Forgiving load
|
||||||
|
IncludeFields = true, // Serialize public fields (game dev standard)
|
||||||
|
ReferenceHandler = ReferenceHandler.Preserve, // Handles circular refs and polymorphism $id metadata
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // Cleaner files
|
||||||
|
AllowTrailingCommas = true,
|
||||||
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region SaveLoad
|
#region SaveLoad
|
||||||
|
|
||||||
|
// Entry point for Generic Load
|
||||||
static public T res_load<T>( string filename ) where T : JsonData
|
static public T res_load<T>( string filename ) where T : JsonData
|
||||||
where T : class
|
|
||||||
{
|
{
|
||||||
return JsonData.load<T>( filename );
|
return (T)actual_load( filename, typeof( T ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entry point for untyped Load
|
||||||
static public JsonData load( string filename )
|
static public JsonData load( string filename )
|
||||||
{
|
{
|
||||||
return actual_load( filename, null );
|
return actual_load( filename, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper for generic load
|
||||||
static public T load<T>( string filename ) where T : JsonData
|
static public T load<T>( string filename ) where T : JsonData
|
||||||
{
|
{
|
||||||
return (T)actual_load( filename, typeof( T ) );
|
return (T)actual_load( filename, typeof( T ) );
|
||||||
@ -50,147 +62,138 @@ namespace lib
|
|||||||
|
|
||||||
static public JsonData actual_load( string filename, Type t )
|
static public JsonData actual_load( string filename, Type t )
|
||||||
{
|
{
|
||||||
JsonData cfg = null;
|
JsonData json = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if( !File.Exists( filename ) )
|
// 1. Path Sanitization
|
||||||
|
if( !File.Exists( filename ) && filename.StartsWith( "res://" ) )
|
||||||
{
|
{
|
||||||
if( filename.StartsWith( "res://" ) )
|
filename = filename.Replace( "res://", "" );
|
||||||
filename.Replace( "res://", "" );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Load or Create
|
||||||
if( File.Exists( filename ) )
|
if( File.Exists( filename ) )
|
||||||
{
|
{
|
||||||
FileStream fs = new FileStream( filename, FileMode.Open, FileAccess.Read );
|
string jsonContent = File.ReadAllText( filename );
|
||||||
|
|
||||||
XmlFormatter2 formatter = new XmlFormatter2();
|
// If T is null, we can't deserialize effectively without Type info.
|
||||||
|
// Defaulting to JsonData or the passed type.
|
||||||
|
Type targetType = t ?? typeof( JsonData );
|
||||||
|
|
||||||
cfg = (JsonData)( t != null ? formatter.DeserializeKnownType( fs, t ) : formatter.Deserialize( fs ) );
|
json = (JsonData)JsonSerializer.Deserialize( jsonContent, targetType, _options );
|
||||||
|
|
||||||
cfg.SetFilename( filename );
|
if( json != null )
|
||||||
|
{
|
||||||
|
json.Filename = filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cfg = CreateTemplate( filename, t );
|
json = CreateTemplate( filename, t );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( IOException )
|
catch( Exception ex ) when( ex is IOException || ex is JsonException )
|
||||||
{
|
{
|
||||||
cfg = CreateTemplate( filename, t );
|
log.error( $"Failed to load {filename}: {ex.Message}" );
|
||||||
|
json = CreateTemplate( filename, t );
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JsonData CreateTemplate( string filename, Type t )
|
private static JsonData CreateTemplate( string filename, Type t )
|
||||||
{
|
{
|
||||||
log.debug( $"JsonData file {filename} not found, creating template." );
|
log.debug( $"JsonData file {filename} not found, creating template." );
|
||||||
|
|
||||||
Type[] types = new Type[0];
|
JsonData json = null;
|
||||||
object[] parms = new object[0];
|
|
||||||
|
|
||||||
//types[ 0 ] = typeof( string );
|
// Handle potential null type if called generically without constraints
|
||||||
//parms[ 0 ] = filename;
|
if( t == null )
|
||||||
JsonData cfg = null;
|
{
|
||||||
|
log.error( "Cannot create template for unknown type." );
|
||||||
ConstructorInfo cons = t?.GetConstructor( types );
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cfg = (JsonData)cons?.Invoke( parms );
|
json = refl.CreateObject( t ) as JsonData;
|
||||||
}
|
}
|
||||||
catch( Exception e )
|
catch( Exception e )
|
||||||
{
|
{
|
||||||
log.error( $"Exception while creating config {t.ToString()}, Msg {e.Message}" );
|
log.error( $"Exception while creating config {t}, Msg {e.Message}" );
|
||||||
}
|
}
|
||||||
|
|
||||||
//cfg.SetFilename( filename );
|
// Write out the template if configured to do so
|
||||||
|
if( json != null && ConfigCfg.s_cfg.writeOutTemplateFiles )
|
||||||
if( ConfigCfg.s_cfg.writeOutTemplateFiles )
|
|
||||||
{
|
{
|
||||||
var templateFile = $"templates/{filename}";
|
var templateFile = $"templates/{filename}";
|
||||||
|
|
||||||
var dirName = Path.GetDirectoryName( templateFile );
|
var dirName = Path.GetDirectoryName( templateFile );
|
||||||
|
|
||||||
Util.checkAndAddDirectory( dirName );
|
Util.checkAndAddDirectory( dirName );
|
||||||
|
|
||||||
log.info( $"Writing out template config of type {t?.Name} in {templateFile}" );
|
log.info( $"Writing out template config of type {t?.Name} in {templateFile}" );
|
||||||
|
|
||||||
saveDebug( cfg, templateFile );
|
// Force set filename for the template save, then restore?
|
||||||
|
// Or just pass path to saveDebug.
|
||||||
|
json.Filename = templateFile;
|
||||||
|
json.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public void saveDebug( JsonData cfg )
|
static public void actual_save( JsonData json, string filename )
|
||||||
{
|
{
|
||||||
JsonData.actual_save( cfg, cfg.Filename );
|
try
|
||||||
}
|
{
|
||||||
|
// Ensure directory exists
|
||||||
|
string dir = Path.GetDirectoryName( filename );
|
||||||
|
if( !string.IsNullOrEmpty( dir ) && !Directory.Exists( dir ) )
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory( dir );
|
||||||
|
}
|
||||||
|
|
||||||
static public void actual_save( JsonData cfg, String filename )
|
// Serialize to string first (safer than open stream + fail)
|
||||||
{
|
string jsonString = JsonSerializer.Serialize( json, json.GetType(), _options );
|
||||||
FileStream fs = new( filename, FileMode.Create, FileAccess.Write );
|
File.WriteAllText( filename, jsonString );
|
||||||
fs.Close();
|
}
|
||||||
|
catch( Exception ex )
|
||||||
|
{
|
||||||
|
log.exception( ex, $"Type { json.GetType().Name } {filename}: {ex.Message}" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Instance Logic
|
||||||
|
|
||||||
public String Filename { get; }
|
//private string _filename = "{unknown}";
|
||||||
}
|
public string Filename { get; set; }
|
||||||
|
|
||||||
/* UNUSED
|
static public void startup()
|
||||||
public record class ConfigRec : ConfigBase
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class JsonData : ConfigBase
|
|
||||||
{
|
|
||||||
|
|
||||||
//private int _test = 0;
|
|
||||||
|
|
||||||
private static ser.XmlCfg s_templateCfg = new()
|
|
||||||
{
|
{
|
||||||
Structure = ser.Datastructure.Tree,
|
// Assuming res.Mgr handles the delegates correctly
|
||||||
POD = ser.POD.Elements,
|
//res.Mgr.Register( JsonData.load );
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
internal static ConfigCfg s_cfg = new();
|
|
||||||
|
|
||||||
public static ConfigCfg Cfg => s_cfg;
|
|
||||||
|
|
||||||
static public void startup( string filename )
|
|
||||||
{
|
|
||||||
res.Mgr.Register( ConfigBase.load );
|
|
||||||
res.Mgr.RegisterSub( typeof( ConfigBase ) );
|
res.Mgr.RegisterSub( typeof( ConfigBase ) );
|
||||||
|
|
||||||
s_cfg = ConfigBase.load<ConfigCfg>( filename );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JsonDataRes : JsonData, res.Resource
|
||||||
|
{
|
||||||
|
public string Filename { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public JsonData()
|
static public class JsonDataEx
|
||||||
|
{
|
||||||
|
|
||||||
|
static public void save( this JsonData json )
|
||||||
{
|
{
|
||||||
|
// Allow overriding path for templates, otherwise use object's filename
|
||||||
|
JsonData.actual_save( json, json.Filename );
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonData( string filename )
|
|
||||||
{
|
|
||||||
_filename = filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _filename = "{unknown}";
|
|
||||||
|
|
||||||
|
|
||||||
public String Filename { get { return _filename; } }
|
|
||||||
|
|
||||||
internal void SetFilename( String filename ) { _filename = filename; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ public static class imm
|
|||||||
ref T obj,
|
ref T obj,
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
[CallerArgumentExpression( "fn" )] string dbgExpression = "" )
|
||||||
@ -37,7 +37,7 @@ public static class imm
|
|||||||
{
|
{
|
||||||
// This will call the 'Timed' override if T is Timed,
|
// This will call the 'Timed' override if T is Timed,
|
||||||
// or the 'Recorded' override if T is Recorded.
|
// or the 'Recorded' override if T is Recorded.
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
obj = obj.Process( fn, reason, dbgMethod, dbgPath, dbgLine, dbgExpression );
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
32
imm/io.cs
32
imm/io.cs
@ -31,7 +31,7 @@ public interface Obj
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Obj Record(
|
Obj Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ public interface Obj<T> : Obj where T : Obj<T>
|
|||||||
T Process(
|
T Process(
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "Processed",
|
string reason = "Processed",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
[CallerArgumentExpression( "fn" )] string expStr = "" );
|
||||||
@ -73,7 +73,7 @@ public interface Obj<T> : Obj where T : Obj<T>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
new T Record(
|
new T Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
|||||||
public virtual T Process(
|
public virtual T Process(
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "Processed",
|
string reason = "Processed",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
@ -215,18 +215,18 @@ public record class Versioned<T> : Obj<T> where T : Versioned<T>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual T Record(
|
public virtual T Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
[CallerLineNumber] int dbgLine = 0 ) => Process( t => t, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements Obj.Record by calling the virtual T Record.
|
/// Implements Obj.Record by calling the virtual T Record.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Obj Obj.Record(
|
Obj Obj.Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgName, dbgPath, dbgLine );
|
[CallerLineNumber] int dbgLine = 0 ) => this.Record( reason, dbgMethod, dbgPath, dbgLine );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -250,7 +250,7 @@ public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
|||||||
public override T Process(
|
public override T Process(
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
[CallerArgumentExpression( "fn" )] string expStr = "" )
|
||||||
@ -265,7 +265,7 @@ public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
|||||||
{
|
{
|
||||||
Version = current.Meta.Version + 1,
|
Version = current.Meta.Version + 1,
|
||||||
Reason = reason,
|
Reason = reason,
|
||||||
MemberName = dbgName,
|
MemberName = dbgMethod,
|
||||||
FilePath = dbgPath,
|
FilePath = dbgPath,
|
||||||
LineNumber = dbgLine,
|
LineNumber = dbgLine,
|
||||||
Expression = expStr,
|
Expression = expStr,
|
||||||
@ -279,11 +279,11 @@ public record class Recorded<T> : Versioned<T> where T : Recorded<T>
|
|||||||
|
|
||||||
public new T Record(
|
public new T Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
{
|
{
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
return Process( t => t, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -351,11 +351,11 @@ public record class Timed<T> : Recorded<T> where T : Timed<T>
|
|||||||
|
|
||||||
public new T Record(
|
public new T Record(
|
||||||
string reason = "Recorded",
|
string reason = "Recorded",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
{
|
{
|
||||||
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
return Process( t => t, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,13 +366,13 @@ public static class TimedExt
|
|||||||
ref T obj,
|
ref T obj,
|
||||||
Func<T, T> fn,
|
Func<T, T> fn,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0,
|
[CallerLineNumber] int dbgLine = 0,
|
||||||
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
[CallerArgumentExpression( "fn" )] string dbgExpression = ""
|
||||||
) where T : io.Timed<T>
|
) where T : io.Timed<T>
|
||||||
{
|
{
|
||||||
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExpression );
|
obj = obj.Process( fn, reason, dbgMethod, dbgPath, dbgLine, dbgExpression );
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -625,34 +625,56 @@ static public class log
|
|||||||
logBase( $"{header}{dbgExpObj}[{type.Name}] ->", level, dbgPath, dbgLine, dbgMethod, cat, dbgExpObj );
|
logBase( $"{header}{dbgExpObj}[{type.Name}] ->", level, dbgPath, dbgLine, dbgMethod, cat, dbgExpObj );
|
||||||
|
|
||||||
// 6. Handle Complex Objects (Properties)
|
// 6. Handle Complex Objects (Properties)
|
||||||
if( maxDepth == 0 )
|
if( maxDepth < 0 )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MemberInfo> members = new();
|
||||||
|
refl.GetAllMembers( type, members );
|
||||||
|
|
||||||
// Get all readable properties
|
// Get all readable properties
|
||||||
var props = type.GetProperties( BindingFlags.Public | BindingFlags.Instance )
|
//var props = type.GetProperties( BindingFlags.Public | BindingFlags.Private | BindingFlags.Instance )
|
||||||
.Where( p => p.CanRead && p.GetIndexParameters().Length == 0 );
|
// .Where( p => p.CanRead && p.GetIndexParameters().Length == 0 );
|
||||||
|
|
||||||
|
var props = members.Where( ( mi) => true );
|
||||||
|
|
||||||
var nextHeader = $"{header}{header}";
|
var nextHeader = $"{header}{header}";
|
||||||
|
|
||||||
//bool firstProp = true;
|
//bool firstProp = true;
|
||||||
foreach( var pi in props )
|
foreach( var mi in members )
|
||||||
{
|
{
|
||||||
var name = pi.Name;
|
var is_pi = mi is PropertyInfo pi;
|
||||||
var piType = pi.PropertyType;
|
var is_fi = mi is FieldInfo fi;
|
||||||
|
|
||||||
|
var name = mi.Name;
|
||||||
|
var piType = mi.MemberType;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var value = pi.GetValue( obj );
|
object? value = GetValue( obj, mi );
|
||||||
props_r( value, nextHeader, --maxDepth, level, cat, prefix, dbgPath, dbgLine, dbgMethod, name );
|
props_r( value, nextHeader, --maxDepth, level, cat, prefix, dbgPath, dbgLine, dbgMethod, name );
|
||||||
}
|
}
|
||||||
catch( Exception ex )
|
catch( Exception ex )
|
||||||
{
|
{
|
||||||
logBase( $"{nextHeader}{name}[{piType.Name}] ex {ex.Message}", level, dbgPath, dbgLine, dbgMethod, cat, name );
|
logBase( $"{nextHeader}{name}[{mi.Name}] ex {ex.Message}", level, dbgPath, dbgLine, dbgMethod, cat, name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static object GetValue( object obj, MemberInfo mi )
|
||||||
|
{
|
||||||
|
if( mi is PropertyInfo pi )
|
||||||
|
{
|
||||||
|
return pi.GetValue( obj );
|
||||||
|
}
|
||||||
|
if( mi is FieldInfo fi )
|
||||||
|
{
|
||||||
|
return fi.GetValue( obj );
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//This might seem a little odd, but the intent is that usually you wont need to set notExpectedValue.
|
//This might seem a little odd, but the intent is that usually you wont need to set notExpectedValue.
|
||||||
static public void expected<T>( T value, string falseString, string trueString = "", T? notExpectedValue = default( T ) )
|
static public void expected<T>( T value, string falseString, string trueString = "", T? notExpectedValue = default( T ) )
|
||||||
|
|||||||
104
reflect/refl.cs
104
reflect/refl.cs
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -18,6 +19,28 @@ using System.Threading.Tasks;
|
|||||||
static public class refl
|
static public class refl
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static public T Create<T>()
|
||||||
|
{
|
||||||
|
var t = typeof( T );
|
||||||
|
return (T)CreateObject( t );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public object CreateObject( Type type )
|
||||||
|
{
|
||||||
|
object newObj = null;
|
||||||
|
if( type.GetConstructor( Type.EmptyTypes ) != null )
|
||||||
|
{
|
||||||
|
newObj = Activator.CreateInstance( type );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newObj = FormatterServices.GetUninitializedObject( type );
|
||||||
|
}
|
||||||
|
|
||||||
|
return newObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class PredEnumerator
|
public class PredEnumerator
|
||||||
{
|
{
|
||||||
public static PredEnumerator<T> Create<T>( IEnumerator<T> en, Predicate<T> pred )
|
public static PredEnumerator<T> Create<T>( IEnumerator<T> en, Predicate<T> pred )
|
||||||
@ -142,6 +165,83 @@ static public class refl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void GetAllMembers( Type t, List<MemberInfo> list )
|
||||||
|
{
|
||||||
|
GetAllMembersRecursive( t, true, list );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GetAllMembersRecursive( Type t, bool recurse, List<MemberInfo> list )
|
||||||
|
{
|
||||||
|
var MemberArr = t.GetMembers(
|
||||||
|
BindingFlags.DeclaredOnly |
|
||||||
|
BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Public |
|
||||||
|
BindingFlags.Instance );
|
||||||
|
|
||||||
|
var en = PredEnumerator.Create<MemberInfo>( MemberArr.AsEnumerable<MemberInfo>(), fa => fa.GetCustomAttribute( typeof( NonSerializedAttribute ) ) == null );
|
||||||
|
|
||||||
|
list.AddRange( new PredEnumerable<MemberInfo>( en ) );
|
||||||
|
|
||||||
|
if( recurse && t.BaseType != null && t.BaseType != typeof( object ) )
|
||||||
|
{
|
||||||
|
GetAllMembers( t.BaseType, list );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void GetAllMembersUntil( Type t, Type tooFar, List<MemberInfo> list )
|
||||||
|
{
|
||||||
|
var MemberArr = t.GetMembers(
|
||||||
|
BindingFlags.DeclaredOnly |
|
||||||
|
BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Public |
|
||||||
|
BindingFlags.Instance );
|
||||||
|
|
||||||
|
var en = PredEnumerator.Create<MemberInfo>( MemberArr.AsEnumerable<MemberInfo>(), fa => fa.GetCustomAttribute( typeof( NonSerializedAttribute ) ) == null );
|
||||||
|
|
||||||
|
list.AddRange( new PredEnumerable<MemberInfo>( en ) );
|
||||||
|
|
||||||
|
if( t.BaseType != null && t.BaseType != tooFar )
|
||||||
|
{
|
||||||
|
GetAllMembers( t.BaseType, list );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImmutableList<MemberInfo> GetAllMembers<T>()
|
||||||
|
{
|
||||||
|
return GetAllMembers( typeof( T ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImmutableList<MemberInfo> GetAllMembers( Type t )
|
||||||
|
{
|
||||||
|
{
|
||||||
|
if( s_MemberCache.TryGetValue( t, out var first ) )
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
//LogGC.RegisterObjectId( t );
|
||||||
|
|
||||||
|
lock( t )
|
||||||
|
{
|
||||||
|
if( s_MemberCache.TryGetValue( t, out var second ) )
|
||||||
|
return second;
|
||||||
|
|
||||||
|
var list = new List<MemberInfo>();
|
||||||
|
|
||||||
|
GetAllMembers( t, list );
|
||||||
|
|
||||||
|
var immList = list.ToImmutableList();
|
||||||
|
|
||||||
|
Interlocked.Exchange( ref s_MemberCache, s_MemberCache.Add( t, immList ) );
|
||||||
|
|
||||||
|
return immList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void GetAllFields( Type t, List<FieldInfo> list )
|
public static void GetAllFields( Type t, List<FieldInfo> list )
|
||||||
{
|
{
|
||||||
GetAllFieldsRecursive( t, true, list );
|
GetAllFieldsRecursive( t, true, list );
|
||||||
@ -164,6 +264,7 @@ static public class refl
|
|||||||
GetAllFields( t.BaseType, list );
|
GetAllFields( t.BaseType, list );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void GetAllFieldsUntil( Type t, Type tooFar, List<FieldInfo> list )
|
public static void GetAllFieldsUntil( Type t, Type tooFar, List<FieldInfo> list )
|
||||||
{
|
{
|
||||||
var fieldArr = t.GetFields(
|
var fieldArr = t.GetFields(
|
||||||
@ -187,8 +288,6 @@ static public class refl
|
|||||||
return GetAllFields( typeof( T ) );
|
return GetAllFields( typeof( T ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static ImmutableList<FieldInfo> GetAllFields( Type t )
|
public static ImmutableList<FieldInfo> GetAllFields( Type t )
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -253,6 +352,7 @@ static public class refl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ImmutableDictionary<Type, ImmutableList<MemberInfo>> s_MemberCache = ImmutableDictionary<Type, ImmutableList<MemberInfo>>.Empty;
|
||||||
static ImmutableDictionary<Type, ImmutableList<FieldInfo>> s_fieldCache = ImmutableDictionary<Type, ImmutableList<FieldInfo>>.Empty;
|
static ImmutableDictionary<Type, ImmutableList<FieldInfo>> s_fieldCache = ImmutableDictionary<Type, ImmutableList<FieldInfo>>.Empty;
|
||||||
static ImmutableDictionary<Type, ImmutableList<PropertyInfo>> s_propCache = ImmutableDictionary<Type, ImmutableList<PropertyInfo>>.Empty;
|
static ImmutableDictionary<Type, ImmutableList<PropertyInfo>> s_propCache = ImmutableDictionary<Type, ImmutableList<PropertyInfo>>.Empty;
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,17 @@ using System.Collections.Immutable;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
namespace res;
|
namespace res;
|
||||||
|
|
||||||
|
public interface Resource
|
||||||
|
{
|
||||||
|
public string Filename { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
// A delegate representing a function that can load a resource of type T.
|
// A delegate representing a function that can load a resource of type T.
|
||||||
public delegate T Load<out T>( string filename );
|
public delegate T Load<out T>( string filename );
|
||||||
|
|
||||||
@ -33,13 +39,13 @@ public abstract class Ref
|
|||||||
protected Ref(
|
protected Ref(
|
||||||
string filename,
|
string filename,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
{
|
{
|
||||||
Filename = filename;
|
Filename = filename;
|
||||||
Reason = reason;
|
Reason = reason;
|
||||||
DbgName = dbgName;
|
DbgName = dbgMethod;
|
||||||
DbgPath = dbgPath;
|
DbgPath = dbgPath;
|
||||||
DbgLine = dbgLine;
|
DbgLine = dbgLine;
|
||||||
|
|
||||||
@ -53,7 +59,7 @@ public abstract class Ref
|
|||||||
/// <returns>The loaded resource object.</returns>
|
/// <returns>The loaded resource object.</returns>
|
||||||
public abstract object Lookup(
|
public abstract object Lookup(
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 );
|
[CallerLineNumber] int dbgLine = 0 );
|
||||||
|
|
||||||
@ -66,6 +72,41 @@ public abstract class Ref
|
|||||||
/// Internal method to trigger the initial load (used by deferred loading, if implemented).
|
/// Internal method to trigger the initial load (used by deferred loading, if implemented).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal virtual void InternalLoad() { }
|
internal virtual void InternalLoad() { }
|
||||||
|
|
||||||
|
static public res.Ref<T> Create<T>( T res, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) where T : class, Resource, new()
|
||||||
|
{
|
||||||
|
if( res.Filename == null )
|
||||||
|
{
|
||||||
|
res.Filename = $"{{{typeof( T ).Name}}}.{Ref<T>.s_codeRes}";
|
||||||
|
log.debug( $"Code Res for {typeof( T ).Name} named {res.Filename}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ref<T>.CreateAsset( res, res.Filename );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public res.Ref<T> Create<T>( string filename, T res, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) where T : class, new()
|
||||||
|
{
|
||||||
|
if( string.IsNullOrWhiteSpace( filename ) )
|
||||||
|
{
|
||||||
|
filename = $"{{{typeof( T ).Name}}}.{Ref<T>.s_codeRes}";
|
||||||
|
log.debug( $"Code Res for {typeof( T ).Name} named {filename}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ref<T>.CreateAsset( res, filename );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public res.Ref<T> Create<T>( string filename, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) where T : class, new()
|
||||||
|
{
|
||||||
|
if( string.IsNullOrWhiteSpace( filename ) )
|
||||||
|
{
|
||||||
|
filename = $"{{{typeof( T ).Name}}}.{Ref<T>.s_codeRes}";
|
||||||
|
log.debug( $"Code Res for {typeof( T ).Name} named {filename}" );
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = refl.Create<T>();
|
||||||
|
|
||||||
|
return Ref<T>.CreateAsset( res, filename );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -76,6 +117,9 @@ public abstract class Ref
|
|||||||
[DebuggerDisplay( "Path = {Filename} / Res = {m_res}" )]
|
[DebuggerDisplay( "Path = {Filename} / Res = {m_res}" )]
|
||||||
public class Ref<T> : Ref where T : class, new()
|
public class Ref<T> : Ref where T : class, new()
|
||||||
{
|
{
|
||||||
|
static internal int s_codeRes = 1024;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
private T? m_res;
|
private T? m_res;
|
||||||
|
|
||||||
@ -88,17 +132,18 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
/// Gets the resource, loading it if necessary.
|
/// Gets the resource, loading it if necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
//[Deprecated("Use Res property instead.")]
|
//[Deprecated("Use Res property instead.")]
|
||||||
|
[JsonIgnore]
|
||||||
public T res => m_res ?? Lookup();
|
public T res => m_res ?? Lookup();
|
||||||
|
|
||||||
public Ref(
|
public Ref(
|
||||||
string filename = "",
|
string filename = "",
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
: base(
|
: base(
|
||||||
!string.IsNullOrWhiteSpace( filename ) ? filename : $"{{{dbgName}_{Path.GetFileNameWithoutExtension( dbgPath )}}}",
|
!string.IsNullOrWhiteSpace( filename ) ? filename : $"{{{dbgMethod}_{Path.GetFileNameWithoutExtension( dbgPath )}}}",
|
||||||
reason, dbgName, dbgPath, dbgLine )
|
reason, dbgMethod, dbgPath, dbgLine )
|
||||||
{
|
{
|
||||||
if( VerboseLogging )
|
if( VerboseLogging )
|
||||||
log.info( $"Ref<T> Created: {GetType().Name}<{typeof( T ).Name}> {Filename}" );
|
log.info( $"Ref<T> Created: {GetType().Name}<{typeof( T ).Name}> {Filename}" );
|
||||||
@ -110,7 +155,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override T Lookup(
|
public override T Lookup(
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
{
|
{
|
||||||
@ -119,7 +164,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
return m_res;
|
return m_res;
|
||||||
|
|
||||||
// Load using the Mgr.
|
// Load using the Mgr.
|
||||||
m_res = Mgr.Load<T>( Filename, $"Ref lookup (orig: {Reason}) bcs {reason}", dbgName, dbgPath, dbgLine );
|
m_res = Mgr.Load<T>( Filename, $"Ref lookup (orig: {Reason}) bcs {reason}", dbgMethod, dbgPath, dbgLine );
|
||||||
if( VerboseLogging )
|
if( VerboseLogging )
|
||||||
log.info( $"Ref.Lookup: {GetType().Name}<{typeof( T ).Name}> {Filename}" );
|
log.info( $"Ref.Lookup: {GetType().Name}<{typeof( T ).Name}> {Filename}" );
|
||||||
return m_res;
|
return m_res;
|
||||||
@ -131,11 +176,11 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override object Lookup(
|
public override object Lookup(
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0)
|
[CallerLineNumber] int dbgLine = 0)
|
||||||
{
|
{
|
||||||
return Lookup(reason, dbgName, dbgPath, dbgLine);
|
return Lookup(reason, dbgMethod, dbgPath, dbgLine);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -159,7 +204,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
public static Ref<T> CreateAsset(
|
public static Ref<T> CreateAsset(
|
||||||
T value, string path,
|
T value, string path,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 )
|
[CallerLineNumber] int dbgLine = 0 )
|
||||||
{
|
{
|
||||||
@ -186,7 +231,7 @@ public class Ref<T> : Ref where T : class, new()
|
|||||||
|
|
||||||
|
|
||||||
var createReason = $"CreateAsset: {value.GetType().Name} - {immMeta?.Reason ?? "N/A"}";
|
var createReason = $"CreateAsset: {value.GetType().Name} - {immMeta?.Reason ?? "N/A"}";
|
||||||
var newRef = new Ref<T>( path, $"{createReason} bcs {reason}", dbgName, dbgPath, dbgLine );
|
var newRef = new Ref<T>( path, $"{createReason} bcs {reason}", dbgMethod, dbgPath, dbgLine );
|
||||||
|
|
||||||
// We should make the newRef hold the 'value' immediately,
|
// We should make the newRef hold the 'value' immediately,
|
||||||
// or ensure loading it back gives the same 'value'.
|
// or ensure loading it back gives the same 'value'.
|
||||||
@ -209,6 +254,9 @@ internal record ResourceHolder<T>( WeakReference<T> WeakRef, string Name, DateTi
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Mgr
|
public static class Mgr
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Internal holder for type-specific loaders.
|
// Internal holder for type-specific loaders.
|
||||||
private abstract class LoadHolder
|
private abstract class LoadHolder
|
||||||
{
|
{
|
||||||
@ -216,7 +264,7 @@ public static class Mgr
|
|||||||
// Its not yet working, and maybe shouldnt exist
|
// Its not yet working, and maybe shouldnt exist
|
||||||
public abstract object Load( string filename,
|
public abstract object Load( string filename,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0
|
[CallerLineNumber] int dbgLine = 0
|
||||||
);
|
);
|
||||||
@ -227,7 +275,7 @@ public static class Mgr
|
|||||||
private readonly Load<T> _fnLoad;
|
private readonly Load<T> _fnLoad;
|
||||||
public LoadHolder( Load<T> fnLoad ) { _fnLoad = fnLoad; }
|
public LoadHolder( Load<T> fnLoad ) { _fnLoad = fnLoad; }
|
||||||
public override object Load( string filename, string reason = "",
|
public override object Load( string filename, string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0
|
[CallerLineNumber] int dbgLine = 0
|
||||||
) => _fnLoad( filename )!;
|
) => _fnLoad( filename )!;
|
||||||
@ -335,27 +383,26 @@ public static class Mgr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a Ref<T> for a given filename.
|
/// Creates a Ref<T> for a given filename.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Ref<T> Lookup<T>(
|
public static Ref<T> Lookup<T>(
|
||||||
string filename,
|
string filename,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 ) where T : class, new()
|
[CallerLineNumber] int dbgLine = 0 ) where T : class, new()
|
||||||
{
|
{
|
||||||
return new Ref<T>( filename, reason, dbgName, dbgPath, dbgLine );
|
return new Ref<T>( filename, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads a resource, handling caching and thread-safe loading.
|
/// Loads a resource, handling caching and thread-safe loading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static T Load<T>(
|
internal static T Load<T>(
|
||||||
string filename,
|
string filename,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0 ) where T : class, new()
|
[CallerLineNumber] int dbgLine = 0 ) where T : class, new()
|
||||||
{
|
{
|
||||||
@ -376,8 +423,8 @@ public static class Mgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Perform the actual load
|
// 4. Perform the actual load
|
||||||
log.warn( $"Loading {typeof( T ).Name}: {filename} ({reason} at {dbgName}:{dbgLine})" );
|
log.warn( $"Loading {typeof( T ).Name}: {filename} ({reason} at {dbgMethod}:{dbgLine})" );
|
||||||
var newValue = ActualLoad<T>( filename, reason, dbgName, dbgPath, dbgLine );
|
var newValue = ActualLoad<T>( filename, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
|
|
||||||
// 5. Cache the new value
|
// 5. Cache the new value
|
||||||
CacheResource( filename, newValue, reason );
|
CacheResource( filename, newValue, reason );
|
||||||
@ -397,7 +444,6 @@ public static class Mgr
|
|||||||
log.info( $"Cached {typeof( T ).Name}: {filename} ({reason})" );
|
log.info( $"Cached {typeof( T ).Name}: {filename} ({reason})" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to retrieve a resource from the cache.
|
/// Tries to retrieve a resource from the cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -422,14 +468,14 @@ public static class Mgr
|
|||||||
private static T ActualLoad<T>(
|
private static T ActualLoad<T>(
|
||||||
string filename,
|
string filename,
|
||||||
string reason = "",
|
string reason = "",
|
||||||
[CallerMemberName] string dbgName = "",
|
[CallerMemberName] string dbgMethod = "",
|
||||||
[CallerFilePath] string dbgPath = "",
|
[CallerFilePath] string dbgPath = "",
|
||||||
[CallerLineNumber] int dbgLine = 0
|
[CallerLineNumber] int dbgLine = 0
|
||||||
) where T : class, new()
|
) where T : class, new()
|
||||||
{
|
{
|
||||||
if( s_loaders.TryGetValue( typeof( T ), out var loaderHolder ) )
|
if( s_loaders.TryGetValue( typeof( T ), out var loaderHolder ) )
|
||||||
{
|
{
|
||||||
var loadedObject = loaderHolder.Load( filename, reason, dbgName, dbgPath, dbgLine );
|
var loadedObject = loaderHolder.Load( filename, reason, dbgMethod, dbgPath, dbgLine );
|
||||||
if( loadedObject is T value )
|
if( loadedObject is T value )
|
||||||
{
|
{
|
||||||
var meta = ( value as io.Obj )?.Meta;
|
var meta = ( value as io.Obj )?.Meta;
|
||||||
@ -437,7 +483,7 @@ public static class Mgr
|
|||||||
// If it's an immutable object, record its loading.
|
// If it's an immutable object, record its loading.
|
||||||
if( value is io.Obj imm )
|
if( value is io.Obj imm )
|
||||||
{
|
{
|
||||||
return (T)imm.Record( $"Loading bcs {reason}", dbgName, dbgPath, dbgLine );
|
return (T)imm.Record( $"Loading bcs {reason}", dbgMethod, dbgPath, dbgLine );
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -121,7 +121,9 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing )
|
|
||||||
|
|
||||||
|
static public (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing )
|
||||||
{
|
{
|
||||||
long id = -1;
|
long id = -1;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
@ -137,14 +139,7 @@ public partial class ObjectHandler : ITypeHandler
|
|||||||
object? newObj = null;
|
object? newObj = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if( type.GetConstructor( Type.EmptyTypes ) != null )
|
newObj = refl.CreateObject( type );
|
||||||
{
|
|
||||||
newObj = Activator.CreateInstance( type );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newObj = FormatterServices.GetUninitializedObject( type );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch( Exception ex )
|
catch( Exception ex )
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user