using System;
using System.Globalization;
using Newtonsoft.Json.Linq;
namespace MCPForUnity.Editor.Helpers
{
///
/// Utility class for coercing JSON parameter values to strongly-typed values.
/// Handles various input formats (strings, numbers, booleans) gracefully.
///
public static class ParamCoercion
{
///
/// Coerces a JToken to an integer value, handling strings and floats.
///
/// The JSON token to coerce
/// Default value if coercion fails
/// The coerced integer value or default
public static int CoerceInt(JToken token, int defaultValue)
{
if (token == null || token.Type == JTokenType.Null)
return defaultValue;
try
{
if (token.Type == JTokenType.Integer)
return token.Value();
var s = token.ToString().Trim();
if (s.Length == 0)
return defaultValue;
if (int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var i))
return i;
if (double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d))
return (int)d;
}
catch
{
// Swallow and return default
}
return defaultValue;
}
///
/// Coerces a JToken to a boolean value, handling strings like "true", "1", etc.
///
/// The JSON token to coerce
/// Default value if coercion fails
/// The coerced boolean value or default
public static bool CoerceBool(JToken token, bool defaultValue)
{
if (token == null || token.Type == JTokenType.Null)
return defaultValue;
try
{
if (token.Type == JTokenType.Boolean)
return token.Value();
var s = token.ToString().Trim().ToLowerInvariant();
if (s.Length == 0)
return defaultValue;
if (bool.TryParse(s, out var b))
return b;
if (s == "1" || s == "yes" || s == "on")
return true;
if (s == "0" || s == "no" || s == "off")
return false;
}
catch
{
// Swallow and return default
}
return defaultValue;
}
///
/// Coerces a JToken to a float value, handling strings and integers.
///
/// The JSON token to coerce
/// Default value if coercion fails
/// The coerced float value or default
public static float CoerceFloat(JToken token, float defaultValue)
{
if (token == null || token.Type == JTokenType.Null)
return defaultValue;
try
{
if (token.Type == JTokenType.Float || token.Type == JTokenType.Integer)
return token.Value();
var s = token.ToString().Trim();
if (s.Length == 0)
return defaultValue;
if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var f))
return f;
}
catch
{
// Swallow and return default
}
return defaultValue;
}
///
/// Coerces a JToken to a string value, with null handling.
///
/// The JSON token to coerce
/// Default value if null or empty
/// The string value or default
public static string CoerceString(JToken token, string defaultValue = null)
{
if (token == null || token.Type == JTokenType.Null)
return defaultValue;
var s = token.ToString();
return string.IsNullOrEmpty(s) ? defaultValue : s;
}
///
/// Coerces a JToken to an enum value, handling strings.
///
/// The enum type
/// The JSON token to coerce
/// Default value if coercion fails
/// The coerced enum value or default
public static T CoerceEnum(JToken token, T defaultValue) where T : struct, Enum
{
if (token == null || token.Type == JTokenType.Null)
return defaultValue;
try
{
var s = token.ToString().Trim();
if (s.Length == 0)
return defaultValue;
if (Enum.TryParse(s, ignoreCase: true, out var result))
return result;
}
catch
{
// Swallow and return default
}
return defaultValue;
}
///
/// Normalizes a property name by removing separators and converting to camelCase.
/// Handles common naming variations from LLMs and humans.
/// Examples:
/// "Use Gravity" → "useGravity"
/// "is_kinematic" → "isKinematic"
/// "max-angular-velocity" → "maxAngularVelocity"
/// "Angular Drag" → "angularDrag"
///
/// The property name to normalize
/// The normalized camelCase property name
public static string NormalizePropertyName(string input)
{
if (string.IsNullOrEmpty(input))
return input;
// Split on common separators: space, underscore, dash
var parts = input.Split(new[] { ' ', '_', '-' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 0)
return input;
// First word is lowercase, subsequent words are Title case (camelCase)
var sb = new System.Text.StringBuilder();
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i];
if (i == 0)
{
// First word: all lowercase
sb.Append(part.ToLowerInvariant());
}
else
{
// Subsequent words: capitalize first letter, lowercase rest
sb.Append(char.ToUpperInvariant(part[0]));
if (part.Length > 1)
sb.Append(part.Substring(1).ToLowerInvariant());
}
}
return sb.ToString();
}
}
}