diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/FormattedTextExtensions.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/FormattedTextExtensions.cs new file mode 100644 index 0000000..9186104 --- /dev/null +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/FormattedTextExtensions.cs @@ -0,0 +1,116 @@ +using System.Linq; +using System.Text; + +namespace YetAnotherPacketParser.Compiler +{ + internal static class FormattedTextExtensions + { + public static void WriteFormattedText(this FormattedText node, StringBuilder builder) + { + Verify.IsNotNull(node, nameof(node)); + + if (!node.Segments.Any()) + { + return; + } + + bool previousBolded = false; + bool previousItalic = false; + bool previousUnderlined = false; + bool previousSubscript = false; + bool previousSuperscript = false; + + foreach (FormattedTextSegment segment in node.Segments) + { + // Close tags before opening new ones + if (previousSuperscript && !segment.IsSuperscript) + { + builder.Append(""); + previousSuperscript = false; + } + + if (previousSubscript && !segment.IsSubscript) + { + builder.Append(""); + previousSubscript = false; + } + + if (previousItalic && !segment.Italic) + { + builder.Append(""); + previousItalic = false; + } + + if (previousUnderlined && !segment.Underlined) + { + builder.Append(""); + previousUnderlined = false; + } + + if (previousBolded ^ segment.Bolded) + { + builder.Append(segment.Bolded ? "" : ""); + previousBolded = segment.Bolded; + } + + if (!previousBolded && segment.Bolded) + { + builder.Append(""); + previousBolded = true; + } + + if (!previousUnderlined && segment.Underlined) + { + builder.Append(""); + previousUnderlined = true; + } + + if (!previousItalic && segment.Italic) + { + builder.Append(""); + previousItalic = true; + } + + if (!previousSubscript && segment.IsSubscript) + { + builder.Append(""); + previousSubscript = true; + } + + if (!previousSuperscript && segment.IsSuperscript) + { + builder.Append(""); + previousSuperscript = true; + } + + builder.Append(segment.Text); + } + + // Close any remaining tags + if (previousBolded) + { + builder.Append(""); + } + + if (previousUnderlined) + { + builder.Append(""); + } + + if (previousItalic) + { + builder.Append(""); + } + + if (previousSubscript) + { + builder.Append(""); + } + + if (previousSuperscript) + { + builder.Append(""); + } + } + } +} diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Html/HtmlCompiler.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Html/HtmlCompiler.cs index 0d94050..0d8d42f 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Html/HtmlCompiler.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Html/HtmlCompiler.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Linq; using System.Text; using System.Threading.Tasks; using YetAnotherPacketParser.Ast; @@ -63,7 +62,7 @@ private static void WriteBonus(BonusNode bonus, StringBuilder builder) builder.Append("

"); builder.Append(bonus.Number); builder.Append(". "); - WriteFormattedText(bonus.Leadin, builder); + bonus.Leadin.WriteFormattedText(builder); builder.Append("
"); foreach (BonusPartNode bonusPart in bonus.Parts) { @@ -91,66 +90,11 @@ private static void WriteBonusPart(BonusPartNode bonusPart, StringBuilder builde private static void WriteQuestion(QuestionNode node, StringBuilder builder) { - WriteFormattedText(node.Question, builder); + node.Question.WriteFormattedText(builder); builder.AppendLine("
"); builder.Append("ANSWER: "); - WriteFormattedText(node.Answer, builder); + node.Answer.WriteFormattedText(builder); builder.AppendLine("
"); } - - // TODO: move this somewhere where it can be tested, or merge with JsonCompiler (different tags needed) - // Would lose some efficiency that way, since we'd recreate a StringBuilder each time - private static void WriteFormattedText(FormattedText node, StringBuilder builder) - { - Verify.IsNotNull(node, nameof(node)); - - if (!node.Segments.Any()) - { - return; - } - - bool previousBolded = false; - bool previousItalic = false; - bool previousUnderlined = false; - - foreach (FormattedTextSegment segment in node.Segments) - { - if (previousBolded ^ segment.Bolded) - { - builder.Append(segment.Bolded ? "" : ""); - previousBolded = segment.Bolded; - } - - if (previousUnderlined ^ segment.Underlined) - { - builder.Append(segment.Underlined ? "" : ""); - previousUnderlined = segment.Underlined; - } - - if (previousItalic ^ segment.Italic) - { - builder.Append(segment.Italic ? "" : ""); - previousItalic = segment.Italic; - } - - builder.Append(segment.Text); - } - - // Close any remaining tags - if (previousBolded) - { - builder.Append(""); - } - - if (previousUnderlined) - { - builder.Append(""); - } - - if (previousItalic) - { - builder.Append(""); - } - } } } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonBonusNode.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonBonusNode.cs index edb372b..109f581 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonBonusNode.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonBonusNode.cs @@ -43,18 +43,18 @@ public JsonBonusNode(BonusNode bonusNode, bool omitSanitizedFields) public string? Leadin_sanitized { get; } - public string? Metadata { get; } + public ICollection Parts { get; } + + public ICollection? Parts_sanitized { get; } public ICollection Answers { get; } public ICollection? Answers_sanitized { get; } - public ICollection Parts { get; } - - public ICollection? Parts_sanitized { get; } - public ICollection Values { get; } public ICollection? DifficultyModifiers { get; } + + public string? Metadata { get; } } } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonTextFormatter.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonTextFormatter.cs index 4ee7905..e1f82b8 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonTextFormatter.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/Json/JsonTextFormatter.cs @@ -8,55 +8,8 @@ internal static class JsonTextFormatter internal static string ToStringWithTags(FormattedText node) { Verify.IsNotNull(node, nameof(node)); - - if (!node.Segments.Any()) - { - return string.Empty; - } - - bool previousBolded = false; - bool previousItalic = false; - bool previousUnderlined = false; - StringBuilder builder = new StringBuilder(); - foreach (FormattedTextSegment segment in node.Segments) - { - if (previousBolded ^ segment.Bolded) - { - builder.Append(segment.Bolded ? "" : ""); - previousBolded = segment.Bolded; - } - - if (previousUnderlined ^ segment.Underlined) - { - builder.Append(segment.Underlined ? "" : ""); - previousUnderlined = segment.Underlined; - } - - if (previousItalic ^ segment.Italic) - { - builder.Append(segment.Italic ? "" : ""); - previousItalic = segment.Italic; - } - - builder.Append(segment.Text); - } - - // Close any remaining tags - if (previousBolded) - { - builder.Append(""); - } - - if (previousUnderlined) - { - builder.Append(""); - } - - if (previousItalic) - { - builder.Append(""); - } + node.WriteFormattedText(builder); return builder.ToString(); } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/SanitizeHtmlTransformer.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/SanitizeHtmlTransformer.cs index c03be5d..cd16776 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/SanitizeHtmlTransformer.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Compiler/SanitizeHtmlTransformer.cs @@ -135,7 +135,9 @@ private FormattedText SanitizeFormattedTexts(FormattedText rawFormattedTexts) sanitizedText, rawSegment.Italic, rawSegment.Bolded, - rawSegment.Underlined); + rawSegment.Underlined, + rawSegment.IsSubscript, + rawSegment.IsSuperscript); sanitizedFormattedTexts.Add(sanitizedFormattedText); } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/FormattedText.cs b/YetAnotherPacketParser/YetAnotherPacketParser/FormattedText.cs index 86d4447..25d77b8 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/FormattedText.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/FormattedText.cs @@ -29,7 +29,12 @@ public FormattedText Substring(int startIndex) { string substringText = segment.Text.Substring(startIndex - index); segments.Add(new FormattedTextSegment( - substringText, segment.Italic, segment.Bolded, segment.Underlined)); + substringText, + segment.Italic, + segment.Bolded, + segment.Underlined, + segment.IsSubscript, + segment.IsSuperscript)); } else if (index >= startIndex) { diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/FormattedTextSegment.cs b/YetAnotherPacketParser/YetAnotherPacketParser/FormattedTextSegment.cs index e530135..2bdc303 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/FormattedTextSegment.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/FormattedTextSegment.cs @@ -4,12 +4,20 @@ namespace YetAnotherPacketParser { internal class FormattedTextSegment { - public FormattedTextSegment(string text, bool italic = false, bool bolded = false, bool underlined = false) + public FormattedTextSegment( + string text, + bool italic = false, + bool bolded = false, + bool underlined = false, + bool isSubscript = false, + bool isSuperscript = false) { this.Text = text ?? throw new ArgumentNullException(nameof(text)); this.Italic = italic; this.Bolded = bolded; this.Underlined = underlined; + this.IsSubscript = isSubscript; + this.IsSuperscript = isSuperscript; } public string Text { get; } @@ -20,12 +28,18 @@ public FormattedTextSegment(string text, bool italic = false, bool bolded = fals public bool Underlined { get; } + public bool IsSubscript { get; } + + public bool IsSuperscript { get; } + public override string ToString() { string boldedString = this.Bolded ? "bolded, " : string.Empty; string italicString = this.Italic ? "italic, " : string.Empty; string underlinedString = this.Underlined ? "underlined, " : string.Empty; - string propertiesString = $"{boldedString}{italicString}{underlinedString}".Trim(); + string subscriptString = this.IsSubscript ? "subscript, " : string.Empty; + string superscriptString = this.IsSuperscript ? "superscript, " : string.Empty; + string propertiesString = $"{boldedString}{italicString}{underlinedString}{subscriptString}{superscriptString}".Trim(); return $"({propertiesString}) {this.Text}"; } @@ -39,7 +53,9 @@ public override bool Equals(object? obj) return this.Text == other.Text && this.Bolded == other.Bolded && this.Italic == other.Italic && - this.Underlined == other.Underlined; + this.Underlined == other.Underlined && + this.IsSubscript == other.IsSubscript && + this.IsSuperscript == other.IsSuperscript; } public override int GetHashCode() @@ -47,7 +63,9 @@ public override int GetHashCode() return (this.Text?.GetHashCode(StringComparison.Ordinal) ?? 0) ^ this.Bolded.GetHashCode() ^ (this.Italic.GetHashCode() << 1) ^ - (this.Underlined.GetHashCode() << 2); + (this.Underlined.GetHashCode() << 2) ^ + (this.IsSubscript.GetHashCode() << 3) ^ + (this.IsSuperscript.GetHashCode() << 4); } } } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/DocxLexer.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/DocxLexer.cs index f57ef4e..d586717 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/DocxLexer.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/DocxLexer.cs @@ -143,6 +143,8 @@ private static List GetLinesFromTextBlockLines(IEnumerable bool bolded = false; bool italic = false; bool underlined = false; + bool subscripted = false; + bool superscripted = false; List lines = new List(); foreach (TextBlockLine textBlockLine in textBlockLines) @@ -156,20 +158,31 @@ private static List GetLinesFromTextBlockLines(IEnumerable bool blockBolded = false; bool blockItalic = false; bool blockUnderlined = false; + bool blockSubscripted = false; + bool blockSuperscripted = false; if (textBlock.Properties != null) { + string? verticalTextAlignmentVal = textBlock.Properties.VerticalTextAlignment?.Val?.ToString(); + blockBolded = textBlock.Properties.Bold != null; blockItalic = textBlock.Properties.Italic != null; blockUnderlined = textBlock.Properties.Underline != null; + blockSubscripted = verticalTextAlignmentVal == "subscript"; + blockSuperscripted = verticalTextAlignmentVal == "superscript"; } - if (blockBolded != bolded || blockItalic != italic || blockUnderlined != underlined) + if (blockBolded != bolded || + blockItalic != italic || + blockUnderlined != underlined || + blockSubscripted != subscripted || + blockSuperscripted != superscripted) { // Formatting has changed. This means the last segment finished. Add it if it has anything. if (currentSegment.Length > 0) { formattedTextSegments.Add( - new FormattedTextSegment(currentSegment.ToString(), italic, bolded, underlined)); + new FormattedTextSegment( + currentSegment.ToString(), italic, bolded, underlined, subscripted, superscripted)); currentSegment.Clear(); } @@ -177,6 +190,8 @@ private static List GetLinesFromTextBlockLines(IEnumerable bolded = blockBolded; italic = blockItalic; underlined = blockUnderlined; + subscripted = blockSubscripted; + superscripted = blockSuperscripted; } currentSegment.Append(textBlock.Text); @@ -195,7 +210,8 @@ private static List GetLinesFromTextBlockLines(IEnumerable // Add the remainder of the line if (currentSegment.Length > 0) { - formattedTextSegments.Add(new FormattedTextSegment(currentSegment.ToString(), italic, bolded, underlined)); + formattedTextSegments.Add(new FormattedTextSegment( + currentSegment.ToString(), italic, bolded, underlined, subscripted, superscripted)); currentSegment.Clear(); } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/HtmlLexer.cs b/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/HtmlLexer.cs index 630c92f..7929eee 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/HtmlLexer.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParser/Lexer/HtmlLexer.cs @@ -66,7 +66,9 @@ private static IList GetTextLines(IHtmlElement body) paragraph.TextContent, previousFormatting.Italic, previousFormatting.Bolded, - previousFormatting.Underlined) + previousFormatting.Underlined, + previousFormatting.Subscripted, + previousFormatting.Superscripted) })); } @@ -141,14 +143,20 @@ private static IResult> ClassifyLines(IList fo if (!node.HasChildNodes && !string.IsNullOrEmpty(node.TextContent)) { segments.Add(new FormattedTextSegment( - node.TextContent, formatting.Italic, formatting.Bolded, formatting.Underlined)); + node.TextContent, + formatting.Italic, + formatting.Bolded, + formatting.Underlined, + formatting.Subscripted, + formatting.Superscripted)); return; } bool isElement = node.NodeType == NodeType.Element; IElement? element = node as Element; - // Need to change formatting if it's B/REQ, U, or I/EM. For other ones, just call GetString on the child. + // Need to change formatting if it's B/REQ, U, I/EM, SUB, or SUP. For other ones, just call GetString on + // the child. if (isElement && element != null) { if (segments.Count > 0 && (element.TagName == "P" || element.TagName == "BR")) @@ -197,6 +205,12 @@ private static void UpdateFormatting(Formatting formatting, IElement element, bo case "U": formatting.Underlined = value; break; + case "SUB": + formatting.Subscripted = value; + break; + case "SUP": + formatting.Superscripted = value; + break; default: break; } @@ -208,6 +222,10 @@ private class Formatting public bool Italic { get; set; } public bool Underlined { get; set; } + + public bool Superscripted { get; set; } + + public bool Subscripted { get; set; } } } } diff --git a/YetAnotherPacketParser/YetAnotherPacketParser/YetAnotherPacketParser.csproj b/YetAnotherPacketParser/YetAnotherPacketParser/YetAnotherPacketParser.csproj index 8063818..f163125 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParser/YetAnotherPacketParser.csproj +++ b/YetAnotherPacketParser/YetAnotherPacketParser/YetAnotherPacketParser.csproj @@ -17,9 +17,9 @@ quizbowl packetparser quizbowlpacketparser https://github.com/alopezlago/YetAnotherPacketParser LICENSE.txt - 1.1.1.0 - 1.1.1.0 - 1.1.1.0 + 1.2.0.0 + 1.2.0.0 + 1.2.0.0 true Recommended All diff --git a/YetAnotherPacketParser/YetAnotherPacketParserCommandLine/YetAnotherPacketParserCommandLine.csproj b/YetAnotherPacketParser/YetAnotherPacketParserCommandLine/YetAnotherPacketParserCommandLine.csproj index 81e9567..cb24eca 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParserCommandLine/YetAnotherPacketParserCommandLine.csproj +++ b/YetAnotherPacketParser/YetAnotherPacketParserCommandLine/YetAnotherPacketParserCommandLine.csproj @@ -9,9 +9,9 @@ (c) 2020 Alejandro Lopez-Lago Yet Another Packet Parser parses quiz bowl packets and translates them to different formats YAPP - 1.1.1.0 - 1.1.1.0 - 1.1.1.0 + 1.2.0.0 + 1.2.0.0 + 1.2.0.0 true Recommended All diff --git a/YetAnotherPacketParser/YetAnotherPacketParserTests/FormattedTextTests.cs b/YetAnotherPacketParser/YetAnotherPacketParserTests/FormattedTextTests.cs new file mode 100644 index 0000000..bbae6ca --- /dev/null +++ b/YetAnotherPacketParser/YetAnotherPacketParserTests/FormattedTextTests.cs @@ -0,0 +1,37 @@ +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using YetAnotherPacketParser; +using YetAnotherPacketParser.Compiler; + +namespace YetAnotherPacketParserTests +{ + [TestClass] + public class FormattedTextTests + { + [TestMethod] + public void TestFormat() + { + FormattedTextSegment[] segments = new FormattedTextSegment[] + { + new FormattedTextSegment("First"), + new FormattedTextSegment("Second", italic: true), + new FormattedTextSegment("Third", italic: true, bolded: true), + new FormattedTextSegment("Fourth", italic: true, bolded: true, underlined: true), + new FormattedTextSegment("Fifth", italic: true, bolded: true, underlined: true, isSubscript: true), + new FormattedTextSegment("Sixth", italic: true, bolded: true, underlined: true, isSuperscript: true), + new FormattedTextSegment("Seventh", italic: true, bolded: true, underlined: true), + new FormattedTextSegment("Eighth", italic: true, bolded: true), + new FormattedTextSegment("Ninth", italic: true), + new FormattedTextSegment("Tenth"), + }; + + FormattedText text = new FormattedText(segments); + StringBuilder builder = new StringBuilder(); + text.WriteFormattedText(builder); + + Assert.AreEqual( + "FirstSecondThirdFourthFifthSixthSeventhEighthNinthTenth", + builder.ToString()); + } + } +} diff --git a/YetAnotherPacketParser/YetAnotherPacketParserTests/HtmlLexerTests.cs b/YetAnotherPacketParser/YetAnotherPacketParserTests/HtmlLexerTests.cs index e14a76d..779dcfb 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParserTests/HtmlLexerTests.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParserTests/HtmlLexerTests.cs @@ -18,7 +18,7 @@ public async Task ParseHtml() const string htmlPacket = @"

- 1. This is a tossup. Underline and italics. + 1. This is a tossup. Underline and italics. H20 and x2.
ANSWER: Tossup Answer
@@ -76,6 +76,10 @@ public async Task ParseHtml() new FormattedTextSegment("Underline", underlined: true), new FormattedTextSegment(" and "), new FormattedTextSegment("italics", italic: true), + new FormattedTextSegment(". H"), + new FormattedTextSegment("2", isSubscript: true), + new FormattedTextSegment("0 and x"), + new FormattedTextSegment("2", isSuperscript: true), new FormattedTextSegment(".\n ") }; CollectionAssert.AreEqual( diff --git a/YetAnotherPacketParser/YetAnotherPacketParserTests/JsonCompilerTests.cs b/YetAnotherPacketParser/YetAnotherPacketParserTests/JsonCompilerTests.cs index b0b663d..eec41f0 100644 --- a/YetAnotherPacketParser/YetAnotherPacketParserTests/JsonCompilerTests.cs +++ b/YetAnotherPacketParser/YetAnotherPacketParserTests/JsonCompilerTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using YetAnotherPacketParser; using YetAnotherPacketParser.Ast;