using System;
using System.Collections.Generic;
using System.Linq;
namespace Mustache
{
///
/// Removes unnecessary lines from the final output.
///
internal sealed class Trimmer
{
private readonly LinkedList _lines;
private LinkedListNode _currentLine;
///
/// Initializes a new instance of a Trimmer.
///
public Trimmer()
{
_lines = new LinkedList();
_currentLine = _lines.AddLast(new LineDetails());
}
///
/// Updates the state of the trimmer, indicating that the given text was encountered before an inline tag.
///
/// The text at the end of the format string.
/// The generator created for the inline tag.
/// A static generator containing the passed text.
public IEnumerable RecordText(string value, bool isTag, bool isOutput)
{
int newLineIndex = value.IndexOf(Environment.NewLine);
if (newLineIndex == -1)
{
StaticGenerator generator = new StaticGenerator() { Value = value };
_currentLine.Value.Generators.Add(generator);
_currentLine.Value.HasTag |= isTag;
_currentLine.Value.HasOutput |= !String.IsNullOrWhiteSpace(value);
yield return generator;
}
else
{
string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
// get the trailing generator
string trailing = lines[0];
StaticGenerator trailingGenerator = new StaticGenerator() { Value = trailing };
_currentLine.Value.Generators.Add(trailingGenerator);
_currentLine.Value.HasOutput |= !String.IsNullOrWhiteSpace(trailing);
yield return trailingGenerator;
// get the middle generators
for (int lineIndex = 1; lineIndex < lines.Length - 1; ++lineIndex)
{
string middle = lines[lineIndex];
StaticGenerator middleGenerator = new StaticGenerator() { Value = middle };
LineDetails middleDetails = new LineDetails() { HasTag = false };
_currentLine = _lines.AddLast(middleDetails);
_currentLine.Value.Generators.Add(middleGenerator);
_currentLine.Value.HasOutput = true;
yield return middleGenerator;
}
// get the leading generator
string leading = lines[lines.Length - 1];
StaticGenerator leadingGenerator = new StaticGenerator() { Value = leading };
LineDetails details = new LineDetails() { HasTag = isTag };
_currentLine = _lines.AddLast(details);
_currentLine.Value.Generators.Add(leadingGenerator);
_currentLine.Value.HasOutput = !String.IsNullOrWhiteSpace(leading);
yield return leadingGenerator;
}
if (isOutput)
{
_currentLine.Value.HasOutput = true;
}
}
public void Trim()
{
removeBlankLines();
separateLines();
removeEmptyGenerators();
}
private void removeBlankLines()
{
LinkedListNode current = _lines.First;
while (current != null)
{
LineDetails details = current.Value;
LinkedListNode temp = current;
current = current.Next;
if (details.HasTag && !details.HasOutput)
{
foreach (StaticGenerator generator in temp.Value.Generators)
{
generator.Prune();
}
temp.List.Remove(temp);
}
}
}
private void separateLines()
{
LinkedListNode current = _lines.First;
while (current != _lines.Last)
{
List generators = current.Value.Generators;
StaticGenerator lastGenerator = generators[generators.Count - 1];
lastGenerator.Value += Environment.NewLine;
current = current.Next;
}
}
private void removeEmptyGenerators()
{
LinkedListNode current = _lines.First;
while (current != null)
{
foreach (StaticGenerator generator in current.Value.Generators)
{
if (generator.Value.Length == 0)
{
generator.Prune();
}
}
current = current.Next;
}
}
private sealed class LineDetails
{
public LineDetails()
{
Generators = new List();
}
public bool HasTag { get; set; }
public List Generators { get; set; }
public bool HasOutput { get; set; }
}
}
}