Example #1
0
        protected override void ShowRecord()
        {
            if (!m_fullyInitialized || IsDisposed || m_mainView.IsDisposed || !Visible)
            {
                return;
            }
            base.ShowRecord();
            var cmo = Clerk.CurrentObject;

            // Don't steal focus
            Enabled = false;
            m_mainView.DocumentCompleted += EnableRecordDocView;
            if (cmo != null && cmo.Hvo > 0)
            {
                var configurationFile = DictionaryConfigurationListener.GetCurrentConfiguration(m_propertyTable);
                if (String.IsNullOrEmpty(configurationFile))
                {
                    m_mainView.DocumentText = String.Format("<html><body><p>{0}</p></body></html>",
                                                            xWorksStrings.ksNoConfiguration);
                    return;
                }
                var configuration = new DictionaryConfigurationModel(configurationFile, Cache);
                var xhtmlPath     = ConfiguredXHTMLGenerator.SavePreviewHtmlWithStyles(new [] { cmo.Hvo }, null, configuration, m_propertyTable);
                m_mainView.Url = new Uri(xhtmlPath);
                m_mainView.Refresh(WebBrowserRefreshOption.Completely);
            }
            else
            {
                m_mainView.DocumentText = "<html><body></body></html>";
            }
        }
 public int CountDictionaryEntries(DictionaryConfigurationModel config)
 {
     int[] entries;
     using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_mediator))
         ConfiguredXHTMLGenerator.GetPublicationDecoratorAndEntries(m_mediator, out entries, DictionaryType);
     return(entries.Count(e => IsGenerated(m_cache, config, e)));
 }
 internal int CountReversalIndexEntries(IReversalIndex ri)
 {
     int[] entries;
     using (ReversalIndexActivator.ActivateReversalIndex(ri.Guid, m_mediator))
         ConfiguredXHTMLGenerator.GetPublicationDecoratorAndEntries(m_mediator, out entries, ReversalType);
     return(entries.Length);
 }
Example #4
0
        public bool IsSortingOnAlphaHeaders()
        {
            var clerk = m_propertyTable.GetValue <RecordClerk>("ActiveClerk", null);

            int[] entriesToSave;
            var   publicationDecorator =
                ConfiguredXHTMLGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out entriesToSave, "Dictionary");

            return(RecordClerk.IsClerkSortingByHeadword(clerk));
        }
        /// <summary>
        /// Determines how many times the entry with the given HVO is generated for the given config (usually 0 or 1,
        /// but can be more if the entry matches more than one Minor Entry node)
        /// </summary>
        internal static bool IsGenerated(FdoCache cache, DictionaryConfigurationModel config, int hvo)
        {
            var entry = (ILexEntry)cache.ServiceLocator.GetObject(hvo);

            if (ConfiguredXHTMLGenerator.IsMainEntry(entry, config))
            {
                return(config.Parts[0].IsEnabled && (!entry.ComplexFormEntryRefs.Any() || ConfiguredXHTMLGenerator.IsListItemSelectedForExport(config.Parts[0], entry)));
            }
            return(entry.PublishAsMinorEntry && config.Parts.Skip(1).Any(part => ConfiguredXHTMLGenerator.IsListItemSelectedForExport(part, entry)));
        }
        private void ExportConfiguredXhtml(string xhtmlPath, DictionaryConfigurationModel configuration, string exportType, IThreadedProgress progress)
        {
            int[] entriesToSave;
            var   publicationDecorator = ConfiguredXHTMLGenerator.GetPublicationDecoratorAndEntries(m_mediator, out entriesToSave, exportType);

            if (progress != null)
            {
                progress.Maximum = entriesToSave.Length;
            }
            ConfiguredXHTMLGenerator.SavePublishedHtmlWithStyles(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_mediator, xhtmlPath, progress);
        }
        public void GenerateLetterHeaderIfNeeded_GeneratesHeaderIfPreviousHeaderDoesNotMatch()
        {
            var entry = CreateInterestingEnglishReversalEntry();

            using (var XHTMLWriter = XmlWriter.Create(XHTMLStringBuilder))
            {
                // SUT
                var last = "A a";
                XHTMLWriter.WriteStartElement("TestElement");
                Assert.DoesNotThrow(() => ConfiguredXHTMLGenerator.GenerateLetterHeaderIfNeeded(entry, ref last, XHTMLWriter, DefaultSettings));
                XHTMLWriter.WriteEndElement();
                XHTMLWriter.Flush();
                const string letterHeaderToMatch = "//div[@class='letHead']/span[@class='letter' and @lang='en' and text()='R r']";
                AssertThatXmlIn.String(XHTMLStringBuilder.ToString()).HasSpecifiedNumberOfMatchesForXpath(letterHeaderToMatch, 1);
            }
        }
        public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfSense()
        {
            var mainRevEntryNode = PreparePrimaryEntryReferencesConfigSetup();
            var reversalEntry    = CreateInterestingEnglishReversalEntry("speechRevForm", "parol", "speech:gloss");
            var variantEntry     = reversalEntry.ReferringSenses.First().Owner as ILexEntry;
            var paroleEntry      = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech");

            CXGTests.CreateVariantForm(Cache, paroleEntry.SensesOS[0], variantEntry, "Spelling Variant");
            //SUT
            var          result       = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings);
            const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='sp. var. of']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1);
            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1);
            const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='speech']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1);
        }
Example #9
0
        private static IEnumerable <GeckoElement> FindMatchingSpans(ConfigurableDictionaryNode selectedNode, GeckoElement parent,
                                                                    ConfigurableDictionaryNode topLevelNode, FdoCache cache)
        {
            var elements     = new List <GeckoElement>();
            var desiredClass = CssGenerator.GetClassAttributeForConfig(selectedNode);

            if (ConfiguredXHTMLGenerator.IsCollectionNode(selectedNode, cache))
            {
                desiredClass = CssGenerator.GetClassAttributeForCollectionItem(selectedNode);
            }
            foreach (var span in parent.GetElementsByTagName("span"))
            {
                if (span.GetAttribute("class") != null && span.GetAttribute("class").Split(' ')[0] == desiredClass &&
                    DoesGeckoElementOriginateFromConfigNode(selectedNode, span, topLevelNode))
                {
                    elements.Add(span);
                }
            }
            return(elements);
        }
        public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry()
        {
            var mainRevEntryNode = PreparePrimaryEntryReferencesConfigSetup();
            var reversalEntry    = CreateInterestingEnglishReversalEntry("spokesmanRevForm", "porte-parole", "spokesman:gloss");
            var referringSense   = reversalEntry.ReferringSenses.First();
            var paroleEntry      = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech");

            paroleEntry.SummaryDefinition.SetAnalysisDefaultWritingSystem("summDefn");
            CXGTests.CreateComplexForm(Cache, paroleEntry, referringSense.Owner as ILexEntry, true);
            //SUT
            var          result        = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings);
            const string headwordXpath = referringSenseXpath + "/span[@class='headword']/span[@lang='fr']//a[text()='porte-parole']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1);
            const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='comp. of']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1);
            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1);
            const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='summDefn']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1);
        }
        public void GenerateXHTMLForEntry_ReversalStringGeneratesContent()
        {
            var formNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalForm",
                Label                 = "Form",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "fr"
                        }
                    },
                    DisplayWritingSystemAbbreviations = false
                }
            };
            var reversalNode = new ConfigurableDictionaryNode
            {
                Children = new List <ConfigurableDictionaryNode> {
                    formNode
                },
                FieldDescription = "ReversalIndexEntry"
            };

            CssGeneratorTests.PopulateFieldsForTesting(reversalNode);
            var rie           = CreateInterestingFrenchReversalEntry();
            var entryHeadWord = rie.ReferringSenses.First().Entry.HeadWord;

            //SUT
            var result = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(rie, reversalNode, null, DefaultSettings);
            var reversalFormDataPath = string.Format("/div[@class='reversalindexentry']/span[@class='reversalform']/span[text()='{0}']",
                                                     TsStringUtils.Compose(rie.LongName));
            var entryDataPath = string.Format("//span[text()='{0}']", entryHeadWord.get_NormalizedForm(FwNormalizationMode.knmNFC).Text);

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(reversalFormDataPath, 1);
            AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath);
        }
        public void GenerateXHTMLForEntry_PrimaryEntryReferences_Ordered()
        {
            var mainRevEntryNode = PreparePrimaryEntryReferencesConfigSetup();

            var reversalEntry = CreateInterestingEnglishReversalEntry();
            var primaryEntry  = reversalEntry.ReferringSenses.First().Entry;
            var refer1        = CXGTests.CreateInterestingLexEntry(Cache, "Component Entry", "CompEntry Sense");
            var refer2        = CXGTests.CreateInterestingLexEntry(Cache, "Variant Entry");
            var refer3        = CXGTests.CreateInterestingLexEntry(Cache, "CompSense Entry", "Component Sense").SensesOS.First();
            var refer4        = CXGTests.CreateInterestingLexEntry(Cache, "Invariant Entry");
            var refer5        = CXGTests.CreateInterestingLexEntry(Cache, "Variante Entrie");

            using (CXGTests.CreateComplexForm(Cache, refer3, primaryEntry, new Guid("00000000-0000-0000-cccc-000000000000"), true))             // Compound
                using (CXGTests.CreateVariantForm(Cache, refer2, primaryEntry, new Guid("00000000-0000-0000-bbbb-000000000000"), "Free Variant"))
                    using (CXGTests.CreateComplexForm(Cache, refer1, primaryEntry, new Guid("00000000-0000-0000-aaaa-000000000000"), true))     // Compound
                        using (CXGTests.CreateVariantForm(Cache, refer4, primaryEntry, new Guid("00000000-0000-0000-dddd-000000000000"), null)) // no Variant Type
                            using (CXGTests.CreateVariantForm(Cache, refer5, primaryEntry, new Guid("00000000-0000-0000-eeee-000000000000"), "Spelling Variant"))
                            {
                                var result   = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); // SUT
                                var assertIt = AssertThatXmlIn.String(result);
                                assertIt.HasSpecifiedNumberOfMatchesForXpath(entryRefTypeXpath, 3);                                                    // should be one Complex Form Type and two Variant Types.
                                const string headwordBit = "/span[@class='headword']/span[@lang='fr']/a[text()='{1}']";
                                const string entryRefWithSiblingXpath = entryRefsXpath + "/span[@class='mainentryref' and preceding-sibling::";
                                const string typeAndHeadwordXpath     = entryRefWithSiblingXpath
                                                                        + entryRefTypeBit + "/span[@class='abbreviation']/span[@lang='en' and text()='{0}']]" + primaryLexemeBit + headwordBit;
                                var adjacentHeadwordXpath = entryRefWithSiblingXpath
                                                            + "span[@class='mainentryref']" + primaryLexemeBit + headwordBit.Replace("{1}", "{0}") + "]" + primaryLexemeBit + headwordBit;
                                // check for proper headings on each referenced headword
                                assertIt.HasSpecifiedNumberOfMatchesForXpath(string.Format(typeAndHeadwordXpath, "comp. of", "Component Entry"), 1);
                                assertIt.HasSpecifiedNumberOfMatchesForXpath(string.Format(adjacentHeadwordXpath, "Component Entry", "CompSense Entry"), 1); // ordered within heading
                                assertIt.HasSpecifiedNumberOfMatchesForXpath(string.Format(typeAndHeadwordXpath, "fr. var. of", "Variant Entry"), 1);
                                assertIt.HasSpecifiedNumberOfMatchesForXpath(string.Format(typeAndHeadwordXpath, "sp. var. of", "Variante Entrie"), 1);
                                // verify there is no heading on the typeless variant
                                assertIt.HasNoMatchForXpath(string.Format(entryRefWithSiblingXpath + "span]" + primaryLexemeBit + headwordBit, null, "Invariant Entry"),
                                                            message: "Invariant Entry is the only typeless entry ref; it should not have any preceding siblings (Types or other Entry Refs)");
                            }
        }
        public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult()
        {
            var reversalFormNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalForm",
                DictionaryNodeOptions = CXGTests.GetWsOptionsForLanguages(new [] { "en" }),
                Label = "Reversal Form"
            };
            var mainEntryNode = new ConfigurableDictionaryNode
            {
                Children = new List <ConfigurableDictionaryNode> {
                    reversalFormNode
                },
                FieldDescription = "ReversalIndexEntry"
            };

            CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode);
            var entry = CreateInterestingEnglishReversalEntry();
            //SUT
            string       result        = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings);
            const string frenchLexForm = "/div[@class='reversalindexentry']/span[@class='reversalform']/span[@lang='en' and text()='ReversalForm']";

            AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1);
        }
        public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand()
        {
            var defOrGlossNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "DefinitionOrGloss",
                Between               = " ",
                After                 = " ",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    DisplayWritingSystemAbbreviations = false,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "reversal"
                        }
                    }
                },
                Children = new List <ConfigurableDictionaryNode>()
            };
            var catInfoNode = new ConfigurableDictionaryNode
            {
                FieldDescription     = "MLPartOfSpeech",
                CSSClassNameOverride = "partofspeech",
                Between = " ",
                After   = " ",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    DisplayWritingSystemAbbreviations = false,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "reversal"
                        }
                    }
                },
                Children = new List <ConfigurableDictionaryNode>()
            };
            var gramInfoNode = new ConfigurableDictionaryNode
            {
                FieldDescription     = "MorphoSyntaxAnalysisRA",
                CSSClassNameOverride = "morphosyntaxanalysis",
                After    = " ",
                Style    = "Dictionary-Contrasting",
                Children = new List <ConfigurableDictionaryNode> {
                    catInfoNode
                }
            };
            var headwordNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalName",
                Between               = " ",
                After                 = " ",
                StyleType             = ConfigurableDictionaryNode.StyleTypes.Character,
                Style                 = "Reversal-Vernacular",
                CSSClassNameOverride  = "headword",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Vernacular,
                    DisplayWritingSystemAbbreviations = false,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "vernacular"
                        }
                    }
                },
                Children = new List <ConfigurableDictionaryNode>()
            };
            var vernFormNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReferringSenses",
                Between               = "; ",
                After                 = " ",
                DictionaryNodeOptions = new DictionaryNodeSenseOptions
                {
                    NumberStyle                  = "Dictionary-SenseNumber",
                    AfterNumber                  = ") ",
                    NumberingStyle               = "%d",
                    NumberEvenASingleSense       = false,
                    ShowSharedGrammarInfoFirst   = true,
                    DisplayEachSenseInAParagraph = false
                },
                Children = new List <ConfigurableDictionaryNode> {
                    headwordNode, gramInfoNode, defOrGlossNode
                }
            };
            var formNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalForm",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    DisplayWritingSystemAbbreviations = false,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "reversal"
                        }
                    }
                },
                Children = new List <ConfigurableDictionaryNode>()
            };
            var mainEntryNode = new ConfigurableDictionaryNode
            {
                FieldDescription = "ReversalIndexEntry",
                Children         = new List <ConfigurableDictionaryNode> {
                    formNode, vernFormNode
                }
            };

            CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode);
            var settings = DefaultSettings;

            var noun     = CreatePartOfSpeech("noun", "n");
            var verb     = CreatePartOfSpeech("verb", "v");
            var revIndex = Cache.ServiceLocator.GetInstance <IReversalIndexRepository>().FindOrCreateIndexForWs(m_wsEn);

            var entry1 = CXGTests.CreateInterestingLexEntry(Cache);

            entry1.CitationForm.set_String(m_wsFr, "premier");
            entry1.SensesOS.First().Gloss.set_String(m_wsEn, "first");
            var msa1 = Cache.ServiceLocator.GetInstance <IMoStemMsaFactory>().Create();

            entry1.MorphoSyntaxAnalysesOC.Add(msa1);
            msa1.PartOfSpeechRA = noun;
            entry1.SensesOS.First().MorphoSyntaxAnalysisRA = msa1;

            var entry2 = CXGTests.CreateInterestingLexEntry(Cache);

            entry2.CitationForm.set_String(m_wsFr, "primary");
            entry2.SensesOS.First().Gloss.set_String(m_wsEn, "first");
            var msa2 = Cache.ServiceLocator.GetInstance <IMoStemMsaFactory>().Create();

            entry2.MorphoSyntaxAnalysesOC.Add(msa2);
            msa2.PartOfSpeechRA = noun;
            entry2.SensesOS.First().MorphoSyntaxAnalysisRA = msa2;

            var testEntry = revIndex.FindOrCreateReversalEntry("first");

            entry1.SensesOS.First().ReversalEntriesRC.Add(testEntry);
            entry2.SensesOS.First().ReversalEntriesRC.Add(testEntry);

            var xhtml = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings);

            // check that the sense gram info appears once before the rest of the sense information.
            Assert.IsNotNullOrEmpty(xhtml);
            const string sharedGramInfo   = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en' and text()='n']";
            const string separateGramInfo = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en']";

            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfo, 1);
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(separateGramInfo, 0);

            var msa2a = Cache.ServiceLocator.GetInstance <IMoStemMsaFactory>().Create();

            entry2.MorphoSyntaxAnalysesOC.Add(msa2a);
            msa2a.PartOfSpeechRA = verb;
            entry2.SensesOS.First().MorphoSyntaxAnalysisRA = msa2a;
            xhtml = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings);
            // check that the sense gram info appears separately for both senses.
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfo, 0);
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(separateGramInfo, 2);
        }
        public void GenerateXHTMLForEntry_VernacularFormWithSubSensesinReversalSubEntry()
        {
            var headwordNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalName",
                Label                 = "Headword",
                CSSClassNameOverride  = "headword",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Vernacular,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "vernacular"
                        }
                    }
                }
            };
            var wsOpts = new DictionaryNodeWritingSystemOptions
            {
                WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                {
                    new DictionaryNodeListOptions.DictionaryNodeOption {
                        Id = "reversal"
                    }
                }
            };
            var glossNode = new ConfigurableDictionaryNode {
                FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts
            };
            var formNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReferringSenses",
                DictionaryNodeOptions = new DictionaryNodeSenseOptions {
                    NumberingStyle = "%d", NumberEvenASingleSense = true
                },
                Children = new List <ConfigurableDictionaryNode> {
                    headwordNode, glossNode
                }
            };
            var subEntryNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "SubentriesOS",
                CSSClassNameOverride  = "subentries",
                DictionaryNodeOptions = new DictionaryNodeListAndParaOptions {
                    DisplayEachInAParagraph = true
                },
                Children = new List <ConfigurableDictionaryNode> {
                    formNode
                }
            };
            var mainEntryNode = new ConfigurableDictionaryNode
            {
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "en"
                        }
                    },
                    DisplayWritingSystemAbbreviations = false
                },
                FieldDescription = "ReversalIndexEntry",
                Children         = new List <ConfigurableDictionaryNode> {
                    subEntryNode
                }
            };

            CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode);
            var testEntry = CreateInterestingEnglishSubReversalEntryWithSubSense();
            //SUT
            var          xhtml          = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings);
            const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense']/span[@class='headword']/span/span/a[text()='1.1']";

            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1);
        }
        public void GenerateXHTMLForEntry_VernacularFormWithSubSenses()
        {
            var headwordNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalName",
                CSSClassNameOverride  = "headword",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Vernacular,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "vernacular"
                        }
                    }
                }
            };
            var wsOpts = new DictionaryNodeWritingSystemOptions
            {
                WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                {
                    new DictionaryNodeListOptions.DictionaryNodeOption {
                        Id = "reversal"
                    }
                }
            };
            var glossNode = new ConfigurableDictionaryNode {
                FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts
            };
            var formNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReferringSenses",
                DictionaryNodeOptions = new DictionaryNodeSenseOptions {
                    NumberingStyle = "%d"
                },
                Children = new List <ConfigurableDictionaryNode> {
                    headwordNode, glossNode
                }
            };
            var mainEntryNode = new ConfigurableDictionaryNode
            {
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "en"
                        }
                    },
                    DisplayWritingSystemAbbreviations = false
                },
                FieldDescription = "ReversalIndexEntry",
                Children         = new List <ConfigurableDictionaryNode> {
                    formNode
                }
            };

            CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode);
            var testEntry = CreateInterestingEnglishReversalEntry();

            AddSingleSubSenseToSense(testEntry, "second gloss", m_wsEn, Cache);
            //SUT
            var xhtml = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings);
            // REVIEW (Hasso) 2016.03: we should probably do something about the leading space in the Sense Number Run, as it is currently in addition to the "between" space.
            const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense']/span[@class='headword']/span/span/a[text()='1.1']";

            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1);
        }
        public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleReferringSenses()
        {
            var headwordNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReversalName",
                CSSClassNameOverride  = "headword",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Vernacular,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "vernacular"
                        }
                    }
                }
            };
            var glossNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "Gloss",
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "reversal"
                        }
                    }
                }
            };
            var formNode = new ConfigurableDictionaryNode
            {
                FieldDescription      = "ReferringSenses",
                DictionaryNodeOptions = new DictionaryNodeSenseOptions {
                    NumberingStyle = "%d"
                },
                Children = new List <ConfigurableDictionaryNode> {
                    headwordNode, glossNode
                }
            };
            var mainEntryNode = new ConfigurableDictionaryNode
            {
                DictionaryNodeOptions = new DictionaryNodeWritingSystemOptions
                {
                    WsType  = DictionaryNodeWritingSystemOptions.WritingSystemType.Reversal,
                    Options = new List <DictionaryNodeListOptions.DictionaryNodeOption>
                    {
                        new DictionaryNodeListOptions.DictionaryNodeOption {
                            Id = "en"
                        }
                    },
                    DisplayWritingSystemAbbreviations = false
                },
                FieldDescription = "ReversalIndexEntry",
                Children         = new List <ConfigurableDictionaryNode> {
                    formNode
                }
            };

            CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode);
            var testEntry = CreateInterestingEnglishReversalEntry();

            AddSenseToReversaEntry(testEntry, "second gloss", m_wsEn, Cache);
            //SUT
            var          xhtml          = ConfiguredXHTMLGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings);
            const string senseNumberOne = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']";
            const string senseNumberTwo = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']";

            //This assert is dependent on the specific entry data created in CreateInterestingEnglishReversalEntry
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1);
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1);

            const string headwordOne = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='1']]/span[@lang='fr' and a[text()='Citation']]";
            const string headwordTwo = "/div[@class='reversalindexentry']/span[@class='referringsenses']/span[@class='sensecontent']/span[@class='referringsense']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='2']]/span[@lang='fr' and a[text()='Citation']]";

            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordOne, 1);
            AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordTwo, 1);
        }