public void Fragment_Is_Serializable() { ApparatusLayerFragment fr = GetFragment(); string json = TestHelper.SerializeFragment(fr); ApparatusLayerFragment fr2 = TestHelper.DeserializeFragment <ApparatusLayerFragment>(json); Assert.Equal(fr.Location, fr2.Location); Assert.Equal(fr.Tag, fr2.Tag); Assert.Equal(fr.Entries.Count, fr2.Entries.Count); for (int i = 0; i < fr.Entries.Count; i++) { ApparatusEntry expected = fr.Entries[i]; ApparatusEntry actual = fr2.Entries[i]; Assert.Equal(expected.Type, actual.Type); Assert.Equal(expected.Tag, actual.Tag); Assert.Equal(expected.Value, actual.Value); Assert.Equal(expected.NormValue, actual.NormValue); Assert.Equal(expected.IsAccepted, actual.IsAccepted); Assert.Equal(expected.GroupId, actual.GroupId); Assert.Equal(expected.Witnesses.Count, actual.Witnesses.Count); Assert.Equal(expected.Authors.Count, actual.Authors.Count); Assert.Equal(expected.Note, actual.Note); } }
private static ApparatusLayerFragment GetFragment(int entryCount = 3) { ApparatusLayerFragment fr = new ApparatusLayerFragment { Location = "1.23", Tag = "tag" }; for (int i = 1; i <= entryCount; i++) { ApparatusEntry entry = new ApparatusEntry { Type = ApparatusEntryType.Replacement, Tag = "tag" + i, Value = "value" + i, NormValue = "norm-value" + i, IsAccepted = i == 1, GroupId = "group-id", }; entry.Witnesses.Add(new ApparatusAnnotatedValue { Value = "w1", Note = "note to w1" }); entry.Authors.Add(new ApparatusAnnotatedValue { Value = "a1", Note = "note to a1" }); fr.Entries.Add(entry); } return(fr); }
private void ParseSource(string source, ApparatusEntry entry) { if (string.IsNullOrEmpty(source)) { return; } foreach (string token in source.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) { entry.Authors.Add(new ApparatusAnnotatedValue { Value = token.Substring(1) }); } }
private void ParseWit(string wit, ApparatusEntry entry) { if (string.IsNullOrEmpty(wit)) { return; } foreach (string token in wit.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) { entry.Witnesses.Add(new ApparatusAnnotatedValue { Value = token.Substring(1) }); } }
/// <summary> /// Parses the specified document. /// </summary> /// <param name="doc">The document.</param> /// <param name="id">The document identifier.</param> /// <param name="textIndex">The index of the corresponding text.</param> /// <returns>Apparatus layer parts.</returns> /// <exception cref="ArgumentNullException">doc or id or textIndex /// </exception> public IEnumerable <TiledTextLayerPart <ApparatusLayerFragment> > Parse( XDocument doc, string id, JsonTextIndex textIndex) { if (doc == null) { throw new ArgumentNullException(nameof(doc)); } if (id == null) { throw new ArgumentNullException(nameof(id)); } _textIndex = textIndex ?? throw new ArgumentNullException(nameof(textIndex)); XElement bodyElem = doc.Root .Element(XmlHelper.TEI + "text") .Element(XmlHelper.TEI + "body"); // create a part to host fragments; we still don't know its item ID var part = CreatePart(null, id); int partFirstLineNr = 1; string divId; foreach (XElement divElem in bodyElem.Elements(XmlHelper.TEI + "div1")) { divId = divElem.Attribute(XmlHelper.XML + "id").Value; Logger?.LogInformation($"==Parsing div1 #{divId}" + " at line " + GetElementLineNumber(divElem)); int appNr = 0; foreach (XElement appElem in divElem.Elements(XmlHelper.TEI + "app")) { Logger?.LogInformation($"--Parsing app #{++appNr}@" + GetElementLineNumber(appElem)); // app -> fragment string type = appElem.Attribute("type")?.Value; ApparatusLayerFragment fr = new ApparatusLayerFragment { // @type -> tag composed by divID + spc + type Tag = divId + (type != null? $" {type}" : "") }; _groupNr = 0; string itemId = null; string[] locs = null; // @from/@to pair provides a single location if (appElem.Attribute("from") != null) { var t = ParseFromTo(appElem); if (t == null) { continue; } itemId = t.Item1; fr.Location = t.Item2; if (fr.Location == null) { Logger?.LogError("Word IDs {WordId} not found", appElem.Attribute("from").Value + "-" + appElem.Attribute("to").Value); continue; } Logger?.LogInformation("Fragment location: {Location}", fr.Location); } // @loc provides multiple locations, each to be assigned // to a clone of this fragment; thus, we keep the locations // in locs for later use else { string loc = appElem.Attribute("loc")?.Value; if (loc == null) { Logger?.LogError("No location for app element"); continue; } var itemIdAndlocs = ParseLoc(loc); if (itemIdAndlocs == null) { Logger?.LogError("Word IDs not found: " + appElem.Attribute("loc").Value); continue; } itemId = itemIdAndlocs.Item1; locs = itemIdAndlocs.Item2; Logger?.LogInformation("Fragment locations: {Location}", string.Join(" ", locs)); } // if the location refers to another item, change part; // this happens either because we still have no item assigned... if (part.ItemId == null) { part.ItemId = itemId; Logger?.LogInformation($"Item ID set to {itemId}"); } // ...or because the assigned item was different from the new one else if (part.ItemId != itemId) { Logger?.LogInformation( $"Item ID changed from {part.ItemId} to {itemId}"); if (part.Fragments.Count > 0) { foreach (var p in SplitPart(part)) { Logger?.LogInformation( $"Completed PART [{p.RoleId}] " + $"{partFirstLineNr}-{GetElementLineNumber(appElem) - 1}: " + GetFragmentLocsDump(p.Fragments)); yield return(p); } } part = CreatePart(itemId, id); partFirstLineNr = GetElementLineNumber(appElem); } // app content: lem, rdg, note foreach (XElement child in appElem.Elements()) { // each child element (lem or rdg or note) is an entry ApparatusEntry entry = new ApparatusEntry { // @type -> tag Tag = child.Attribute("type")?.Value }; fr.Entries.Add(entry); // parse its content XmlApparatusVarContent content; Logger?.LogInformation($"-Parsing {child.Name.LocalName}@" + GetElementLineNumber(child)); switch (child.Name.LocalName) { case "lem": entry.IsAccepted = true; goto case "rdg"; case "rdg": // @wit @source ParseWit(child.Attribute("wit")?.Value, entry); ParseSource(child.Attribute("source")?.Value, entry); content = ParseVariantContent(child); // rdg@n content.AddIdent(child); AddContentToEntry(content, entry); break; case "note": entry.Type = ApparatusEntryType.Note; content = ParseVariantContent(child); AddContentToEntry(content, entry); break; default: Logger?.LogError("Unexpected element {ElementName} in app", child.Name.LocalName); break; } } // duplicate fragment for @loc if (locs != null) { // assign the same group ID to all the entries with a variant string groupId = BuildGroupId(fr.Entries); foreach (var entry in fr.Entries.Where(e => e.Value != null)) { entry.GroupId = groupId; } foreach (string loc in locs) { string json = JsonConvert.SerializeObject(fr); ApparatusLayerFragment clone = JsonConvert.DeserializeObject <ApparatusLayerFragment>(json); clone.Location = loc; AddFragmentToPart(clone, part, appElem.Attribute("loc").Value); Logger?.LogInformation(GetFragmentDump(clone)); } } else { AddFragmentToPart(fr, part, appElem.Attribute("from").Value + "-" + appElem.Attribute("to").Value); Logger?.LogInformation(GetFragmentDump(fr)); } } // app } //div if (part.Fragments.Count > 0) { foreach (var p in SplitPart(part)) { Logger?.LogInformation( $"Completed PART [{p.RoleId}] " + $"{partFirstLineNr}-end: " + GetFragmentLocsDump(p.Fragments)); yield return(p); } } _textIndex = null; }
private void AddContentToEntry(XmlApparatusVarContent content, ApparatusEntry entry) { // value if (!string.IsNullOrWhiteSpace(content.Value)) { entry.Value = content.Value.Trim(); } // ident's if (content.Idents.Count > 0) { entry.NormValue = string.Join(" ", content.Idents); } // notes if (content.Notes.Count > 0) { // corner case: only note section 1 if (content.Notes.Count == 1 && content.Notes[0].SectionId == 1 && content.Notes[0].Target == null) { entry.Note = ApplyMarkdown(content.Notes[0].Value); return; } // first process wit/source notes, grouped by target foreach (var group in from n in content.Notes where n.Target != null group n by n.Target into g select g) { ApparatusAnnotatedValue target = entry.Witnesses.Find(w => w.Value == group.Key); if (target != null) { AddNoteToWitOrSource(group.ToList(), target); continue; } target = entry.Authors.Find(a => a.Value == group.Key); if (target != null) { AddNoteToWitOrSource(group.ToList(), target); } else { Logger?.LogError($"Target \"{group.Key}\" not found"); } } // then process untargeted notes StringBuilder sb = new StringBuilder(); int curSect = 1; HashSet <int> sections = new HashSet <int>(); foreach (XmlApparatusNote note in content.Notes .Where(n => n.Target == null) .OrderBy(n => n.SectionId)) { if (sections.Contains(note.SectionId)) { Logger?.LogError( $"Note section {note.SectionId} overwritten by \"{note.Value}\""); } while (curSect < note.SectionId) { sb.Append(NOTE_SECT_SEP); curSect++; } sb.Append(note.Value); sections.Add(note.SectionId); } if (sb.Length > 0) { entry.Note = ApplyMarkdown(sb.ToString()); } } }
/// <summary> /// Creates and seeds a new fragment. /// </summary> /// <param name="item">The item this part should belong to.</param> /// <param name="location">The location.</param> /// <param name="baseText">The base text.</param> /// <returns>A new fragment.</returns> /// <exception cref="ArgumentNullException">item or location or /// baseText</exception> public override ITextLayerFragment GetFragment( IItem item, string location, string baseText) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (location == null) { throw new ArgumentNullException(nameof(location)); } if (baseText == null) { throw new ArgumentNullException(nameof(baseText)); } Faker f = new Faker(); ApparatusLayerFragment fr = new ApparatusLayerFragment { Location = location, Tag = f.Lorem.Word() }; int n = Randomizer.Seed.Next(1, 4); for (int i = 1; i <= n; i++) { ApparatusEntry entry = new ApparatusEntry { Type = (ApparatusEntryType)Randomizer.Seed.Next(0, 4) }; if (Randomizer.Seed.Next(1, 6) == 1) { entry.Tag = f.Lorem.Word(); } // note if (entry.Type == ApparatusEntryType.Note) { entry.Note = f.Lorem.Sentence(); // authors foreach (string author in SeedHelper.RandomPickOf(_authors, 2)) { entry.Authors.Add( new ApparatusAnnotatedValue { Value = author }); } } // variant else { entry.Value = f.Lorem.Word(); entry.NormValue = entry.Value.ToUpperInvariant(); entry.IsAccepted = i == 1; // witnesses foreach (string witness in SeedHelper.RandomPickOf(_witnesses, 2)) { entry.Authors.Add( new ApparatusAnnotatedValue { Value = witness }); } // authors foreach (string author in SeedHelper.RandomPickOf(_authors, 1)) { entry.Authors.Add( new ApparatusAnnotatedValue { Value = author }); } } fr.Entries.Add(entry); } return(fr); }
private void AppendEntryContent(ApparatusEntry entry, XElement app, string partId) { XElement target = new XElement(XmlHelper.TEI + (entry.IsAccepted ? "lem" : "rdg")); // only replacement/note are allowed for TEI switch (entry.Type) { case ApparatusEntryType.Replacement: // replacement = lem/rdg with value target.Add(entry.Value); // groupId = @n (temporary solution) if (!string.IsNullOrEmpty(entry.GroupId)) { target.SetAttributeValue("n", entry.GroupId); } // note if (!string.IsNullOrEmpty(entry.Note)) { target.Add(_noteRenderer.Render(entry.Note)); } app.Add(target); break; case ApparatusEntryType.Note: // note = add/note if (string.IsNullOrEmpty(entry.Note)) { Logger?.LogInformation("Note entry without content: " + $"{entry} ({partId})"); } else { target.Add(_noteRenderer.Render(entry.Note)); } app.Add(target); break; default: Logger?.LogError( $"Invalid apparatus entry type: {entry.Type} ({partId})"); return; } // tag: @type if (!string.IsNullOrEmpty(entry.Tag)) { target.SetAttributeValue("type", entry.Tag); } // witnesses and authors: @wit, @source, note, add // witnesses (@wit) if (entry.Witnesses?.Count > 0) { string ids = RenderEntrySources(entry.Witnesses, target); if (ids.Length > 0) { target.SetAttributeValue("wit", ids); } } // authors (@source) if (entry.Authors?.Count > 0) { string ids = RenderEntrySources(entry.Authors, target); if (ids.Length > 0) { target.SetAttributeValue("source", ids); } } // normValue = ident's with optional @n // usually only lem/rdg should have ident's if (!string.IsNullOrEmpty(entry.NormValue)) { if (entry.Type != ApparatusEntryType.Replacement) { Logger?.LogWarning( "NormValue in non-replacement entry: " + $"\"{entry.NormValue}\" ({partId})"); } // ident [@n]+ foreach (Match m in _identRegex.Matches(entry.NormValue)) { XElement ident = new XElement(XmlHelper.TEI + "ident", m.Groups["i"].Value); if (m.Groups["n"].Length > 0) { ident.SetAttributeValue("n", m.Groups["n"].Value); } target.Add(ident); } } }