Files
sbox-public/engine/Sandbox.Tools/Utility/ReflectionSerializedObject.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

147 lines
3.6 KiB
C#

using System;
using System.Reflection;
using System.Text.Json;
namespace Sandbox.Internal;
/// <summary>
/// An implementation of SerializedObject which uses reflection to fill out properties. This is only accessible from
/// tools by default. We (probably) shouldn't trust the client with such things, as they can access
/// </summary>
class ReflectionSerializedObject : SerializedObject
{
internal object TargetObject;
public override string TypeName => TargetObject.GetType().Name;
public override string TypeTitle => TypeName;
public override bool IsValid => (TargetObject as IValid)?.IsValid() ?? TargetObject is not null;
public ReflectionSerializedObject( object target )
{
TargetObject = target;
BuildProperties();
}
void BuildProperties()
{
PropertyList ??= new();
var type = TargetObject.GetType();
var properties = type.GetProperties( BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy )
.Where( IsPropertyAcceptable );
foreach ( var prop in properties )
{
PropertyList.Add( new TypeSerializedProperty( this, prop ) );
}
}
private bool IsPropertyAcceptable( PropertyInfo x )
{
if ( !x.CanRead ) return false;
if ( x.GetMethod.IsStatic ) return false;
var info = DisplayInfo.ForMember( x );
//if ( x.GetIndexParameters().Length > 0 ) return false;
//if ( x.PropertyType.IsByRefLike ) return false;
return info.Browsable;
}
}
file class TypeSerializedProperty : SerializedProperty
{
private ReflectionSerializedObject Owner;
private PropertyInfo Prop;
private DisplayInfo Info;
public override SerializedObject Parent => Owner;
public override string DisplayName => Info.Name;
public override string Name => Prop.Name;
public override string Description => Info.Description;
public override System.Type PropertyType => Prop.PropertyType;
public override string GroupName => Info.Group;
public TypeSerializedProperty( ReflectionSerializedObject typeSerializedObject, PropertyInfo prop )
{
Owner = typeSerializedObject;
Prop = prop;
Info = DisplayInfo.ForMember( prop );
}
public override void SetValue<T>( T value )
{
try
{
// correct type
if ( value == null || value.GetType().IsAssignableTo( Prop.PropertyType ) )
{
NotePreChange();
Prop.SetValue( Owner.TargetObject, value );
}
else if ( Prop.PropertyType.IsEnum )
{
NotePreChange();
var changedValue = Enum.ToObject( Prop.PropertyType, value );
if ( changedValue is not null )
{
Prop.SetValue( Owner.TargetObject, changedValue );
}
}
else if ( value is IConvertible )
{
NotePreChange();
var changedValue = Convert.ChangeType( value, Prop.PropertyType );
if ( changedValue is not null )
{
Prop.SetValue( Owner.TargetObject, changedValue );
}
}
else
{
return;
}
NoteChanged();
}
catch ( System.Exception e )
{
var l = new Logger( "ReflectionSerializedProperty" );
l.Warning( e, $"Error setting {PropertyType} to {value} ({value?.GetType()})" );
}
}
public override T GetValue<T>( T defaultValue )
{
var value = Prop.GetValue( Owner.TargetObject );
if ( Prop.PropertyType.IsAssignableTo( typeof( T ) ) )
return (T)value;
if ( typeof( T ) == typeof( string ) )
return (T)(object)$"{value}";
if ( Prop.PropertyType == typeof( string ) )
{
return JsonSerializer.Deserialize<T>( (string)value );
}
var jsonElement = JsonSerializer.SerializeToElement( value );
return jsonElement.Deserialize<T>();
}
/// <inheritdoc />
public override IEnumerable<Attribute> GetAttributes()
{
return Prop.GetCustomAttributes() ?? base.GetAttributes();
}
}