Migrating toward WingPropHandle

This commit is contained in:
2026-04-03 17:05:48 -04:00
parent a0c5a56476
commit daa9216ddf
4 changed files with 113 additions and 8 deletions

View File

@@ -274,6 +274,31 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
return Result;
}
/////////////////////////////////////////////////////////////////////////////
//
// Organize by Name
//
/////////////////////////////////////////////////////////////////////////////
bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr<IPropertyHandle>> &Result)
{
Result.Empty();
TSet<FName> DuplicateNames;
for (const TSharedPtr<IPropertyHandle> &H : HList)
{
FName Name = H->GetProperty()->GetFName();
if (Result.Contains(Name)) DuplicateNames.Add(Name);
else Result.Add(Name, H);
}
if (DuplicateNames.IsEmpty()) return true;
UWingServer::Print(TEXT("More than one property with name:"));
for (FName DupName : DuplicateNames)
{
UWingServer::Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName));
}
UWingServer::Print(TEXT("\n"));
return false;
}
/////////////////////////////////////////////////////////////////////////////
//
@@ -426,6 +451,59 @@ bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValu
return true;
}
bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional)
{
bool Ok = true;
// Organize the properties by name.
TMap<FName, TSharedPtr<IPropertyHandle>> OrganizedProps;
if (!OrganizeByName(Props, OrganizedProps)) Ok = false;
// Parse the keys in the json, make sure they're syntactically valid and
// that they match the names of actual properties, and that there are no dups.
TSet<FName> Specified;
for (const auto& KV : Json.Values)
{
FName Name = WingUtils::CheckInternalizeID(KV.Key);
if (Name.IsNone()) { Ok = false; continue; }
if (!OrganizedProps.Contains(Name))
{
UWingServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
Ok = false;
}
if (!WingUtils::FindNoDuplicateName(Specified, Name, TEXT("parameter"))) Ok = false;
}
// Make sure that all required properties have been specified.
if (!AllOptional)
{
for (const TSharedPtr<IPropertyHandle> &H : Props)
{
if (H->HasMetaData(TEXT("Optional"))) continue;
FName Name = H->GetProperty()->GetFName();
if (!Specified.Contains(Name))
{
UWingServer::Printf(TEXT("Required parameter %s not specified\n"),
*WingUtils::ExternalizeID(Name));
Ok = false;
}
}
}
// If anything is wrong, return early without setting anything.
if (!Ok) return false;
// Populate each property from JSON. This could fail too, but at this
// point, we're committed.
for (const auto& KV : Json.Values)
{
FName Name = WingUtils::CheckInternalizeID(KV.Key);
if (!SetJson(*OrganizedProps[Name], KV.Value)) Ok = false;
}
return Ok;
}
/////////////////////////////////////////////////////////////////////////////
//
// Print

View File

@@ -1,6 +1,6 @@
#include "WingServer.h"
#include "WingHandler.h"
#include "WingProperty.h"
#include "WingPropHandle.h"
#include "WingManual.h"
#include "WingLogCapture.h"
#include "WingUtils.h"
@@ -328,7 +328,9 @@ void UWingServer::TryCallHandler(const FString &Line)
Handler->ConfigurationObject = Found->Config.Get();
// Populate the handler object with the request parameters.
if (!FWingProperty::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
WingPropHandle Props;
WingPropHandle::Handles Handles = Props.AllProperties(HandlerObj.Get(), true, CPF_None);
if (!WingPropHandle::PopulateFromJson(Handles, *Request, false))
{
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
return;

View File

@@ -56,9 +56,6 @@
// Name sanitization
// ============================================================
FName WingUtils::GetFName(const FWingProperty &Prop) { return Prop.Prop->GetFName(); }
FName WingUtils::GetFName(const TSharedPtr<IPropertyHandle> &H) { return H->GetProperty()->GetFName(); }
FString WingUtils::ExternalizeID(FName Name)
{
return WingTokenizer::ExternalizeID(Name);
@@ -272,6 +269,17 @@ FString WingUtils::FormatName(const UWidget *Widget)
return ExternalizeID(Widget->GetFName());
}
FName WingUtils::GetFName(const FWingProperty &Prop)
{
return Prop.Prop->GetFName();
}
FName WingUtils::GetFName(const TSharedPtr<IPropertyHandle> &H)
{
return H->GetProperty()->GetFName();
}
// ============================================================
// Formatting other things
// ============================================================

View File

@@ -4,6 +4,9 @@
#include "IPropertyRowGenerator.h"
#include "PropertyHandle.h"
class FJsonObject;
class FJsonValue;
// WingPropHandle: A module that provides easy access to
// IPropertyHandle objects. To use this module, construct a
// WingPropHandle, then use it to fetch properties from
@@ -52,13 +55,27 @@ public:
// expression properties), widgets (includes slot properties).
Handles GetDetails(UObject* Obj, bool Mutable);
// Get/set text with special handling for enums (smarter prefix
// matching via WingUtils::StringToEnum) and FEdGraphPinType
// (human-readable format via UWingTypes).
// Organize properties by name. If there's more than one property
// of a given name, print an error and returns false, and returns a map
// that contains one of the two duplicates.
static bool OrganizeByName(const Handles &H, TMap<FName, TSharedPtr<IPropertyHandle>> &Result);
// Get/set text. Compared to the built in methods for getting and setting
// text, these support more concise enum values, FEdGraphPinType, and
// more concise Class properties.
static FString GetText(IPropertyHandle& Handle);
static bool SetText(IPropertyHandle& Handle, const FString& Text);
// Store a Json value into a property handle. The Json value must be
// a string, number, or boolean.
static bool SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue);
// Populate a whole bunch of properties from a Json object. If
// AllOptional is true, the Json may supply a subset of the properties.
// If not, the Json must supply all of them, excepting properties that
// are explicitly marked Optional.
static bool PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional);
// Print a single property in a standardized format:
// editable|readonly Type Name = Value
static void Print(IPropertyHandle& Handle, FStringBuilderBase& Out);