214 lines
6.6 KiB
C#
214 lines
6.6 KiB
C#
using System.Text.Json;
|
|
|
|
namespace Inspectron.Epson.Templates.Interpreter;
|
|
|
|
public class DataContext
|
|
{
|
|
private readonly JsonElement _root;
|
|
private readonly DataContext? _parent;
|
|
private readonly Dictionary<string, JsonElement> _localVariables = new();
|
|
private readonly Dictionary<string, object> _loopMetadata = new();
|
|
|
|
public DataContext(JsonElement root)
|
|
{
|
|
_root = root;
|
|
_parent = null;
|
|
}
|
|
|
|
private DataContext(JsonElement root, DataContext parent)
|
|
{
|
|
_root = root;
|
|
_parent = parent;
|
|
}
|
|
|
|
public DataContext CreateChildContext(string variableName, JsonElement value, int index, int count)
|
|
{
|
|
var child = new DataContext(_root, this);
|
|
child._localVariables[variableName] = value;
|
|
child._loopMetadata["_index"] = index;
|
|
child._loopMetadata["_number"] = index + 1;
|
|
child._loopMetadata["_first"] = index == 0;
|
|
child._loopMetadata["_last"] = index == count - 1;
|
|
child._loopMetadata["_count"] = count;
|
|
return child;
|
|
}
|
|
|
|
public JsonElement? Resolve(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
return _root;
|
|
}
|
|
|
|
// Check loop metadata first
|
|
if (_loopMetadata.TryGetValue(path, out var metadata))
|
|
{
|
|
return JsonSerializer.SerializeToElement(metadata);
|
|
}
|
|
|
|
var segments = path.Split('.');
|
|
var firstSegment = segments[0];
|
|
|
|
// Check local variables (loop variables)
|
|
if (_localVariables.TryGetValue(firstSegment, out var localValue))
|
|
{
|
|
return NavigatePath(localValue, segments.Skip(1).ToArray());
|
|
}
|
|
|
|
// Check parent context for local variables
|
|
if (_parent != null)
|
|
{
|
|
// First check if parent has this as a local variable
|
|
var parentResult = _parent.ResolveLocalOnly(firstSegment);
|
|
if (parentResult.HasValue)
|
|
{
|
|
return NavigatePath(parentResult.Value, segments.Skip(1).ToArray());
|
|
}
|
|
|
|
// Also check parent's loop metadata
|
|
if (_parent._loopMetadata.TryGetValue(path, out var parentMetadata))
|
|
{
|
|
return JsonSerializer.SerializeToElement(parentMetadata);
|
|
}
|
|
}
|
|
|
|
// Navigate from root
|
|
return NavigatePath(_root, segments);
|
|
}
|
|
|
|
private JsonElement? ResolveLocalOnly(string name)
|
|
{
|
|
if (_localVariables.TryGetValue(name, out var value))
|
|
{
|
|
return value;
|
|
}
|
|
return _parent?.ResolveLocalOnly(name);
|
|
}
|
|
|
|
private JsonElement? NavigatePath(JsonElement element, string[] segments)
|
|
{
|
|
var current = element;
|
|
|
|
foreach (var segment in segments)
|
|
{
|
|
if (current.ValueKind == JsonValueKind.Null || current.ValueKind == JsonValueKind.Undefined)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// Handle array access: collection.count or collection.length
|
|
if (segment.Equals("count", StringComparison.OrdinalIgnoreCase) ||
|
|
segment.Equals("length", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (current.ValueKind == JsonValueKind.Array)
|
|
{
|
|
return JsonSerializer.SerializeToElement(current.GetArrayLength());
|
|
}
|
|
}
|
|
|
|
// Handle array index: collection.0, collection.1
|
|
if (int.TryParse(segment, out var index))
|
|
{
|
|
if (current.ValueKind == JsonValueKind.Array && index >= 0 && index < current.GetArrayLength())
|
|
{
|
|
current = current[index];
|
|
continue;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Handle object property
|
|
if (current.ValueKind == JsonValueKind.Object)
|
|
{
|
|
if (current.TryGetProperty(segment, out var property))
|
|
{
|
|
current = property;
|
|
}
|
|
else
|
|
{
|
|
// Try case-insensitive match
|
|
var found = false;
|
|
foreach (var prop in current.EnumerateObject())
|
|
{
|
|
if (prop.Name.Equals(segment, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
current = prop.Value;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
public IEnumerable<JsonElement> ResolveCollection(string path)
|
|
{
|
|
var element = Resolve(path);
|
|
if (element.HasValue && element.Value.ValueKind == JsonValueKind.Array)
|
|
{
|
|
foreach (var item in element.Value.EnumerateArray())
|
|
{
|
|
yield return item;
|
|
}
|
|
}
|
|
}
|
|
|
|
public string? GetString(string path)
|
|
{
|
|
var element = Resolve(path);
|
|
if (!element.HasValue) return null;
|
|
|
|
return element.Value.ValueKind switch
|
|
{
|
|
JsonValueKind.String => element.Value.GetString(),
|
|
JsonValueKind.Number => element.Value.GetRawText(),
|
|
JsonValueKind.True => "true",
|
|
JsonValueKind.False => "false",
|
|
JsonValueKind.Null => null,
|
|
_ => element.Value.GetRawText()
|
|
};
|
|
}
|
|
|
|
public bool IsTruthy(string path)
|
|
{
|
|
var element = Resolve(path);
|
|
if (!element.HasValue) return false;
|
|
|
|
return element.Value.ValueKind switch
|
|
{
|
|
JsonValueKind.Null or JsonValueKind.Undefined => false,
|
|
JsonValueKind.False => false,
|
|
JsonValueKind.True => true,
|
|
JsonValueKind.String => !string.IsNullOrEmpty(element.Value.GetString()),
|
|
JsonValueKind.Number => element.Value.GetDouble() != 0,
|
|
JsonValueKind.Array => element.Value.GetArrayLength() > 0,
|
|
JsonValueKind.Object => true,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
public object? GetValue(string path)
|
|
{
|
|
var element = Resolve(path);
|
|
if (!element.HasValue) return null;
|
|
|
|
return element.Value.ValueKind switch
|
|
{
|
|
JsonValueKind.String => element.Value.GetString(),
|
|
JsonValueKind.Number => element.Value.TryGetInt64(out var l) ? l : element.Value.GetDouble(),
|
|
JsonValueKind.True => true,
|
|
JsonValueKind.False => false,
|
|
JsonValueKind.Null => null,
|
|
_ => element.Value.GetRawText()
|
|
};
|
|
}
|
|
}
|