コード例 #1
0
        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);
            }
        }
コード例 #2
0
        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);
        }
コード例 #3
0
        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)
                });
            }
        }
コード例 #4
0
        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)
                });
            }
        }
コード例 #5
0
        /// <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;
        }
コード例 #6
0
        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());
                }
            }
        }
コード例 #7
0
        /// <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);
        }
コード例 #8
0
        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);
                }
            }
        }