| File: Operations\PhasedOperation.cs | Web Access |
| Project: src\sdk\src\TemplateEngine\Microsoft.TemplateEngine.Core\Microsoft.TemplateEngine.Core.csproj (Microsoft.TemplateEngine.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text; using Microsoft.TemplateEngine.Core.Contracts; namespace Microsoft.TemplateEngine.Core.Operations { public class PhasedOperation : IOperationProvider { private readonly IReadOnlyList<Phase> _config; private readonly bool _initialState; public PhasedOperation(string? id, IReadOnlyList<Phase> config, bool initialState) { Id = id; _config = config; _initialState = initialState; } public string? Id { get; } public IOperation GetOperation(Encoding encoding, IProcessorState processorState) { Dictionary<ITokenConfig, int> tokenMap = new Dictionary<ITokenConfig, int>(); List<IToken> tokens = new List<IToken>(); Stack<IEnumerator<Phase>> sourceParents = new Stack<IEnumerator<Phase>>(); Stack<List<SpecializedPhase>> targetParents = new Stack<List<SpecializedPhase>>(); IEnumerator<Phase>? currentSource = _config.GetEnumerator(); List<SpecializedPhase> currentTarget = new List<SpecializedPhase>(); while (sourceParents.Count > 0 || currentSource != null) { while (currentSource?.MoveNext() ?? false) { Phase c = currentSource.Current; if (!tokenMap.TryGetValue(c.Match, out int existingMatchToken)) { IToken bytes = c.Match.ToToken(encoding); existingMatchToken = tokenMap[c.Match] = tokens.Count; tokens.Add(bytes); } SpecializedPhase target = new SpecializedPhase { Replacement = c.Replacement != null ? encoding.GetBytes(c.Replacement) : tokens[existingMatchToken].Value, Match = existingMatchToken, }; foreach (ITokenConfig reset in c.ResetsWith) { if (!tokenMap.TryGetValue(reset, out int existingResetToken)) { IToken token = reset.ToToken(encoding); existingResetToken = tokenMap[reset] = tokens.Count; tokens.Add(token); } target.ResetsWith.Add(existingResetToken); } currentTarget.Add(target); if (c.Next.Count > 0) { sourceParents.Push(currentSource); currentSource = currentSource.Current.Next.GetEnumerator(); targetParents.Push(currentTarget); currentTarget = new List<SpecializedPhase>(); } } currentSource?.Dispose(); currentSource = null; if (sourceParents.Count > 0) { currentSource = sourceParents.Pop(); List<SpecializedPhase> children = currentTarget; currentTarget = targetParents.Pop(); currentTarget[currentTarget.Count - 1].Next.AddRange(children); } } return new Implementation(this, tokens, currentTarget, _initialState); } private class Implementation : IOperation { private readonly PhasedOperation _definition; private readonly IReadOnlyList<SpecializedPhase> _entryPoints; private SpecializedPhase? _currentPhase; public Implementation(PhasedOperation definition, IReadOnlyList<IToken> config, IReadOnlyList<SpecializedPhase> entryPoints, bool initialState) { _definition = definition; Tokens = config; _entryPoints = entryPoints; IsInitialStateOn = string.IsNullOrEmpty(_definition.Id) || initialState; } public string? Id => _definition.Id; public IReadOnlyList<IToken> Tokens { get; } public bool IsInitialStateOn { get; } public int HandleMatch(IProcessorState processor, int bufferLength, ref int currentBufferPosition, int token) { IReadOnlyList<SpecializedPhase> nextPhases = _currentPhase?.Next ?? _entryPoints; SpecializedPhase? match = nextPhases.FirstOrDefault(x => x.Match == token); if (match != null) { _currentPhase = match.Next.Count > 0 ? match : null; processor.WriteToTarget(match.Replacement!, 0, match.Replacement!.Length); return match.Replacement.Length; } if (_currentPhase != null && _currentPhase.ResetsWith.Contains(token)) { _currentPhase = null; } processor.WriteToTarget(Tokens[token].Value, Tokens[token].Start, Tokens[token].Length); return Tokens[token].Length; } } private class SpecializedPhase { public SpecializedPhase() { Next = new List<SpecializedPhase>(); ResetsWith = new List<int>(); } public int Match { get; set; } public List<SpecializedPhase> Next { get; } public byte[]? Replacement { get; set; } public List<int> ResetsWith { get; } } } }