templates support
This commit is contained in:
213
Inspectron.Epson.Templates/Interpreter/DataContext.cs
Normal file
213
Inspectron.Epson.Templates/Interpreter/DataContext.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
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()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user