using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace OpenTap.Package
{
internal static class ExpansionHelper
{
private static TraceSource log = Log.CreateSource("XML Expander");
///
/// Replace all the occurrences of the token '$(token)' with 'value' in the element
///
///
///
///
internal static void ReplaceToken(XElement element, string token, string value)
{
var textNodes = element.Nodes().OfType().ToArray();
foreach (var textNode in textNodes)
{
var curr = textNode.Value;
var newValue = textNode.Value.Replace($"$({token})", value)
.Replace($"$!({token})", value);
if (curr != newValue)
{
log.Debug($"Expanded '{curr}' -> '{newValue}'");
textNode.Value = newValue;
}
}
foreach (var attribute in element.Attributes().ToArray())
{
var curr = attribute.Value;
var newValue = attribute.Value.Replace($"$({token})", value)
.Replace($"$!({token})", value);
if (curr != newValue)
{
log.Debug($"Expanded '{curr}' -> '{newValue}'");
attribute.Value = newValue;
}
}
}
}
internal class ElementExpander
{
private static TraceSource log = Log.CreateSource($"Variable Expander");
private List ElementExpanders = new List();
internal void AddProvider(IElementExpander prov)
{
ElementExpanders.Add(prov);
isOrdered = false;
}
Type[] getDependents(IElementExpander e) => e.GetType().GetCustomAttributes().Select(d => d.Dependent).ToArray();
private bool isOrdered;
void order()
{
var orderedExpanders = new List();
var expanders = ElementExpanders.ToList();
// Track progress in order to detect cycles;
// this iterative approach is guaranteed to progress at least 1 item per iteration, but only if there are no circular dependencies.
var progress = -1;
while (expanders.Any())
{
if (orderedExpanders.Count == progress)
{
var issue = string.Join(", ", expanders.Select(e => e.GetType().Name));
throw new Exception(
$"Cycle detected while resolving expander order: [{issue}]. Aborting Package XML preprocessing.");
}
progress = orderedExpanders.Count;
foreach (var exp in expanders.ToArray())
{
var dependents = getDependents(exp);
var satisfied = orderedExpanders.Select(o => o.GetType());
if (dependents.All(d => satisfied.Contains(d)))
{
orderedExpanders.Add(exp);
expanders.Remove(exp);
}
}
}
ElementExpanders = orderedExpanders;
isOrdered = true;
}
internal void ExpandElement(XElement element)
{
if (!isOrdered) order();
foreach (var provider in ElementExpanders)
{
provider.Expand(element);
}
}
}
}