From 846c9842f0694521498c166f223defdff1b1d7b2 Mon Sep 17 00:00:00 2001 From: Hasso Date: Wed, 9 Aug 2023 10:10:28 -0500 Subject: [PATCH 001/415] Bump version to 9.1.25 Change-Id: I440738107a843fbb8af396c367f5249065f26658 --- Src/MasterVersionInfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/MasterVersionInfo.txt b/Src/MasterVersionInfo.txt index 7a3190bad8..2981e3d224 100644 --- a/Src/MasterVersionInfo.txt +++ b/Src/MasterVersionInfo.txt @@ -1,4 +1,4 @@ FWMAJOR=9 FWMINOR=1 -FWREVISION=24 +FWREVISION=25 FWBETAVERSION= From 6d705480d12f7d7fe6be85c5367fd47b3fa78af9 Mon Sep 17 00:00:00 2001 From: Hasso Date: Thu, 10 Aug 2023 10:22:33 -0500 Subject: [PATCH 002/415] LT-21577: clarify installer metadata for testers FW doesn't know when FB was built. We could find out, but only testers see this information. Change-Id: Ib9615095305d6042404691adf588bda329f4bfbf --- Src/Common/FwUtils/FwUpdateChooserDlg.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Src/Common/FwUtils/FwUpdateChooserDlg.cs b/Src/Common/FwUtils/FwUpdateChooserDlg.cs index 61166e3b00..427d31a77f 100644 --- a/Src/Common/FwUtils/FwUpdateChooserDlg.cs +++ b/Src/Common/FwUtils/FwUpdateChooserDlg.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2021 SIL International +// Copyright (c) 2021-2023 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -21,11 +21,12 @@ public FwUpdateChooserDlg(FwUpdate current, IEnumerable available) : t // FwUpdate.ToString does not always include the base build number (which could be informative) but does include the installer type, // even though we have no way of knowing what that was for the current version; define our own relevant parts here. tbInstructions.Text = - $"The following installers are available. You currently have {current.Version}_b{current.BaseBuild} built {current.Date} installed. " + - "Additional patches may be available on the listed base (online and offline) installers. " + - "To download an update, select it and click Download; another dialog will appear when the download is complete. " + - $"To install base installers, you may need to uninstall FLEx and then install manually from {FwDirectoryFinder.DownloadedUpdates}."; + $"The following installers are available. You currently have {current.Version}_b{current.BaseBuild} built {current.Date:yyyy-MM-dd} installed" + + " (date is invalid for FLEx Bridge). Additional patches may be available on the listed base (online and offline) installers. " + + "To download an update, double-click, or select it and click Download; another dialog will appear when the download is complete. " + + $"To install FieldWorks base installers, you may need to uninstall FLEx and then install manually from {FwDirectoryFinder.DownloadedUpdates}."; + // ReSharper disable once CoVariantArrayConversion lbChooseVersion.Items.AddRange(available.ToArray()); } From 031f8c2f545296b1121a3a5a0ba62cbe21aa499e Mon Sep 17 00:00:00 2001 From: Hasso Date: Thu, 7 Sep 2023 11:36:51 -0500 Subject: [PATCH 003/415] LT-21603: Remove references to deprecated SyncMsg and a few other TE relics. Since the removal of TE, SyncMsg.ksyncStyle is the only SyncMsg ever sent. Change-Id: I27150fbf9d3a6ee01a16d69723ce652799f84440 --- FW.sln.DotSettings | 1 + Src/Common/Controls/FwControls/Persistence.cs | 3 +- .../FrameworkTests/FrameworkTests.csproj | 5 +- .../Framework/FrameworkTests/FwAppTests.cs | 243 ------------------ Src/Common/Framework/FwApp.cs | 192 +------------- Src/Common/Framework/IFwMainWnd.cs | 27 +- Src/Common/RootSite/IApp.cs | 25 +- Src/Common/RootSite/UndoActions.cs | 67 ----- Src/FwCoreDlgs/FwStylesDlg.cs | 6 +- .../LexTextControls/ConfigureHomographDlg.cs | 4 +- .../DataNotebook/ImportCharMappingDlg.cs | 2 +- Src/LexText/LexTextDll/LexTextApp.cs | 21 -- Src/ParatextImport/ParatextImportManager.cs | 2 +- Src/xWorks/FwXApp.cs | 12 - Src/xWorks/FwXWindow.cs | 67 +---- 15 files changed, 29 insertions(+), 648 deletions(-) delete mode 100644 Src/Common/Framework/FrameworkTests/FwAppTests.cs diff --git a/FW.sln.DotSettings b/FW.sln.DotSettings index e61ba2334c..b32adec6bf 100644 --- a/FW.sln.DotSettings +++ b/FW.sln.DotSettings @@ -303,6 +303,7 @@ True True True + True True True True diff --git a/Src/Common/Controls/FwControls/Persistence.cs b/Src/Common/Controls/FwControls/Persistence.cs index 5db08e9fe9..1bd089d85b 100644 --- a/Src/Common/Controls/FwControls/Persistence.cs +++ b/Src/Common/Controls/FwControls/Persistence.cs @@ -292,9 +292,10 @@ public bool EnableSaveWindowSettings /// /// Returns a Rectangle representing the position and size of the window in its /// normal (non-minimized, non-maximized) state. + /// Internal for tests. /// /// ------------------------------------------------------------------------------------ - public Rectangle NormalStateDesktopBounds + internal Rectangle NormalStateDesktopBounds { get { diff --git a/Src/Common/Framework/FrameworkTests/FrameworkTests.csproj b/Src/Common/Framework/FrameworkTests/FrameworkTests.csproj index 9ed682827f..c6884e7029 100644 --- a/Src/Common/Framework/FrameworkTests/FrameworkTests.csproj +++ b/Src/Common/Framework/FrameworkTests/FrameworkTests.csproj @@ -93,7 +93,7 @@ prompt AllRules.ruleset AnyCPU - + ..\..\..\..\Output\Debug\ false @@ -234,9 +234,6 @@ AssemblyInfoForTests.cs - - Code - Code diff --git a/Src/Common/Framework/FrameworkTests/FwAppTests.cs b/Src/Common/Framework/FrameworkTests/FwAppTests.cs deleted file mode 100644 index 542ecc52c8..0000000000 --- a/Src/Common/Framework/FrameworkTests/FwAppTests.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2004-2013 SIL International -// This software is licensed under the LGPL, version 2.1 or later -// (http://www.gnu.org/licenses/lgpl-2.1.html) -// -// File: FwAppTests.cs -// Responsibility: TE Team - -using System; -using System.Diagnostics; -using System.Windows.Forms; - -using NUnit.Framework; -using SIL.FieldWorks.Common.ViewsInterfaces; -using SIL.FieldWorks.Common.RootSites; -using SIL.LCModel.Utils; -using SIL.LCModel; - -namespace SIL.FieldWorks.Common.Framework -{ -#if WANTTESTPORT // (Common) FWR-251 Tests need to be updated for new synchronization approach - /// ---------------------------------------------------------------------------------------- - /// - /// Tests the FwApp class - /// - /// ---------------------------------------------------------------------------------------- - [TestFixture] - public class FwAppTests : MemoryOnlyBackendProviderTestBase - { - private DynamicMock m_mainWnd; - private FwApp m_app; - - /// ------------------------------------------------------------------------------------ - /// - /// Initialize tests - /// - /// ------------------------------------------------------------------------------------ - [SetUp] - public override void TestSetup() - { - base.TestSetup(); - - m_mainWnd = new DynamicMock(typeof(IFwMainWnd)); - m_mainWnd.SetupResult("Cache", Cache); - m_app = new DummyFwApp(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Resets values so tests are independent of each other - /// - /// ------------------------------------------------------------------------------------ - [TearDown] - public override void TestTearDown() - { - if (m_app != null) - { - m_app.Dispose(); // Ensure cache disposed and WSF shutdown. - m_app = null; - } - m_mainWnd = null; - - base.TestTearDown(); - } - - #region Test for Synchronize - /// ------------------------------------------------------------------------------------ - /// - /// Tests that (Pre)Synchronize gets called - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void Synchronize() - { - m_mainWnd.Expect("PreSynchronize", new IsAnything()); - m_mainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - // This should call (Pre)Synchronize - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - - m_mainWnd.Verify(); - } - #endregion - - #region Tests for Suppress/ResumeSynchronize methods - /// ------------------------------------------------------------------------------------ - /// - /// Tests Suppress synchronize method - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void SuppressSynchronize() - { - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - // This should call nothing - m_app.SuppressSynchronize(Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - - m_mainWnd.Verify(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Tests Resume method - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void ResumeSynchronize() - { - m_mainWnd.Expect("PreSynchronize", new IsAnything()); - m_mainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - m_app.SuppressSynchronize(Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - // This should call (Pre)Synchronize - m_app.ResumeSynchronize(Cache); - - m_mainWnd.Verify(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Tests that suppress/resume synchronize stores identical messages only once, - /// and also that the message goes to all main windows, - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void MultiMessageSynchronize_IdenticalMessages() - { - m_mainWnd.Expect("PreSynchronize", new IsAnything()); - m_mainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - DynamicMock otherMainWnd = new DynamicMock(typeof(IFwMainWnd)); - otherMainWnd.SetupResult("Cache", Cache); - otherMainWnd.Expect("PreSynchronize", new IsAnything()); - otherMainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)otherMainWnd.MockInstance); - - m_app.SuppressSynchronize(Cache); - // we expect that the identical message will be discarded - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - - // This should call (Pre)Synchronize only once on each window - m_app.ResumeSynchronize(Cache); - - m_mainWnd.Verify(); - otherMainWnd.Verify(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Tests that suppress/resume synchronize stores message with different cache - /// multiple times - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void MultiMessageSynchronize_DifferentCache() - { - m_mainWnd.Expect("PreSynchronize", new IsAnything()); - m_mainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - LcmCache differentCache = LcmCache.CreateCache(FDOBackendProviderType.kMemoryOnly, BackendBulkLoadDomain.All, null); - try - { - DynamicMock otherMainWnd = new DynamicMock(typeof(IFwMainWnd)); - otherMainWnd.SetupResult("Cache", differentCache); - otherMainWnd.Expect("PreSynchronize", new IsAnything()); - otherMainWnd.ExpectAndReturn("Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)otherMainWnd.MockInstance); - - m_app.SuppressSynchronize(Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, differentCache); - - // This should call (Pre)Synchronize once for each main window - m_app.ResumeSynchronize(Cache); - - m_mainWnd.Verify(); - otherMainWnd.Verify(); - } - finally - { - differentCache.Dispose(); - } - } - - /// ------------------------------------------------------------------------------------ - /// - /// Tests that suppress/resume synchronize stores different messages - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void MultiMessageSynchronize_DifferentMessages() - { - m_mainWnd.ExpectAndReturn(2, "PreSynchronize", true, new IsAnything()); - m_mainWnd.ExpectAndReturn(2, "Synchronize", true, new IsAnything()); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - m_app.SuppressSynchronize(Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - m_app.Synchronize(SyncMsg.ksyncDelPss, Cache); - - // This should call (Pre)Synchronize twice - m_app.ResumeSynchronize(Cache); - - m_mainWnd.Verify(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Tests that suppress/resume calls synchronize in expected order - /// - /// ------------------------------------------------------------------------------------ - [Test] - public void ResumeCallsMessagesInExpectedOrder() - { - m_mainWnd.ExpectAndReturn(3, "PreSynchronize", true, new IsAnything()); - m_mainWnd.ExpectAndReturn("Synchronize", true, SyncMsg.ksyncSimpleEdit); - m_mainWnd.ExpectAndReturn("Synchronize", true, SyncMsg.ksyncScriptureNewBook); - // Even though UndoRedo is the first synch message to be sent, it should be processed last. - m_mainWnd.ExpectAndReturn("Synchronize", true, SyncMsg.ksyncUndoRedo); - m_app.MainWindows.Add((IFwMainWnd)m_mainWnd.MockInstance); - - m_app.SuppressSynchronize(Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - m_app.Synchronize(SyncMsg.ksyncSimpleEdit, Cache); - m_app.Synchronize(SyncMsg.ksyncScriptureNewBook, Cache); - m_app.Synchronize(SyncMsg.ksyncUndoRedo, Cache); - - // This should call (Pre)Synchronize three times - m_app.ResumeSynchronize(Cache); - - m_mainWnd.Verify(); - } - #endregion - } -#endif -} diff --git a/Src/Common/Framework/FwApp.cs b/Src/Common/Framework/FwApp.cs index 0ed0d636cc..0fe7622f1f 100644 --- a/Src/Common/Framework/FwApp.cs +++ b/Src/Common/Framework/FwApp.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2002-2017 SIL International +// Copyright (c) 2002-2023 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -144,22 +144,6 @@ public enum ResourceStringType public abstract class FwApp : IApp, ISettings, IDisposable, IHelpTopicProvider, IMessageFilter, IFeedbackInfoProvider, IProjectSpecificSettingsKeyProvider { - #region SuppressedCacheInfo class - /// ------------------------------------------------------------------------------------ - /// - /// Helper class that contains queued SyncMsgs and a reference count for - /// Suppress/ResumeSynchronize. - /// - /// ------------------------------------------------------------------------------------ - private class SuppressedCacheInfo - { - /// Reference count - public int Count = 1; - /// SyncMsg queue - public Queue Queue = new Queue(); - } - #endregion - #region Member variables /// @@ -198,7 +182,6 @@ private class SuppressedCacheInfo protected DebugProcs m_debugProcs; #endif private FwRegistrySettings m_registrySettings; - private SuppressedCacheInfo m_suppressedCacheInfo; /// /// null means that we are not suppressing view refreshes. /// True means we're suppressing and we need to do a refresh when finished. @@ -358,8 +341,6 @@ public void InitAndShowMainWindow(Form fwMainWindow, Form wndCopyFrom) if (wndCopyFrom != null) { AdjustNewWindowPosition(fwMainWindow, wndCopyFrom); - // TODO BryanW: see AfMdiMainWnd::CmdWndNew() for other items that need to be - // coordinated } else if (fwMainWindow.WindowState != FormWindowState.Maximized) { @@ -403,10 +384,6 @@ protected void AdjustNewWindowPosition(Form wndNew, Form wndCopyFrom) // Here we subtract twice the caption height, which with the offset below insets it all around. rcNewWnd.Width -= SystemInformation.CaptionHeight * 2; rcNewWnd.Height -= SystemInformation.CaptionHeight * 2; - // JohnT: this old approach fails if the old window's position has never been - // persisted. NormalStateDesktopBounds crashes, not finding anything in the - // property table. - // rcNewWnd = ((IFwMainWnd)wndCopyFrom).NormalStateDesktopBounds; } //Offset right and down @@ -518,7 +495,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { - UpdateAppRuntimeCounter(); + UpdateAppRuntimeCounter(); Logger.WriteEvent("Disposing app: " + GetType().Name); RegistrySettings.FirstTimeAppHasBeenRun = false; @@ -565,7 +542,6 @@ protected virtual void Dispose(bool disposing) m_registrySettings = null; m_findPattern = null; m_findReplaceDlg = null; - m_suppressedCacheInfo = null; m_refreshView = null; PictureHolder = null; #if DEBUG @@ -1453,170 +1429,16 @@ public bool ShowFindReplaceDialog(bool fReplace, RootSite rootsite) #endregion #region Synchronization methods - /// ------------------------------------------------------------------------------------ - /// - /// Suppress execution of all synchronize messages and store them in a queue instead. - /// - /// ------------------------------------------------------------------------------------ - public void SuppressSynchronize() - { - CheckDisposed(); - - if (m_suppressedCacheInfo != null) - m_suppressedCacheInfo.Count++; // Nested call - else - m_suppressedCacheInfo = new SuppressedCacheInfo(); - } - - /// ------------------------------------------------------------------------------------ /// - /// Resume execution of synchronize messages. If there are any messages in the queue - /// execute them now. + /// Cycle through the applications main windows and synchronize them with database changes. /// - /// ------------------------------------------------------------------------------------ - public void ResumeSynchronize() + public virtual void Synchronize() { CheckDisposed(); - if (m_suppressedCacheInfo == null) - return; // Nothing to do - - m_suppressedCacheInfo.Count--; - if (m_suppressedCacheInfo.Count > 0) - return; // Still nested - - BeginUpdate(); - Queue messages = m_suppressedCacheInfo.Queue; - m_suppressedCacheInfo = null; - - bool fProcessUndoRedoAfter = false; - SyncMsg savedUndoRedo = SyncMsg.ksyncFullRefresh; // Arbitrary - foreach (SyncMsg synchMsg in messages) + foreach (var wnd in MainWindows) { - if (synchMsg == SyncMsg.ksyncUndoRedo) - { - // we must process this synch message after all the others - fProcessUndoRedoAfter = true; - savedUndoRedo = synchMsg; - continue; - } - // Do the synch - if (!Synchronize(synchMsg)) - { - fProcessUndoRedoAfter = false; // Refresh already done, final UndoRedo unnecessary - break; // One resulted in Refresh everything, ignore other synch msgs. - } - } - if (fProcessUndoRedoAfter) - Synchronize(savedUndoRedo); - - // NOTE: This code may present a race condition, because there is a slight - // possibility that a sync message can come to the App at - // this point and then get cleared from the syncMessages list and never get run. - EndUpdate(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Suppress all calls to until - /// is called. - /// - /// Used by to do only one refresh of the - /// view. - /// ------------------------------------------------------------------------------------ - private void BeginUpdate() - { - CheckDisposed(); - - Debug.Assert(m_refreshView == null, "Nested BeginUpdate"); - m_refreshView = false; - } - - /// ------------------------------------------------------------------------------------ - /// - /// Do a if it was called at least once after - /// - /// - /// ------------------------------------------------------------------------------------ - private void EndUpdate() - { - CheckDisposed(); - - Debug.Assert(m_refreshView != null, "EndUpdate called without BeginUpdate"); - - bool needRefresh = (bool)m_refreshView; - m_refreshView = null; // Make sure we don't try suppress the following RefreshAllViews() - if (needRefresh) - RefreshAllViews(); - } - - /// ------------------------------------------------------------------------------------ - /// - /// Cycle through the applications main windows and synchronize them with database - /// changes. - /// - /// synchronization information record - /// false if a RefreshAllViews was performed or presync failed; this suppresses - /// subsequent sync messages. True to continue processing. - /// ------------------------------------------------------------------------------------ - public virtual bool Synchronize(SyncMsg sync) - { - CheckDisposed(); - - if (m_suppressedCacheInfo != null) - { - Queue messages = m_suppressedCacheInfo.Queue; - if (!messages.Contains(sync)) - messages.Enqueue(sync); - return true; - } - - if (sync == SyncMsg.ksyncFullRefresh) - { - RefreshAllViews(); - return false; - } - - foreach (IFwMainWnd wnd in MainWindows) - wnd.PreSynchronize(sync); - - if (sync == SyncMsg.ksyncWs) - { - // REVIEW TeTeam: AfLpInfo::Synchronize calls AfLpInfo::FullRefresh, which - // clears the cache, loads the styles, loads ws and updates wsf, load project - // basics, updates LinkedFiles root, load overlays and refreshes possibility - // lists. I don't think we need to do any of these here. - RefreshAllViews(); - return false; - } - - foreach (IFwMainWnd wnd in MainWindows) - { - if (!wnd.Synchronize(sync)) - { - // The window itself was not able to process the message successfully; - // play safe and refresh everything - RefreshAllViews(); - return false; - } - } - - return true; - } - - /// ------------------------------------------------------------------------------------ - /// - /// To participate in automatic synchronization from the database (calling SyncFromDb - /// in a useful manner) and application must override this, providing a unique Guid. - /// Typically this is the Guid defined by a static AppGuid method. - /// - /// ------------------------------------------------------------------------------------ - public virtual Guid SyncGuid - { - get - { - CheckDisposed(); - return Guid.Empty; + wnd.Synchronize(); } } #endregion @@ -1904,7 +1726,7 @@ private static void CollectMovableFilesFromFolder(ICmFolder folder, } else { - //if the file does not exist in the destination LinkeFiles location then copy/move it. + //if the file does not exist in the destination LinkedFiles location then copy/move it. rgFilesToMove.Add(sFilepath); } } diff --git a/Src/Common/Framework/IFwMainWnd.cs b/Src/Common/Framework/IFwMainWnd.cs index cc383bed01..00a0fb90cf 100644 --- a/Src/Common/Framework/IFwMainWnd.cs +++ b/Src/Common/Framework/IFwMainWnd.cs @@ -44,7 +44,7 @@ public interface IFwMainWnd : IxWindow /// ------------------------------------------------------------------------------------ /// - /// Create the client windows and add correspnding stuff to the sidebar, View menu, + /// Create the client windows and add corresponding stuff to the sidebar, View menu, /// etc. Subclasses must override this. /// /// ------------------------------------------------------------------------------------ @@ -52,33 +52,12 @@ public interface IFwMainWnd : IxWindow /// ------------------------------------------------------------------------------------ /// - /// Gets a Rectangle representing the position and size of the window in its - /// normal (non-minimized, non-maximized) state. - /// - /// ------------------------------------------------------------------------------------ - Rectangle NormalStateDesktopBounds - { - get; - } - - /// ------------------------------------------------------------------------------------ - /// - /// Called just before a window syncronizes it's views with DB changes (e.g. when an - /// undo or redo command is issued). - /// - /// syncronization message - /// ------------------------------------------------------------------------------------ - void PreSynchronize(SyncMsg sync); - - /// ------------------------------------------------------------------------------------ - /// - /// Called when a window syncronizes it's views with DB changes (e.g. when an undo or + /// Called when a window synchronizes it's views with DB changes (e.g. when an undo or /// redo command is issued). /// - /// syncronization message /// true if successful; false results in RefreshAllWindows. /// ------------------------------------------------------------------------------------ - bool Synchronize(SyncMsg sync); + void Synchronize(); /// ------------------------------------------------------------------------------------ /// diff --git a/Src/Common/RootSite/IApp.cs b/Src/Common/RootSite/IApp.cs index 2c9913dfc6..65de859c08 100644 --- a/Src/Common/RootSite/IApp.cs +++ b/Src/Common/RootSite/IApp.cs @@ -9,19 +9,15 @@ // // -using System; using System.Windows.Forms; using Microsoft.Win32; using SIL.FieldWorks.Common.FwUtils; -using SIL.LCModel; namespace SIL.FieldWorks.Common.RootSites { /// /// Interface for application. - /// TODO: The only place this interface is used in RootSite is in SyncUndoAction. The only - /// place that SyncUndoAction is used is in FrameWork. This means that IApp could be moved - /// to a better place. + /// TODO: This interface is not used in RootSite. This means that IApp could be moved to a better place. /// public interface IApp { @@ -83,25 +79,10 @@ public interface IApp /// void RestartSpellChecking(); - /// ------------------------------------------------------------------------------------ /// - /// Cycle through the applications main windows and synchronize them with database - /// changes. + /// Cycle through the applications main windows and synchronize them with database changes. /// - /// synchronization information record - /// true to continue processing; set to false to prevent - /// processing of subsequent sync messages. - /// ------------------------------------------------------------------------------------ - bool Synchronize(SyncMsg sync); - - /// ------------------------------------------------------------------------------------ - /// - /// To participate in automatic synchronization from the database (calling SyncFromDb - /// in a useful manner) and application must override this, providing a unique Guid. - /// Typically this is the Guid defined by a static AppGuid method. - /// - /// ------------------------------------------------------------------------------------ - Guid SyncGuid { get; } + void Synchronize(); /// ----------------------------------------------------------------------------------- /// diff --git a/Src/Common/RootSite/UndoActions.cs b/Src/Common/RootSite/UndoActions.cs index 02b40ee166..a6f4f1dda7 100644 --- a/Src/Common/RootSite/UndoActions.cs +++ b/Src/Common/RootSite/UndoActions.cs @@ -9,7 +9,6 @@ using System.Windows.Forms; using SIL.FieldWorks.Common.ViewsInterfaces; -using SIL.LCModel; using SIL.LCModel.Infrastructure; using SIL.Reporting; @@ -132,70 +131,4 @@ internal void ResetSelection() } } #endregion - - #region Class SyncUndoAction - /// ---------------------------------------------------------------------------------------- - /// - /// Handle Undo and Redo (as well as Do) for a sync message. Basically it just means that - /// we have to do a sync again. - /// - /// ---------------------------------------------------------------------------------------- - public class SyncUndoAction : UndoActionBase - { - #region Data members - private SyncMsg m_syncMsg; - private IApp m_app; - #endregion - - /// ------------------------------------------------------------------------------------ - /// - /// Initializes a new instance of the class. - /// - /// The application. - /// The sync message. - /// ------------------------------------------------------------------------------------ - public SyncUndoAction(IApp app, SyncMsg syncMsg) - { - m_app = app; - m_syncMsg = syncMsg; - } - - /// ------------------------------------------------------------------------------------ - /// - /// Does this instance. - /// - /// ------------------------------------------------------------------------------------ - public void Do() - { - m_app.Synchronize(m_syncMsg); - } - - #region Overrides of UndoActionBase - /// ------------------------------------------------------------------------------------ - /// - /// Reapplies (or "redoes") an action. - /// - /// ------------------------------------------------------------------------------------ - public override bool Redo() - { - Do(); - return true; - } - - /// ------------------------------------------------------------------------------------ - /// - /// Reverses (or "undoes") an action. Sets pfSuccess to true if successful. If not successful - /// because the database state has changed unexpectedly, sets pfSuccess to false but still - /// returns S_OK. More catastrophic errors may produce error result codes. - /// - /// ------------------------------------------------------------------------------------ - public override bool Undo() - { - Do(); - return true; - } - #endregion - } - - #endregion } diff --git a/Src/FwCoreDlgs/FwStylesDlg.cs b/Src/FwCoreDlgs/FwStylesDlg.cs index 8c5e6f1cc4..135db9c2db 100644 --- a/Src/FwCoreDlgs/FwStylesDlg.cs +++ b/Src/FwCoreDlgs/FwStylesDlg.cs @@ -237,7 +237,7 @@ public static void RunStylesDialogForCombo(ComboBox combo, Action fixCombo, stri stylesDlg.SetPropsToFactorySettings = setPropsToFactorySettings; if (stylesDlg.ShowDialog(owner) == DialogResult.OK && stylesDlg.ChangeType != StyleChangeType.None) { - app.Synchronize(SyncMsg.ksyncStyle); + app.Synchronize(); var selectedStyle = stylesDlg.SelectedStyle; m_oldStyle = comboStartingSelectedStyle; if (fixCombo != null) @@ -1299,7 +1299,7 @@ public override bool Undo() // Inform all the application windows that the user issued an undo command after // having applied a style change via the StylesDialog box. if (m_fForUndo) - m_app.Synchronize(SyncMsg.ksyncStyle); + m_app.Synchronize(); return true; } @@ -1313,7 +1313,7 @@ public override bool Redo() // Inform all the application windows that the user issued a redo command after // an undo command after having applied a style change via the StylesDialog box. if (!m_fForUndo) - m_app.Synchronize(SyncMsg.ksyncStyle); + m_app.Synchronize(); return true; } diff --git a/Src/LexText/LexTextControls/ConfigureHomographDlg.cs b/Src/LexText/LexTextControls/ConfigureHomographDlg.cs index 73d770bfd5..5911887eed 100644 --- a/Src/LexText/LexTextControls/ConfigureHomographDlg.cs +++ b/Src/LexText/LexTextControls/ConfigureHomographDlg.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2015-2016 SIL International +// Copyright (c) 2015-2016 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -153,7 +153,7 @@ private void RunStyleDialog(string styleName) dlg.CanSelectParagraphBackgroundColor = false; if (dlg.ShowDialog(this) == DialogResult.OK && dlg.ChangeType != StyleChangeType.None) { - m_app.Synchronize(SyncMsg.ksyncStyle); + m_app.Synchronize(); LcmStyleSheet stylesheet = new LcmStyleSheet(); stylesheet.Init(m_cache, m_cache.LangProject.Hvo, LangProjectTags.kflidStyles); m_stylesheet = stylesheet; diff --git a/Src/LexText/LexTextControls/DataNotebook/ImportCharMappingDlg.cs b/Src/LexText/LexTextControls/DataNotebook/ImportCharMappingDlg.cs index 9a560cb7d3..0e67743c45 100644 --- a/Src/LexText/LexTextControls/DataNotebook/ImportCharMappingDlg.cs +++ b/Src/LexText/LexTextControls/DataNotebook/ImportCharMappingDlg.cs @@ -138,7 +138,7 @@ private void m_btnStyles_Click(object sender, EventArgs e) (dlg.ChangeType & StyleChangeType.Added) > 0 || (dlg.ChangeType & StyleChangeType.RenOrDel) > 0)) { - m_app.Synchronize(SyncMsg.ksyncStyle); + m_app.Synchronize(); LcmStyleSheet stylesheet = new LcmStyleSheet(); stylesheet.Init(m_cache, m_cache.LangProject.Hvo, LangProjectTags.kflidStyles); m_stylesheet = stylesheet; diff --git a/Src/LexText/LexTextDll/LexTextApp.cs b/Src/LexText/LexTextDll/LexTextApp.cs index 2b9c181099..4cb3e10a76 100644 --- a/Src/LexText/LexTextDll/LexTextApp.cs +++ b/Src/LexText/LexTextDll/LexTextApp.cs @@ -192,27 +192,6 @@ public static Guid AppGuid } } - /// - /// This application processes DB sync records. - /// - public override Guid SyncGuid - { - get - { - CheckDisposed(); - return AppGuid; - } - } - - //public override string ProductName - //{ - // get - // { - // CheckDisposed(); - // return LexTextStrings.kstidApplicationName; - // } - //} - public override string DefaultConfigurationPathname { get diff --git a/Src/ParatextImport/ParatextImportManager.cs b/Src/ParatextImport/ParatextImportManager.cs index 27e494d74d..a8196eb6f4 100644 --- a/Src/ParatextImport/ParatextImportManager.cs +++ b/Src/ParatextImport/ParatextImportManager.cs @@ -305,7 +305,7 @@ protected ScrReference CompleteImport(ScrReference firstImported) { // Refresh all the views of all applications connected to the same DB. This // will cause any needed Scripture data to be reloaded lazily. - m_app.Synchronize(SyncMsg.ksyncStyle); + m_app.Synchronize(); } return firstImported; } diff --git a/Src/xWorks/FwXApp.cs b/Src/xWorks/FwXApp.cs index c818d25810..7d168da845 100644 --- a/Src/xWorks/FwXApp.cs +++ b/Src/xWorks/FwXApp.cs @@ -56,18 +56,6 @@ public virtual string DefaultConfigurationPathname } } - public override bool Synchronize(SyncMsg sync) - { - CheckDisposed(); - - if (sync == SyncMsg.ksyncUndoRedo || sync == SyncMsg.ksyncFullRefresh) - { - OnMasterRefresh(null); - return true; - } - return base.Synchronize (sync); - } - /// /// This is the one (and should be only) handler for the user Refresh command. /// Refresh wants to first clean up the cache, then give things like Clerks a diff --git a/Src/xWorks/FwXWindow.cs b/Src/xWorks/FwXWindow.cs index 8cada808fc..732e1256d4 100644 --- a/Src/xWorks/FwXWindow.cs +++ b/Src/xWorks/FwXWindow.cs @@ -1563,7 +1563,7 @@ private void HandleUndoResult(UndoResult ures, bool fPrivate) if (!fPrivate && m_app != null) { // currently implemented, this will cause this app to do a master refresh, - m_app.Synchronize(SyncMsg.ksyncUndoRedo); + m_app.Synchronize(); } else { @@ -2205,23 +2205,6 @@ public override void OnPropertyChanged(string name) base.OnPropertyChanged(name); } - /// ----------------------------------------------------------------------------------- - /// - /// Returns the NormalStateDesktopBounds property from the persistence object. - /// - /// ----------------------------------------------------------------------------------- - public Rectangle NormalStateDesktopBounds - { - get - { - CheckDisposed(); - - var loc = m_propertyTable.GetValue("windowLocation"); - var size = m_propertyTable.GetValue("windowSize", /*hack*/new Size(400, 400)); - return new Rectangle(loc, size); - } - } - /// ----------------------------------------------------------------------------------- /// /// Create the client windows and add corresponding stuff to the sidebar, View menu, @@ -2249,57 +2232,17 @@ public void EnableWindow(bool fEnable) Enabled = fEnable; } - /// ------------------------------------------------------------------------------------ - /// - /// Called just before a window synchronizes its views with DB changes (e.g. when an - /// undo or redo command is issued). - /// - /// synchronization message - /// ------------------------------------------------------------------------------------ - public virtual void PreSynchronize(SyncMsg sync) - { - CheckDisposed(); - // TODO: Implement it. This is copied from TE. - } - - /// - /// If a property requests it, do a db sync. - /// - public virtual void OnIdle(object sender) - { - CheckDisposed(); - - /* Bad things happen, when this is done and the parser is running. - * TODO: Figure out how they can co-exist. - if (PropertyTable.PropertyTable.GetBoolProperty("SyncOnIdle", false) && FwApp.App != null - && FwApp.App.SyncGuid != Guid.Empty) - { - FwApp.App.SyncFromDb(); - } - */ - } - - /// ------------------------------------------------------------------------------------ /// /// Called when a window synchronizes its views with DB changes (e.g. when an undo or /// redo command is issued). /// - /// synchronization message - /// True if the sync message was handled; false, indicating that the - /// application should refresh all windows. - /// ------------------------------------------------------------------------------------ - public virtual bool Synchronize(SyncMsg sync) + public virtual void Synchronize() { CheckDisposed(); - if (sync == SyncMsg.ksyncStyle) - { - // force our stylesheet to resync (LT-7382). - ResyncStylesheet(); - ResyncRootboxStyles(); - return true; - } - return false; + // force our stylesheet to resync (LT-7382). + ResyncStylesheet(); + ResyncRootboxStyles(); } /// ------------------------------------------------------------------------------------ From 43507dc91c6c39b64247a1b5248f573e4aae86d6 Mon Sep 17 00:00:00 2001 From: Hasso Date: Fri, 29 Sep 2023 15:17:12 -0500 Subject: [PATCH 004/415] LT-21638: Save import settings only if a file has been chosen - Fix a crash if the import filename is empty - Also fix tab order in import dialog Change-Id: Ia7656137df5a2a48e61955bed4e7268150e02851 --- .../LexTextControls/LexImportWizard.cs | 38 ++++++++++--------- .../LexTextControls/LexImportWizard.resx | 2 +- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Src/LexText/LexTextControls/LexImportWizard.cs b/Src/LexText/LexTextControls/LexImportWizard.cs index df6ba25898..06a1c9709b 100644 --- a/Src/LexText/LexTextControls/LexImportWizard.cs +++ b/Src/LexText/LexTextControls/LexImportWizard.cs @@ -2114,33 +2114,35 @@ protected override void OnCancelButton() // if it's known to be dirty OR the shift key is down - ask to save the settings file if (m_dirtySenseLastSave || (Control.ModifierKeys & Keys.Shift) == Keys.Shift) { - // LT-7057: if no settings file, don't ask to save - if (UsesInvalidFileNames(true)) - return; // finsih with out prompting to save... + // LT-7057, LT-21638: if no settings file or no input file, don't ask to save + if (string.IsNullOrEmpty(m_DatabaseFileName.Text) || UsesInvalidFileNames(true)) + return; - // ask to save the settings - DialogResult result = DialogResult.Yes; - // if we're not importing a phaseX file, then ask + // if we're importing a phaseX file, save settings automatically; otherwise, ask first. + var result = DialogResult.Yes; if (GetDictionaryFileAsPhaseFileNumber() == 0) result = MessageBox.Show(this, LexTextControls.ksAskRememberImportSettings, LexTextControls.ksSaveSettings_, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3); - if (result == DialogResult.Yes) + switch (result) { - // before saving we need to make sure all the data structures are populated - while (CurrentStepNumber <= 6) + case DialogResult.Yes: { - EnableNextButton(); - m_CurrentStepNumber++; + // before saving we need to make sure all the data structures are populated + while (CurrentStepNumber <= 6) + { + EnableNextButton(); + m_CurrentStepNumber++; + } + SaveSettings(); + break; } - SaveSettings(); - } - else if (result == DialogResult.Cancel) - { - // This is how do we stop the cancel process... - this.DialogResult = DialogResult.None; - m_fCanceling = false; + case DialogResult.Cancel: + // This is how do we stop the cancel process... + this.DialogResult = DialogResult.None; + m_fCanceling = false; + break; } } } diff --git a/Src/LexText/LexTextControls/LexImportWizard.resx b/Src/LexText/LexTextControls/LexImportWizard.resx index 2950cefbe6..542148eed7 100644 --- a/Src/LexText/LexTextControls/LexImportWizard.resx +++ b/Src/LexText/LexTextControls/LexImportWizard.resx @@ -1447,7 +1447,7 @@ 386, 21 - 15 + 9 m_SettingsFileName From 3721b1b38b173a09282ed1a2e5ddabbe6f104e94 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 11 Oct 2023 14:15:03 -0400 Subject: [PATCH 005/415] LT-21571: Fix crash when adding a sound file String.Format was expecting two arguments. Added the second one. Change-Id: Icd73eac4f68052659ecb20dd91fd4cc6f511d2dd --- Src/FwCoreDlgs/MoveOrCopyFilesController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/FwCoreDlgs/MoveOrCopyFilesController.cs b/Src/FwCoreDlgs/MoveOrCopyFilesController.cs index 36100272d9..ad87a7a835 100644 --- a/Src/FwCoreDlgs/MoveOrCopyFilesController.cs +++ b/Src/FwCoreDlgs/MoveOrCopyFilesController.cs @@ -110,7 +110,7 @@ internal static string PerformMoveCopyOrLeaveFile(string sFile, string sNewDir, var promptAlreadyExists = string.Format(FwCoreDlgs.ksAlreadyExists, sNewFile); if (batchMode) { - promptAlreadyExists = string.Format(FwCoreDlgs.ksClickNoToLeave, promptAlreadyExists); + promptAlreadyExists = string.Format(FwCoreDlgs.ksClickNoToLeave, promptAlreadyExists, sFile); } if (MessageBox.Show(promptAlreadyExists, FwCoreDlgs.kstidWarning, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) { From 4bab90c236692f65285e55b46296811038d30b70 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Wed, 11 Oct 2023 15:51:30 -0400 Subject: [PATCH 006/415] LT-21447: Display circumfixes per Leipzig standard for APRs Fix circumfix parsing when using an affix process rule so that circumfixes show in both the prefix and suffix slots of the parsing results. Change-Id: Ic6a1582a115f3cb414a54f1f2d1cab952fad0bc0 --- Src/LexText/ParserCore/HCParser.cs | 40 ++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Src/LexText/ParserCore/HCParser.cs b/Src/LexText/ParserCore/HCParser.cs index 9ee3ffb981..ad025e9bdc 100644 --- a/Src/LexText/ParserCore/HCParser.cs +++ b/Src/LexText/ParserCore/HCParser.cs @@ -12,8 +12,9 @@ using System.Xml.Linq; using SIL.LCModel; using SIL.LCModel.Infrastructure; -using SIL.Machine.Morphology.HermitCrab; using SIL.Machine.Annotations; +using SIL.Machine.Morphology.HermitCrab; +using SIL.Machine.Morphology.HermitCrab.MorphologicalRules; using SIL.ObjectModel; namespace SIL.FieldWorks.WordWorks.Parser @@ -279,6 +280,10 @@ private static string GetValueString(IFsSymFeatVal value) private bool GetMorphs(Word ws, out List result) { var morphs = new Dictionary(); + + var aprCircumfixes = new List(); + bool isSuffixPortionOfAprCircumfix = false; + result = new List(); foreach (Annotation morph in ws.Morphs) { @@ -286,11 +291,42 @@ private bool GetMorphs(Word ws, out List result) var formID = (int?) allomorph.Properties[FormID] ?? 0; if (formID == 0) continue; + + isSuffixPortionOfAprCircumfix = false; var formID2 = (int?) allomorph.Properties[FormID2] ?? 0; + if (formID2 == 0 && allomorph is AffixProcessAllomorph) + { + // Per the Leipzig glossing rules (https://www.eva.mpg.de/lingua/resources/glossing-rules.php), + // circumfixes should appear both before and after the material they attach to. + // HC does not have an overt marker for a circumfix when it is an affix processing rule (aka APR). + // The following code determines when an APR is marked as a circumfix in FLEx and ensures the + // two instances of it as a morph are included in the result at the correct places. + // This is a fix for https://jira.sil.org/browse/LT-21447 + IMoForm circumForm; + if (!m_cache.ServiceLocator.GetInstance().TryGetObject(formID, out circumForm)) + { + result = null; + return false; + } + if (circumForm.MorphTypeRA.Guid == MoMorphTypeTags.kguidMorphCircumfix) + { + if (aprCircumfixes.Contains(formID)) + { + isSuffixPortionOfAprCircumfix = true; + } + else + { + // Remember this allomorph as an APR that is a circumfix + aprCircumfixes.Add(formID); + } + } + } + + string formStr = ws.Shape.GetNodes(morph.Range).ToString(ws.Stratum.CharacterDefinitionTable, false); int curFormID; MorphInfo morphInfo; - if (!morphs.TryGetValue(allomorph.Morpheme, out morphInfo)) + if (!morphs.TryGetValue(allomorph.Morpheme, out morphInfo) || isSuffixPortionOfAprCircumfix) { curFormID = formID; } From 1a14dd0fe0c4f457fbfa284c8e882080f681ca21 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 18 Oct 2023 14:31:25 -0400 Subject: [PATCH 007/415] LT-21639: Remove trailing '/' from Webonary site name Change-Id: Id5617e9d32476678669663fd7f6e3b9f2763f557 --- Src/xWorks/UploadToWebonaryController.cs | 6 ++++++ Src/xWorks/xWorksTests/UploadToWebonaryControllerTests.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Src/xWorks/UploadToWebonaryController.cs b/Src/xWorks/UploadToWebonaryController.cs index ab17d37d9a..105c4bf49b 100644 --- a/Src/xWorks/UploadToWebonaryController.cs +++ b/Src/xWorks/UploadToWebonaryController.cs @@ -226,6 +226,12 @@ internal static string NormalizeSiteName(string siteName) { siteName = siteName.Substring(domainIndex + domainSlash.Length); } + + // Remove a trailing '/' + if (siteName.EndsWith("/")) + { + siteName = siteName.Substring(0, siteName.Length - 1); + } return siteName; } diff --git a/Src/xWorks/xWorksTests/UploadToWebonaryControllerTests.cs b/Src/xWorks/xWorksTests/UploadToWebonaryControllerTests.cs index 8db3fddd59..995b393d92 100644 --- a/Src/xWorks/xWorksTests/UploadToWebonaryControllerTests.cs +++ b/Src/xWorks/xWorksTests/UploadToWebonaryControllerTests.cs @@ -151,6 +151,7 @@ public void Dispose() [TestCase("English", "english")] [TestCase(UploadToWebonaryController.WebonaryOrg + "/thAI", "thai")] [TestCase("httpS://www.Webonary.org/tPi", "tpi")] + [TestCase("httpS://www.Webonary.org/tPi/", "tpi")] public void NormalizeSiteName(string userEntered, string expected) { Assert.That(UploadToWebonaryController.NormalizeSiteName(userEntered), Is.EqualTo(expected)); From d74b7363b47739cac6abb12ce12409e21d5a16f3 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 19 Oct 2023 13:11:06 -0400 Subject: [PATCH 008/415] Remove the version from the reference to SIL.LCModel.Core The project shouldn't reference a specific version. This is to fix the following error: D:\fwrepo\fw\Src\FXT\FxtDll\FxtDllTests\DumperTests.cs(34,23): error CS0012: The type 'ILgWritingSystemFactory' is defined in an assembly that is not referenced. You must add a reference to assembly 'SIL.LCModel.Core, Version=11.0.0.0, Culture=neutral, PublicKeyToken=f245775b81dcfaab'. [D:\fwrepo\fw\Src\FXT\FxtDll\FxtDllTests\FxtDllTests.csproj] Change-Id: I49060ac9e9009b808fcc73d618240f31bfa3aeda --- Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj b/Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj index 989d6b0ef4..a3ee153730 100644 --- a/Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj +++ b/Src/FXT/FxtDll/FxtDllTests/FxtDllTests.csproj @@ -150,7 +150,9 @@ SIL.LCModel ..\..\..\..\Output\Debug\SIL.LCModel.dll - + + ..\..\..\..\Output\Debug\SIL.LCModel.Core.dll + False ..\..\..\..\Output\Debug\SIL.LCModel.Tests.dll From f61c2a908be40492f675795c21d526ffef9d82c5 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 25 Oct 2023 13:38:08 -0400 Subject: [PATCH 009/415] LT-21618: Use libpalaso 13.0.0-beta0074 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also update bindingRedirect’s to 13.0.0.0 Change-Id: Ia1e550357e5114bc001eb5a3476cbf24083d7287 --- Build/mkall.targets | 2 +- Build/nuget-common/packages.config | 28 +++++++++---------- Src/AppForTests.config | 24 ++++++++-------- Src/Common/FieldWorks/App.config | 23 +++++++-------- Src/GenerateHCConfig/App.config | 2 +- .../ParaText8PluginTests/App.config | 8 +++--- 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/Build/mkall.targets b/Build/mkall.targets index 1be5cbebe2..08e2458ccf 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -283,7 +283,7 @@ 5.2.0-beta0003 - 12.1.0-beta0020 + 13.0.0-beta0074 9.4.0.1-beta 10.2.0-beta0075 70.1.123 diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index bc374ec735..f5e0f05591 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -38,17 +38,17 @@ - + - + - + @@ -59,21 +59,21 @@ - + - - + + - - - - - - - - + + + + + + + + diff --git a/Src/AppForTests.config b/Src/AppForTests.config index f973413dcd..4e524462f8 100644 --- a/Src/AppForTests.config +++ b/Src/AppForTests.config @@ -37,38 +37,38 @@ Comment out the following section when the ParatextData and FieldWorks versions --> - - + + - - + + - - + + - - + + - - + + - + - + diff --git a/Src/Common/FieldWorks/App.config b/Src/Common/FieldWorks/App.config index 79763c570e..97d0f89283 100644 --- a/Src/Common/FieldWorks/App.config +++ b/Src/Common/FieldWorks/App.config @@ -31,33 +31,34 @@ Comment out the following section when the ParatextData and FieldWorks versions - - + + - - + + - - + + - - + + - - + + - + + diff --git a/Src/GenerateHCConfig/App.config b/Src/GenerateHCConfig/App.config index 360381fbca..952b24d7ff 100644 --- a/Src/GenerateHCConfig/App.config +++ b/Src/GenerateHCConfig/App.config @@ -12,7 +12,7 @@ - + diff --git a/Src/Paratext8Plugin/ParaText8PluginTests/App.config b/Src/Paratext8Plugin/ParaText8PluginTests/App.config index 4af6e04702..940b3451a2 100644 --- a/Src/Paratext8Plugin/ParaText8PluginTests/App.config +++ b/Src/Paratext8Plugin/ParaText8PluginTests/App.config @@ -1,4 +1,4 @@ - + @@ -22,19 +22,19 @@ Also, comment out separate items in mkall.targets and packages.config - + - + - + From df6caaf99268cb3769d4eb276eb902ebbd133841 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 11 Oct 2023 13:19:03 -0700 Subject: [PATCH 010/415] Change GenerateXHTML* to GenerateContent* * The old method names were inaccurate and could cause confusion during the refactoring ahead of LT-21634 Change-Id: I881346d133c1317b8bbf4223ad4cae64115a175d --- Src/xWorks/ConfiguredLcmGenerator.cs | 161 ++-- Src/xWorks/LcmJsonGenerator.cs | 4 +- Src/xWorks/LcmXhtmlGenerator.cs | 8 +- .../ConfiguredLcmGeneratorTests.cs | 10 +- .../ConfiguredLcmUsfmGeneratorTests.cs | 44 +- .../ConfiguredXHTMLGeneratorReversalTests.cs | 24 +- .../ConfiguredXHTMLGeneratorTests.cs | 702 +++++++++--------- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 6 +- .../xWorksTests/LcmJsonGeneratorTests.cs | 46 +- 9 files changed, 503 insertions(+), 502 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 1538db8066..872a546542 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -217,32 +217,32 @@ internal static string GetWsForEntryType(ICmObject entry, LcmCache cache) /// If it is a Minor Entry, first checks whether the entry should be published as a Minor Entry; then, generates XHTML for each applicable /// Minor Entry configuration node. /// - public static string GenerateXHTMLForEntry(ICmObject entryObj, DictionaryConfigurationModel configuration, + public static string GenerateContentForEntry(ICmObject entryObj, DictionaryConfigurationModel configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index = -1) { if (IsMainEntry(entryObj, configuration)) - return GenerateXHTMLForMainEntry(entryObj, configuration.Parts[0], publicationDecorator, settings, index); + return GenerateContentForMainEntry(entryObj, configuration.Parts[0], publicationDecorator, settings, index); var entry = (ILexEntry)entryObj; return entry.PublishAsMinorEntry - ? GenerateXHTMLForMinorEntry(entry, configuration, publicationDecorator, settings, index) + ? GenerateContentForMinorEntry(entry, configuration, publicationDecorator, settings, index) : string.Empty; } - public static string GenerateXHTMLForMainEntry(ICmObject entry, ConfigurableDictionaryNode configuration, + public static string GenerateContentForMainEntry(ICmObject entry, ConfigurableDictionaryNode configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index) { if (configuration.DictionaryNodeOptions != null && ((ILexEntry)entry).ComplexFormEntryRefs.Any() && !IsListItemSelectedForExport(configuration, entry)) return string.Empty; - return GenerateXHTMLForEntry(entry, configuration, publicationDecorator, settings, index); + return GenerateContentForEntry(entry, configuration, publicationDecorator, settings, index); } - private static string GenerateXHTMLForMinorEntry(ICmObject entry, DictionaryConfigurationModel configuration, + private static string GenerateContentForMinorEntry(ICmObject entry, DictionaryConfigurationModel configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index) { // LT-15232: show minor entries using only the last applicable Minor Entry node (not more than once) var applicablePart = configuration.Parts.Skip(1).LastOrDefault(part => IsListItemSelectedForExport(part, entry)); - return applicablePart == null ? string.Empty : GenerateXHTMLForEntry(entry, applicablePart, publicationDecorator, settings, index); + return applicablePart == null ? string.Empty : GenerateContentForEntry(entry, applicablePart, publicationDecorator, settings, index); } /// @@ -264,7 +264,7 @@ internal static bool IsMainEntry(ICmObject entry, DictionaryConfigurationModel c /// Generates XHTML for an ICmObject for a specific ConfigurableDictionaryNode /// the configuration node must match the entry type - internal static string GenerateXHTMLForEntry(ICmObject entry, ConfigurableDictionaryNode configuration, + internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDictionaryNode configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index = -1) { Guard.AgainstNull(settings, nameof(settings)); @@ -297,7 +297,7 @@ internal static string GenerateXHTMLForEntry(ICmObject entry, ConfigurableDictio var pieces = configuration.ReferencedOrDirectChildren .Select(config => - GenerateXHTMLForFieldByReflection(entry, config, publicationDecorator, + GenerateContentForFieldByReflection(entry, config, publicationDecorator, settings)) .Where(content => !string.IsNullOrEmpty(content)).ToList(); if (pieces.Count == 0) @@ -346,7 +346,7 @@ public static string GetClassNameAttributeForConfig(ConfigurableDictionaryNode c /// write out appropriate XHTML. /// /// We use a significant amount of boilerplate code for fields and subfields. Make sure you update both. - internal static string GenerateXHTMLForFieldByReflection(object field, ConfigurableDictionaryNode config, + internal static string GenerateContentForFieldByReflection(object field, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, SenseInfo info = new SenseInfo(), bool fUseReverseSubField = false) { @@ -359,20 +359,20 @@ internal static string GenerateXHTMLForFieldByReflection(object field, Configura object propertyValue = null; if (config.DictionaryNodeOptions is DictionaryNodeGroupingOptions) { - return GenerateXHTMLForGroupingNode(field, config, publicationDecorator, settings); + return GenerateContentForGroupingNode(field, config, publicationDecorator, settings); } if (config.FieldDescription == "DefinitionOrGloss") { if (field is ILexSense) { - return GenerateXHTMLForDefinitionOrGloss(field as ILexSense, config, settings); + return GenerateContentForDefOrGloss(field as ILexSense, config, settings); } if (field is ILexEntryRef) { var ret = new StringBuilder(); foreach (var sense in (((field as ILexEntryRef).Owner as ILexEntry).AllSenses)) { - ret.Append(GenerateXHTMLForDefinitionOrGloss(sense, config, settings)); + ret.Append(GenerateContentForDefOrGloss(sense, config, settings)); } return ret.ToString(); } @@ -453,24 +453,24 @@ internal static string GenerateXHTMLForFieldByReflection(object field, Configura { case PropertyType.CollectionType: if (!IsCollectionEmpty(propertyValue)) - return GenerateXHTMLForCollection(propertyValue, config, publicationDecorator, field, settings, info); + return GenerateContentForCollection(propertyValue, config, publicationDecorator, field, settings, info); return string.Empty; case PropertyType.MoFormType: - return GenerateXHTMLForMoForm(propertyValue as IMoForm, config, settings); + return GenerateContentForMoForm(propertyValue as IMoForm, config, settings); case PropertyType.CmObjectType: - return GenerateXHTMLForICmObject(propertyValue as ICmObject, config, settings); + return GenerateContentForICmObject(propertyValue as ICmObject, config, settings); case PropertyType.CmPictureType: fileProperty = propertyValue as ICmFile; fileOwner = field as ICmObject; return fileProperty != null && fileOwner != null - ? GenerateXHTMLForPicture(fileProperty, config, fileOwner, settings) - : GenerateXHTMLForPictureCaption(propertyValue, config, settings); + ? GenerateContentForPicture(fileProperty, config, fileOwner, settings) + : GenerateContentForPictureCaption(propertyValue, config, settings); case PropertyType.CmPossibility: - return GenerateXHTMLForPossibility(propertyValue, config, publicationDecorator, settings); + return GenerateContentForPossibility(propertyValue, config, publicationDecorator, settings); case PropertyType.CmFileType: fileProperty = propertyValue as ICmFile; @@ -490,30 +490,30 @@ internal static string GenerateXHTMLForFieldByReflection(object field, Configura if (fileOwner != null) { return IsVideo(fileProperty.InternalPath) - ? GenerateXHTMLForVideoFile(fileProperty.ClassName, fileOwner.Guid.ToString(), srcAttr, MovieCamera, settings) - : GenerateXHTMLForAudioFile(fileProperty.ClassName, fileOwner.Guid.ToString(), srcAttr, LoudSpeaker, settings); + ? GenerateContentForVideoFile(fileProperty.ClassName, fileOwner.Guid.ToString(), srcAttr, MovieCamera, settings) + : GenerateContentForAudioFile(fileProperty.ClassName, fileOwner.Guid.ToString(), srcAttr, LoudSpeaker, settings); } } return string.Empty; } - var bldr = new StringBuilder(GenerateXHTMLForValue(field, propertyValue, config, settings)); + var bldr = new StringBuilder(GenerateContentForValue(field, propertyValue, config, settings)); if (config.ReferencedOrDirectChildren != null) { foreach (var child in config.ReferencedOrDirectChildren) { - bldr.Append(GenerateXHTMLForFieldByReflection(propertyValue, child, publicationDecorator, settings)); + bldr.Append(GenerateContentForFieldByReflection(propertyValue, child, publicationDecorator, settings)); } } return bldr.ToString(); } - private static string GenerateXHTMLForGroupingNode(object field, ConfigurableDictionaryNode config, + private static string GenerateContentForGroupingNode(object field, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { if (config.ReferencedOrDirectChildren != null && config.ReferencedOrDirectChildren.Any(child => child.IsEnabled)) { return settings.ContentGenerator.GenerateGroupingNode(field, config, publicationDecorator, settings, - (f, c, p, s) => GenerateXHTMLForFieldByReflection(f, c, p, s)); + (f, c, p, s) => GenerateContentForFieldByReflection(f, c, p, s)); } return string.Empty; } @@ -607,7 +607,7 @@ private static bool GetPropValueForCustomField(object fieldOwner, ConfigurableDi return true; } - private static string GenerateXHTMLForVideoFile(string className, string mediaId, string srcAttribute, string caption, GeneratorSettings settings) + private static string GenerateContentForVideoFile(string className, string mediaId, string srcAttribute, string caption, GeneratorSettings settings) { if (string.IsNullOrEmpty(srcAttribute) && string.IsNullOrEmpty(caption)) return string.Empty; @@ -741,7 +741,7 @@ private static string GetClassNameForCustomFieldParent(ConfigurableDictionaryNod return parentNodeType.Name; } - private static string GenerateXHTMLForPossibility(object propertyValue, ConfigurableDictionaryNode config, + private static string GenerateContentForPossibility(object propertyValue, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { if (config.ReferencedOrDirectChildren == null || !config.ReferencedOrDirectChildren.Any(node => node.IsEnabled)) @@ -749,7 +749,7 @@ private static string GenerateXHTMLForPossibility(object propertyValue, Configur var bldr = new StringBuilder(); foreach (var child in config.ReferencedOrDirectChildren) { - var content = GenerateXHTMLForFieldByReflection(propertyValue, child, publicationDecorator, settings); + var content = GenerateContentForFieldByReflection(propertyValue, child, publicationDecorator, settings); bldr.Append(content); } if (bldr.Length > 0) @@ -757,20 +757,20 @@ private static string GenerateXHTMLForPossibility(object propertyValue, Configur return string.Empty; } - private static string GenerateXHTMLForPictureCaption(object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static string GenerateContentForPictureCaption(object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // todo: get sense numbers and captions into the same div and get rid of this if else string content; if (config.DictionaryNodeOptions != null) - content = GenerateXHTMLForStrings(propertyValue as IMultiString, config, settings); + content = GenerateContentForStrings(propertyValue as IMultiString, config, settings); else - content = GenerateXHTMLForString(propertyValue as ITsString, config, settings); + content = GenerateContentForString(propertyValue as ITsString, config, settings); if (!String.IsNullOrEmpty(content)) return settings.ContentGenerator.WriteProcessedObject(true, content, GetClassNameAttributeForConfig(config)); return String.Empty; } - private static string GenerateXHTMLForPicture(ICmFile pictureFile, ConfigurableDictionaryNode config, ICmObject owner, + private static string GenerateContentForPicture(ICmFile pictureFile, ConfigurableDictionaryNode config, ICmObject owner, GeneratorSettings settings) { var srcAttribute = GenerateSrcAttributeFromFilePath(pictureFile, settings.UseRelativePaths ? "pictures" : null, settings); @@ -827,7 +827,7 @@ private static string GenerateSrcAttributeForMediaFromFilePath(string filename, return settings.UseRelativePaths ? filePath : new Uri(filePath).ToString(); } - private static string GenerateXHTMLForDefinitionOrGloss(ILexSense sense, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static string GenerateContentForDefOrGloss(ILexSense sense, ConfigurableDictionaryNode config, GeneratorSettings settings) { var wsOption = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; if (wsOption == null) @@ -1257,7 +1257,7 @@ private static MemberInfo GetProperty(Type lookupType, ConfigurableDictionaryNod return propInfo; } - private static string GenerateXHTMLForMoForm(IMoForm moForm, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static string GenerateContentForMoForm(IMoForm moForm, ConfigurableDictionaryNode config, GeneratorSettings settings) { // Don't export if there is no such data if (moForm == null) @@ -1266,13 +1266,13 @@ private static string GenerateXHTMLForMoForm(IMoForm moForm, ConfigurableDiction { throw new NotImplementedException("Children for MoForm types not yet supported."); } - return GenerateXHTMLForStrings(moForm.Form, config, settings, moForm.Owner.Guid); + return GenerateContentForStrings(moForm.Form, config, settings, moForm.Owner.Guid); } /// /// This method will generate the XHTML that represents a collection and its contents /// - private static string GenerateXHTMLForCollection(object collectionField, ConfigurableDictionaryNode config, + private static string GenerateContentForCollection(object collectionField, ConfigurableDictionaryNode config, DictionaryPublicationDecorator pubDecorator, object collectionOwner, GeneratorSettings settings, SenseInfo info = new SenseInfo()) { // To be used for things like shared grammatical info @@ -1295,7 +1295,7 @@ private static string GenerateXHTMLForCollection(object collectionField, Configu if (config.DictionaryNodeOptions is DictionaryNodeSenseOptions) { - bldr.Append(GenerateXHTMLForSenses(config, pubDecorator, settings, collection, info, ref sharedCollectionInfo)); + bldr.Append(GenerateContentForSenses(config, pubDecorator, settings, collection, info, ref sharedCollectionInfo)); } else { @@ -1303,11 +1303,11 @@ private static string GenerateXHTMLForCollection(object collectionField, Configu ConfigurableDictionaryNode lexEntryTypeNode; if (IsVariantEntryType(config, out lexEntryTypeNode)) { - bldr.Append(GenerateXHTMLForILexEntryRefCollection(config, collection, cmOwner, pubDecorator, settings, lexEntryTypeNode, false)); + bldr.Append(GenerateContentForEntryRefCollection(config, collection, cmOwner, pubDecorator, settings, lexEntryTypeNode, false)); } else if (IsComplexEntryType(config, out lexEntryTypeNode)) { - bldr.Append(GenerateXHTMLForILexEntryRefCollection(config, collection, cmOwner, pubDecorator, settings, lexEntryTypeNode, true)); + bldr.Append(GenerateContentForEntryRefCollection(config, collection, cmOwner, pubDecorator, settings, lexEntryTypeNode, true)); } else if (IsPrimaryEntryReference(config, out lexEntryTypeNode)) { @@ -1319,14 +1319,14 @@ private static string GenerateXHTMLForCollection(object collectionField, Configu && lexEntryTypeNode.ReferencedOrDirectChildren.Any(y => y.IsEnabled)) { Debug.Assert(config.DictionaryNodeOptions == null, - "double calls to GenerateXHTMLForILexEntryRefsByType don't play nicely with ListOptions. Everything will be generated twice (if it doesn't crash)"); + "double calls to GenerateContentForLexEntryRefsByType don't play nicely with ListOptions. Everything will be generated twice (if it doesn't crash)"); // Display typeless refs foreach (var entry in lerCollection.Where(item => !item.ComplexEntryTypesRS.Any() && !item.VariantEntryTypesRS.Any())) bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, lexEntryTypeNode)); // Display refs of each type - GenerateXHTMLForILexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, lexEntryTypeNode, + GenerateContentForLexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, lexEntryTypeNode, true); // complex - GenerateXHTMLForILexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, lexEntryTypeNode, + GenerateContentForLexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, lexEntryTypeNode, false); // variants } else @@ -1338,11 +1338,11 @@ private static string GenerateXHTMLForCollection(object collectionField, Configu } else if (config.FieldDescription.StartsWith("Subentries")) { - GenerateXHTMLForSubentries(config, collection, cmOwner, pubDecorator, settings, bldr); + GenerateContentForSubentries(config, collection, cmOwner, pubDecorator, settings, bldr); } else if (IsLexReferenceCollection(config)) { - GenerateXHTMLForILexReferenceCollection(config, collection.Cast(), cmOwner, pubDecorator, settings, bldr); + GenerateContentForLexRefCollection(config, collection.Cast(), cmOwner, pubDecorator, settings, bldr); } else { @@ -1418,7 +1418,7 @@ private static bool IsPrimaryEntryReference(ConfigurableDictionaryNode config, o return false; } - private static string GenerateXHTMLForILexEntryRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, + private static string GenerateContentForEntryRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, ConfigurableDictionaryNode typeNode, bool isComplex) { var bldr = new StringBuilder(); @@ -1441,7 +1441,7 @@ private static string GenerateXHTMLForILexEntryRefCollection(ConfigurableDiction foreach (var entry in lerCollection.Where(item => !item.ComplexEntryTypesRS.Any() && !item.VariantEntryTypesRS.Any())) bldr.Append(GenerateCollectionItemContent(config, pubDecorator, entry, collectionOwner, settings, typeNode)); // Display refs of each type - GenerateXHTMLForILexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, typeNode, isComplex); + GenerateContentForLexEntryRefsByType(config, lerCollection, collectionOwner, pubDecorator, settings, bldr, typeNode, isComplex); } else { @@ -1452,7 +1452,7 @@ private static string GenerateXHTMLForILexEntryRefCollection(ConfigurableDiction return bldr.ToString(); } - private static void GenerateXHTMLForILexEntryRefsByType(ConfigurableDictionaryNode config, List lerCollection, object collectionOwner, DictionaryPublicationDecorator pubDecorator, + private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryNode config, List lerCollection, object collectionOwner, DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, StringBuilder bldr, ConfigurableDictionaryNode typeNode, bool isComplex) { var lexEntryTypes = isComplex @@ -1496,7 +1496,7 @@ private static void GenerateXHTMLForILexEntryRefsByType(ConfigurableDictionaryNo } } - private static void GenerateXHTMLForSubentries(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, + private static void GenerateContentForSubentries(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, StringBuilder bldr) { var listOptions = config.DictionaryNodeOptions as DictionaryNodeListOptions; @@ -1576,7 +1576,7 @@ private static bool IsCollectionInNeedOfSorting(string fieldDescr) /// /// This method will generate the XHTML that represents a senses collection and its contents /// - private static string GenerateXHTMLForSenses(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static string GenerateContentForSenses(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, IEnumerable senseCollection, SenseInfo info, ref string sharedGramInfo) { // Check whether all the senses have been excluded from publication. See https://jira.sil.org/browse/LT-15697. @@ -1669,7 +1669,7 @@ child.DictionaryNodeOptions is DictionaryNodeSenseOptions && private static string InsertGramInfoBeforeSenses(ILexSense item, ConfigurableDictionaryNode gramInfoNode, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { - var content = GenerateXHTMLForFieldByReflection(item, gramInfoNode, publicationDecorator, settings); + var content = GenerateContentForFieldByReflection(item, gramInfoNode, publicationDecorator, settings); if (string.IsNullOrEmpty(content)) return string.Empty; return settings.ContentGenerator.GenerateGramInfoBeforeSensesContent(content); @@ -1768,7 +1768,7 @@ private static string GenerateSenseContent(ConfigurableDictionaryNode config, Di { if (child.FieldDescription != "MorphoSyntaxAnalysisRA" || !isSameGrammaticalInfo) { - bldr.Append(GenerateXHTMLForFieldByReflection(item, child, publicationDecorator, settings, info)); + bldr.Append(GenerateContentForFieldByReflection(item, child, publicationDecorator, settings, info)); } } } @@ -1798,7 +1798,7 @@ private static string GeneratePictureContent(ConfigurableDictionaryNode config, { if (child.FieldDescription == "PictureFileRA") { - var content = GenerateXHTMLForFieldByReflection(item, child, publicationDecorator, settings); + var content = GenerateContentForFieldByReflection(item, child, publicationDecorator, settings); contentGenerator.WriteProcessedContents(writer, content); break; } @@ -1812,7 +1812,7 @@ private static string GeneratePictureContent(ConfigurableDictionaryNode config, { if (child.FieldDescription != "PictureFileRA") { - var content = GenerateXHTMLForFieldByReflection(item, child, publicationDecorator, settings); + var content = GenerateContentForFieldByReflection(item, child, publicationDecorator, settings); captionBldr.Append(content); } } @@ -1831,7 +1831,7 @@ private static string GenerateCollectionItemContent(ConfigurableDictionaryNode c object item, object collectionOwner, GeneratorSettings settings, ConfigurableDictionaryNode factoredTypeField = null) { if (item is IMultiStringAccessor) - return GenerateXHTMLForStrings((IMultiStringAccessor)item, config, settings); + return GenerateContentForStrings((IMultiStringAccessor)item, config, settings); if ((config.DictionaryNodeOptions is DictionaryNodeListOptions && !IsListItemSelectedForExport(config, item, collectionOwner)) || config.ReferencedOrDirectChildren == null) return string.Empty; @@ -1844,7 +1844,7 @@ private static string GenerateCollectionItemContent(ConfigurableDictionaryNode c { bldr.Append(child.FieldDescription == LookupComplexEntryType ? GenerateSubentryTypeChild(child, publicationDecorator, (ILexEntry)item, collectionOwner, settings) - : GenerateXHTMLForFieldByReflection(item, child, publicationDecorator, settings)); + : GenerateContentForFieldByReflection(item, child, publicationDecorator, settings)); } } else if (config.DictionaryNodeOptions is DictionaryNodePictureOptions) @@ -1856,7 +1856,7 @@ private static string GenerateCollectionItemContent(ConfigurableDictionaryNode c // If a type field has been factored out and generated then skip generating it here foreach (var child in config.ReferencedOrDirectChildren.Where(child => !ReferenceEquals(child, factoredTypeField))) { - bldr.Append(GenerateXHTMLForFieldByReflection(item, child, publicationDecorator, settings)); + bldr.Append(GenerateContentForFieldByReflection(item, child, publicationDecorator, settings)); } } if (bldr.Length == 0) @@ -1866,7 +1866,7 @@ private static string GenerateCollectionItemContent(ConfigurableDictionaryNode c return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), collectionContent); } - private static void GenerateXHTMLForILexReferenceCollection(ConfigurableDictionaryNode config, + private static void GenerateContentForLexRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject cmOwner, DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, StringBuilder bldr) { @@ -2002,12 +2002,12 @@ private static string GenerateCrossReferenceChildren(ConfigurableDictionaryNode child.CSSClassNameOverride = CssGenerator.GetClassAttributeForConfig(child); // Flag to prepend "Reverse" to child.SubField when it is used. settings.ContentGenerator.WriteProcessedContents(xw, - GenerateXHTMLForFieldByReflection(reference, child, publicationDecorator, settings, fUseReverseSubField: true)); + GenerateContentForFieldByReflection(reference, child, publicationDecorator, settings, fUseReverseSubField: true)); } else { settings.ContentGenerator.WriteProcessedContents(xw, - GenerateXHTMLForFieldByReflection(reference, child, publicationDecorator, settings)); + GenerateContentForFieldByReflection(reference, child, publicationDecorator, settings)); } break; default: @@ -2029,7 +2029,7 @@ private static string GenerateSubentryTypeChild(ConfigurableDictionaryNode confi var complexEntryRef = EntryRefForSubentry(subEntry, mainEntryOrSense); return complexEntryRef == null ? string.Empty - : GenerateXHTMLForCollection(complexEntryRef.ComplexEntryTypesRS, config, publicationDecorator, subEntry, settings); + : GenerateContentForCollection(complexEntryRef.ComplexEntryTypesRS, config, publicationDecorator, subEntry, settings); } private static ILexEntryRef EntryRefForSubentry(ILexEntry subEntry, object mainEntryOrSense) @@ -2105,7 +2105,7 @@ private static string GetRomanSenseCounter(string numberingStyle, int senseNumbe return roman; } - private static string GenerateXHTMLForICmObject(ICmObject propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static string GenerateContentForICmObject(ICmObject propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // Don't export if there is no such data if (propertyValue == null || config.ReferencedOrDirectChildren == null || !config.ReferencedOrDirectChildren.Any(node => node.IsEnabled)) @@ -2113,7 +2113,7 @@ private static string GenerateXHTMLForICmObject(ICmObject propertyValue, Configu var bldr = new StringBuilder(); foreach (var child in config.ReferencedOrDirectChildren) { - var content = GenerateXHTMLForFieldByReflection(propertyValue, child, null, settings); + var content = GenerateContentForFieldByReflection(propertyValue, child, null, settings); bldr.Append(content); } if (bldr.Length > 0) @@ -2293,7 +2293,7 @@ private static bool IsCollectionEmpty(object collection) /// data to generate xhtml for /// /// - private static string GenerateXHTMLForValue(object field, object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static string GenerateContentForValue(object field, object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // If we're working with a headword, either for this entry or another one (Variant or Complex Form, etc.), store that entry's GUID // so we can generate a link to the main or minor entry for this headword. @@ -2340,7 +2340,7 @@ private static string GenerateXHTMLForValue(object field, object propertyValue, { if (!TsStringUtils.IsNullOrEmpty((ITsString)propertyValue)) { - var content = GenerateXHTMLForString((ITsString)propertyValue, config, settings, guid); + var content = GenerateContentForString((ITsString)propertyValue, config, settings, guid); if (!string.IsNullOrEmpty(content)) return settings.ContentGenerator.WriteProcessedCollection(false, content, GetClassNameAttributeForConfig(config)); } @@ -2348,7 +2348,7 @@ private static string GenerateXHTMLForValue(object field, object propertyValue, } else if (propertyValue is IMultiStringAccessor) { - return GenerateXHTMLForStrings((IMultiStringAccessor)propertyValue, config, settings, guid); + return GenerateContentForStrings((IMultiStringAccessor)propertyValue, config, settings, guid); } else if (propertyValue is int) { @@ -2365,8 +2365,8 @@ private static string GenerateXHTMLForValue(object field, object propertyValue, else if (propertyValue is IMultiAccessorBase) { if (field is ISenseOrEntry) - return GenerateXHTMLForVirtualStrings(((ISenseOrEntry)field).Item, (IMultiAccessorBase)propertyValue, config, settings, guid); - return GenerateXHTMLForVirtualStrings((ICmObject)field, (IMultiAccessorBase)propertyValue, config, settings, guid); + return GenerateContentForVirtualStrings(((ISenseOrEntry)field).Item, (IMultiAccessorBase)propertyValue, config, settings, guid); + return GenerateContentForVirtualStrings((ICmObject)field, (IMultiAccessorBase)propertyValue, config, settings, guid); } else if (propertyValue is String) { @@ -2380,7 +2380,7 @@ private static string GenerateXHTMLForValue(object field, object propertyValue, IStTxtPara stp = para as IStTxtPara; if (stp == null) continue; - var contentPara = GenerateXHTMLForString(stp.Contents, config, settings, guid); + var contentPara = GenerateContentForString(stp.Contents, config, settings, guid); if (!String.IsNullOrEmpty(contentPara)) { bldr.Append(contentPara); @@ -2419,17 +2419,17 @@ private static string WriteElementContents(object propertyValue, return String.Empty; } - private static string GenerateXHTMLForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, + private static string GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings) { - return GenerateXHTMLForStrings(multiStringAccessor, config, settings, Guid.Empty); + return GenerateContentForStrings(multiStringAccessor, config, settings, Guid.Empty); } /// /// This method will generate an XHTML span with a string for each selected writing system in the /// DictionaryWritingSystemOptions of the configuration that also has data in the given IMultiStringAccessor /// - private static string GenerateXHTMLForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, + private static string GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid guid) { var wsOptions = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; @@ -2484,7 +2484,7 @@ private static string GenerateXHTMLForStrings(IMultiStringAccessor multiStringAc /// This method will generate an XHTML span with a string for each selected writing system in the /// DictionaryWritingSystemOptions of the configuration that also has data in the given IMultiAccessorBase /// - private static string GenerateXHTMLForVirtualStrings(ICmObject owningObject, IMultiAccessorBase multiStringAccessor, + private static string GenerateContentForVirtualStrings(ICmObject owningObject, IMultiAccessorBase multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid guid) { var wsOptions = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; @@ -2530,19 +2530,19 @@ private static string GenerateWsPrefixAndString(ConfigurableDictionaryNode confi return String.Empty; } var wsName = settings.Cache.WritingSystemFactory.get_EngineOrNull(wsId).Id; - var content = GenerateXHTMLForString(requestedString, config, settings, guid, wsName); + var content = GenerateContentForString(requestedString, config, settings, guid, wsName); if (String.IsNullOrEmpty(content)) return String.Empty; return settings.ContentGenerator.GenerateWsPrefixWithString(settings, wsOptions.DisplayWritingSystemAbbreviations, wsId, content); } - private static string GenerateXHTMLForString(ITsString fieldValue, ConfigurableDictionaryNode config, + private static string GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem = null) { - return GenerateXHTMLForString(fieldValue, config, settings, Guid.Empty, writingSystem); + return GenerateContentForString(fieldValue, config, settings, Guid.Empty, writingSystem); } - private static string GenerateXHTMLForString(ITsString fieldValue, ConfigurableDictionaryNode config, + private static string GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid linkTarget, string writingSystem = null) { if (TsStringUtils.IsNullOrEmpty(fieldValue)) @@ -2554,7 +2554,7 @@ private static string GenerateXHTMLForString(ITsString fieldValue, ConfigurableD { var audioId = fieldText.Substring(0, fieldText.IndexOf(".", StringComparison.Ordinal)); var srcAttr = GenerateSrcAttributeForMediaFromFilePath(fieldText, "AudioVisual", settings); - var fileContent = GenerateXHTMLForAudioFile(writingSystem, audioId, srcAttr, string.Empty, settings); + var fileContent = GenerateContentForAudioFile(writingSystem, audioId, srcAttr, string.Empty, settings); var content = GenerateAudioWsContent(writingSystem, linkTarget, fileContent, settings); if (!string.IsNullOrEmpty(content)) return settings.ContentGenerator.WriteProcessedObject(false, content, null); @@ -2704,7 +2704,7 @@ private static void GenerateRunWithPossibleLink(GeneratorSettings settings, stri /// Source location path for audio file /// Inner text for hyperlink (unicode icon for audio) /// - private static string GenerateXHTMLForAudioFile(string classname, + private static string GenerateContentForAudioFile(string classname, string audioId, string srcAttribute, string audioIcon, GeneratorSettings settings) { if (string.IsNullOrEmpty(audioId) && string.IsNullOrEmpty(srcAttribute) && string.IsNullOrEmpty(audioIcon)) @@ -2805,7 +2805,7 @@ select match.Groups["rowcontents"] into rowContentsGroup private static void GenerateTableTitle(ITsString title, IFragmentWriter writer, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem) { - settings.ContentGenerator.AddTableTitle(writer, GenerateXHTMLForString(title, config, settings, writingSystem)); + settings.ContentGenerator.AddTableTitle(writer, GenerateContentForString(title, config, settings, writingSystem)); } /// @@ -2847,7 +2847,7 @@ private static void GenerateTableRow(ITsString rowUSFM, IFragmentWriter writer, { var contentsGroup = cell.Groups["content"]; var cellLim = contentsGroup.Index + contentsGroup.Length; - var contentXHTML = GenerateXHTMLForString(rowUSFM.GetSubstring(contentsGroup.Index, cellLim), config, settings, writingSystem); + var contentXHTML = GenerateContentForString(rowUSFM.GetSubstring(contentsGroup.Index, cellLim), config, settings, writingSystem); var alignment = HorizontalAlign.NotSet; if (cell.Groups["align"].Success) { @@ -3001,6 +3001,7 @@ private static bool IsTypeBeforeForm(ConfigurableDictionaryNode config) public class GeneratorSettings { public ILcmContentGenerator ContentGenerator = new LcmXhtmlGenerator(); +// public ILcmStylesGenerator StylesGenerator = new CssGenerator(); public LcmCache Cache { get; } public ReadOnlyPropertyTable PropertyTable { get; } public bool UseRelativePaths { get; } diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 011de54430..1f87ad8814 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -546,10 +546,10 @@ public static List SavePublishedJsonWithStyles(int[] entriesToSave, Dict var generateEntryAction = new Action(() => { - var entryContent = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, configuration, + var entryContent = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, publicationDecorator, settings, index); entryStringBuilder.Append(entryContent); - var displayXhtmlContent = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, configuration, + var displayXhtmlContent = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, publicationDecorator, displayXhtmlSettings, index); displayXhtmlBuilder.Append(displayXhtmlContent); if (progress != null) diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index 5cbf146199..8b45a62d85 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -112,7 +112,7 @@ public static void SavePublishedHtmlWithStyles(int[] entryHvos, DictionaryPublic var generateEntryAction = new Action(() => { - var entryContent = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, configuration, publicationDecorator, settings); + var entryContent = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, publicationDecorator, settings); entryStringBuilder.Append(entryContent); if (progress != null) progress.Position++; @@ -226,7 +226,7 @@ public static string GenerateEntryHtmlWithStyles(ICmObject entry, DictionaryConf var exportSettings = new ConfiguredLcmGenerator.GeneratorSettings(readOnlyPropTable.GetValue("cache"), readOnlyPropTable, false, false, null, ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropTable, configuration)); GenerateOpeningHtml(previewCssPath, custCssPath, exportSettings, writer); - var content = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, configuration, pubDecorator, exportSettings); + var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, pubDecorator, exportSettings); writer.WriteRaw(content); GenerateClosingHtml(writer); writer.Flush(); @@ -360,7 +360,7 @@ public static List GenerateNextFewEntries(DictionaryPublicationDecorator var firstEntry = Math.Max(0, oldCurrentPageRange.Item1 - entriesToAddCount); for (var i = firstEntry; i < oldCurrentPageRange.Item1; ++i) { - entries.Add(ConfiguredLcmGenerator.GenerateXHTMLForEntry(settings.Cache.ServiceLocator.ObjectRepository.GetObject(entryHvos[i]), + entries.Add(ConfiguredLcmGenerator.GenerateContentForEntry(settings.Cache.ServiceLocator.ObjectRepository.GetObject(entryHvos[i]), currentConfig, publicationDecorator, settings)); } } @@ -369,7 +369,7 @@ public static List GenerateNextFewEntries(DictionaryPublicationDecorator var lastEntry = Math.Min(oldAdjacentPageRange.Item2, oldCurrentPageRange.Item2 + entriesToAddCount); for (var i = oldCurrentPageRange.Item2 + 1; i <= lastEntry; ++i) { - entries.Add(ConfiguredLcmGenerator.GenerateXHTMLForEntry(settings.Cache.ServiceLocator.ObjectRepository.GetObject(entryHvos[i]), + entries.Add(ConfiguredLcmGenerator.GenerateContentForEntry(settings.Cache.ServiceLocator.ObjectRepository.GetObject(entryHvos[i]), currentConfig, publicationDecorator, settings)); } } diff --git a/Src/xWorks/xWorksTests/ConfiguredLcmGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredLcmGeneratorTests.cs index f20dd76ccd..e69e0f9a92 100644 --- a/Src/xWorks/xWorksTests/ConfiguredLcmGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredLcmGeneratorTests.cs @@ -95,9 +95,9 @@ public void GenerateXHTMLForEntry_NullArgsThrowArgumentNull() var entry = factory.Create(); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateXHTMLForEntry(null, mainEntryNode, null, settings)); - Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, (ConfigurableDictionaryNode)null, null, settings)); - Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, null)); + Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateContentForEntry(null, mainEntryNode, null, settings)); + Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateContentForEntry(entry, (ConfigurableDictionaryNode)null, null, settings)); + Assert.Throws(typeof(ArgumentNullException), () => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, null)); } [Test] @@ -108,11 +108,11 @@ public void GenerateXHTMLForEntry_BadConfigurationThrows() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT //Test a blank main node description - Assert.That(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings), + Assert.That(() => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings), Throws.InstanceOf().With.Message.Contains("Invalid configuration")); //Test a configuration with a valid but incorrect type mainEntryNode.FieldDescription = "LexSense"; - Assert.That(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings), + Assert.That(() => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings), Throws.InstanceOf().With.Message.Contains("doesn't configure this type")); } diff --git a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs index 78b951aaa0..5b5b060a16 100644 --- a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs @@ -140,7 +140,7 @@ public void NoUSFM_GeneratesPlainText() const string plainText = "Plain Text"; var entry = CreateInterestingLexEntry(plainText); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToUSFMField + "/span[@lang='en' and text()='" + plainText + "']", 1); AssertThatXmlIn.String(result).HasNoMatchForXpath("//table"); @@ -153,7 +153,7 @@ public void NoLeadingUSFM_GeneratesPlainText() const string plainText = "Plain Text\n\\d ignore me"; var entry = CreateInterestingLexEntry(plainText); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasNoMatchForXpath("//table"); AssertIsGood(result); } @@ -165,7 +165,7 @@ public void LeadingTitle_GeneratesTable() const string titleUSFM = @"\d " + title; var entry = CreateInterestingLexEntry(titleUSFM); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToTitle + "[@lang='en' and text()='" + title + "']", 1); AssertIsGood(result); @@ -176,7 +176,7 @@ public void LeadingTableRow_GeneratesTable() { var entry = CreateInterestingLexEntry("\\tr\n"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertIsGood(result); } @@ -186,7 +186,7 @@ public void TitleAndTableRow_GeneratesBoth() { var entry = CreateInterestingLexEntry(@"\d title \tr \tc "); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToTitle + "[@lang='en' and text()='title']", 1); @@ -202,7 +202,7 @@ public void NoGapNoContentTitleAndRow_DoesNotThrow() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); // Verify that the field is in the results AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField, 1); @@ -218,7 +218,7 @@ public void WhitespaceOnlyBetweenTitleAndRow_DoesNotThrow() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); // Verify that the partially-typed table is in the results AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToTitle + "[text()='" + TR + "']", 1); @@ -233,7 +233,7 @@ public void MissingSpaces_NoCells() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); // Verify that the field is in the results AssertThatXmlIn.String(result).HasNoMatchForXpath("//th"); @@ -256,7 +256,7 @@ public void ManyRowsAndCells() var entry = CreateInterestingLexEntry($"{TR} {TC}1 {a1}\t{TC}2 {a2} \t \r\n{TC}3 {a3} {TR}" + $"\t{TC}1 {b1} {TC}2 {b2} {TC}3 {b3} {TR} {TC}1 {c1} {TC}2 {c2} {TC}3 {c3}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(XPathToTitle); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToRow, 3); @@ -287,7 +287,7 @@ public void EmptyCell() const string a3 = "ty"; var entry = CreateInterestingLexEntry($"{TR} {TC}1 {a1}\t{TC}2 {TC}3 {a3}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); const string xpathToA = XPathToRow + "/td[span[@lang='en' and text()='" + a1 + "']]/following-sibling::td[not(node())]/following-sibling::td[span[text()='" + a3 + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -300,7 +300,7 @@ public void EmptyCells() { var entry = CreateInterestingLexEntry($"{TR} {TC}1 {TC}2\r\n{TC}3\t{TC}4 "); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); const string xpathToA = XPathToRow + "/td[not(node())]/following-sibling::td[not(node())]/following-sibling::td[not(node())]/following-sibling::td[not(node())]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -316,7 +316,7 @@ public void TableHeading_GeneratesTableHeader() const string b2h = "not normally expected, but not forbidden"; var entry = CreateInterestingLexEntry($"{TR} {TH}1 {a1h} {TC}2\r{a2c}\n{TR} {TC}1 {b1c} {TH}2 {b2h}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); const string xpathToA = XPathToRow + "/th[span[@lang='en' and text()='" + a1h + "']]/following-sibling::td[span[@lang='en' and text()='" + a2c + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -340,7 +340,7 @@ public void TableCellAlignment() var entry = CreateInterestingLexEntry($"{TR} {TH}r1 {a1} {TH}2\r{a2}\n{TR} {TC}1 {b1} {TC}r2 {b2} " + $"{TR} {TH}c1 {c1} {TH}l2\r{c2}\n{TR} {TC}l1 {d1} {TC}c2 {d2}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); const string xpathToA = XPathToRow + "/th[@style='text-align: right;' and span[@lang='en' and text()='" + a1 + "']]/following-sibling::th[not(@style) and span[@lang='en' and text()='" + a2 + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -371,7 +371,7 @@ public void MultipleTables() $@"\d {title1} {TR} {TC}1 {la1} \d {title2} {TR} {TH}1 {za1} {TH}2 {za2} {TR} {TC}1 {zb1} {TC}2 {zb2} \d {TR} {TH}1 {loneCell}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 3); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToTitle, 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToRow, 4); // 1 + 2 + 1 = 4 @@ -385,7 +385,7 @@ public void CellRange(int min, int lim) { var entry = CreateInterestingLexEntry($"{TR} {TC}{min}-{lim} home on the range"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); // +1 because the range includes both ends AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath($"{XPathToRow}/td[@colspan='{lim - min + 1}']", 1); AssertIsGood(result); @@ -401,7 +401,7 @@ public void CellRange_None_NoColSpan(string range) const string content = "seldom"; var entry = CreateInterestingLexEntry($"{TR} {TC}{range} {content}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath($"{XPathToRow}/td[not(@colspan)]/span[text()='{content}']", 1); AssertIsGood(result); } @@ -412,7 +412,7 @@ public void BadUSFM_RowWithoutCells() const string junk = "no cells"; var entry = CreateInterestingLexEntry($"{TR} {junk}"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); var expected = string.Format(xWorksStrings.InvalidUSFM_TextAfterTR, junk); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + $"//span[{StyleBigRed} and text() = '{expected}']", 1); @@ -426,7 +426,7 @@ public void BadUSFM_RowWithTextBeforeCells() const string junk = "oops"; var entry = CreateInterestingLexEntry($"{TR} {junk} {TC}1 data"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); var expected = string.Format(xWorksStrings.InvalidUSFM_TextAfterTR, junk); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + @@ -441,7 +441,7 @@ public void BadUSFM_PartiallyTyped_NoErrors(string usfm, string xpath) { var entry = CreateInterestingLexEntry(usfm); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); Assert.That(result, Does.EndWith("")); AssertIsGood(result); @@ -452,7 +452,7 @@ public void BadUSFM_PartiallyTyped_NoErrors([Values(@"\t", TC, TH + "1", TH + "l { var entry = CreateInterestingLexEntry($@"{TR} {marker}\th2 exists"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + $"//span[{StyleBigRed} and text()='{marker}']", 1); Assert.That(result, Does.EndWith("")); @@ -464,7 +464,7 @@ public void BadUSFM_PartiallyTyped_NoError() { var entry = CreateInterestingLexEntry(TR + @" \t"); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "//span[" + StyleBigRed + @" and text()='\t']", 1); Assert.That(result, Does.EndWith("")); @@ -478,7 +478,7 @@ public void MistypedMarker([Values("tre", "tcp", "t", "td", "tl", "t2", "h", "th const string cellAfter = "after"; var cellWithBadMark = $@"in \{misMark} between"; var entry = CreateInterestingLexEntry($"{TR} {TC} {cellBefore} {TC} {cellWithBadMark} {TC} {cellAfter}"); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell, 3); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell + "[text()='" + cellBefore + "']", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell + "[text()='" + cellAfter + "']", 1); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs index 7c1d42bcc4..1fa28b8559 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs @@ -119,7 +119,7 @@ public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var entry = CreateInterestingEnglishReversalEntry(); //SUT - string result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings); + string result = ConfiguredLcmGenerator.GenerateContentForEntry(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); } @@ -146,7 +146,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry( paroleEntry.SummaryDefinition.SetAnalysisDefaultWritingSystem("summDefn"); CXGTests.CreateComplexForm(Cache, paroleEntry, sense.Owner as ILexEntry, true); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); const string headwordXpath = senseXpath + "/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']"; @@ -165,7 +165,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfSense( var paroleEntry = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech"); CXGTests.CreateComplexForm(Cache, paroleEntry.SensesOS[0], sense.Owner as ILexEntry, true); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); 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); @@ -182,7 +182,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfSense( var paroleEntry = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech"); CXGTests.CreateVariantForm(Cache, paroleEntry.SensesOS[0], variantEntry, "Spelling Variant"); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(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); @@ -200,7 +200,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfEntry( paroleEntry.SummaryDefinition.SetAnalysisDefaultWritingSystem("summDefn"); CXGTests.CreateVariantForm(Cache, paroleEntry, variantEntry, "Spelling Variant"); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(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); @@ -226,7 +226,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferences_Ordered() 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 = ConfiguredLcmGenerator.GenerateXHTMLForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); // SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(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}']"; @@ -401,7 +401,7 @@ public void GenerateXHTMLForEntry_ReversalStringGeneratesContent() var entryHeadWord = rie.SensesRS.First().Entry.HeadWord; //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(rie, reversalNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(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); @@ -461,7 +461,7 @@ public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleReferencedSens var testEntry = CreateInterestingEnglishReversalEntry(); AddSenseToReversaEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); const string senseNumberOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' 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 @@ -523,7 +523,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSenses() var testEntry = CreateInterestingEnglishReversalEntry(); AddSingleSubSenseToSense(testEntry, "second gloss", m_wsEn, Cache); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(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='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); @@ -585,7 +585,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSensesinReversalSubEntry( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var testEntry = CreateInterestingEnglishSubReversalEntryWithSubSense(); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -703,7 +703,7 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() testEntry.SensesRS.Add(entry1.SensesOS.First()); testEntry.SensesRS.Add(entry2.SensesOS.First()); - var xhtml = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); // check that the sense gram info appears once before the rest of the sense information. Assert.That(xhtml, Is.Not.Null.Or.Empty); const string sharedGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en' and text()='n']"; @@ -715,7 +715,7 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() entry2.MorphoSyntaxAnalysesOC.Add(msa2a); msa2a.PartOfSpeechRA = verb; entry2.SensesOS.First().MorphoSyntaxAnalysisRA = msa2a; - xhtml = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(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); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 30389fd0ff..762be15883 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -171,7 +171,7 @@ public void ResetModelAssembly() private const string TestVariantName = "Crazy Variant"; [Test] - public void GenerateXHTMLForEntry_HeadwordConfigurationGeneratesCorrectResult() + public void GenerateContentForEntry_HeadwordConfigurationGeneratesCorrectResult() { var headwordNode = new ConfigurableDictionaryNode { @@ -189,13 +189,13 @@ public void GenerateXHTMLForEntry_HeadwordConfigurationGeneratesCorrectResult() AddHeadwordToEntry(entry, "HeadWordTest", m_wsFr); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[@lang='fr']/a[text()='HeadWordTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchHeadwordOfHeadwordTest, 1); } [Test] - public void GenerateXHTMLForEntry_InvalidUnicodeHeadword_GeneratesErrorResult() + public void GenerateContentForEntry_InvalidUnicodeHeadword_GeneratesErrorResult() { var headwordNode = new ConfigurableDictionaryNode { @@ -212,7 +212,7 @@ public void GenerateXHTMLForEntry_InvalidUnicodeHeadword_GeneratesErrorResult() var entry = CreateInterestingLexEntry(Cache, "\uD900"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string invalidCharsHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[text()='\u0fff\u0fff\u0fff']"; // change Headword back to something legal so that we don't crash trying to save bad data into the cache. AddHeadwordToEntry(entry, "notbadanymore", Cache.DefaultVernWs); @@ -220,7 +220,7 @@ public void GenerateXHTMLForEntry_InvalidUnicodeHeadword_GeneratesErrorResult() } [Test] - public void GenerateXHTMLForEntry_SortByHeadwordWithSpecificWsGeneratesLetterHeadings() + public void GenerateContentForEntry_SortByHeadwordWithSpecificWsGeneratesLetterHeadings() { var firstAEntry = CreateInterestingLexEntry(Cache, "alpha1"); // PublicationDecorator is used to force generation of Letter Headings when there is only one entry @@ -258,7 +258,7 @@ public void GenerateXHTMLForEntry_SortByHeadwordWithSpecificWsGeneratesLetterHea } [Test] - public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult() + public void GenerateContentForEntry_LexemeFormConfigurationGeneratesCorrectResult() { var headwordNode = new ConfigurableDictionaryNode { @@ -279,13 +279,13 @@ public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult( morph.Form.set_String(wsFr, TsStringUtils.MakeString("LexemeFormTest", wsFr)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string frenchLexForm = "/div[@class='lexentry']/span[@class='lexemeformoa']/span[@lang='fr']/a[text()='LexemeFormTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } [Test] - public void GenerateXHTMLForEntry_PronunciationLocationGeneratesCorrectResult() + public void GenerateContentForEntry_PronunciationLocationGeneratesCorrectResult() { var nameNode = new ConfigurableDictionaryNode { @@ -324,13 +324,13 @@ public void GenerateXHTMLForEntry_PronunciationLocationGeneratesCorrectResult() pronunciation.LocationRA = location; var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string hereLocation = "/div[@class='lexentry']/span[@class='pronunciations']/span[@class='pronunciation']/span[@class='location']/span[@class='name']/span[@lang='fr' and text()='Here!']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(hereLocation, 1); } [Test] - public void GenerateXHTMLForEntry_PronunciationVideoFileGeneratesAnchorTag() + public void GenerateContentForEntry_PronunciationVideoFileGeneratesAnchorTag() { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -402,7 +402,7 @@ public void GenerateXHTMLForEntry_PronunciationVideoFileGeneratesAnchorTag() const string mediaFileAnchor2 = entryPart + variantsPart + varPronPart + mediaFilePart + movieCamSearch; //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); Assert.That(result, Contains.Substring(videoFileUrl1)); Assert.That(result, Contains.Substring(videoFileUrl2)); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(mediaFileAnchor1, 1); @@ -439,7 +439,7 @@ private static ConfigurableDictionaryNode CreateMediaNode() } [Test] - public void GenerateXHTMLForEntry_NoEnabledConfigurationsWritesNothing() + public void GenerateContentForEntry_NoEnabledConfigurationsWritesNothing() { var homographNum = new ConfigurableDictionaryNode { @@ -456,12 +456,12 @@ public void GenerateXHTMLForEntry_NoEnabledConfigurationsWritesNothing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); Assert.IsEmpty(result, "Should not have generated anything for a disabled node"); } [Test] - public void GenerateXHTMLForEntry_HomographNumbersGeneratesCorrectResult() + public void GenerateContentForEntry_HomographNumbersGeneratesCorrectResult() { var homographNum = new ConfigurableDictionaryNode { FieldDescription = "HomographNumber" }; var mainEntryNode = new ConfigurableDictionaryNode @@ -476,9 +476,9 @@ public void GenerateXHTMLForEntry_HomographNumbersGeneratesCorrectResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); XHTMLStringBuilder.AppendLine(""); //keep the xml valid (single root element) //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); XHTMLStringBuilder.Append(result); - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryTwo, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings); XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); @@ -489,7 +489,7 @@ public void GenerateXHTMLForEntry_HomographNumbersGeneratesCorrectResult() } [Test] - public void GenerateXHTMLForEntry_HeadwordRefConfigurationGeneratesWithTwoWS() + public void GenerateContentForEntry_HeadwordRefConfigurationGeneratesWithTwoWS() { var mainEntry = CreateInterestingLexEntry(Cache, "MainEntry"); var compareReferencedEntry = CreateInterestingLexEntry(Cache, "bFR", "b comparable"); @@ -502,7 +502,7 @@ public void GenerateXHTMLForEntry_HeadwordRefConfigurationGeneratesWithTwoWS() var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(crossRefOwnerTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordWsInCrossRefsXpath("en", "bEN"), 1); @@ -510,7 +510,7 @@ public void GenerateXHTMLForEntry_HeadwordRefConfigurationGeneratesWithTwoWS() } [Test] - public void GenerateXHTMLForEntry_OneSenseWithGlossGeneratesCorrectResult() + public void GenerateContentForEntry_OneSenseWithGlossGeneratesCorrectResult() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; @@ -530,7 +530,7 @@ public void GenerateXHTMLForEntry_OneSenseWithGlossGeneratesCorrectResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string oneSenseWithGlossOfGloss = xpathThruSense + "//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithGlossOfGloss, 1); @@ -538,7 +538,7 @@ public void GenerateXHTMLForEntry_OneSenseWithGlossGeneratesCorrectResult() [Test] - public void GenerateXHTMLForEntry_OneEntryWithSenseAndOneWithoutWorks() + public void GenerateContentForEntry_OneEntryWithSenseAndOneWithoutWorks() { var glossNode = new ConfigurableDictionaryNode { @@ -579,9 +579,9 @@ public void GenerateXHTMLForEntry_OneEntryWithSenseAndOneWithoutWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); XHTMLStringBuilder.AppendLine(""); //keep the xml valid (single root element) //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); XHTMLStringBuilder.Append(result); - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryTwo, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings); XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); result = XHTMLStringBuilder.ToString(); @@ -594,7 +594,7 @@ public void GenerateXHTMLForEntry_OneEntryWithSenseAndOneWithoutWorks() } [Test] - public void GenerateXHTMLForEntry_DefaultRootGeneratesResult() + public void GenerateContentForEntry_DefaultRootGeneratesResult() { var defaultRoot = string.Concat( Path.Combine(FwDirectoryFinder.DefaultConfigurations, "Dictionary", "Root"), DictionaryConfigurationModel.FileExtension); @@ -602,13 +602,13 @@ public void GenerateXHTMLForEntry_DefaultRootGeneratesResult() var dictionaryModel = new DictionaryConfigurationModel(defaultRoot, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings); var entryExists = "/div[@class='entry' and @id='g" + entry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryExists, 1); } [Test] - public void GenerateXHTMLForEntry_DoesNotDescendThroughDisabledNode() + public void GenerateContentForEntry_DoesNotDescendThroughDisabledNode() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var senses = new ConfigurableDictionaryNode @@ -639,7 +639,7 @@ public void GenerateXHTMLForEntry_DoesNotDescendThroughDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string sensesThatShouldNotBe = "/div[@class='entry']/span[@class='senses']"; const string headwordThatShouldNotBe = "//span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(sensesThatShouldNotBe, 0); @@ -647,7 +647,7 @@ public void GenerateXHTMLForEntry_DoesNotDescendThroughDisabledNode() } [Test] - public void GenerateXHTMLForEntry_ProduceNothingWithOnlyDisabledNode() + public void GenerateContentForEntry_ProduceNothingWithOnlyDisabledNode() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var senses = new ConfigurableDictionaryNode @@ -670,12 +670,12 @@ public void GenerateXHTMLForEntry_ProduceNothingWithOnlyDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); Assert.IsEmpty(result, "With only one subnode that is disabled, there should be nothing generated!"); } [Test] - public void GenerateXHTMLForEntry_TwoSensesWithSameInfoShowGramInfoFirst() + public void GenerateContentForEntry_TwoSensesWithSameInfoShowGramInfoFirst() { var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions { @@ -728,7 +728,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithSameInfoShowGramInfoFirst() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -736,7 +736,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithSameInfoShowGramInfoFirst() } [Test] - public void GenerateXHTMLForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished_ShowGramInfoFirst() + public void GenerateContentForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished_ShowGramInfoFirst() { var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions { @@ -813,7 +813,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished_S Cache.ServiceLocator.GetInstance().LexDbEntries, mainDict); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -821,7 +821,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished_S } [Test] - public void GenerateXHTMLForEntry_TwoSensesWithDifferentGramInfoShowInfoInSenses() + public void GenerateContentForEntry_TwoSensesWithDifferentGramInfoShowInfoInSenses() { var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions { @@ -878,7 +878,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithDifferentGramInfoShowInfoInSenses var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 2); @@ -886,7 +886,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithDifferentGramInfoShowInfoInSenses } [Test] - public void GenerateXHTMLForEntry_TwoSensesWithNoGramInfoDisplaysNothingForSharedGramInfo() + public void GenerateContentForEntry_TwoSensesWithNoGramInfoDisplaysNothingForSharedGramInfo() { var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions { @@ -927,7 +927,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithNoGramInfoDisplaysNothingForShare var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -935,7 +935,7 @@ public void GenerateXHTMLForEntry_TwoSensesWithNoGramInfoDisplaysNothingForShare } [Test] - public void GenerateXHTMLForEntry_MorphemeType() + public void GenerateContentForEntry_MorphemeType() { var morphemeTypeAbbrev = new ConfigurableDictionaryNode() { @@ -987,13 +987,13 @@ public void GenerateXHTMLForEntry_MorphemeType() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string morphTypePath = "//span[@class='morphosyntaxanalysis']/span[@class='morphtypes']/span[@class='morphtype']/span[@class='abbreviation']/span[@lang='en' and text()='sfx']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(morphTypePath, 1); } [Test] - public void GenerateXHTMLForEntry_MakesSpanForRA() + public void GenerateContentForEntry_MakesSpanForRA() { var gramInfoAbbrev = new ConfigurableDictionaryNode() { @@ -1027,13 +1027,13 @@ public void GenerateXHTMLForEntry_MakesSpanForRA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); } [Test] - public void GenerateXHTMLForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() + public void GenerateContentForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() { var gramInfoAbbrev = new ConfigurableDictionaryNode() { @@ -1078,7 +1078,7 @@ public void GenerateXHTMLForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1087,7 +1087,7 @@ public void GenerateXHTMLForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() /// If the dictionary configuration specifies to export grammatical info, but there is no such grammatical info object to export, don't write a span. /// [Test] - public void GenerateXHTMLForEntry_DoesNotMakeSpanForRAIfNoData() + public void GenerateContentForEntry_DoesNotMakeSpanForRAIfNoData() { var gramInfoNode = new ConfigurableDictionaryNode { FieldDescription = "MorphoSyntaxAnalysisRA" }; var sensesNode = new ConfigurableDictionaryNode @@ -1112,7 +1112,7 @@ public void GenerateXHTMLForEntry_DoesNotMakeSpanForRAIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1121,7 +1121,7 @@ public void GenerateXHTMLForEntry_DoesNotMakeSpanForRAIfNoData() /// If the dictionary configuration specifies to export scientific category, but there is no data in the field to export, don't write a span. /// [Test] - public void GenerateXHTMLForEntry_DoesNotMakeSpanForTSStringIfNoData() + public void GenerateContentForEntry_DoesNotMakeSpanForTSStringIfNoData() { var scientificName = new ConfigurableDictionaryNode { FieldDescription = "ScientificName" }; var sensesNode = new ConfigurableDictionaryNode @@ -1146,13 +1146,13 @@ public void GenerateXHTMLForEntry_DoesNotMakeSpanForTSStringIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string scientificCatPath = xpathThruSense + "/span[@class='scientificname']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(scientificCatPath, 0); } [Test] - public void GenerateXHTMLForEntry_SupportsGramAbbrChildOfMSARA() + public void GenerateContentForEntry_SupportsGramAbbrChildOfMSARA() { var gramAbbrNode = new ConfigurableDictionaryNode { @@ -1202,7 +1202,7 @@ public void GenerateXHTMLForEntry_SupportsGramAbbrChildOfMSARA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; @@ -1215,7 +1215,7 @@ public void GenerateXHTMLForEntry_SupportsGramAbbrChildOfMSARA() } [Test] - public void GenerateXHTMLForEntry_DontDisplayNotSure() + public void GenerateContentForEntry_DontDisplayNotSure() { var gramAbbrNode = new ConfigurableDictionaryNode { @@ -1265,7 +1265,7 @@ public void GenerateXHTMLForEntry_DontDisplayNotSure() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='']"; const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; @@ -1278,7 +1278,7 @@ public void GenerateXHTMLForEntry_DontDisplayNotSure() } [Test] - public void GenerateXHTMLForEntry_DefinitionOrGlossWorks() + public void GenerateContentForEntry_DefinitionOrGlossWorks() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var senses = new ConfigurableDictionaryNode @@ -1299,13 +1299,13 @@ public void GenerateXHTMLForEntry_DefinitionOrGlossWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } [Test] - public void GenerateXHTMLForEntry_DefinitionOrGlossWorks_WithAbbrev() + public void GenerateContentForEntry_DefinitionOrGlossWorks_WithAbbrev() { var senses = new ConfigurableDictionaryNode { @@ -1329,14 +1329,14 @@ public void GenerateXHTMLForEntry_DefinitionOrGlossWorks_WithAbbrev() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } [Test] - public void GenerateXHTMLForEntry_DefinitionOrGloss_HandlePerWS() + public void GenerateContentForEntry_DefinitionOrGloss_HandlePerWS() { var wsOpts = GetWsOptionsForLanguages(new[] { "en", "es" }); var senses = new ConfigurableDictionaryNode @@ -1358,13 +1358,13 @@ public void GenerateXHTMLForEntry_DefinitionOrGloss_HandlePerWS() entryOne.SensesOS.First().Definition.set_String(wsEs, "definition"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss' and span[1]='gloss' and span[2]='definition']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGlossTwoWs, 1); } [Test] - public void GenerateXHTMLForEntry_ReferencedComplexFormDefinitionOrGloss_HandlePerWS() + public void GenerateContentForEntry_ReferencedComplexFormDefinitionOrGloss_HandlePerWS() { // LT-19073: Definition and gloss display behaviour for LT-7445 should apply to "Definition (or Gloss)" field in Referenced Complex Froms. // Check that different combinations of present or missing definition have successful fallback to gloss, and independently of other senses. @@ -1417,7 +1417,7 @@ public void GenerateXHTMLForEntry_ReferencedComplexFormDefinitionOrGloss_HandleP CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); // SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); // set of xpaths and required number of matches. var checkthis = new Dictionary() @@ -1441,7 +1441,7 @@ public void GenerateXHTMLForEntry_ReferencedComplexFormDefinitionOrGloss_HandleP } [Test] - public void GenerateXHTMLForEntry_OtherReferencedComplexForms() + public void GenerateContentForEntry_OtherReferencedComplexForms() { var complexformoptions = new DictionaryNodeListAndParaOptions { @@ -1490,7 +1490,7 @@ public void GenerateXHTMLForEntry_OtherReferencedComplexForms() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -1502,7 +1502,7 @@ public void GenerateXHTMLForEntry_OtherReferencedComplexForms() } [Test] - public void GenerateXHTMLForEntry_DuplicateConfigNodeWithSpaceWorks() + public void GenerateContentForEntry_DuplicateConfigNodeWithSpaceWorks() { var defOrGloss = new ConfigurableDictionaryNode { @@ -1527,13 +1527,13 @@ public void GenerateXHTMLForEntry_DuplicateConfigNodeWithSpaceWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithHyphenSuffix = "//span[@class='senses_test-one']/span[@class='sense_test-one']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } [Test] - public void GenerateXHTMLForEntry_DuplicateConfigNodeWithPuncWorks() + public void GenerateContentForEntry_DuplicateConfigNodeWithPuncWorks() { var defOrGloss = new ConfigurableDictionaryNode { @@ -1558,13 +1558,13 @@ public void GenerateXHTMLForEntry_DuplicateConfigNodeWithPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithHyphenSuffix = "//span[@class='senses_-test']/span[@class='sense_-test']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } [Test] - public void GenerateXHTMLForEntry_DuplicateConfigNodeWithMultiPuncWorks() + public void GenerateContentForEntry_DuplicateConfigNodeWithMultiPuncWorks() { var defOrGloss = new ConfigurableDictionaryNode { @@ -1589,13 +1589,13 @@ public void GenerateXHTMLForEntry_DuplicateConfigNodeWithMultiPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string senseWithHyphenSuffix = "//span[@class='senses_-test-']/span[@class='sense_-test-']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } [Test] - public void GenerateXHTMLForEntry_HeadWordRefVirtualPropWorks() + public void GenerateContentForEntry_HeadWordRefVirtualPropWorks() { var wsOpts = GetWsOptionsForLanguages(new[] { "vernacular" }); const string headWord = "mlhw"; @@ -1648,14 +1648,14 @@ public void GenerateXHTMLForEntry_HeadWordRefVirtualPropWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); var headwordMatch = string.Format("//span[@class='{0}']//span[@class='{1}']/span[text()='{2}']", nters, headWord, entryThreeForm); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordMatch, 1); } [Test] - public void GenerateXHTMLForEntry_EtymologyLanguageWorks() + public void GenerateContentForEntry_EtymologyLanguageWorks() { //This test also proves to verify that .NET String properties can be generated var abbrNode = new ConfigurableDictionaryNode @@ -1702,7 +1702,7 @@ public void GenerateXHTMLForEntry_EtymologyLanguageWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languages']/span[@class='language']/span[@class='abbreviation']/span[@lang='en' and text()='ar']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithArabicSrcLanguage, 1); const string etymologyWithGeorgianNotes = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languagenotes']/span[@lang='en' and text()='Georgian']"; @@ -1811,7 +1811,7 @@ public void GenerateEntryHtmlWithStyles_DoesNotShowMinorEntriesTwice([Values(tru } [Test] - public void GenerateXHTMLForEntry_LexemeBasedConsidersComplexFormsMainEntries() + public void GenerateContentForEntry_LexemeBasedConsidersComplexFormsMainEntries() { var configModel = CreateInterestingConfigurationModel(Cache, m_propertyTable); for (var i = 1; i < configModel.Parts.Count; i++) @@ -1836,7 +1836,7 @@ public void GenerateXHTMLForEntry_LexemeBasedConsidersComplexFormsMainEntries() /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleSenses() + public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; @@ -1857,7 +1857,7 @@ public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleSenses() AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' 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 CreateInterestingLexEntry @@ -1994,7 +1994,7 @@ public void AreThereEnabledSubsensesWithNumberingStyle_SubsensesHaveNumberingSty /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_SingleSenseGetsNoSenseNumber() + public void GenerateContentForEntry_SingleSenseGetsNoSenseNumber() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2018,7 +2018,7 @@ public void GenerateXHTMLForEntry_SingleSenseGetsNoSenseNumber() Assert.That(testEntry.AllSenses.Count, Is.EqualTo(1), "Test set up incorrectly. There should just be one sense."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(1), "Test not set up correctly. There should be no subsenses."); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberOne); @@ -2037,7 +2037,7 @@ public void GenerateXHTMLForEntry_SingleSenseGetsNoSenseNumber() /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSingleSense_WithNoSenseNumber() + public void GenerateContentForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSingleSense_WithNoSenseNumber() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2086,7 +2086,7 @@ public void GenerateXHTMLForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSingl Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. @@ -2105,7 +2105,7 @@ public void GenerateXHTMLForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSingl /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSingleSense_WithNoSenseNumber() + public void GenerateContentForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSingleSense_WithNoSenseNumber() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2153,7 +2153,7 @@ public void GenerateXHTMLForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSing Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. @@ -2172,7 +2172,7 @@ public void GenerateXHTMLForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSing /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_SubsenseStyleInfluencesSenseNumberShown() + public void GenerateContentForEntry_SubsenseStyleInfluencesSenseNumberShown() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2220,7 +2220,7 @@ public void GenerateXHTMLForEntry_SubsenseStyleInfluencesSenseNumberShown() Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 1); // Should have sense number on top sense. @@ -2229,7 +2229,7 @@ public void GenerateXHTMLForEntry_SubsenseStyleInfluencesSenseNumberShown() } [Test] - public void GenerateXHTMLForEntry_NumberingSingleSenseAlsoCountsSubSense() + public void GenerateContentForEntry_NumberingSingleSenseAlsoCountsSubSense() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions @@ -2270,13 +2270,13 @@ public void GenerateXHTMLForEntry_NumberingSingleSenseAlsoCountsSubSense() AddSingleSubSenseToSense("gloss", testEntry.SensesOS.First()); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string SenseOneSubSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(SenseOneSubSense, 1); } [Test] - public void GenerateXHTMLForEntry_SensesAndSubSensesWithDifferentNumberingStyle() + public void GenerateContentForEntry_SensesAndSubSensesWithDifferentNumberingStyle() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var DictionaryNodeSenseOptions = new DictionaryNodeSenseOptions @@ -2327,7 +2327,7 @@ public void GenerateXHTMLForEntry_SensesAndSubSensesWithDifferentNumberingStyle( AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2340,7 +2340,7 @@ public void GenerateXHTMLForEntry_SensesAndSubSensesWithDifferentNumberingStyle( } [Test] - public void GenerateXHTMLForEntry_SensesAndSubSensesWithNumberingStyle() + public void GenerateContentForEntry_SensesAndSubSensesWithNumberingStyle() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2392,7 +2392,7 @@ public void GenerateXHTMLForEntry_SensesAndSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2409,7 +2409,7 @@ public void GenerateXHTMLForEntry_SensesAndSubSensesWithNumberingStyle() /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_NoSenseNumberFIfStyleSaysNoNumbering() + public void GenerateContentForEntry_NoSenseNumberFIfStyleSaysNoNumbering() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2444,7 +2444,7 @@ public void GenerateXHTMLForEntry_NoSenseNumberFIfStyleSaysNoNumbering() AddSenseToEntry(testEntry, "gloss", m_wsEn, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 0); // Should not have produced sense number if style said not to number it. @@ -2453,7 +2453,7 @@ public void GenerateXHTMLForEntry_NoSenseNumberFIfStyleSaysNoNumbering() } [Test] - public void GenerateXHTMLForEntry_SensesNoneAndSubSensesWithNumberingStyle() + public void GenerateContentForEntry_SensesNoneAndSubSensesWithNumberingStyle() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2504,7 +2504,7 @@ public void GenerateXHTMLForEntry_SensesNoneAndSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string subSensesNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; const string subSenseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -2513,7 +2513,7 @@ public void GenerateXHTMLForEntry_SensesNoneAndSubSensesWithNumberingStyle() } [Test] - public void GenerateXHTMLForEntry_SensesGeneratedForMultipleSubSenses() + public void GenerateContentForEntry_SensesGeneratedForMultipleSubSenses() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2546,7 +2546,7 @@ public void GenerateXHTMLForEntry_SensesGeneratedForMultipleSubSenses() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2559,7 +2559,7 @@ public void GenerateXHTMLForEntry_SensesGeneratedForMultipleSubSenses() } [Test] - public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleJoined() + public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleJoined() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2601,7 +2601,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleJoined() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; @@ -2613,7 +2613,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleJoined() } [Test] - public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleSeparatedByDot() + public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleSeparatedByDot() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2655,7 +2655,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleSeparatedByDo AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; @@ -2667,7 +2667,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleSeparatedByDo } [Test] - public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleNone() + public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleNone() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2709,7 +2709,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleNone() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; @@ -2721,7 +2721,7 @@ public void GenerateXHTMLForEntry_SubSenseParentSenseNumberingStyleNone() } [Test] - public void GenerateXHTMLForEntry_SubSubSensesWithNumberingStyle() + public void GenerateContentForEntry_SubSubSensesWithNumberingStyle() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2754,7 +2754,7 @@ public void GenerateXHTMLForEntry_SubSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; @@ -2777,7 +2777,7 @@ public void GenerateXHTMLForEntry_SubSubSensesWithNumberingStyle() } [Test] - public void GenerateXHTMLForEntry_GeneratesGramInfoFirstEvenSingleSense() + public void GenerateContentForEntry_GeneratesGramInfoFirstEvenSingleSense() { var posNoun = CreatePartOfSpeech("noun", "n"); @@ -2854,7 +2854,7 @@ public void GenerateXHTMLForEntry_GeneratesGramInfoFirstEvenSingleSense() } [Test] - public void GenerateXHTMLForEntry_SubSensesOfSingleSenses_GetFullNumbers() + public void GenerateContentForEntry_SubSensesOfSingleSenses_GetFullNumbers() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2899,7 +2899,7 @@ public void GenerateXHTMLForEntry_SubSensesOfSingleSenses_GetFullNumbers() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[0], "subGloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; const string subSenseContent = senseContent + "/span[@class='sense']/span[@class='shares senses']/span[@class='sensecontent']"; const string subSenseNumberOneOne = subSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]//span[@lang='en' and text()='subGloss']"; @@ -2914,7 +2914,7 @@ public void GenerateXHTMLForEntry_SubSensesOfSingleSenses_GetFullNumbers() /// Sense numbers for Main Entry->Senses->Subentries->Senses should not contain the Component Sense's number [Test] - public void GenerateXHTMLForEntry_SubentriesSensesDontGetMainEntrySensesNumbers() + public void GenerateContentForEntry_SubentriesSensesDontGetMainEntrySensesNumbers() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); @@ -2952,7 +2952,7 @@ public void GenerateXHTMLForEntry_SubentriesSensesDontGetMainEntrySensesNumbers( CreateComplexForm(Cache, testEntry.SensesOS[0], subEntry, true); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='senses']/span[@class='sensecontent']"; @@ -2972,7 +2972,7 @@ public void GenerateXHTMLForEntry_SubentriesSensesDontGetMainEntrySensesNumbers( /// (See LT-17906.) /// [Test] - public void GenerateXHTMLForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseOption() + public void GenerateContentForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseOption() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; @@ -2992,7 +2992,7 @@ public void GenerateXHTMLForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseOpt var testEntry = CreateInterestingLexEntry(Cache); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -3002,7 +3002,7 @@ public void GenerateXHTMLForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseOpt } [Test] - public void GenerateXHTMLForEntry_SenseContentWithGuid() + public void GenerateContentForEntry_SenseContentWithGuid() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; @@ -3022,7 +3022,7 @@ public void GenerateXHTMLForEntry_SenseContentWithGuid() var testEntry = CreateInterestingLexEntry(Cache); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); const string senseEntryGuid = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuid, 1); @@ -3031,7 +3031,7 @@ public void GenerateXHTMLForEntry_SenseContentWithGuid() } [Test] - public void GenerateXHTMLForEntry_ExampleAndTranslationAreGenerated() + public void GenerateContentForEntry_ExampleAndTranslationAreGenerated() { var translationNode = new ConfigurableDictionaryNode { @@ -3075,7 +3075,7 @@ public void GenerateXHTMLForEntry_ExampleAndTranslationAreGenerated() AddExampleToSense(testEntry.SensesOS[0], example, Cache, m_wsFr, m_wsEn, translation); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents']/span[@class='examplescontent']"; var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3085,7 +3085,7 @@ public void GenerateXHTMLForEntry_ExampleAndTranslationAreGenerated() } [Test] - public void GenerateXHTMLForEntry_ExampleSentenceAndTranslationAreGenerated() + public void GenerateContentForEntry_ExampleSentenceAndTranslationAreGenerated() { var translationNode = new ConfigurableDictionaryNode { @@ -3129,7 +3129,7 @@ public void GenerateXHTMLForEntry_ExampleSentenceAndTranslationAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3139,7 +3139,7 @@ public void GenerateXHTMLForEntry_ExampleSentenceAndTranslationAreGenerated() } [Test] - public void GenerateXHTMLForEntry_LineSeperatorUnicodeCharBecomesBrElement() + public void GenerateContentForEntry_LineSeperatorUnicodeCharBecomesBrElement() { var translationNode = new ConfigurableDictionaryNode { @@ -3183,7 +3183,7 @@ public void GenerateXHTMLForEntry_LineSeperatorUnicodeCharBecomesBrElement() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3193,7 +3193,7 @@ public void GenerateXHTMLForEntry_LineSeperatorUnicodeCharBecomesBrElement() } [Test] - public void GenerateXHTMLForEntry_ExtendedNoteChildrenAreGenerated() + public void GenerateContentForEntry_ExtendedNoteChildrenAreGenerated() { var translationNode = new ConfigurableDictionaryNode { @@ -3258,7 +3258,7 @@ public void GenerateXHTMLForEntry_ExtendedNoteChildrenAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); @@ -3276,7 +3276,7 @@ public void GenerateXHTMLForEntry_ExtendedNoteChildrenAreGenerated() } [Test] - public void GenerateXHTMLForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() + public void GenerateContentForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() { var translationNode = new ConfigurableDictionaryNode { @@ -3340,7 +3340,7 @@ public void GenerateXHTMLForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); @@ -3382,7 +3382,7 @@ private ICmPossibility CreateExtendedNoteType(string name) } [Test] - public void GenerateXHTMLForEntry_EnvironmentsAndAllomorphsAreGenerated() + public void GenerateContentForEntry_EnvironmentsAndAllomorphsAreGenerated() { var stringRepNode = new ConfigurableDictionaryNode { @@ -3416,7 +3416,7 @@ public void GenerateXHTMLForEntry_EnvironmentsAndAllomorphsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); const string xPathThruAllomorph = "/div[@class='lexentry']/span[@class='alternateformsos']/span[@class='alternateformso']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( xPathThruAllomorph + "/span[@class='form']/span[@lang='fr' and text()='Allomorph']", 1); @@ -3425,7 +3425,7 @@ public void GenerateXHTMLForEntry_EnvironmentsAndAllomorphsAreGenerated() } [Test] - public void GenerateXHTMLForEntry_ReferencedComplexFormsIncludesSubentriesAndOtherReferencedComplexForms() + public void GenerateContentForEntry_ReferencedComplexFormsIncludesSubentriesAndOtherReferencedComplexForms() { var complexFormNode = new ConfigurableDictionaryNode { @@ -3453,13 +3453,13 @@ public void GenerateXHTMLForEntry_ReferencedComplexFormsIncludesSubentriesAndOth var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } [Test] - public void GenerateXHTMLForEntry_GeneratesLinksForReferencedForms() + public void GenerateContentForEntry_GeneratesLinksForReferencedForms() { var headwordNode = new ConfigurableDictionaryNode { @@ -3505,7 +3505,7 @@ public void GenerateXHTMLForEntry_GeneratesLinksForReferencedForms() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( @@ -3513,7 +3513,7 @@ public void GenerateXHTMLForEntry_GeneratesLinksForReferencedForms() } [Test] - public void GenerateXHTMLForEntry_GeneratesLinksForPrimaryEntryReferences() + public void GenerateContentForEntry_GeneratesLinksForPrimaryEntryReferences() { var mainEntry = CreateInterestingLexEntry(Cache); AddHeadwordToEntry(mainEntry, "Test", m_wsFr); @@ -3604,7 +3604,7 @@ public void GenerateXHTMLForEntry_GeneratesLinksForPrimaryEntryReferences() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(otherMainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href]", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( @@ -3612,7 +3612,7 @@ public void GenerateXHTMLForEntry_GeneratesLinksForPrimaryEntryReferences() } [Test] - public void GenerateXHTMLForEntry_GeneratesLinksForCrossReferences() + public void GenerateContentForEntry_GeneratesLinksForCrossReferences() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3651,13 +3651,13 @@ public void GenerateXHTMLForEntry_GeneratesLinksForCrossReferences() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); } [Test] - public void GenerateXHTMLForEntry_GeneratesLinksForCrossReferencesWithReferencedNodes() + public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferencedNodes() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3703,13 +3703,13 @@ public void GenerateXHTMLForEntry_GeneratesLinksForCrossReferencesWithReferenced var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='minimallexreferences refdrefs']/span[@class='minimallexreference refdref']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); } [Test] - public void GenerateXHTMLForEntry_GeneratesCrossReferencesOnUnCheckConfigTargets() + public void GenerateContentForEntry_GeneratesCrossReferencesOnUnCheckConfigTargets() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3759,13 +3759,13 @@ public void GenerateXHTMLForEntry_GeneratesCrossReferencesOnUnCheckConfigTargets var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT- - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasNoMatchForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']"); } [Test] - public void GenerateXHTMLForEntry_GeneratesForwardNameForSymmetricCrossReferences() + public void GenerateContentForEntry_GeneratesForwardNameForSymmetricCrossReferences() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3799,7 +3799,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForSymmetricCrossReference var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); const string anyNameXpath = @@ -3809,7 +3809,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForSymmetricCrossReference } [Test] - public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardCrossReferences() + public void GenerateContentForEntry_GeneratesForwardNameForForwardCrossReferences() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3844,7 +3844,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardCrossReferences( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -3854,7 +3854,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardCrossReferences( } [Test] - public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseCrossReferences() + public void GenerateContentForEntry_GeneratesReverseNameForReverseCrossReferences() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3889,7 +3889,7 @@ public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseCrossReferences( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -3899,7 +3899,7 @@ public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseCrossReferences( } [Test] - public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardLexicalRelations() + public void GenerateContentForEntry_GeneratesForwardNameForForwardLexicalRelations() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -3939,7 +3939,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardLexicalRelations var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -3949,7 +3949,7 @@ public void GenerateXHTMLForEntry_GeneratesForwardNameForForwardLexicalRelations } [Test] - public void GenerateXHTMLForEntry_GeneratesLexicalRelationsLabelWithNoRepetition() + public void GenerateContentForEntry_GeneratesLexicalRelationsLabelWithNoRepetition() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry1 = CreateInterestingLexEntry(Cache); @@ -3990,14 +3990,14 @@ public void GenerateXHTMLForEntry_GeneratesLexicalRelationsLabelWithNoRepetition var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); } [Test] - public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseLexicalRelations() + public void GenerateContentForEntry_GeneratesReverseNameForReverseLexicalRelations() { var mainEntry = CreateInterestingLexEntry(Cache); var referencedEntry = CreateInterestingLexEntry(Cache); @@ -4037,7 +4037,7 @@ public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseLexicalRelations var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -4047,7 +4047,7 @@ public void GenerateXHTMLForEntry_GeneratesReverseNameForReverseLexicalRelations } [Test] - public void GenerateXHTMLForEntry_LexicalRelationsSortbyNodeOptionsOrder() + public void GenerateContentForEntry_LexicalRelationsSortbyNodeOptionsOrder() { var mainEntry = CreateInterestingLexEntry(Cache); var compareReferencedEntry = CreateInterestingLexEntry(Cache); @@ -4092,7 +4092,7 @@ public void GenerateXHTMLForEntry_LexicalRelationsSortbyNodeOptionsOrder() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); const string NameXpath = "//span[@class='minimallexreferences']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name']/span[@lang='en' and text()='{1}']"; var fwdNameFirstXpath = string.Format(NameXpath, "1", etyRefTypeName); var fwdNameSecondXpath = string.Format(NameXpath, "2", comRefTypeName); @@ -4104,7 +4104,7 @@ public void GenerateXHTMLForEntry_LexicalRelationsSortbyNodeOptionsOrder() Options = DictionaryDetailsControllerTests.ListOfEnabledDNOsFromStrings(new[] { comRefType.Guid + ":f", etyRefType.Guid + ":f" }) }; - var resultAfterChange = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var resultAfterChange = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); var fwdNameChangedFirstXpath = string.Format(NameXpath, "1", comRefTypeName); var fwdNameChangedSecondXpath = string.Format(NameXpath, "2", etyRefTypeName); AssertThatXmlIn.String(resultAfterChange).HasSpecifiedNumberOfMatchesForXpath(fwdNameChangedFirstXpath, 1); @@ -4112,7 +4112,7 @@ public void GenerateXHTMLForEntry_LexicalRelationsSortbyNodeOptionsOrder() } [Test] - public void GenerateXHTMLForEntry_GeneratesAsymmetricRelationsProperly() + public void GenerateContentForEntry_GeneratesAsymmetricRelationsProperly() { const string firstWord = "corps"; var bodyEntry = CreateInterestingLexEntry(Cache, firstWord, "body"); @@ -4171,7 +4171,7 @@ public void GenerateXHTMLForEntry_GeneratesAsymmetricRelationsProperly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(armEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(armEntry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(output).HasNoMatchForXpath(fwdNameXpath); @@ -4192,7 +4192,7 @@ public void GenerateXHTMLForEntry_GeneratesAsymmetricRelationsProperly() } [Test] - public void GenerateXHTMLForEntry_GeneratesConfigTargetsForSubSenseProperly() + public void GenerateContentForEntry_GeneratesConfigTargetsForSubSenseProperly() { const string firstHeadword = "homme"; var firstEntry = CreateInterestingLexEntry(Cache, firstHeadword); @@ -4249,7 +4249,7 @@ public void GenerateXHTMLForEntry_GeneratesConfigTargetsForSubSenseProperly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(firstEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings); var goodTarget = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstHeadword); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); @@ -4258,7 +4258,7 @@ public void GenerateXHTMLForEntry_GeneratesConfigTargetsForSubSenseProperly() } [Test] - public void GenerateXHTMLForEntry_GeneratesConfigTargetsForTreeBetweenSenses() + public void GenerateContentForEntry_GeneratesConfigTargetsForTreeBetweenSenses() { const string headword = "headword"; var firstEntry = CreateInterestingLexEntry(Cache, headword, "b1"); @@ -4318,7 +4318,7 @@ public void GenerateXHTMLForEntry_GeneratesConfigTargetsForTreeBetweenSenses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings); var goodTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget1, 1); @@ -4712,7 +4712,7 @@ public void IsListItemSelectedForExport_EntryWithNoOptions_Throws() } [Test] - public void GenerateXHTMLForEntry_NoncheckedListItemsAreNotGenerated() + public void GenerateContentForEntry_NoncheckedListItemsAreNotGenerated() { var formNode = new ConfigurableDictionaryNode { @@ -4751,13 +4751,13 @@ public void GenerateXHTMLForEntry_NoncheckedListItemsAreNotGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); } [Test] - public void GenerateXHTMLForEntry_CheckedListItemsAreGenerated() + public void GenerateContentForEntry_CheckedListItemsAreGenerated() { var formNode = new ConfigurableDictionaryNode { @@ -4802,13 +4802,13 @@ public void GenerateXHTMLForEntry_CheckedListItemsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); } [Test] - public void GenerateXHTMLForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() + public void GenerateContentForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() { var mainEntry = CreateInterestingLexEntry(Cache); var variantForm = CreateInterestingLexEntry(Cache); @@ -4859,14 +4859,14 @@ public void GenerateXHTMLForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='referencedentries']" + "/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); } [Test] - public void GenerateXHTMLForEntry_ReferencedComplexFormsUnderSensesIncludesSubentriesAndOtherReferencedComplexForms() + public void GenerateContentForEntry_ReferencedComplexFormsUnderSensesIncludesSubentriesAndOtherReferencedComplexForms() { var complexFormNode = new ConfigurableDictionaryNode { @@ -4901,7 +4901,7 @@ public void GenerateXHTMLForEntry_ReferencedComplexFormsUnderSensesIncludesSuben var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( xpathThruSense + "/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } @@ -5075,7 +5075,7 @@ public void GenerateLetterHeaderIfNeeded_GeneratesHeaderCitationFormSorting() } [Test] - public void GenerateXHTMLForEntry_OneSenseWithSinglePicture() + public void GenerateContentForEntry_OneSenseWithSinglePicture() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var thumbNailNode = new ConfigurableDictionaryNode { FieldDescription = "PictureFileRA", CSSClassNameOverride = "photo" }; @@ -5104,7 +5104,7 @@ public void GenerateXHTMLForEntry_OneSenseWithSinglePicture() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']//span[text()='caption']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -5113,7 +5113,7 @@ public void GenerateXHTMLForEntry_OneSenseWithSinglePicture() } [Test] - public void GenerateXHTMLForEntry_PictureFileMissing() + public void GenerateContentForEntry_PictureFileMissing() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var thumbNailNode = new ConfigurableDictionaryNode { FieldDescription = "PictureFileRA", CSSClassNameOverride = "photo" }; @@ -5142,13 +5142,13 @@ public void GenerateXHTMLForEntry_PictureFileMissing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); Assert.That(result, Is.Empty); } /// LT-21573: PictureFileRA can be null after an incomplete SFM import [Test] - public void GenerateXHTMLForEntry_PictureFileRAMissing() + public void GenerateContentForEntry_PictureFileRAMissing() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var thumbNailNode = new ConfigurableDictionaryNode { FieldDescription = "PictureFileRA", CSSClassNameOverride = "photo" }; @@ -5178,12 +5178,12 @@ public void GenerateXHTMLForEntry_PictureFileRAMissing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); Assert.That(result, Is.Empty); } [Test] - public void GenerateXHTMLForEntry_PictureWithCreator() + public void GenerateContentForEntry_PictureWithCreator() { var thumbNailNode = new ConfigurableDictionaryNode { FieldDescription = "PictureFileRA", CSSClassNameOverride = "photo" }; var creatorNode = new ConfigurableDictionaryNode @@ -5211,7 +5211,7 @@ public void GenerateXHTMLForEntry_PictureWithCreator() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='creator' and text()='Jason Naylor']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -5220,7 +5220,7 @@ public void GenerateXHTMLForEntry_PictureWithCreator() } [Test] - public void GenerateXHTMLForEntry_PictureWithNonUnicodePathLinksCorrectly() + public void GenerateContentForEntry_PictureWithNonUnicodePathLinksCorrectly() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5243,13 +5243,13 @@ public void GenerateXHTMLForEntry_PictureWithNonUnicodePathLinksCorrectly() // generates a src attribute with an absolute file path var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); } [Test] - public void GenerateXHTMLForEntry_PictureCopiedAndRelativePathUsed() + public void GenerateContentForEntry_PictureCopiedAndRelativePathUsed() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5271,7 +5271,7 @@ public void GenerateXHTMLForEntry_PictureCopiedAndRelativePathUsed() try { //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5288,7 +5288,7 @@ public void GenerateXHTMLForEntry_PictureCopiedAndRelativePathUsed() } [Test] - public void GenerateXHTMLForEntry_MissingPictureFileDoesNotCrashOnCopy() + public void GenerateContentForEntry_MissingPictureFileDoesNotCrashOnCopy() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5308,7 +5308,7 @@ public void GenerateXHTMLForEntry_MissingPictureFileDoesNotCrashOnCopy() try { //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5324,7 +5324,7 @@ public void GenerateXHTMLForEntry_MissingPictureFileDoesNotCrashOnCopy() } [Test] - public void GenerateXHTMLForEntry_TwoDifferentFilesGetTwoDifferentResults() + public void GenerateContentForEntry_TwoDifferentFilesGetTwoDifferentResults() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5363,7 +5363,7 @@ public void GenerateXHTMLForEntry_TwoDifferentFilesGetTwoDifferentResults() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5388,7 +5388,7 @@ public void GenerateXHTMLForEntry_TwoDifferentFilesGetTwoDifferentResults() } [Test] - public void GenerateXHTMLForEntry_UniqueIdsForSameFile() + public void GenerateContentForEntry_UniqueIdsForSameFile() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5418,7 +5418,7 @@ public void GenerateXHTMLForEntry_UniqueIdsForSameFile() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); const string pictureXPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img"; var pictureWithComposedPath = pictureXPath + "[contains(@src, '" + pictureRelativePath + "')]"; @@ -5439,7 +5439,7 @@ public void GenerateXHTMLForEntry_UniqueIdsForSameFile() } [Test] - public void GenerateXHTMLForEntry_BadFileNameDoesNotCrash() + public void GenerateContentForEntry_BadFileNameDoesNotCrash() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5457,11 +5457,11 @@ public void GenerateXHTMLForEntry_BadFileNameDoesNotCrash() var tempFolder = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "ConfigDictPictureExportTest")); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings)); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings)); } [Test] - public void GenerateXHTMLForEntry_NullFilePathDoesNotCrash() + public void GenerateContentForEntry_NullFilePathDoesNotCrash() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5478,11 +5478,11 @@ public void GenerateXHTMLForEntry_NullFilePathDoesNotCrash() var tempFolder = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "ConfigDictPictureExportTest")); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings)); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings)); } [Test] - public void GenerateXHTMLForEntry_NullInternalPathDoesNotCrash() + public void GenerateContentForEntry_NullInternalPathDoesNotCrash() { var thumbNailNode = new ConfigurableDictionaryNode { @@ -5529,11 +5529,11 @@ public void GenerateXHTMLForEntry_NullInternalPathDoesNotCrash() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings)); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings)); } [Test] - public void GenerateXHTMLForEntry_TwoDifferentLinksToTheSamefileWorks() + public void GenerateContentForEntry_TwoDifferentLinksToTheSamefileWorks() { var mainEntryNode = CreatePictureModel(); var testEntry = CreateInterestingLexEntry(Cache); @@ -5560,7 +5560,7 @@ public void GenerateXHTMLForEntry_TwoDifferentLinksToTheSamefileWorks() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5578,7 +5578,7 @@ public void GenerateXHTMLForEntry_TwoDifferentLinksToTheSamefileWorks() } [Test] - public void GenerateXHTMLForEntry_StringCustomFieldGeneratesContent() + public void GenerateContentForEntry_StringCustomFieldGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5602,14 +5602,14 @@ public void GenerateXHTMLForEntry_StringCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_CustomFieldInGroupingNodeGeneratesContent() + public void GenerateContentForEntry_CustomFieldInGroupingNodeGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5639,14 +5639,14 @@ public void GenerateXHTMLForEntry_CustomFieldInGroupingNodeGeneratesContent() Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = $"/div[@class='lexentry']/span[@class='grouping_customgroup']/span[@class='customstring']/span[text()='" + customData + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_CustomFieldInNestedGroupingNodeGeneratesContent() + public void GenerateContentForEntry_CustomFieldInNestedGroupingNodeGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5688,7 +5688,7 @@ public void GenerateXHTMLForEntry_CustomFieldInNestedGroupingNodeGeneratesConten Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string grpXPath = "/span[@class='grouping_customgroup']"; var customDataPath = $"/div[@class='lexentry']{grpXPath}{grpXPath}{grpXPath}/span[@class='customstring']/span[text()='{customData}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -5696,7 +5696,7 @@ public void GenerateXHTMLForEntry_CustomFieldInNestedGroupingNodeGeneratesConten } [Test] - public void GenerateXHTMLForEntry_GetPropertyTypeForConfigurationNode_StringCustomFieldIsPrimitive() + public void GenerateContentForEntry_GetPropertyTypeForConfigurationNode_StringCustomFieldIsPrimitive() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5724,7 +5724,7 @@ public void GenerateXHTMLForEntry_GetPropertyTypeForConfigurationNode_StringCust } [Test] - public void GenerateXHTMLForEntry_StringCustomFieldOnSenseGeneratesContent() + public void GenerateContentForEntry_StringCustomFieldOnSenseGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexSense"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5756,14 +5756,14 @@ public void GenerateXHTMLForEntry_StringCustomFieldOnSenseGeneratesContent() Cache.MainCacheAccessor.SetString(testSence.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='l']/span[@class='es']/span[@class='e']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_StringCustomFieldOnExampleGeneratesContent() + public void GenerateContentForEntry_StringCustomFieldOnExampleGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexExampleSentence"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5802,7 +5802,7 @@ public void GenerateXHTMLForEntry_StringCustomFieldOnExampleGeneratesContent() Cache.MainCacheAccessor.SetString(exampleSentence.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format( "/div[@class='l']/span[@class='es']//span[@class='xs']/span[@class='x']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -5810,7 +5810,7 @@ public void GenerateXHTMLForEntry_StringCustomFieldOnExampleGeneratesContent() } [Test] - public void GenerateXHTMLForEntry_StringCustomFieldOnAllomorphGeneratesContent() + public void GenerateContentForEntry_StringCustomFieldOnAllomorphGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("MoForm"), 0, CellarPropertyType.String, Guid.Empty)) @@ -5841,7 +5841,7 @@ public void GenerateXHTMLForEntry_StringCustomFieldOnAllomorphGeneratesContent() Cache.MainCacheAccessor.SetString(allomorph.Hvo, customField.Flid, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format( "/div[@class='l']/span[@class='as']/span[@class='a']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -5849,7 +5849,7 @@ public void GenerateXHTMLForEntry_StringCustomFieldOnAllomorphGeneratesContent() } [Test] - public void GenerateXHTMLForEntry_MultiStringCustomFieldGeneratesContent() + public void GenerateContentForEntry_MultiStringCustomFieldGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomString", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.MultiString, Guid.Empty)) @@ -5873,14 +5873,14 @@ public void GenerateXHTMLForEntry_MultiStringCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetMultiStringAlt(testEntry.Hvo, customField.Flid, m_wsEn, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForEntry() + public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentForEntry() { var entryCustom = new ConfigurableDictionaryNode { @@ -5935,7 +5935,7 @@ public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForE TsStringUtils.MakeString(senseCustomData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryDataPath, 1); var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); @@ -5944,7 +5944,7 @@ public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForE } [Test] - public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForSense() + public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentForSense() { var entryCustom = new ConfigurableDictionaryNode { @@ -5999,7 +5999,7 @@ public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForS TsStringUtils.MakeString(senseCustomData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath, message: "Ref is to Sense; should be no Entry Custom Data"); var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); @@ -6008,7 +6008,7 @@ public void GenerateXHTMLForEntry_CustomFieldOnISenseOrEntryGeneratesContentForS } [Test] - public void GenerateXHTMLForEntry_CustomFieldOnRefdLexEntryGeneratesContent() + public void GenerateContentForEntry_CustomFieldOnRefdLexEntryGeneratesContent() { var customConfig = new ConfigurableDictionaryNode { @@ -6039,14 +6039,14 @@ public void GenerateXHTMLForEntry_CustomFieldOnRefdLexEntryGeneratesContent() Cache.MainCacheAccessor.SetMultiStringAlt(refdEntry.Hvo, customField.Flid, m_wsEn, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='vars']/span[@class='var']/span[@class='owningentry_customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_MultiStringDefinition_GeneratesMultilingualSpans() + public void GenerateContentForEntry_MultiStringDefinition_GeneratesMultilingualSpans() { var definitionNode = new ConfigurableDictionaryNode { @@ -6075,7 +6075,7 @@ public void GenerateXHTMLForEntry_MultiStringDefinition_GeneratesMultilingualSpa var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var definitionXpath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition']/span[@lang='en']"; var str1Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[0]); var str2Xpath = string.Format(definitionXpath + "/span[@lang='fr' and text()='{0}']", multirunContent[1]); @@ -6091,7 +6091,7 @@ public void GenerateXHTMLForEntry_MultiStringDefinition_GeneratesMultilingualSpa } [Test] - public void GenerateXHTMLForEntry_ListItemCustomFieldGeneratesContent() + public void GenerateContentForEntry_ListItemCustomFieldGeneratesContent() { var wsEn = Cache.WritingSystemFactory.GetWsFromStr("en"); var possibilityItem = Cache.LanguageProject.LocationsOA.FindOrCreatePossibility("Djbuti", wsEn); @@ -6123,14 +6123,14 @@ public void GenerateXHTMLForEntry_ListItemCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, possibilityItem.Hvo); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string customDataPath = "/div[@class='lexentry']/span[@class='customlistitem']/span[@class='name']/span[text()='Djbuti']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_MultiListItemCustomFieldGeneratesContent() + public void GenerateContentForEntry_MultiListItemCustomFieldGeneratesContent() { var wsEn = Cache.WritingSystemFactory.GetWsFromStr("en"); var possibilityItem1 = Cache.LanguageProject.LocationsOA.FindOrCreatePossibility("Dallas", wsEn); @@ -6164,7 +6164,7 @@ public void GenerateXHTMLForEntry_MultiListItemCustomFieldGeneratesContent() Cache.MainCacheAccessor.Replace(testEntry.Hvo, customField.Flid, 0, 0, new[] { possibilityItem1.Hvo, possibilityItem2.Hvo }, 2); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); const string customDataPath1 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Dallas']"; const string customDataPath2 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Barcelona']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath1, 1); @@ -6173,7 +6173,7 @@ public void GenerateXHTMLForEntry_MultiListItemCustomFieldGeneratesContent() } [Test] - public void GenerateXHTMLForEntry_DateCustomFieldGeneratesContent() + public void GenerateContentForEntry_DateCustomFieldGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomDate", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.Time, Guid.Empty)) @@ -6196,14 +6196,14 @@ public void GenerateXHTMLForEntry_DateCustomFieldGeneratesContent() SilTime.SetTimeProperty(Cache.MainCacheAccessor, testEntry.Hvo, customField.Flid, customData); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customdate' and text()='{0}']", customData.ToLongDateString()); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_IntegerCustomFieldGeneratesContent() + public void GenerateContentForEntry_IntegerCustomFieldGeneratesContent() { using (var customField = new CustomFieldForTest(Cache, "CustomInteger", Cache.MetaDataCacheAccessor.GetClassId("LexEntry"), 0, CellarPropertyType.Integer, Guid.Empty)) @@ -6226,14 +6226,14 @@ public void GenerateXHTMLForEntry_IntegerCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetInt(testEntry.Hvo, customField.Flid, customData); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='custominteger' and text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } [Test] - public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() + public void GenerateContentForEntry_MultiLineCustomFieldGeneratesContent() { using ( var customField = new CustomFieldForTest(Cache, "MultiplelineTest", @@ -6257,7 +6257,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, text.Hvo); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, rootNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, settings); const string customDataPath = "/div[@class='lexentry']/div/span[text()='First para Custom string'] | /div[@class='lexentry']/div/span[text()='Second para Custom string']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 2); @@ -6265,7 +6265,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() } [Test] - public void GenerateXHTMLForEntry_VariantOfReferencedHeadWord() + public void GenerateContentForEntry_VariantOfReferencedHeadWord() { var headwordNode = new ConfigurableDictionaryNode { @@ -6295,7 +6295,7 @@ public void GenerateXHTMLForEntry_VariantOfReferencedHeadWord() CreateVariantForm(Cache, variantForm, mainEntry); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); const string referencedEntries = "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result) @@ -6303,7 +6303,7 @@ public void GenerateXHTMLForEntry_VariantOfReferencedHeadWord() } [Test] - public void GenerateXHTMLForEntry_WsAudiowithHyperlink() + public void GenerateContentForEntry_WsAudiowithHyperlink() { CoreWritingSystemDefinition wsEnAudio; Cache.ServiceLocator.WritingSystemManager.GetOrSet("en-Zxxx-x-audio", out wsEnAudio); @@ -6326,7 +6326,7 @@ public void GenerateXHTMLForEntry_WsAudiowithHyperlink() senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string audioTagwithSource = "//audio/source/@src"; AssertThatXmlIn.String(result) @@ -6348,7 +6348,7 @@ public void GenerateXHTMLForEntry_WsAudiowithHyperlink() [Test] [TestCase(true)] //Is WebExport so the copied .wav file should be converted to an .mp3 file [TestCase(false)] //Is not a WebExport so the copied .wav file should remain a .wav file - public void GenerateXHTMLForEntry_AudioConversionDestinationDoesNotExist(bool isWebExport) + public void GenerateContentForEntry_AudioConversionDestinationDoesNotExist(bool isWebExport) { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -6415,14 +6415,14 @@ public void GenerateXHTMLForEntry_AudioConversionDestinationDoesNotExist(bool is File.Copy(path, Path.Combine(destination, Path.GetFileName(path)), true); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); if (isWebExport) { Assert.That(result, Contains.Substring("abu2.mp3"), "The automatic audio conversion in the CopyFileSafely method failed"); } else { - Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateXHTMLForEntry returned a string that did not include abu2.wav"); + Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateContentForEntry returned a string that did not include abu2.wav"); } } } @@ -6435,7 +6435,7 @@ public void GenerateXHTMLForEntry_AudioConversionDestinationDoesNotExist(bool is [Test] [TestCase(true)] //Is WebExport so the copied .wav file should be converted to an .mp3 file [TestCase(false)] //Is not a WebExport so the copied .wav file should remain a .wav file - public void GenerateXHTMLForEntry_AudioConversionIdenticalFileExists(bool isWebExport) + public void GenerateContentForEntry_AudioConversionIdenticalFileExists(bool isWebExport) { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -6504,14 +6504,14 @@ public void GenerateXHTMLForEntry_AudioConversionIdenticalFileExists(bool isWebE File.Copy(path, Path.Combine(destination, Path.GetFileName(path)), true); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); if (isWebExport) { Assert.That(result, Contains.Substring("abu2.mp3"), "The automatic audio conversion in the CopyFileSafely method failed"); } else { - Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateXHTMLForEntry returned a string that did not include abu2.wav"); + Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateContentForEntry returned a string that did not include abu2.wav"); } } } @@ -6525,7 +6525,7 @@ public void GenerateXHTMLForEntry_AudioConversionIdenticalFileExists(bool isWebE [Test] [TestCase(true)] //Is WebExport so the copied .wav file should be converted to an .mp3 file [TestCase(false)] //Is not a WebExport so the copied .wav file should remain a .wav file - public void GenerateXHTMLForEntry_AudioConversionNonIdenticalFileExists(bool isWebExport) + public void GenerateContentForEntry_AudioConversionNonIdenticalFileExists(bool isWebExport) { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -6600,20 +6600,20 @@ public void GenerateXHTMLForEntry_AudioConversionNonIdenticalFileExists(bool isW } //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); if (isWebExport) { Assert.That(result, Contains.Substring("abu21.mp3"), "The automatic audio conversion code in the CopyFileSafely method did not change the file name as it should have since a file with the same name but different contents already exists"); } else { - Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateXHTMLForEntry returned a string that did not include abu2.wav"); + Assert.That(result, Contains.Substring("abu2.wav"), "ConfiguredLcmGenerator.GenerateContentForEntry returned a string that did not include abu2.wav"); } } } [Test] - public void GenerateXHTMLForEntry_WsAudiowithRelativePaths() + public void GenerateContentForEntry_WsAudiowithRelativePaths() { CoreWritingSystemDefinition wsEnAudio; Cache.ServiceLocator.WritingSystemManager.GetOrSet("en-Zxxx-x-audio", out wsEnAudio); @@ -6636,7 +6636,7 @@ public void GenerateXHTMLForEntry_WsAudiowithRelativePaths() senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, "//audio/source/@src"); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); const string safeAudioId = "gTest_Audi_o"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("//audio[contains(@id," + safeAudioId + ")]", 1); @@ -6651,7 +6651,7 @@ public void GenerateXHTMLForEntry_WsAudiowithRelativePaths() } [Test] - public void GenerateXHTMLForEntry_WsAudioCrashOnPrimarySelection() + public void GenerateContentForEntry_WsAudioCrashOnPrimarySelection() { CoreWritingSystemDefinition wsEn, wsEnAudio; Cache.ServiceLocator.WritingSystemManager.GetOrSet("en-Zxxx-x-audio", out wsEnAudio); @@ -6719,7 +6719,7 @@ public void GenerateXHTMLForEntry_WsAudioCrashOnPrimarySelection() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, "//audio/source/@src"); // SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings), "Having an audio ws first should not cause crash."); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings), "Having an audio ws first should not cause crash."); } finally { @@ -6729,7 +6729,7 @@ public void GenerateXHTMLForEntry_WsAudioCrashOnPrimarySelection() } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentryUnderSense() + public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentryUnderSense() { var lexentry = CreateInterestingLexEntry(Cache); var lexsense = lexentry.SensesOS[0]; @@ -6772,7 +6772,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentryUnderSense( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -6784,7 +6784,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentryUnderSense( } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentry() + public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentry() { var lexentry = CreateInterestingLexEntry(Cache); @@ -6820,7 +6820,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentry() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); var fwdNameXpath = string.Format( "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -6835,7 +6835,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubentry() /// Whether the subentry is under a sense of the main entry. We do *not* support subentries under senses of subentries. /// [Test] - public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubsubentry([Values(true, false)] bool isUnderSense) + public void GenerateContentForEntry_GeneratesComplexFormTypeForSubsubentry([Values(true, false)] bool isUnderSense) { var lexentry = CreateInterestingLexEntry(Cache); @@ -6891,7 +6891,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubsubentry([Values var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string fwdNameXpath = "//span[@class='subentries subentries']/span[@class='subentry subentry']/span[@class='subentries subentries']/span[@class='subentry subentry']" + "/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; @@ -6905,7 +6905,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormTypeForSubsubentry([Values } [Test] - public void GenerateXHTMLForEntry_DoesntGeneratesComplexFormType_WhenDisabled() + public void GenerateContentForEntry_DoesntGeneratesComplexFormType_WhenDisabled() { var lexentry = CreateInterestingLexEntry(Cache); @@ -6952,14 +6952,14 @@ public void GenerateXHTMLForEntry_DoesntGeneratesComplexFormType_WhenDisabled() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); StringAssert.DoesNotContain(complexRefAbbr, result); } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexForm_WithEmptyList() + public void GenerateContentForEntry_GeneratesComplexForm_WithEmptyList() { var lexentry = CreateInterestingLexEntry(Cache); var complexEntry = CreateInterestingLexEntry(Cache); @@ -7000,11 +7000,11 @@ public void GenerateXHTMLForEntry_GeneratesComplexForm_WithEmptyList() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings), "Having an empty complexentrytype list after the click event should not cause crash."); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings), "Having an empty complexentrytype list after the click event should not cause crash."); } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexForm_NoTypeSpecified() + public void GenerateContentForEntry_GeneratesComplexForm_NoTypeSpecified() { var lexentry = CreateInterestingLexEntry(Cache); var complexEntry = CreateInterestingLexEntry(Cache); @@ -7039,7 +7039,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexForm_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword']"; @@ -7047,7 +7047,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexForm_NoTypeSpecified() } [Test] - public void GenerateXHTMLForEntry_GeneratesSubentry_NoTypeSpecified() + public void GenerateContentForEntry_GeneratesSubentry_NoTypeSpecified() { var lexentry = CreateInterestingLexEntry(Cache); var subentry = CreateInterestingLexEntry(Cache); @@ -7081,7 +7081,7 @@ public void GenerateXHTMLForEntry_GeneratesSubentry_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='headword']"; @@ -7090,7 +7090,7 @@ public void GenerateXHTMLForEntry_GeneratesSubentry_NoTypeSpecified() // ComplexForm: Don't generate the reference if we are hiding minor entries AND we are publishing to Webonary. [Test] - public void GenerateXHTMLForEntry_ComplexFormDontGenerateReference() + public void GenerateContentForEntry_ComplexFormDontGenerateReference() { var lexentry = CreateInterestingLexEntry(Cache); var subentry = CreateInterestingLexEntry(Cache); @@ -7127,19 +7127,19 @@ public void GenerateXHTMLForEntry_ComplexFormDontGenerateReference() // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries and publishing to Webonary this should NOT generate the reference. settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, true); - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withoutReference, 2); } [Test] - public void GenerateXHTMLForEntry_GeneratesVariant_WithEmptyList() + public void GenerateContentForEntry_GeneratesVariant_WithEmptyList() { var lexentry = CreateInterestingLexEntry(Cache); var variantEntry = CreateInterestingLexEntry(Cache); @@ -7179,11 +7179,11 @@ public void GenerateXHTMLForEntry_GeneratesVariant_WithEmptyList() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings), "Having an empty variantentrytype list after the click event should not cause crash."); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings), "Having an empty variantentrytype list after the click event should not cause crash."); } [Test] - public void GenerateXHTMLForEntry_GeneratesVariant_NoTypeSpecified() + public void GenerateContentForEntry_GeneratesVariant_NoTypeSpecified() { var lexentry = CreateInterestingLexEntry(Cache); var variantEntry = CreateInterestingLexEntry(Cache); @@ -7226,7 +7226,7 @@ public void GenerateXHTMLForEntry_GeneratesVariant_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string refTypeXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']"; @@ -7234,7 +7234,7 @@ public void GenerateXHTMLForEntry_GeneratesVariant_NoTypeSpecified() } [Test] - public void GenerateXHTMLForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMatter() + public void GenerateContentForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMatter() { var lexentry = CreateInterestingLexEntry(Cache); var variantEntry = CreateInterestingLexEntry(Cache); @@ -7264,24 +7264,24 @@ public void GenerateXHTMLForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMatt var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(variantEntry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); Assert.That(result, Is.Null.Or.Empty); // try with HideMinorEntry off variantEntryRef.HideMinorEntry = 0; - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(variantEntry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); // Should get the same results if in Root based view model.IsRootBased = true; - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(variantEntry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); variantEntryRef.HideMinorEntry = 1; - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(variantEntry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); Assert.That(result, Is.Null.Or.Empty); } // Variant: Continue to generate the reference even if we are hiding the minor entry (useful for preview). [Test] - public void GenerateXHTMLForEntry_VariantGenerateReferenceForHiddenEntry() + public void GenerateContentForEntry_VariantGenerateReferenceForHiddenEntry() { var lexentry = CreateInterestingLexEntry(Cache); var variantEntry = CreateInterestingLexEntry(Cache); @@ -7332,19 +7332,19 @@ public void GenerateXHTMLForEntry_VariantGenerateReferenceForHiddenEntry() // When not hiding minor entries this should generate the reference. variantEntryRef.HideMinorEntry = 0; - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries this should still generate the reference. variantEntryRef.HideMinorEntry = 1; - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); } // Variant: Don't generate the reference if we are hiding minor entries AND we are publishing to Webonary. [Test] - public void GenerateXHTMLForEntry_VariantDontGenerateReference() + public void GenerateContentForEntry_VariantDontGenerateReference() { var lexentry = CreateInterestingLexEntry(Cache); var variantEntry = CreateInterestingLexEntry(Cache); @@ -7396,13 +7396,13 @@ public void GenerateXHTMLForEntry_VariantDontGenerateReference() // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries and publishing to Webonary this should NOT generate the reference. settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, true); - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withoutReference, 2); } @@ -7410,7 +7410,7 @@ public void GenerateXHTMLForEntry_VariantDontGenerateReference() public enum FormType { Specified, Unspecified, None } [Test] - public void GenerateXHTMLForEntry_ReferencedNode_GeneratesBothClasses() + public void GenerateContentForEntry_ReferencedNode_GeneratesBothClasses() { var lexentry = CreateInterestingLexEntry(Cache); var subentry = CreateInterestingLexEntry(Cache); @@ -7445,13 +7445,13 @@ public void GenerateXHTMLForEntry_ReferencedNode_GeneratesBothClasses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string headwordXpath = "//span[@class='reffingsubs sharedsubentries']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } [Test] - public void GenerateXHTMLForEntry_GeneratesCorrectMainAndMinorEntries() + public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() { var firstMainEntry = CreateInterestingLexEntry(Cache); var idiom = CreateInterestingLexEntry(Cache, "entry1", "myComplexForm"); @@ -7479,18 +7479,18 @@ public void GenerateXHTMLForEntry_GeneratesCorrectMainAndMinorEntries() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForMainEntry(idiom, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); var complexOptions = (DictionaryNodeListOptions)mainEntryNode.DictionaryNodeOptions; complexOptions.Options[0].IsEnabled = false; - result = ConfiguredLcmGenerator.GenerateXHTMLForMainEntry(idiom, mainEntryNode, null, settings, 1); + result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 1); Assert.IsEmpty(result); } /// Note that the "Unspecified" Types mentioned here are truly unspecified, not the specified Type "Unspecified Form Type" [Test] - public void GenerateXHTMLForEntry_GeneratesCorrectMinorEntries( + public void GenerateContentForEntry_GeneratesCorrectMinorEntries( [Values(FormType.Specified, FormType.Unspecified, FormType.None)] FormType complexForm, [Values(true, false)] bool isUnspecifiedComplexTypeEnabled, [Values(FormType.Specified, FormType.Unspecified, FormType.None)] FormType variantForm, @@ -7548,7 +7548,7 @@ public void GenerateXHTMLForEntry_GeneratesCorrectMinorEntries( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(minorEntry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(minorEntry, model, null, settings); var isComplexFormShowing = complexForm == FormType.Unspecified && isUnspecifiedComplexTypeEnabled; var isVariantFormShowing = variantForm == FormType.Unspecified && isUnspecifiedVariantTypeEnabled; @@ -7580,7 +7580,7 @@ public void IsCollectionType() } [Test] - public void GenerateXHTMLForEntry_FilterByPublication() + public void GenerateContentForEntry_FilterByPublication() { // Note that my HS French is nonexistent after 40+ years. But this is only test code... var typeMain = CreatePublicationType("main", Cache); @@ -7794,7 +7794,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryCorps, mainEntryNode, pubEverything, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubEverything, settings); Console.WriteLine(output); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. @@ -7806,7 +7806,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 1); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryCorps, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubMain, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7820,7 +7820,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchBodyIsBig, 1); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryCorps, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubTest, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7834,7 +7834,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchCorpseIsDead, 1); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryBras, mainEntryNode, pubEverything, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubEverything, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7843,7 +7843,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 2); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryBras, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubMain, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7854,7 +7854,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() //SUT // We can still produce test publication output for the entry since we have a copy of it. Its senses and // examples should not be displayed because the senses are separately hidden. - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryBras, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubTest, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test output doesn't display the senses and examples. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7863,7 +7863,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 0); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOreille, mainEntryNode, pubEverything, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubEverything, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7874,7 +7874,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() //SUT // We can still produce main publication output for the entry since we have a copy of it. Its sense and // example should not be displayed because the sense is separately hidden. - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOreille, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubMain, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test output doesn't display the sense and example. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7883,7 +7883,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 0); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOreille, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubTest, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7897,7 +7897,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7908,7 +7908,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 1); //SUT - output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubTest, settings); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7920,7 +7920,7 @@ public void GenerateXHTMLForEntry_FilterByPublication() } [Test] - public void GenerateXHTMLForEntry_GeneratesVariantEntryTypesLabelWithNoRepetition() + public void GenerateContentForEntry_GeneratesVariantEntryTypesLabelWithNoRepetition() { var variantTypeNameNode = new ConfigurableDictionaryNode { @@ -7972,14 +7972,14 @@ public void GenerateXHTMLForEntry_GeneratesVariantEntryTypesLabelWithNoRepetitio using (CreateVariantForm(Cache, entryEntry, ve4, new Guid("00000000-0000-0000-0000-000000000004"), null)) // no Type; none generated { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings); const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 2); } } [Test] - public void GenerateXHTMLForEntry_GeneratesVariantEntryTypesShowOnlySelectedListItem() + public void GenerateContentForEntry_GeneratesVariantEntryTypesShowOnlySelectedListItem() { var variantTypeNameNode = new ConfigurableDictionaryNode { @@ -8035,7 +8035,7 @@ public void GenerateXHTMLForEntry_GeneratesVariantEntryTypesShowOnlySelectedList CreateVariantForm(Cache, entryEntry, ve1, "Free Variant"); // unique Type; CreateVariantForm(Cache, entryEntry, ve2, "Spelling Variant"); // unique Type; UnChecked var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings); const string matchFreeVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Free Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFreeVariantRef, 1); const string matchSpellingVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Spelling Variant']"; @@ -8043,7 +8043,7 @@ public void GenerateXHTMLForEntry_GeneratesVariantEntryTypesShowOnlySelectedList } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexFormEntryTypesLabelWithNoRepetition() + public void GenerateContentForEntry_GeneratesComplexFormEntryTypesLabelWithNoRepetition() { var typeMain = Cache.LangProject.LexDbOA.PublicationTypesOA.PossibilitiesOS[0]; @@ -8098,13 +8098,13 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormEntryTypesLabelWithNoRepet }; CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); const string matchComplexFormRef = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormRef, 1); } [Test] - public void GenerateXHTMLForEntry_GeneratesComplexFormEntryTypesAndNamesGroup() + public void GenerateContentForEntry_GeneratesComplexFormEntryTypesAndNamesGroup() { var typeMain = Cache.LangProject.LexDbOA.PublicationTypesOA.PossibilitiesOS[0]; @@ -8161,7 +8161,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormEntryTypesAndNamesGroup() }; CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); const string matchComplexFormTypeCompound = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Compound']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeCompound, 1); const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Idiom']"; @@ -8171,7 +8171,7 @@ public void GenerateXHTMLForEntry_GeneratesComplexFormEntryTypesAndNamesGroup() } [Test] - public void GenerateXHTMLForEntry_ComplexFormAndSenseInPara() + public void GenerateContentForEntry_ComplexFormAndSenseInPara() { var lexentry = CreateInterestingLexEntry(Cache); @@ -8214,7 +8214,7 @@ public void GenerateXHTMLForEntry_ComplexFormAndSenseInPara() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, DefaultSettings); const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; var paracontinuationxpath = string.Format( "div[@class='lexentry']//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", @@ -8224,7 +8224,7 @@ public void GenerateXHTMLForEntry_ComplexFormAndSenseInPara() } [Test] - public void GenerateXHTMLForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefinition() + public void GenerateContentForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefinition() { var wsEn = Cache.WritingSystemFactory.GetWsFromStr("en"); var lexentry = CreateInterestingLexEntry(Cache); @@ -8273,25 +8273,25 @@ public void GenerateXHTMLForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefini var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry1, minorEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, settings); const string complexFormEntryRefXpath = "div[@class='minorentrycomplex']/span[@class='complexformentryrefs']/span[@class='complexformentryref']"; const string referencedEntriesXpath = "/span[@class='referencedentries']/span[@class='referencedentry']"; const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntrySummaryDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath1, 1); //SUT - var result2 = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry2, minorEntryNode, null, settings); + var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, settings); const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='gloss2']"; AssertThatXmlIn.String(result2).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath2, 1); //SUT - var result3 = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry3, minorEntryNode, null, settings); + var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, settings); const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntryS3Defn']"; AssertThatXmlIn.String(result3).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath3, 1); } [Test] - public void GenerateXHTMLForEntry_ContinuationParagraphWithEmtpyContentDoesNotGenerateSelfClosingTag() + public void GenerateContentForEntry_ContinuationParagraphWithEmtpyContentDoesNotGenerateSelfClosingTag() { var lexentry = CreateInterestingLexEntry(Cache); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = GetWsOptionsForLanguages(new[] { "en" }) }; @@ -8316,7 +8316,7 @@ public void GenerateXHTMLForEntry_ContinuationParagraphWithEmtpyContentDoesNotGe var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); Assert.That(result, Does.Not.Match(@"
"), @@ -8882,7 +8882,7 @@ public void SavePublishedHtmlWithCustomCssFile() } [Test] - public void GenerateXHTMLForEntry_EmbeddedWritingSystemGeneratesCorrectResult() + public void GenerateContentForEntry_EmbeddedWritingSystemGeneratesCorrectResult() { var headwordNode = new ConfigurableDictionaryNode { @@ -8902,7 +8902,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemGeneratesCorrectResult() var multiRunString = frenchString.Insert(12, englishStr); entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='en']"; const string nestedFr = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); @@ -8910,7 +8910,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemGeneratesCorrectResult() } [Test] - public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGeneratesCorrectResult() + public void GenerateContentForEntry_EmbeddedWritingSystemOfOppositeDirectionGeneratesCorrectResult() { var headwordNode = new ConfigurableDictionaryNode { @@ -8929,7 +8929,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera var wsHe = Cache.ServiceLocator.WritingSystemManager.GetWsFromStr("he"); entry.Bibliography.set_String(wsHe, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; const string extraDirection = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; @@ -8939,7 +8939,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera } [Test] - public void GenerateXHTMLForEntry_WritingSystemOfSameDirectionGeneratesNoExtraDirectionSpan() + public void GenerateContentForEntry_WritingSystemOfSameDirectionGeneratesNoExtraDirectionSpan() { var headwordNode = new ConfigurableDictionaryNode { @@ -8959,7 +8959,7 @@ public void GenerateXHTMLForEntry_WritingSystemOfSameDirectionGeneratesNoExtraDi entry.Bibliography.set_String(wsHe, multiRunString); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, true); // Right-to-Left //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']"; const string extraDirection0 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']"; @@ -8971,7 +8971,7 @@ public void GenerateXHTMLForEntry_WritingSystemOfSameDirectionGeneratesNoExtraDi } [Test] - public void GenerateXHTMLForEntry_EmbeddedHyperlinkGeneratesAnchor() + public void GenerateContentForEntry_EmbeddedHyperlinkGeneratesAnchor() { var headwordNode = new ConfigurableDictionaryNode { @@ -8997,7 +8997,7 @@ public void GenerateXHTMLForEntry_EmbeddedHyperlinkGeneratesAnchor() (char)FwObjDataTypes.kodtExternalPathName + testUrl); entry.Bibliography.set_String(m_wsFr, stringBldr.GetString()); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); string nestedLink = $"/div[@class='lexentry']/span[@class='bib']/span/span/a[@href='{testUrl}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedLink, 1); } @@ -9024,7 +9024,7 @@ private static string HeadwordWsInCrossRefsXpath(string ws, string headword) // } [Test] - public void GenerateXHTMLForEntry_CompareRelations_SimpleSituations_SortByHeadword([Values(true, false)] bool SeparateReferences) + public void GenerateContentForEntry_CompareRelations_SimpleSituations_SortByHeadword([Values(true, false)] bool SeparateReferences) { var mainEntry = CreateInterestingLexEntry(Cache, "MainEntry"); var compareReferencedEntry1 = CreateInterestingLexEntry(Cache, "b", "b comparable"); @@ -9050,7 +9050,7 @@ public void GenerateXHTMLForEntry_CompareRelations_SimpleSituations_SortByHeadwo var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(crossRefOwnerTypeXpath, 1); // ensure there is only one AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); // ...the *correct* one AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "a"), 1); @@ -9059,7 +9059,7 @@ public void GenerateXHTMLForEntry_CompareRelations_SimpleSituations_SortByHeadwo } [Test] - public void GenerateXHTMLForEntry_CompareRelations_ComplexSituation_SortByHeadword() + public void GenerateContentForEntry_CompareRelations_ComplexSituation_SortByHeadword() { var mainEntry = CreateInterestingLexEntry(Cache, "MainEntry"); var compareReferencedEntry1 = CreateInterestingLexEntry(Cache, "b", "b comparable"); @@ -9075,7 +9075,7 @@ public void GenerateXHTMLForEntry_CompareRelations_ComplexSituation_SortByHeadwo var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "a"), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(2, "b"), 1); @@ -9085,7 +9085,7 @@ public void GenerateXHTMLForEntry_CompareRelations_ComplexSituation_SortByHeadwo } [Test] - public void GenerateXHTMLForEntry_CrossRefs_Sequences_SequencePreserved() + public void GenerateContentForEntry_CrossRefs_Sequences_SequencePreserved() { var alphaEntry = CreateInterestingLexEntry(Cache, "alpha", "alpha"); var redEntry = CreateInterestingLexEntry(Cache, "rouge", "red"); @@ -9101,7 +9101,7 @@ public void GenerateXHTMLForEntry_CrossRefs_Sequences_SequencePreserved() var mainEntryNode = ModelForCrossReferences(new[] { colorType.Guid.ToString(), greekType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(alphaEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(alphaEntry, mainEntryNode, null, DefaultSettings); // first sequence: colors: ARGB AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(colorTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "alpha"), 2); // the first in both @@ -9114,7 +9114,7 @@ public void GenerateXHTMLForEntry_CrossRefs_Sequences_SequencePreserved() } [Test] - public void GenerateXHTMLForEntry_CrossRefs_Unidirectional_SequencePreserved() + public void GenerateContentForEntry_CrossRefs_Unidirectional_SequencePreserved() { var stoogesEntry = CreateInterestingLexEntry(Cache, "Stooges"); var larryEntry = CreateInterestingLexEntry(Cache, "Larry"); @@ -9126,7 +9126,7 @@ public void GenerateXHTMLForEntry_CrossRefs_Unidirectional_SequencePreserved() var mainEntryNode = ModelForCrossReferences(new[] { characterType.Guid + ":f" }); // SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(stoogesEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(stoogesEntry, mainEntryNode, null, DefaultSettings); // sequence of Stooges: AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(characterTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "Larry"), 1); @@ -9182,7 +9182,7 @@ private static ConfigurableDictionaryNode ModelForCrossReferences(IEnumerable [Test] - public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true, false)] bool usingSubfield) + public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(true, false)] bool usingSubfield) { var manEntry = CreateInterestingLexEntry(Cache, "homme", "man"); var womanEntry = CreateInterestingLexEntry(Cache, "femme", "woman"); @@ -9288,7 +9288,7 @@ public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true //SUT //Console.WriteLine(LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { manEntry.Hvo, familyEntry.Hvo, girlEntry.Hvo, individualEntry.Hvo }, null, // new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_mediator)); // full output for diagnostics - var manResult = ConfiguredLcmGenerator.GenerateXHTMLForEntry(manEntry, mainEntryNode, null, settings); + var manResult = ConfiguredLcmGenerator.GenerateContentForEntry(manEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(manResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); // antonyms are grouped into one span var idxAntonymAbbr = manResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); var idxWhole = manResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9314,7 +9314,7 @@ public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true // Ignore if usingSubfield. Justification: Part-Whole direction is miscalculated for field=Entry, subfield=MinimalLexReferences (LT-17571) if (!usingSubfield) { - var familyResult = ConfiguredLcmGenerator.GenerateXHTMLForEntry(familyEntry, mainEntryNode, null, settings); + var familyResult = ConfiguredLcmGenerator.GenerateContentForEntry(familyEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(familyResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); idxAntonymAbbr = familyResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = familyResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9327,7 +9327,7 @@ public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true Assert.Less(idxAntonymAbbr, idxAntonymName, "Antonym name should come after Antonym abbreviation"); // SUT: Ensure that both directions of part-whole are kept separate - var girlResult = ConfiguredLcmGenerator.GenerateXHTMLForEntry(girlEntry, mainEntryNode, null, settings); + var girlResult = ConfiguredLcmGenerator.GenerateContentForEntry(girlEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(girlResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); // whole and part idxAntonymAbbr = girlResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = girlResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9340,7 +9340,7 @@ public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true Assert.AreEqual(-1, idxAntonymName, "Antonym name relation should not exist for fille (girl)"); } - var individualResult = ConfiguredLcmGenerator.GenerateXHTMLForEntry(individualEntry, mainEntryNode, null, settings); + var individualResult = ConfiguredLcmGenerator.GenerateContentForEntry(individualEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(individualResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 1); idxAntonymAbbr = individualResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = individualResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9356,7 +9356,7 @@ public void GenerateXHTMLForEntry_LexicalReferencesOrderedCorrectly([Values(true /// LT-17384. LT-17762. Intermittent failures should NOT be ignored. /// [Test] - public void GenerateXHTMLForEntry_VariantsOfEntryAreOrdered() + public void GenerateContentForEntry_VariantsOfEntryAreOrdered() { var lexentry = CreateInterestingLexEntry(Cache); @@ -9400,7 +9400,7 @@ public void GenerateXHTMLForEntry_VariantsOfEntryAreOrdered() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); // Test that variantformentrybackref items are in alphabetical order Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), @@ -9421,7 +9421,7 @@ public void GenerateXHTMLForEntry_VariantsOfEntryAreOrdered() /// LT-20622 Order of Type and Form is important. /// [Test] - public void GenerateXHTMLForEntry_TypeBeforeForm() + public void GenerateContentForEntry_TypeBeforeForm() { var lexentry = CreateInterestingLexEntry(Cache); @@ -9462,7 +9462,7 @@ public void GenerateXHTMLForEntry_TypeBeforeForm() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); // Test that variantentrytypes is before variantformentrybackref Assert.That(result.IndexOf("variantentrytypes", StringComparison.InvariantCulture), @@ -9472,7 +9472,7 @@ public void GenerateXHTMLForEntry_TypeBeforeForm() /// LT-17918. Intermittent failures should NOT be ignored. [Test] - public void GenerateXHTMLForEntry_ComplexFormsAreOrderedAsUserSpecified( + public void GenerateContentForEntry_ComplexFormsAreOrderedAsUserSpecified( [Values(true, false)] bool useNotSubentries, [Values(true, false)] bool useVirtualOrdering, [Values(true, false)] bool showInPara) { var lexentry = CreateInterestingLexEntry(Cache); @@ -9530,7 +9530,7 @@ public void GenerateXHTMLForEntry_ComplexFormsAreOrderedAsUserSpecified( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); // Test that variantformentrybackref items are in (alphabetical or) virtual order Assert.That(result.IndexOf(headwords[0], StringComparison.InvariantCulture), @@ -9547,7 +9547,7 @@ public void GenerateXHTMLForEntry_ComplexFormsAreOrderedAsUserSpecified( /// The implementation code changes were done in GenerateXHTMLForILexEntryRefsByType. /// [Test] - public void GenerateXHTMLForFieldByReflection_VariantFormTypesAreOrderedBasedOnOptionOrdering() + public void GenerateContentForFieldByReflection_VariantFormTypesAreOrderedBasedOnOptionOrdering() { var variantTypeNameNode = new ConfigurableDictionaryNode { @@ -9594,7 +9594,7 @@ public void GenerateXHTMLForFieldByReflection_VariantFormTypesAreOrderedBasedOnO CreateInterestingLexEntry(Cache, "headwordB").MakeVariantOf(lexentry, (ILexEntryType)finalTypeInOptionsList); // SUT1 - var result = ConfiguredLcmGenerator.GenerateXHTMLForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordB", StringComparison.InvariantCulture)), "variant forms not appearing in an order corresponding to their type sorting"); @@ -9603,7 +9603,7 @@ public void GenerateXHTMLForFieldByReflection_VariantFormTypesAreOrderedBasedOnO ((DictionaryNodeListOptions)variantFormNode.DictionaryNodeOptions).Options.Reverse(); // SUT2 - result = ConfiguredLcmGenerator.GenerateXHTMLForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); Assert.That(result.IndexOf("headwordB", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordA", StringComparison.InvariantCulture)), "variant forms not appearing in an order corresponding to their type sorting"); @@ -9611,10 +9611,10 @@ public void GenerateXHTMLForFieldByReflection_VariantFormTypesAreOrderedBasedOnO /// /// LT-18018. - /// The implementation code changes were done in GenerateXHTMLForILexEntryRefsByType. + /// The implementation code changes were done in GenerateContentForLexEntryRefsByType. /// [Test] - public void GenerateXHTMLForFieldByReflection_SubentryTypesAreOrderedBasedOnOptionOrdering() + public void GenerateContentForFieldByReflection_SubentryTypesAreOrderedBasedOnOptionOrdering() { var complexFormTypeNameNode = new ConfigurableDictionaryNode { @@ -9654,7 +9654,7 @@ public void GenerateXHTMLForFieldByReflection_SubentryTypesAreOrderedBasedOnOpti CreateComplexForm(Cache, lexentry, CreateInterestingLexEntry(Cache, "headwordB"), true, finalTypeInOptionsListGuid); // SUT1 - var result = ConfiguredLcmGenerator.GenerateXHTMLForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordB", StringComparison.InvariantCulture)), "Subentries should be sorted by Type"); @@ -9663,7 +9663,7 @@ public void GenerateXHTMLForFieldByReflection_SubentryTypesAreOrderedBasedOnOpti ((DictionaryNodeListOptions)subentryNode.DictionaryNodeOptions).Options.Reverse(); // SUT2 - result = ConfiguredLcmGenerator.GenerateXHTMLForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); Assert.That(result.IndexOf("headwordB", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordA", StringComparison.InvariantCulture)), "Subentries should be sorted by Type"); @@ -9673,7 +9673,7 @@ public void GenerateXHTMLForFieldByReflection_SubentryTypesAreOrderedBasedOnOpti /// LT-18171:Crash displaying entry or doing xhtml export /// [Test] - public void GenerateXHTMLForFieldByReflection_NullOrEmptyMediaFilePathDoesNotCrash() + public void GenerateContentForFieldByReflection_NullOrEmptyMediaFilePathDoesNotCrash() { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -9731,7 +9731,7 @@ public void GenerateXHTMLForFieldByReflection_NullOrEmptyMediaFilePathDoesNotCra var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings), "Invalid filename in CmFile should not lead to crash"); + Assert.DoesNotThrow(() => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings), "Invalid filename in CmFile should not lead to crash"); } [TestCase("Bob", false, "Bo")] @@ -9970,7 +9970,7 @@ public void GenerateNextFewEntries_DownReturnsRequestedEntries() } [Test] - public void GenerateXHTMLForEntry_GroupingNodeGeneratesSpanAndInnerContentWorks() + public void GenerateContentForEntry_GroupingNodeGeneratesSpanAndInnerContentWorks() { var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; @@ -9994,7 +9994,7 @@ public void GenerateXHTMLForEntry_GroupingNodeGeneratesSpanAndInnerContentWorks( var testEntry = CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); const string oneSenseWithGlossOfGloss = "/div[@class='lexentry']/span[@class='grouping_sensegroup']" + "/span[@class='senses']/span[@class='sense']//span[@lang='en' and text()='gloss']"; @@ -10003,7 +10003,7 @@ public void GenerateXHTMLForEntry_GroupingNodeGeneratesSpanAndInnerContentWorks( } [Test] - public void GenerateXHTMLForEntry_GeneratesNFC() + public void GenerateContentForEntry_GeneratesNFC() { var node = new ConfigurableDictionaryNode { @@ -10027,14 +10027,14 @@ public void GenerateXHTMLForEntry_GeneratesNFC() entry.CitationForm.set_String(wsKo, headword); Assert.That(entry.CitationForm.get_String(wsKo).get_IsNormalizedForm(FwNormalizationMode.knmNFD), "Should be NFDecomposed in memory"); Assert.AreEqual(6, headword.Text.Length); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, node, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, node, null, DefaultSettings); var tsResult = TsStringUtils.MakeString(result, Cache.DefaultAnalWs); Assert.False(TsStringUtils.IsNullOrEmpty(tsResult), "Results should have been generated"); Assert.That(tsResult.get_IsNormalizedForm(FwNormalizationMode.knmNFC), "Resulting XHTML should be NFComposed"); } [Test] - public void GenerateXHTMLForEntry_CompareRelations_ComplexSituation_CustomSort() + public void GenerateContentForEntry_CompareRelations_ComplexSituation_CustomSort() { CoreWritingSystemDefinition ws = Cache.LangProject.DefaultVernacularWritingSystem; var customRule = new IcuRulesCollationDefinition("standard") @@ -10056,7 +10056,7 @@ public void GenerateXHTMLForEntry_CompareRelations_ComplexSituation_CustomSort() Assert.That(comRefType, Is.Not.Null); var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "atest"), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(2, "ctest"), 1); @@ -10163,7 +10163,7 @@ public void GenerateXHTMLTemplate_MagicAnalysisWsIdWorks() } [Test] - public void GenerateXHTMLForEntry_BadWaveFileThrowsWithEntryInfo() + public void GenerateContentForEntry_BadWaveFileThrowsWithEntryInfo() { var pronunciationsNode = new ConfigurableDictionaryNode { @@ -10199,7 +10199,7 @@ public void GenerateXHTMLForEntry_BadWaveFileThrowsWithEntryInfo() Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), false, true); //SUT Assert.That( - () => ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, + () => ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings), Throws.Exception.With.Message.Contains("Exception generating entry:")); } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index eeeb9ebd3c..78aa37f124 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -1198,7 +1198,7 @@ public void ClassMappingOverrides_ApplyAtRoot() using (var XHTMLWriter = XmlWriter.Create(xhtmResult)) { XHTMLWriter.WriteStartElement("body"); - var content = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, testNode, null, DefaultSettings); + var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testNode, null, DefaultSettings); XHTMLWriter.WriteRaw(content); XHTMLWriter.WriteEndElement(); XHTMLWriter.Flush(); @@ -1240,7 +1240,7 @@ public void ClassMappingOverrides_ApplyToChildren() Assert.That(cssResult, Does.Not.Contain(".headword")); Assert.That(cssResult, Contains.Substring(".tailwind")); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, testParentNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testParentNode, null, DefaultSettings); const string positiveTest = "//*[@class='tailwind']"; const string negativeTest = "//*[@class='headword']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); @@ -1281,7 +1281,7 @@ public void CssAndXhtmlMatchOnSenseCollectionItems() var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .gloss")); - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, testEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings); const string positiveTest = "/*[@class='lexentry']/span[@class='senses']/span[@class='sense']/span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index eb1e286415..18e21fd0d1 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -156,7 +156,7 @@ public void GenerateJsonForEntry_OneSenseWithGlossGeneratesCorrectResult() var entry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); Console.WriteLine(result); var expectedResult = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]},]}"; @@ -189,7 +189,7 @@ public void GenerateJsonForEntry_DefinitionOrGloss_HandlePerWS() var wsEs = ConfiguredXHTMLGeneratorTests.EnsureWritingSystemSetup(Cache, "es", false); entry.SensesOS.First().Definition.set_String(wsEs, "definition"); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""senses"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss"": [{""lang"":""en"",""value"":""gloss""}, @@ -255,7 +255,7 @@ public void GenerateJsonForEntry_TwoSensesWithSameInfoShowGramInfoFirst_Json() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null) { ContentGenerator = new LcmJsonGenerator(Cache) }; //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0); Console.WriteLine(result); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""msas"": {""mlpartofspeech"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]}, @@ -296,7 +296,7 @@ public void GenerateJsonForEntry_OneSenseWithSinglePicture() var settings = DefaultSettings; //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", ""sensenumber"": [{""lang"":""en"",""value"":""1""}],""caption"": [{""lang"":""en"",""value"":""caption""}]}]}"; @@ -510,7 +510,7 @@ public void GenerateJsonForEntry_FilterByPublication() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0); Assert.That(output, Is.Not.Null.Or.Empty); var expectedResults = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses\": [{\"guid\":\"g" + @@ -580,7 +580,7 @@ public void GenerateJsonForEntry_TypeAfterForm() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNodeTypeAfter); //SUT - var outputTypeAfter = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0); + var outputTypeAfter = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0); Assert.That(outputTypeAfter, Is.Not.Null.Or.Empty); var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + @@ -615,7 +615,7 @@ public void GenerateJsonForEntry_WsAudiowithHyperlink() const string audioFileName = "Test Audi'o.wav"; senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"": ""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; @@ -672,7 +672,7 @@ public void GenerateJsonForEntry_SensibleJsonForVideoFiles() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var output = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0); Assert.That(output, Is.Not.Null.Or.Empty); var expectedResults = "{\"xhtmlTemplate\":\"lexentry\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + @@ -702,7 +702,7 @@ public void GenerateJsonForEntry_SenseNumbersGeneratedForMultipleSenses() var testEntry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); ConfiguredXHTMLGeneratorTests.AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses"":[{""senseNumber"":""1"", ""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""gloss""}]}, {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; @@ -731,7 +731,7 @@ public void GenerateJsonForEntry_EmbeddedWritingSystemGeneratesCorrectResult() var multiRunString = frenchString.Insert(12, englishStr); entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib"": [{""lang"":""fr"",""value"":""French with ""}, {""lang"":""en"",""value"":""English""},{""lang"":""fr"",""value"":"" embedded""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -757,7 +757,7 @@ public void GenerateJsonForEntry_UnicodeLineBreak_GeneratesValidJson() var englishStr = TsStringUtils.MakeString("English\u2028with line break", m_wsEn); entry.Bibliography.set_String(m_wsFr, englishStr); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""bib"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -804,7 +804,7 @@ public void GenerateJsonForEntry_GeneratesForwardNameForForwardLexicalRelations( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""sensesos"": [{""lexsensereferences"": [{""ownertype_name"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -847,7 +847,7 @@ public void GenerateJsonForEntry_EmptyNameOnLexicalRelation_GeneratesEmptyButVal CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""sensesos"": [{""lexsensereferences"": [{}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -874,12 +874,12 @@ public void GenerateJsonForEntry_HomographNumbersGeneratesCorrectResult() var entryTwo = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""1"", ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0); expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""2"", ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -905,13 +905,13 @@ public void GenerateJsonForEntry_GeneratesSpecifiedSortIndex() var entryOne = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); // default value of -1 - result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entryOne, mainEntryNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings); expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -958,7 +958,7 @@ public void GenerateJsonForEntry_TwoDifferentPicturesGetUniqueWebFriendlyPaths() try { //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0); // Bug: The second filename should be different after the export with relative path settings (fix later) var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, @@ -1024,21 +1024,21 @@ public void GenerateJsonForEntry_MinorComplexForm_TemplateTypeCorrect_GeneratesG CssGeneratorTests.PopulateFieldsForTesting(minorEntryNode); ; //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); //SUT - var result2 = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0); + var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0); expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result2, expected); //SUT - var result3 = ConfiguredLcmGenerator.GenerateXHTMLForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0); + var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0); expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -1183,7 +1183,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera entry.Bibliography.set_String(wsHe, multiRunString); //SUT - var json = ConfiguredLcmGenerator.GenerateXHTMLForEntry(entry, mainEntryNode, null, DefaultSettings); + var json = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, ""bib"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -1212,7 +1212,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() var text = ConfiguredXHTMLGeneratorTests.CreateMultiParaText("Custom string", Cache); Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, text.Hvo); //SUT - var result = ConfiguredLcmGenerator.GenerateXHTMLForEntry(testEntry, rootNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, DefaultSettings, 0); var expectedResults = @"{""xhtmlTemplate"":""lexentry"", ""guid"":""g" + testEntry.Guid + @""", From bdb49ec4c3dce8beba9346be730ca62a0bea1bbc Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 27 Oct 2023 13:26:30 -0700 Subject: [PATCH 011/415] Simplifly and reduce generated css styles * Make CssGenerator an instace variable that collects styles while the content is being generated * Generate the css selector to choose only classname and necessary path instead of the full path to the element * Add styles to a dictionary and only generate a new style for a class name if it is not identical to the first one created for that * If a style with the same classname had been seen then include the parent configuration node in the name, adding a number if this also had also been seen before * Use the unique style name in the class attribute of the xhtml Signed-off-by: Jason Naylor Change-Id: Ibfabf18b3616084b5eb4e9341a3de8a015826f84 --- Src/xWorks/ConfiguredLcmGenerator.cs | 122 ++-- Src/xWorks/CssGenerator.cs | 632 +++++++++++------- Src/xWorks/ILcmContentGenerator.cs | 4 +- Src/xWorks/LcmJsonGenerator.cs | 2 +- Src/xWorks/LcmXhtmlGenerator.cs | 14 +- .../ConfiguredXHTMLGeneratorTests.cs | 35 +- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 619 +++++++++-------- 7 files changed, 830 insertions(+), 598 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 872a546542..cc83eb4fdc 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -262,7 +262,7 @@ internal static bool IsMainEntry(ICmObject entry, DictionaryConfigurationModel c return lexEntry.EntryRefsOS.Any(ler => ler.RefType == LexEntryRefTags.krtComplexForm); } - /// Generates XHTML for an ICmObject for a specific ConfigurableDictionaryNode + /// Generates content with the GeneratorSettings.ContentGenerator for an ICmObject for a specific ConfigurableDictionaryNode /// the configuration node must match the entry type internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDictionaryNode configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index = -1) @@ -343,7 +343,7 @@ public static string GetClassNameAttributeForConfig(ConfigurableDictionaryNode c /// /// This method will use reflection to pull data out of the given object based on the given configuration and - /// write out appropriate XHTML. + /// write out appropriate content using the settings parameter. /// /// We use a significant amount of boilerplate code for fields and subfields. Make sure you update both. internal static string GenerateContentForFieldByReflection(object field, ConfigurableDictionaryNode config, @@ -379,6 +379,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu } if (config.IsCustomField && config.SubField == null) { + // REVIEW: We have overloaded terms here, this is a C# class not a css class, consider a different name var customFieldOwnerClassName = GetClassNameForCustomFieldParent(config, settings.Cache); if (!GetPropValueForCustomField(field, config, cache, customFieldOwnerClassName, config.FieldDescription, ref propertyValue)) return string.Empty; @@ -452,10 +453,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu switch (typeForNode) { case PropertyType.CollectionType: - if (!IsCollectionEmpty(propertyValue)) - return GenerateContentForCollection(propertyValue, config, publicationDecorator, field, settings, info); - return string.Empty; - + return !IsCollectionEmpty(propertyValue) ? GenerateContentForCollection(propertyValue, config, publicationDecorator, field, settings, info) : string.Empty; case PropertyType.MoFormType: return GenerateContentForMoForm(propertyValue as IMoForm, config, settings); @@ -512,7 +510,8 @@ private static string GenerateContentForGroupingNode(object field, ConfigurableD { if (config.ReferencedOrDirectChildren != null && config.ReferencedOrDirectChildren.Any(child => child.IsEnabled)) { - return settings.ContentGenerator.GenerateGroupingNode(field, config, publicationDecorator, settings, + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.GenerateGroupingNode(field, className, config, publicationDecorator, settings, (f, c, p, s) => GenerateContentForFieldByReflection(f, c, p, s)); } return string.Empty; @@ -752,8 +751,12 @@ private static string GenerateContentForPossibility(object propertyValue, Config var content = GenerateContentForFieldByReflection(propertyValue, child, publicationDecorator, settings); bldr.Append(content); } + if (bldr.Length > 0) - return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); + { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), className); + } return string.Empty; } @@ -765,22 +768,27 @@ private static string GenerateContentForPictureCaption(object propertyValue, Con content = GenerateContentForStrings(propertyValue as IMultiString, config, settings); else content = GenerateContentForString(propertyValue as ITsString, config, settings); - if (!String.IsNullOrEmpty(content)) - return settings.ContentGenerator.WriteProcessedObject(true, content, GetClassNameAttributeForConfig(config)); - return String.Empty; + if (!string.IsNullOrEmpty(content)) + { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.WriteProcessedObject(true, content, className); + } + return string.Empty; } private static string GenerateContentForPicture(ICmFile pictureFile, ConfigurableDictionaryNode config, ICmObject owner, GeneratorSettings settings) { var srcAttribute = GenerateSrcAttributeFromFilePath(pictureFile, settings.UseRelativePaths ? "pictures" : null, settings); - if (!String.IsNullOrEmpty(srcAttribute)) + if (!string.IsNullOrEmpty(srcAttribute)) { - // the XHTML id attribute must be unique. The owning ICmPicture has a unique guid. - // The ICmFile is used for all references to the same file within the project, so its guid is not unique. - return settings.ContentGenerator.AddImage(GetClassNameAttributeForConfig(config), srcAttribute, owner.Guid.ToString()); + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + // An XHTML id attribute must be unique but the ICmfile is used for all references to the same file within the project. + // The ICmPicture that owns the file does have unique guid so we use that. + var ownerGuid = owner.Guid.ToString(); + return settings.ContentGenerator.AddImage(className, srcAttribute, ownerGuid); } - return String.Empty; + return string.Empty; } /// @@ -846,9 +854,13 @@ private static string GenerateContentForDefOrGloss(ILexSense sense, Configurable } } } + if (bldr.Length > 0) - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); - return String.Empty; + { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); + } + return string.Empty; } internal static string CopyFileSafely(GeneratorSettings settings, string source, string relativeDestination) @@ -1353,9 +1365,10 @@ private static string GenerateContentForCollection(object collectionField, Confi if (bldr.Length > 0 || sharedCollectionInfo.Length > 0) { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; return config.DictionaryNodeOptions is DictionaryNodeSenseOptions ? - settings.ContentGenerator.WriteProcessedSenses(false, bldr.ToString(), GetClassNameAttributeForConfig(config), sharedCollectionInfo) : - settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); + settings.ContentGenerator.WriteProcessedSenses(false, bldr.ToString(), className, sharedCollectionInfo) : + settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); } return string.Empty; } @@ -1488,7 +1501,7 @@ private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryN ? GenerateCollectionItemContent(typeNode, pubDecorator, lexEntryType, lexEntryType.Owner, settings) : null; - var className = generateLexType ? GetClassNameAttributeForConfig(typeNode) : null; + var className = generateLexType ? settings.StylesGenerator.AddStyles(typeNode).Trim('.') : null; var refsByType = settings.ContentGenerator.AddLexReferences(generateLexType, lexTypeContent, className, innerBldr.ToString(), IsTypeBeforeForm(config)); bldr.Append(refsByType); @@ -1574,7 +1587,7 @@ private static bool IsCollectionInNeedOfSorting(string fieldDescr) } /// - /// This method will generate the XHTML that represents a senses collection and its contents + /// This method will generate the Content that represents a senses collection and its contents /// private static string GenerateContentForSenses(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, IEnumerable senseCollection, SenseInfo info, ref string sharedGramInfo) @@ -1615,6 +1628,7 @@ private static string GenerateContentForSenses(ConfigurableDictionaryNode config info.SenseCounter++; bldr.Append(GenerateSenseContent(config, publicationDecorator, item, isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); } + settings.StylesGenerator.AddStyles(config); return bldr.ToString(); } @@ -2116,9 +2130,13 @@ private static string GenerateContentForICmObject(ICmObject propertyValue, Confi var content = GenerateContentForFieldByReflection(propertyValue, child, null, settings); bldr.Append(content); } + if (bldr.Length > 0) - return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); - return String.Empty; + { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), className); + } + return string.Empty; } /// Write the class element in the span for an individual item in the collection @@ -2342,25 +2360,33 @@ private static string GenerateContentForValue(object field, object propertyValue { var content = GenerateContentForString((ITsString)propertyValue, config, settings, guid); if (!string.IsNullOrEmpty(content)) - return settings.ContentGenerator.WriteProcessedCollection(false, content, GetClassNameAttributeForConfig(config)); + { + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.WriteProcessedCollection(false, content, className); + } } - return String.Empty; + return string.Empty; } - else if (propertyValue is IMultiStringAccessor) + if (propertyValue is IMultiStringAccessor) { return GenerateContentForStrings((IMultiStringAccessor)propertyValue, config, settings, guid); } - else if (propertyValue is int) + + if (propertyValue is int) { - return settings.ContentGenerator.AddProperty(GetClassNameAttributeForConfig(config), false, propertyValue.ToString()); + var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.AddProperty(cssClassName, false, + propertyValue.ToString()); } - else if (propertyValue is DateTime) + if (propertyValue is DateTime) { - return settings.ContentGenerator.AddProperty(GetClassNameAttributeForConfig(config), false, ((DateTime)propertyValue).ToLongDateString()); + var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.AddProperty(cssClassName, false, ((DateTime)propertyValue).ToLongDateString()); } else if (propertyValue is GenDate) { - return settings.ContentGenerator.AddProperty(GetClassNameAttributeForConfig(config), false, ((GenDate)propertyValue).ToLongString()); + var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.AddProperty(cssClassName, false, ((GenDate)propertyValue).ToLongString()); } else if (propertyValue is IMultiAccessorBase) { @@ -2368,30 +2394,29 @@ private static string GenerateContentForValue(object field, object propertyValue return GenerateContentForVirtualStrings(((ISenseOrEntry)field).Item, (IMultiAccessorBase)propertyValue, config, settings, guid); return GenerateContentForVirtualStrings((ICmObject)field, (IMultiAccessorBase)propertyValue, config, settings, guid); } - else if (propertyValue is String) + else if (propertyValue is string) { - return settings.ContentGenerator.AddProperty(GetClassNameAttributeForConfig(config), false, propertyValue.ToString()); + var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.AddProperty(cssClassName, false, propertyValue.ToString()); } else if (propertyValue is IStText) { var bldr = new StringBuilder(); foreach (var para in (propertyValue as IStText).ParagraphsOS) { - IStTxtPara stp = para as IStTxtPara; + var stp = para as IStTxtPara; if (stp == null) continue; var contentPara = GenerateContentForString(stp.Contents, config, settings, guid); - if (!String.IsNullOrEmpty(contentPara)) + if (!string.IsNullOrEmpty(contentPara)) { bldr.Append(contentPara); bldr.AppendLine(); } } if (bldr.Length > 0) - { return settings.ContentGenerator.WriteProcessedCollection(true, bldr.ToString(), GetClassNameAttributeForConfig(config)); - } - return String.Empty; + return string.Empty; } else { @@ -2475,9 +2500,10 @@ private static string GenerateContentForStrings(IMultiStringAccessor multiString } if (bldr.Length > 0) { - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); } - return String.Empty; + return string.Empty; } /// @@ -2517,7 +2543,8 @@ private static string GenerateContentForVirtualStrings(ICmObject owningObject, I } if (bldr.Length > 0) { - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); } return String.Empty; } @@ -2562,6 +2589,7 @@ private static string GenerateContentForString(ITsString fieldValue, Configurabl } else if (config.IsCustomField && IsUSFM(fieldValue.Text)) { + // Review: Are any styles needed for tables? return GenerateTablesFromUSFM(fieldValue, config, settings, writingSystem); } else @@ -3001,7 +3029,7 @@ private static bool IsTypeBeforeForm(ConfigurableDictionaryNode config) public class GeneratorSettings { public ILcmContentGenerator ContentGenerator = new LcmXhtmlGenerator(); -// public ILcmStylesGenerator StylesGenerator = new CssGenerator(); + public ILcmStylesGenerator StylesGenerator = new CssGenerator(); public LcmCache Cache { get; } public ReadOnlyPropertyTable PropertyTable { get; } public bool UseRelativePaths { get; } @@ -3031,6 +3059,7 @@ public GeneratorSettings(LcmCache cache, ReadOnlyPropertyTable propertyTable, bo RightToLeft = rightToLeft; IsWebExport = isWebExport; IsTemplate = isTemplate; + StylesGenerator.Init(propertyTable); } } @@ -3045,6 +3074,13 @@ internal struct SenseInfo } } + public interface ILcmStylesGenerator + { + void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyPropertyTable propertyTable); + string AddStyles(ConfigurableDictionaryNode node); + void Init(ReadOnlyPropertyTable propertyTable); + } + /// /// A disposable writer for generating a fragment of a larger document. /// diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index af3f0667bb..df13a07648 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -3,12 +3,15 @@ // (http://www.gnu.org/licenses/lgpl-2.1.html) using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; using ExCSS; +using SIL.Code; using SIL.Extensions; using SIL.FieldWorks.Common.Framework; using SIL.LCModel.Core.KernelInterfaces; @@ -23,7 +26,7 @@ namespace SIL.FieldWorks.XWorks { - public static class CssGenerator + public class CssGenerator : ILcmStylesGenerator { /// /// id that triggers using the default selection on a character style instead of a writing system specific one @@ -48,8 +51,98 @@ public static class CssGenerator {"kuntSquiggle", "squiggle"} }; + private LcmCache _cache; + private ReadOnlyPropertyTable _propertyTable; + private Dictionary> _styleDictionary = new Dictionary>(); + private StyleSheet _styleSheet = new StyleSheet(); + + public void Init(ReadOnlyPropertyTable propertyTable) + { + _propertyTable = propertyTable; + } + + public void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyPropertyTable propertyTable) + { + var cache = propertyTable.GetValue("cache"); + var propStyleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); + LoadBulletUnicodes(); + LoadNumberingStyles(); + GenerateLetterHeaderCss(propertyTable, propStyleSheet); + GenerateCssForDefaultStyles(propertyTable, propStyleSheet, model); + MakeLinksLookLikePlainText(_styleSheet); + GenerateBidirectionalCssShim(_styleSheet); + GenerateCssForAudioWs(_styleSheet, cache); + } + + public string AddStyles(ConfigurableDictionaryNode node) + { + var className = $".{GetClassAttributeForConfig(node)}"; + lock (_styleDictionary) + { + var styleContent = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); + if (!styleContent.Any()) + { + return className; + } + if (!_styleDictionary.ContainsKey(className)) + { + _styleDictionary[className] = styleContent; + return className; + } + // If the content is the same, then do nothing + if (AreStyleRulesListsEquivalent(_styleDictionary[className], styleContent)) + { + return className; + } + // Otherwise get a unique but useful class name and re-generate the style with the new name + className = $".{GetBestUniqueNameForNode(_styleDictionary, node)}"; + _styleDictionary[className] = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); + return className; + } + } + + public static bool AreStyleRulesListsEquivalent(List first, + List second) + { + return first.Count == second.Count && first.TrueForAll(rule => second.Any(otherrule => otherrule.ToString().Equals(rule.ToString()))); + } + + /// + /// Finds an unused class name for the configuration node. This should be called when there are two nodes in the DictionaryConfigurationModel + /// have the same class name, but different style content. We want this name to be usefully recognizable. + /// + /// + public static string GetBestUniqueNameForNode(Dictionary> styles, + ConfigurableDictionaryNode node) + { + Guard.AgainstNull(node.Parent, "There should not be duplicate class names at the top of tree."); + // first try pre-pending the parent node classname + var className = $"{GetClassAttributeForConfig(node.Parent)}-{GetClassAttributeForConfig(node)}"; + int counter = 0; + while (styles.ContainsKey(className)) + { + className = $"{className}-{++counter}"; + } + return className; + } + + public string GetStylesString() + { + if (!_styleDictionary.Any()) + { + return string.Empty; + } + foreach (var styleList in _styleDictionary.Values) + { + _styleSheet.Rules.AddRange(styleList); + } + + return _styleSheet.ToString(true); + } + /// /// Generate all the css rules necessary to represent every enabled portion of the given configuration + /// USE FOR UNIT TESTING REAL CODE IS MODEL DRIVEN AND ONLY GETS /// /// /// Necessary to access the styles as configured in FLEx @@ -63,39 +156,48 @@ public static string GenerateCssFromConfiguration(DictionaryConfigurationModel m var cache = propertyTable.GetValue("cache"); LoadBulletUnicodes(); LoadNumberingStyles(); - GenerateLetterHeaderCss(propertyTable, propStyleSheet, styleSheet); - GenerateCssForDefaultStyles(propertyTable, propStyleSheet, styleSheet, model); + styleSheet.Rules.AddRange(GenerateLetterHeaderCss(propertyTable, propStyleSheet)); + styleSheet.Rules.AddRange(GenerateCssForDefaultStyles(propertyTable, propStyleSheet, model)); MakeLinksLookLikePlainText(styleSheet); GenerateBidirectionalCssShim(styleSheet); GenerateCssForAudioWs(styleSheet, cache); - foreach(var configNode in model.Parts.Where(x => x.IsEnabled).Concat(model.SharedItems.Where(x => x.Parent != null))) - { - GenerateCssFromConfigurationNode(configNode, styleSheet, null, propertyTable); + var allNodeRules = new List(); + var configNodesToTraverse = model.Parts.Where(x => x.IsEnabled).Concat(model.SharedItems.Where(x => x.Parent != null)).ToList(); + while(configNodesToTraverse.Any()) + { + var currentNode = configNodesToTraverse[0]; + allNodeRules.AddRange(GenerateCssFromConfigurationNode(currentNode, $".{GetClassAttributeForConfig(currentNode)}", propertyTable)); + if(currentNode.Children != null) + configNodesToTraverse.AddRange(currentNode.Children); + configNodesToTraverse.Remove(currentNode); } + styleSheet.Rules.AddRange(allNodeRules); // Pretty-print the stylesheet return CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFC) .Normalize(styleSheet.ToString(true, 1)); } - private static void GenerateCssForDefaultStyles(ReadOnlyPropertyTable propertyTable, LcmStyleSheet propStyleSheet, - StyleSheet styleSheet, DictionaryConfigurationModel model) + private static List GenerateCssForDefaultStyles(ReadOnlyPropertyTable propertyTable, LcmStyleSheet propStyleSheet, DictionaryConfigurationModel model) { + var styles = new List(); if (propStyleSheet == null) - return; + return styles; if (propStyleSheet.Styles.Contains("Normal")) - GenerateCssForWsSpanWithNormalStyle(styleSheet, propertyTable); + styles.AddRange(GenerateCssForWsSpanWithNormalStyle(propertyTable)); var entryBaseStyle = ConfiguredLcmGenerator.GetEntryStyle(model); if (propStyleSheet.Styles.Contains(entryBaseStyle)) - GenerateDictionaryNormalParagraphCss(styleSheet, propertyTable, entryBaseStyle); + styles.AddRange(GenerateDictionaryNormalParagraphCss(propertyTable, entryBaseStyle)); if (propStyleSheet.Styles.Contains(LetterHeadingStyleName)) { - GenerateCssForWritingSystems(".letter", LetterHeadingStyleName, styleSheet, propertyTable); + styles.AddRange(GenerateCssForWritingSystems(".letter", LetterHeadingStyleName, propertyTable)); } - GenerateDictionaryMinorParagraphCss(styleSheet, propertyTable, model); + styles.AddRange(GenerateDictionaryMinorParagraphCss(propertyTable, model)); + + return styles; } private static void MakeLinksLookLikePlainText(StyleSheet styleSheet) @@ -124,8 +226,9 @@ private static void GenerateBidirectionalCssShim(StyleSheet styleSheet) styleSheet.Rules.Add(rule); } - private static void GenerateCssForWsSpanWithNormalStyle(StyleSheet styleSheet, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssForWsSpanWithNormalStyle(ReadOnlyPropertyTable propertyTable) { + var styles = new List(); // Generate the rules for the programmatic default style info ( var defaultStyleProps = GetOnlyCharacterStyle(GenerateCssStyleFromLcmStyleSheet("Normal", DefaultStyle, propertyTable)); if (!defaultStyleProps.Any(p => p.Name == "font-size")) @@ -134,23 +237,27 @@ private static void GenerateCssForWsSpanWithNormalStyle(StyleSheet styleSheet, R } var defaultRule = new StyleRule { Value = "body" }; defaultRule.Declarations.Properties.AddRange(defaultStyleProps); - styleSheet.Rules.Add(defaultRule); + styles.Add(defaultRule); // Then generate the rules for all the writing system overrides - GenerateCssForWritingSystems("span", "Normal", styleSheet, propertyTable); + styles.AddRange(GenerateCssForWritingSystems("span", "Normal", propertyTable)); + return styles; } - private static void GenerateDictionaryNormalParagraphCss(StyleSheet styleSheet, ReadOnlyPropertyTable propertyTable, string entryBaseStyle) + private static List GenerateDictionaryNormalParagraphCss(ReadOnlyPropertyTable propertyTable, string entryBaseStyle) { + var styles = new List(); var dictNormalRule = new StyleRule { Value = "div.entry" }; var dictNormalStyle = GenerateCssStyleFromLcmStyleSheet(entryBaseStyle, 0, propertyTable); dictNormalRule.Declarations.Properties.AddRange(GetOnlyParagraphStyle(dictNormalStyle)); - styleSheet.Rules.Add(dictNormalRule); + styles.Add(dictNormalRule); // Then generate the rules for all the writing system overrides - GenerateCssForWritingSystems("div.entry span", entryBaseStyle, styleSheet, propertyTable); + styles.AddRange(GenerateCssForWritingSystems("div.entry span", entryBaseStyle, propertyTable)); + return styles; } - private static void GenerateDictionaryMinorParagraphCss(StyleSheet styleSheet, ReadOnlyPropertyTable propertyTable, DictionaryConfigurationModel model) + private static List GenerateDictionaryMinorParagraphCss(ReadOnlyPropertyTable propertyTable, DictionaryConfigurationModel model) { + var styles = new List(); // Use the style set in all the parts following main entry, if no style is specified assume Dictionary-Minor for (var i = 1; i < model.Parts.Count; ++i) { @@ -163,16 +270,19 @@ private static void GenerateDictionaryMinorParagraphCss(StyleSheet styleSheet, R var dictionaryMinorStyle = GenerateCssStyleFromLcmStyleSheet(styleName, 0, propertyTable); var minorRule = new StyleRule { Value = string.Format("div.{0}", GetClassAttributeForConfig(minorEntryNode)) }; minorRule.Declarations.Properties.AddRange(GetOnlyParagraphStyle(dictionaryMinorStyle)); - styleSheet.Rules.Add(minorRule); + styles.Add(minorRule); // Then generate the rules for all the writing system overrides - GenerateCssForWritingSystems(string.Format("div.{0} span", GetClassAttributeForConfig(minorEntryNode)), styleName, styleSheet, propertyTable); + styles.AddRange(GenerateCssForWritingSystems(string.Format("div.{0} span", GetClassAttributeForConfig(minorEntryNode)), styleName, propertyTable)); } } + + return styles; } - private static void GenerateCssForWritingSystems(string selector, string styleName, StyleSheet styleSheet, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssForWritingSystems(string selector, string styleName, ReadOnlyPropertyTable propertyTable) { var cache = propertyTable.GetValue("cache"); + var styleRules = new List(); // Generate the rules for all the writing system overrides foreach (var aws in cache.ServiceLocator.WritingSystems.AllWritingSystems) { @@ -181,8 +291,10 @@ private static void GenerateCssForWritingSystems(string selector, string styleNa var wsRule = new StyleRule { Value = selector + String.Format("[lang|=\"{0}\"]", aws.LanguageTag) }; var styleDecls = GenerateCssStyleFromLcmStyleSheet(styleName, aws.Handle, propertyTable); wsRule.Declarations.Properties.AddRange(GetOnlyCharacterStyle(styleDecls)); - styleSheet.Rules.Add(wsRule); + styleRules.Add(wsRule); } + + return styleRules; } private static void GenerateCssForAudioWs(StyleSheet styleSheet, LcmCache cache) @@ -210,71 +322,65 @@ private static void GenerateCssForAudioWs(StyleSheet styleSheet, LcmCache cache) /// /// Generates css rules for a configuration node and adds them to the given stylesheet (recursive). /// - private static void GenerateCssFromConfigurationNode(ConfigurableDictionaryNode configNode, StyleSheet styleSheet, - string baseSelection, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssFromConfigurationNode(ConfigurableDictionaryNode configNode, string baseSelection, ReadOnlyPropertyTable propertyTable) { var cache = propertyTable.GetValue("cache"); - var rule = new StyleRule(); - var senseOptions = configNode.DictionaryNodeOptions as DictionaryNodeSenseOptions; - var listAndParaOpts = configNode.DictionaryNodeOptions as IParaOption; - if (senseOptions != null) - { - // Try to generate the css for the sense number before the baseSelection is updated because - // the sense number is a sibling of the sense element and we are normally applying styles to the - // children of collections. Also set display:block on span - GenerateCssForSenses(configNode, senseOptions, styleSheet, ref baseSelection, propertyTable); - } - else if (listAndParaOpts != null) - { - GenerateCssFromListAndParaOptions(configNode, listAndParaOpts, styleSheet, ref baseSelection, cache, propertyTable); - var wsOptions = configNode.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; - if (wsOptions != null && wsOptions.DisplayWritingSystemAbbreviations) + switch (configNode.DictionaryNodeOptions) + { + case DictionaryNodeSenseOptions senseOptions: + // Try to generate the css for the sense number before the baseSelection is updated because + // the sense number is a sibling of the sense element and we are normally applying styles to the + // children of collections. Also set display:block on span + return GenerateCssForSenses(configNode, senseOptions, ref baseSelection, propertyTable); + case IParaOption listAndParaOpts: { - if (DictionaryConfigurationModel.NoteInParaStyles.Contains(configNode.FieldDescription)) + var listAndParaRules = new List(); + listAndParaRules = GenerateCssFromListAndParaOptions(configNode, listAndParaOpts, ref baseSelection, cache, propertyTable); + var wsOptions = listAndParaOpts as DictionaryNodeWritingSystemOptions; // Some paragraph and list options extend ws options + if (wsOptions != null && wsOptions.DisplayWritingSystemAbbreviations) { - baseSelection = baseSelection + "> span"; + if (DictionaryConfigurationModel.NoteInParaStyles.Contains(configNode.FieldDescription)) + { + baseSelection = baseSelection + "> span"; + } + listAndParaRules.AddRange(GenerateCssForWritingSystemPrefix(configNode, baseSelection, propertyTable)); } - GenerateCssForWritingSystemPrefix(configNode, styleSheet, baseSelection, propertyTable); + return listAndParaRules; } - } - else - { - if (configNode.DictionaryNodeOptions is DictionaryNodePictureOptions) + case DictionaryNodePictureOptions pictureOptions: { - GenerateCssFromPictureOptions(configNode, (DictionaryNodePictureOptions)configNode.DictionaryNodeOptions, styleSheet, baseSelection); + return GenerateCssFromPictureOptions(configNode, pictureOptions, baseSelection, cache, propertyTable); } - var selectors = GenerateSelectorsFromNode(baseSelection, configNode, out baseSelection, - cache, propertyTable); - - var wsOptions = configNode.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; - if (wsOptions != null) + default: { - GenerateCssFromWsOptions(configNode, wsOptions, styleSheet, baseSelection, propertyTable); - if (wsOptions.DisplayWritingSystemAbbreviations) + var rule = new StyleRule(); + + var selectors = GenerateSelectorsFromNode(configNode, ref baseSelection, + cache, propertyTable); + + var wsOptions = configNode.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; + if (wsOptions != null) { - GenerateCssForWritingSystemPrefix(configNode, styleSheet, baseSelection, propertyTable); + selectors.AddRange(GenerateCssFromWsOptions(configNode, wsOptions, baseSelection, propertyTable)); + if (wsOptions.DisplayWritingSystemAbbreviations) + { + selectors.AddRange(GenerateCssForWritingSystemPrefix(configNode, baseSelection, propertyTable)); + } } - } - rule.Value = baseSelection; + rule.Value = baseSelection; + selectors.Add(rule); - // if the configuration node defines a style then add all the rules generated from that style - if (!string.IsNullOrEmpty(configNode.Style)) - { - //Generate the rules for the default font info - rule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(configNode.Style, DefaultStyle, configNode, - propertyTable)); - GenerateCssForWritingSystems(baseSelection + " span", configNode.Style, styleSheet, propertyTable); + // if the configuration node defines a style then add all the rules generated from that style + if (!string.IsNullOrEmpty(configNode.Style)) + { + //Generate the rules for the default font info + rule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(configNode.Style, DefaultStyle, configNode, + propertyTable)); + selectors.AddRange(GenerateCssForWritingSystems(baseSelection + " span", configNode.Style, propertyTable)); + } + + return selectors; } - styleSheet.Rules.AddRange(CheckRangeOfRulesForEmpties(selectors)); - if (!IsEmptyRule(rule)) - styleSheet.Rules.Add(rule); - } - if(configNode.Children == null) - return; - //Recurse into each child - foreach(var child in configNode.Children.Where(x => x.IsEnabled)) - { - GenerateCssFromConfigurationNode(child, styleSheet, baseSelection, propertyTable); } } @@ -299,16 +405,17 @@ private static IEnumerable RemoveBeforeAfterSelectorRules(IEnumerable return rules.Where(rule => !IsBeforeOrAfter(rule)); } - private static void GenerateCssForSenses(ConfigurableDictionaryNode configNode, DictionaryNodeSenseOptions senseOptions, - StyleSheet styleSheet, ref string baseSelection, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssForSenses(ConfigurableDictionaryNode configNode, DictionaryNodeSenseOptions senseOptions, ref string baseSelection, ReadOnlyPropertyTable propertyTable) { - var selectors = GenerateSelectorsFromNode(baseSelection, configNode, out baseSelection, propertyTable.GetValue("cache"), propertyTable); + var styleRules = new List(); + var selectors = GenerateSelectorsFromNode(configNode, ref baseSelection, propertyTable.GetValue("cache"), propertyTable); // Insert '> .sensecontent' between '.*senses' and '.*sense' (where * could be 'referring', 'sub', or similar) - var senseContentSelector = string.Format("{0}> .sensecontent", baseSelection.Substring(0, baseSelection.LastIndexOf('.'))); + var collectionSelector = baseSelection.Substring(0, baseSelection.LastIndexOf('.')); + var senseContentSelector = $"{collectionSelector}> .sensecontent"; var senseItemName = baseSelection.Substring(baseSelection.LastIndexOf('.')); if (senseOptions.DisplayEachSenseInAParagraph) - selectors = RemoveBeforeAfterSelectorRules(selectors); - styleSheet.Rules.AddRange(CheckRangeOfRulesForEmpties(selectors)); + selectors = new List(RemoveBeforeAfterSelectorRules(selectors)); + styleRules.AddRange(CheckRangeOfRulesForEmpties(selectors)); var senseNumberRule = new StyleRule(); // Not using SelectClassName here; sense and sensenumber are siblings and the configNode is for the Senses collection. // Select the base plus the node's unmodified class attribute and append the sensenumber matcher. @@ -320,21 +427,21 @@ private static void GenerateCssForSenses(ConfigurableDictionaryNode configNode, senseNumberRule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(senseOptions.NumberStyle, DefaultStyle, propertyTable)); } if (!IsEmptyRule(senseNumberRule)) - styleSheet.Rules.Add(senseNumberRule); + styleRules.Add(senseNumberRule); if(!String.IsNullOrEmpty(senseOptions.BeforeNumber)) { var beforeDeclaration = new StyleDeclaration { new Property("content") { Term = new PrimitiveTerm(UnitType.String, senseOptions.BeforeNumber) } }; - styleSheet.Rules.Add(new StyleRule(beforeDeclaration) { Value = senseNumberSelector + ":before" }); + styleRules.Add(new StyleRule(beforeDeclaration) { Value = senseNumberSelector + ":before" }); } if(!String.IsNullOrEmpty(senseOptions.AfterNumber)) { var afterDeclaration = new StyleDeclaration(); afterDeclaration.Add(new Property("content") { Term = new PrimitiveTerm(UnitType.String, senseOptions.AfterNumber) }); var afterRule = new StyleRule(afterDeclaration) { Value = senseNumberSelector + ":after" }; - styleSheet.Rules.Add(afterRule); + styleRules.Add(afterRule); } // set the base selection to the sense level under the sense content baseSelection = string.Format("{0} > {1}", senseContentSelector, senseItemName); @@ -348,7 +455,7 @@ private static void GenerateCssForSenses(ConfigurableDictionaryNode configNode, Value = baseSelection }; if (!IsEmptyRule(senseCharRule)) - styleSheet.Rules.Add(senseCharRule); + styleRules.Add(senseCharRule); var senseParaDeclaration = GetOnlyParagraphStyle(styleDeclaration); senseParaDeclaration.Add(new Property("display") @@ -361,8 +468,8 @@ private static void GenerateCssForSenses(ConfigurableDictionaryNode configNode, Value = senseOptions.DisplayFirstSenseInline ? string.Format("{0} + {1}", senseContentSelector, ".sensecontent") : senseContentSelector }; - styleSheet.Rules.Add(senseParaRule); - GenerateCssforBulletedList(configNode, styleSheet, senseParaRule.Value, propertyTable, styleDeclaration); + styleRules.Add(senseParaRule); + styleRules.AddRange(GenerateCssforBulletedList(configNode, collectionSelector, senseContentSelector, propertyTable, styleDeclaration)); } else { @@ -372,34 +479,36 @@ private static void GenerateCssForSenses(ConfigurableDictionaryNode configNode, Value = baseSelection }; if (!IsEmptyRule(senseContentRule)) - styleSheet.Rules.Add(senseContentRule); + styleRules.Add(senseContentRule); } if (senseOptions.ShowSharedGrammarInfoFirst) { - var collectionSelector = senseContentSelector.Substring(0, senseContentSelector.LastIndexOf(" .", StringComparison.Ordinal)); foreach (var gramInfoNode in configNode.Children.Where(node => node.FieldDescription == "MorphoSyntaxAnalysisRA" && node.IsEnabled)) { - GenerateCssFromConfigurationNode(gramInfoNode, styleSheet, collectionSelector + " .sharedgrammaticalinfo", propertyTable); + styleRules.AddRange(GenerateCssFromConfigurationNode(gramInfoNode, collectionSelector + "> .sharedgrammaticalinfo", propertyTable)); } } + + return styleRules; } - /// - /// Generates Bulleted List style properties - /// - /// Dictionary Node - /// Stylesheet to add the new rule - /// Style name for the bullet property - /// propertyTable to get the styles - /// Style properties collection - private static void GenerateCssforBulletedList(ConfigurableDictionaryNode configNode, StyleSheet styleSheet, string bulletSelector, ReadOnlyPropertyTable propertyTable, StyleDeclaration styleDeclaration) + /// + /// Generates Bulleted List style properties + /// + /// Dictionary Node + /// Style selector for collection + /// Style name for the bulleted items + /// propertyTable to get the styles + /// Style properties collection + private static List GenerateCssforBulletedList(ConfigurableDictionaryNode configNode, string collectionSelector, string bulletSelector, ReadOnlyPropertyTable propertyTable, StyleDeclaration styleDeclaration) { + var styles = new List(); if (configNode.Style != null) { if (styleDeclaration.Properties.Count == 0) styleDeclaration = GenerateCssStyleFromLcmStyleSheet(configNode.Style, DefaultStyle, propertyTable); - GenerateCssForCounterReset(styleSheet, bulletSelector, styleDeclaration, false); + styles.AddRange(GenerateCssForCounterReset(collectionSelector, styleDeclaration)); var senseOptions = configNode.DictionaryNodeOptions as DictionaryNodeSenseOptions; var senseSufixRule = senseOptions != null && senseOptions.DisplayFirstSenseInline ? ":not(:first-child):before" : ":before"; var bulletRule = new StyleRule { Value = bulletSelector + senseSufixRule }; @@ -425,24 +534,25 @@ private static void GenerateCssforBulletedList(ConfigurableDictionaryNode config } if (!IsEmptyRule(bulletRule)) { - styleSheet.Rules.Add(bulletRule); + styles.Add(bulletRule); } } + + return styles; } - private static void GenerateCssFromListAndParaOptions(ConfigurableDictionaryNode configNode, - IParaOption listAndParaOpts, StyleSheet styleSheet, ref string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssFromListAndParaOptions(ConfigurableDictionaryNode configNode, + IParaOption listAndParaOpts, ref string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) { - var selectors = GenerateSelectorsFromNode(baseSelection, configNode, out baseSelection, cache, propertyTable); + var styleRules = GenerateSelectorsFromNode(configNode, ref baseSelection, cache, propertyTable); List blockDeclarations; if (string.IsNullOrEmpty(configNode.Style)) blockDeclarations = new List {new StyleDeclaration()}; else { blockDeclarations = GenerateCssStyleFromLcmStyleSheet(configNode.Style, 0, configNode, propertyTable, true); - GenerateCssForWritingSystems(baseSelection + " span", configNode.Style, styleSheet, propertyTable); + styleRules.AddRange(GenerateCssForWritingSystems(baseSelection + " span", configNode.Style, propertyTable)); } - var styleRules = selectors as StyleRule[] ?? selectors.ToArray(); if (listAndParaOpts.DisplayEachInAParagraph) { foreach (var declaration in blockDeclarations) @@ -452,17 +562,17 @@ private static void GenerateCssFromListAndParaOptions(ConfigurableDictionaryNode { Value = baseSelection }; - styleSheet.Rules.Add(blockRule); - GenerateCssForCounterReset(styleSheet, baseSelection, declaration, true); + styleRules.Add(blockRule); + styleRules.AddRange(GenerateCssForCounterReset(SelectBareClassName(configNode, baseSelection, cache), declaration)); var bulletRule = AdjustRuleIfParagraphNumberScheme(blockRule, configNode, propertyTable); // REVIEW (Hasso) 2016.10: could these two lines be moved outside the loop? // REVIEW (Hasso) 2016.10: both of these following lines add all rules but BeforeAfter (so if the condition in the first line // REVIEW (cont) is true, both excluded rule categories will nonetheless be added) - styleSheet.Rules.AddRange(DictionaryConfigurationModel.NoteInParaStyles.Contains(configNode.FieldDescription) - ? RemoveBeforeAndAfterForNoteInParaRules(styleRules) - : RemoveBeforeAfterSelectorRules(styleRules)); - styleSheet.Rules.AddRange(RemoveBeforeAfterSelectorRules(styleRules)); - styleSheet.Rules.Add(bulletRule); + var prunedStyles = + DictionaryConfigurationModel.NoteInParaStyles.Contains(configNode.FieldDescription) + ? RemoveBeforeAndAfterForNoteInParaRules(styleRules) + : RemoveBeforeAfterSelectorRules(styleRules); + styleRules = new List(prunedStyles) { bulletRule }; } } else @@ -475,10 +585,12 @@ private static void GenerateCssFromListAndParaOptions(ConfigurableDictionaryNode Value = baseSelection }; if (!IsEmptyRule(complexContentRule)) - styleSheet.Rules.Add(complexContentRule); + styleRules.Add(complexContentRule); } - styleSheet.Rules.AddRange(styleRules); + styleRules.AddRange(styleRules); } + + return styleRules; } @@ -490,25 +602,22 @@ private static IEnumerable RemoveBeforeAndAfterForNoteInParaRules(IEn /// /// Generates Counter reset style properties /// - /// Stylesheet to add the new rule - /// Style name for the bullet property + /// Style selector for the collection that has bulletted items /// Style properties collection - /// Split baseSelection by space/greater than - private static void GenerateCssForCounterReset(StyleSheet styleSheet, string baseSelection, StyleDeclaration declaration, bool isSplitBySpace) + private static List GenerateCssForCounterReset(string collectionSelector, StyleDeclaration declaration) { var resetSection = GetOnlyCounterResetContent(declaration); if (!string.IsNullOrEmpty(resetSection)) { - string bulletParentSelector = baseSelection.Substring(0, baseSelection.LastIndexOf('>') - 1); - if (isSplitBySpace) - bulletParentSelector = baseSelection.Substring(0, baseSelection.LastIndexOf(' ')); - var resetRule = new StyleRule {Value = bulletParentSelector}; + var resetRule = new StyleRule {Value = collectionSelector}; resetRule.Declarations.Add(new Property("counter-reset") { Term = new PrimitiveTerm(UnitType.Attribute, resetSection) }); - styleSheet.Rules.Add(resetRule); + return new List{resetRule}; } + + return new List(); } /// @@ -547,8 +656,8 @@ private static StyleRule AdjustRuleIfParagraphNumberScheme(StyleRule rule, Confi return rule; } - private static void GenerateCssFromWsOptions(ConfigurableDictionaryNode configNode, DictionaryNodeWritingSystemOptions wsOptions, - StyleSheet styleSheet, string baseSelection, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssFromWsOptions(ConfigurableDictionaryNode configNode, DictionaryNodeWritingSystemOptions wsOptions, + string baseSelection, ReadOnlyPropertyTable propertyTable) { var cache = propertyTable.GetValue("cache"); foreach(var ws in wsOptions.Options.Where(opt => opt.IsEnabled)) @@ -558,28 +667,33 @@ private static void GenerateCssFromWsOptions(ConfigurableDictionaryNode configNo var wsIdString = possiblyMagic == 0 ? ws.Id : WritingSystemServices.GetWritingSystemList(cache, possiblyMagic, true).First().Id; var wsId = cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(wsIdString); var wsRule = new StyleRule {Value = baseSelection + String.Format("[lang|=\"{0}\"]", wsIdString)}; - if (!String.IsNullOrEmpty(configNode.Style)) + if (!string.IsNullOrEmpty(configNode.Style)) wsRule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(configNode.Style, wsId, propertyTable)); if (!IsEmptyRule(wsRule)) - styleSheet.Rules.Add(wsRule); + return new List {wsRule}; } + + return new List(); } - private static void GenerateCssForWritingSystemPrefix(ConfigurableDictionaryNode configNode, StyleSheet styleSheet, string baseSelection, ReadOnlyPropertyTable propertyTable) + private static List GenerateCssForWritingSystemPrefix(ConfigurableDictionaryNode configNode, string baseSelection, ReadOnlyPropertyTable propertyTable) { + var styleRules = new List(); var wsRule1 = new StyleRule { Value = string.Format("{0}.{1}", baseSelection, WritingSystemPrefix)}; wsRule1.Declarations.Properties.AddRange(GetOnlyCharacterStyle(GenerateCssStyleFromLcmStyleSheet(WritingSystemStyleName, 0, configNode, propertyTable))); - styleSheet.Rules.Add(wsRule1); + styleRules.Add(wsRule1); var wsRule2 = new StyleRule { Value = string.Format("{0}.{1}:after", baseSelection, WritingSystemPrefix) }; wsRule2.Declarations.Properties.Add(new Property("content"){Term = new PrimitiveTerm(UnitType.String, " ")}); - styleSheet.Rules.Add(wsRule2); + styleRules.Add(wsRule2); + return styleRules; } - private static void GenerateCssFromPictureOptions(ConfigurableDictionaryNode configNode, DictionaryNodePictureOptions pictureOptions, - StyleSheet styleSheet, string baseSelection) + private static List GenerateCssFromPictureOptions(ConfigurableDictionaryNode configNode, DictionaryNodePictureOptions pictureOptions, + string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) { + var styles = GenerateSelectorsFromNode(configNode, ref baseSelection, cache, propertyTable); var pictureAndCaptionRule = new StyleRule(); - pictureAndCaptionRule.Value = baseSelection + " " + SelectClassName(configNode); + pictureAndCaptionRule.Value = baseSelection; var pictureProps = pictureAndCaptionRule.Declarations.Properties; pictureProps.Add(new Property("float") { Term = new PrimitiveTerm(UnitType.Ident, "right") }); @@ -594,7 +708,7 @@ private static void GenerateCssFromPictureOptions(ConfigurableDictionaryNode con { Term = new PrimitiveTerm(UnitType.Ident, pictureOptions.PictureLocation.ToString().ToLowerInvariant()) }); - styleSheet.Rules.Add(pictureAndCaptionRule); + styles.Add(pictureAndCaptionRule); var pictureRule = new StyleRule(); pictureRule.Value = pictureAndCaptionRule.Value + " img"; @@ -627,121 +741,140 @@ private static void GenerateCssFromPictureOptions(ConfigurableDictionaryNode con }); } if (!IsEmptyRule(pictureRule)) - styleSheet.Rules.Add(pictureRule); + styles.Add(pictureRule); + return styles; } /// /// This method will generate before and after rules if the configuration node requires them. It also generates the selector for the node /// - private static IEnumerable GenerateSelectorsFromNode( - string parentSelector, ConfigurableDictionaryNode configNode, - out string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) - // REVIEW (Hasso) 2016.10: parentSelector and baseSelector could be combined into a single `ref` parameter + private static List GenerateSelectorsFromNode(ConfigurableDictionaryNode configNode, + ref string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) { - // TODO: REFACTOR this method to handle certain nodes more specifically. The options type should be used to branch into node specific code. - parentSelector = GetParentForFactoredReference(parentSelector, configNode); var rules = new List(); var fwStyles = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); - // simpleSelector is used for nodes that use before and after. Collection type nodes produce wrong + // collectionSelector is used for nodes that use before and after. Collection type nodes produce wrong // results if we use baseSelection in handling before and after content. See LT-17048. - string simpleSelector; + string collectionSelector; + string collectionItemSelector; string pictCaptionContent = ".captionContent "; - if (parentSelector == null) + if (configNode.Parent == null) { - baseSelection = SelectClassName(configNode); - simpleSelector = SelectBareClassName(configNode); + collectionSelector = SelectBareClassName(configNode, baseSelection); + baseSelection = SelectClassName(configNode, baseSelection); GenerateFlowResetForBaseNode(baseSelection, rules); } else { - if(!String.IsNullOrEmpty(configNode.Between)) + // Headword, Gloss, and Caption are contained in a captionContent area. + if (configNode.Parent.DictionaryNodeOptions is DictionaryNodePictureOptions) + { + collectionSelector = pictCaptionContent + SelectBareClassName(configNode, baseSelection, cache); + baseSelection = pictCaptionContent + SelectClassName(configNode, baseSelection, cache); + } + else + { + collectionSelector = SelectBareClassName(configNode, baseSelection, cache); + baseSelection = SelectClassName(configNode, baseSelection, cache); + } + collectionItemSelector = $".{GetClassAttributeForCollectionItem(configNode)}"; + if (!string.IsNullOrEmpty(configNode.Between)) { - // content is generated before each item which follows an item of the same name - // eg. .complexformrefs>.complexformref + .complexformref:before { content: "," } var dec = new StyleDeclaration(); dec.Add(new Property("content") { Term = new PrimitiveTerm(UnitType.String, SpecialCharacterHandling.MakeSafeCss(configNode.Between)) }); if (fwStyles != null && fwStyles.Styles.Contains(BeforeAfterBetweenStyleName)) dec.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(BeforeAfterBetweenStyleName, cache.DefaultAnalWs, propertyTable)); - var collectionSelector = "." + GetClassAttributeForConfig(configNode); - if (configNode.Parent.DictionaryNodeOptions is DictionaryNodePictureOptions) - collectionSelector = pictCaptionContent + "." + GetClassAttributeForConfig(configNode); - var itemSelector = " ." + GetClassAttributeForCollectionItem(configNode); - var betweenSelector = String.Format("{0}> {1}>{2}+{2}:before", parentSelector, collectionSelector, itemSelector); - ConfigurableDictionaryNode dummy; - // use default (class-named) between selector for factored references, because "span+span" erroneously matches Type spans - if (configNode.DictionaryNodeOptions != null && !ConfiguredLcmGenerator.IsFactoredReference(configNode, out dummy)) + if (baseSelection == null) { - var wsOptions = configNode.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; - var senseOptions = configNode.DictionaryNodeOptions as DictionaryNodeSenseOptions; - // If wsOptions are enabled generate a between rule which will not put content between the abbreviation and the ws data - if (wsOptions != null) + baseSelection = $".{configNode.Parent.CSSClassNameOverride}"; + } + var betweenSelector = string.Format("{0}> {1}+ {1}:before", collectionSelector, collectionItemSelector); + if (IsFactoredReferenceType(configNode)) + { + // Between factored Type goes between a reference (last in the list for its Type) + // and its immediately-following Type "list" (label on the following list of references) + betweenSelector = GetBetweenForFactoredReference(baseSelection, configNode); + } + else + { + switch (configNode.DictionaryNodeOptions) { - if (wsOptions.DisplayWritingSystemAbbreviations) + case DictionaryNodeSenseOptions senseOptions: { - betweenSelector = String.Format("{0}> {1}> span.{2} ~ span.{2}:before", parentSelector, collectionSelector, - WritingSystemPrefix); + if (senseOptions.ShowSharedGrammarInfoFirst) + { + betweenSelector = string.Format("{0}> {1}.sensecontent + {1}:before", collectionSelector, " span"); + } + else + { + betweenSelector = $""; + } + break; } - else + case DictionaryNodeWritingSystemOptions wsOptions: { - var enabledWsOptions = wsOptions.Options.Where(x => x.IsEnabled).ToArray(); - //Fix LT-17238: Between rule added as before rule to ws span which iterates from last ws to second ws span - //First Ws is skipped as between rules no longer needed before first WS span - for (var i = enabledWsOptions.Count() - 1; i > 0; i--) + var selectorOfWsOptOwner = ConfiguredLcmGenerator.IsCollectionNode(configNode, cache) + ? $"{collectionSelector}> {collectionItemSelector}>" + : $"{collectionSelector.Replace("> span", ">").TrimEnd('>')}>"; + if (wsOptions.DisplayWritingSystemAbbreviations) + { + betweenSelector = $"{selectorOfWsOptOwner} span.{WritingSystemPrefix} ~ span.{WritingSystemPrefix}:before"; + } + else { - betweenSelector = (i == enabledWsOptions.Count() - 1 ? string.Empty : (betweenSelector + ",")) + - String.Format("{0}> {1}> span+span[lang|='{2}']:before", parentSelector, collectionSelector, - enabledWsOptions[i].Id); + var enabledWsOptions = wsOptions.Options.Where(x => x.IsEnabled).ToArray(); + betweenSelector = $"{selectorOfWsOptOwner} {collectionItemSelector}+ {collectionItemSelector}:before"; + //Fix LT-17238: Between rule added as before rule to ws span which iterates from last ws to second ws span + //First Ws is skipped as between rules no longer needed before first WS span + for (var i = enabledWsOptions.Length - 1; i > 0; i--) + { + betweenSelector = (i == enabledWsOptions.Length - 1 ? string.Empty : betweenSelector + ",") + + string.Format("{0} span+span[lang|='{1}']:before", selectorOfWsOptOwner, enabledWsOptions[i].Id); + } } + break; + } + case DictionaryNodePictureOptions _: + { + collectionSelector = pictCaptionContent + "." + GetClassAttributeForConfig(configNode); + betweenSelector = string.Format("{0}> {1}+{1}:before", collectionSelector, " div"); + break; + } + case DictionaryNodeListOptions listOptions: + { + betweenSelector = string.Format("{0}> {1} + {1}:before", collectionSelector, collectionItemSelector); + break; + } + default: + { + betweenSelector = string.Format("{0}> {1} + {1}:before", collectionSelector, collectionItemSelector); + break; } } - else if (senseOptions != null && senseOptions.ShowSharedGrammarInfoFirst) - betweenSelector = String.Format("{0}> {1}>{2}.sensecontent+{2}:before", parentSelector, collectionSelector, " span"); - else if (configNode.FieldDescription == "PicturesOfSenses") - betweenSelector = String.Format("{0}> {1}>{2}+{2}:before", parentSelector, collectionSelector, " div"); - else - betweenSelector = String.Format("{0}> {1}>{2}+{2}:before", parentSelector, collectionSelector, " span"); - } - else if (IsFactoredReferenceType(configNode)) - { - // Between factored Type goes between a reference (last in the list for its Type) - // and its immediately-following Type "list" (label on the following list of references) - betweenSelector = string.Format("{0}> .{1}+{2}:before", - parentSelector, GetClassAttributeForCollectionItem(configNode.Parent), collectionSelector); } var betweenRule = new StyleRule(dec) { Value = betweenSelector }; rules.Add(betweenRule); } - // Headword, Gloss, and Caption are contained in a captionContent area. - if (configNode.Parent.DictionaryNodeOptions is DictionaryNodePictureOptions) - { - baseSelection = parentSelector + "> " + pictCaptionContent + SelectClassName(configNode, cache); - simpleSelector = parentSelector + "> " + pictCaptionContent + SelectBareClassName(configNode, cache); - } - else - { - baseSelection = parentSelector + "> " + SelectClassName(configNode, cache); - simpleSelector = parentSelector + "> " + SelectBareClassName(configNode, cache); - } } - if(!String.IsNullOrEmpty(configNode.Before)) + if (!string.IsNullOrEmpty(configNode.Before)) { var dec = new StyleDeclaration(); dec.Add(new Property("content") { Term = new PrimitiveTerm(UnitType.String, SpecialCharacterHandling.MakeSafeCss(configNode.Before)) }); if (fwStyles != null && fwStyles.Styles.Contains(BeforeAfterBetweenStyleName)) dec.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(BeforeAfterBetweenStyleName, cache.DefaultAnalWs, propertyTable)); - var selectorBase = simpleSelector; + var selectorBase = collectionSelector; if (configNode.FieldDescription == "PicturesOfSenses") selectorBase += "> div:first-child"; var beforeRule = new StyleRule(dec) { Value = GetBaseSelectionWithSelectors(selectorBase, ":before") }; rules.Add(beforeRule); } - if(!String.IsNullOrEmpty(configNode.After)) + if(!string.IsNullOrEmpty(configNode.After)) { var dec = new StyleDeclaration(); dec.Add(new Property("content") { Term = new PrimitiveTerm(UnitType.String, SpecialCharacterHandling.MakeSafeCss(configNode.After)) }); if (fwStyles != null && fwStyles.Styles.Contains(BeforeAfterBetweenStyleName)) dec.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(BeforeAfterBetweenStyleName, cache.DefaultAnalWs, propertyTable)); - var selectorBase = simpleSelector; + var selectorBase = collectionSelector; if (configNode.FieldDescription == "PicturesOfSenses") selectorBase += "> div:last-child"; var afterRule = new StyleRule(dec) { Value = GetBaseSelectionWithSelectors(selectorBase, ":after") }; @@ -753,14 +886,21 @@ private static IEnumerable GenerateSelectorsFromNode( /// /// If configNode is the Type node for a factored collection of references, strip the collection singular selector from the parent selector /// - private static string GetParentForFactoredReference(string parentSelector, ConfigurableDictionaryNode configNode) + private static string GetBetweenForFactoredReference(string baseSelector, ConfigurableDictionaryNode configNode) { - if(!IsFactoredReferenceType(configNode)) - return parentSelector; + if (!IsFactoredReferenceType(configNode) || configNode.Parent == null) + { + Debug.Fail("Error in logic leading to FactoredReference between selector generation"); + return string.Empty; + } + // TODO: We need to refactor so that we can get the real (possibly adjusted) class name for the parent node and use it here var parentPlural = GetClassAttributeForConfig(configNode.Parent); var parentSingular = GetClassAttributeForCollectionItem(configNode.Parent); - return parentSelector.Replace(string.Format(".{0} .{1}", parentPlural, parentSingular), '.' + parentPlural); + // The base selector will come in as a collection selector e.g. '.complexFormTypes .complexFormType' + // we only want the first class so split it and take the first item + var typeCollectionClass = baseSelector.Split(' ')[0]; + return $".{parentPlural} > .{parentSingular} + {typeCollectionClass}:before"; } private static bool IsFactoredReferenceType(ConfigurableDictionaryNode configNode) @@ -802,20 +942,20 @@ private static void GenerateFlowResetForBaseNode(string baseSelection, List /// defaults to null, necessary for generating correct css for custom field nodes /// - private static string SelectClassName(ConfigurableDictionaryNode configNode, LcmCache cache = null) + private static string SelectClassName(ConfigurableDictionaryNode configNode, string adjustedClassName, LcmCache cache = null) { var type = ConfiguredLcmGenerator.GetPropertyTypeForConfigurationNode(configNode, cache); - return SelectClassName(configNode, type); + return SelectClassName(configNode, adjustedClassName, type); } - private static string SelectClassName(ConfigurableDictionaryNode configNode, ConfiguredLcmGenerator.PropertyType type) + private static string SelectClassName(ConfigurableDictionaryNode configNode, string adjustedClassName, ConfiguredLcmGenerator.PropertyType type) { switch(type) { case ConfiguredLcmGenerator.PropertyType.CollectionType: { // for collections we generate a css selector to match each item e.g '.senses .sense' - return string.Format(".{0} .{1}", GetClassAttributeForConfig(configNode), GetClassAttributeForCollectionItem(configNode)); + return string.Format("{0} .{1}", adjustedClassName, GetClassAttributeForCollectionItem(configNode)); } case ConfiguredLcmGenerator.PropertyType.CmPictureType: { @@ -832,12 +972,12 @@ private static string SelectClassName(ConfigurableDictionaryNode configNode, Con { spanStyle = "> span"; } - return "." + GetClassAttributeForConfig(configNode) + spanStyle; + return adjustedClassName + spanStyle; } goto default; } default: - return "." + GetClassAttributeForConfig(configNode); + return adjustedClassName; } } @@ -870,12 +1010,12 @@ internal static string GetClassAttributeForCollectionItem(ConfigurableDictionary /// output of this method for :before and :after rules in the css is sufficient to fix the bug reported in /// LT-17048. A better name might be nice, but this one is fairly descriptive. /// - private static string SelectBareClassName(ConfigurableDictionaryNode configNode, LcmCache cache = null) + private static string SelectBareClassName(ConfigurableDictionaryNode configNode, string adjustedClassName, LcmCache cache = null) { var type = ConfiguredLcmGenerator.GetPropertyTypeForConfigurationNode(configNode, cache); if (type == ConfiguredLcmGenerator.PropertyType.CollectionType) return "." + GetClassAttributeForConfig(configNode); - return SelectClassName(configNode, type); + return SelectClassName(configNode, adjustedClassName, type); } /// @@ -1521,33 +1661,7 @@ private static bool GetFontValue(InheritableStyleProp wsFontInfo, IStylePr return true; } - /// - /// Extension method to provide a css string conversion from an FwTextAlign enum value - /// - /// - /// - public static String AsCssString(this FwTextAlign align) - { - switch(align) - { - case (FwTextAlign.ktalJustify): - return "justify"; - case (FwTextAlign.ktalCenter): - return "center"; - case (FwTextAlign.ktalLeading): - return "start"; - case (FwTextAlign.ktalTrailing): - return "end"; - case (FwTextAlign.ktalLeft): - return "left"; - case (FwTextAlign.ktalRight): - return "right"; - default: - return "inherit"; - } - } - - public static void GenerateLetterHeaderCss(ReadOnlyPropertyTable propertyTable, LcmStyleSheet mediatorStyleSheet, StyleSheet styleSheet) + public static List GenerateLetterHeaderCss(ReadOnlyPropertyTable propertyTable, LcmStyleSheet mediatorStyleSheet) { var letHeadRule = new StyleRule { Value = ".letHead" }; letHeadRule.Declarations.Properties.Add(new Property("-moz-column-count") { Term = new PrimitiveTerm(UnitType.Number, 1) }); @@ -1557,7 +1671,7 @@ public static void GenerateLetterHeaderCss(ReadOnlyPropertyTable propertyTable, letHeadRule.Declarations.Properties.Add(new Property("width") { Term = new PrimitiveTerm(UnitType.Percentage, 100) }); letHeadRule.Declarations.Properties.AddRange(GetOnlyParagraphStyle(GenerateCssStyleFromLcmStyleSheet(LetterHeadingStyleName, 0, propertyTable))); - styleSheet.Rules.Add(letHeadRule); + return new List {letHeadRule}; } public static string GenerateCssForPageButtons() @@ -1703,4 +1817,38 @@ public static string CopyCustomCssAndGetPath(string destinationFolder, LcmCache return CopyCustomCssToTempFolder(configDir, destinationFolder, cssName); } } + + public static class CssExtensions + { + /// + /// Extension method to provide a css string conversion from an FwTextAlign enum value + /// + /// + /// + public static string AsCssString(this FwTextAlign align) + { + switch (align) + { + case (FwTextAlign.ktalJustify): + return "justify"; + case (FwTextAlign.ktalCenter): + return "center"; + case (FwTextAlign.ktalLeading): + return "start"; + case (FwTextAlign.ktalTrailing): + return "end"; + case (FwTextAlign.ktalLeft): + return "left"; + case (FwTextAlign.ktalRight): + return "right"; + default: + return "inherit"; + } + } + + public static List NonEmpty(this List rules) + { + return new List(rules.Where(s => s.Declarations.Any())); + } + } } diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index b937acef1d..8eb9d40657 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -19,8 +19,8 @@ public interface ILcmContentGenerator string WriteProcessedObject(bool isBlock, string elementContent, string className); string WriteProcessedCollection(bool isBlock, string elementContent, string className); string GenerateGramInfoBeforeSensesContent(string content); - string GenerateGroupingNode(object field, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, - Func childContentGenrator); + string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, + Func childContentGenerator); string AddSenseData(string senseNumberSpan, bool isBlockProperty, Guid ownerGuid, string senseContent, string className); string AddCollectionItem(bool isBlock, string collectionItemClass, string content); string AddProperty(string className, bool isBlockProperty, string content); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 1f87ad8814..24d7150dfb 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -103,7 +103,7 @@ public string GenerateGramInfoBeforeSensesContent(string content) return $"{content}"; } - public string GenerateGroupingNode(object field, ConfigurableDictionaryNode config, + public string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, Func childContentGenerator) { diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index 8b45a62d85..7445826f60 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -95,6 +95,7 @@ public static void SavePublishedHtmlWithStyles(int[] entryHvos, DictionaryPublic var custCssPath = CssGenerator.CopyCustomCssAndGetPath(Path.GetDirectoryName(xhtmlPath), cache, false); var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, true, true, Path.GetDirectoryName(xhtmlPath), ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropertyTable, configuration), Path.GetFileName(cssPath) == "configured.css"); + settings.StylesGenerator.AddGlobalStyles(configuration, readOnlyPropertyTable); GenerateOpeningHtml(cssPath, custCssPath, settings, xhtmlWriter); Tuple currentPageBounds = GetPageForCurrentEntry(settings, entryHvos, entriesPerPage); GenerateTopOfPageButtonsIfNeeded(settings, entryHvos, entriesPerPage, currentPageBounds, xhtmlWriter, cssWriter); @@ -149,7 +150,7 @@ public static void SavePublishedHtmlWithStyles(int[] entryHvos, DictionaryPublic cssWriter.Write(CssGenerator.GenerateCssForSelectedEntry(settings.RightToLeft)); ConfiguredLcmGenerator.CopyFileSafely(settings, Path.Combine(FwDirectoryFinder.FlexFolder, ImagesFolder, CurrentEntryMarker), CurrentEntryMarker); } - cssWriter.Write(CssGenerator.GenerateCssFromConfiguration(configuration, readOnlyPropertyTable)); + cssWriter.Write(((CssGenerator)settings.StylesGenerator).GetStylesString()); cssWriter.Flush(); } } @@ -225,12 +226,13 @@ public static string GenerateEntryHtmlWithStyles(ICmObject entry, DictionaryConf var readOnlyPropTable = new ReadOnlyPropertyTable(propertyTable); var exportSettings = new ConfiguredLcmGenerator.GeneratorSettings(readOnlyPropTable.GetValue("cache"), readOnlyPropTable, false, false, null, ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropTable, configuration)); + exportSettings.StylesGenerator.AddGlobalStyles(configuration, new ReadOnlyPropertyTable(propertyTable)); GenerateOpeningHtml(previewCssPath, custCssPath, exportSettings, writer); var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, pubDecorator, exportSettings); writer.WriteRaw(content); GenerateClosingHtml(writer); writer.Flush(); - cssWriter.Write(CssGenerator.GenerateCssFromConfiguration(configuration, readOnlyPropTable)); + cssWriter.Write(((CssGenerator)exportSettings.StylesGenerator).GetStylesString()); cssWriter.Flush(); } @@ -636,20 +638,20 @@ public string GenerateGramInfoBeforeSensesContent(string content) } } - public string GenerateGroupingNode(object field, ConfigurableDictionaryNode config, + public string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, - Func childContentGenrator) + Func childContentGenerator) { var bldr = new StringBuilder(); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("span"); - xw.WriteAttributeString("class", CssGenerator.GetClassAttributeForConfig(config)); + xw.WriteAttributeString("class", className); var innerBuilder = new StringBuilder(); foreach (var child in config.ReferencedOrDirectChildren) { - var childContent = childContentGenrator(field, child, publicationDecorator, settings); + var childContent = childContentGenerator(field, child, publicationDecorator, settings); innerBuilder.Append(childContent); } var innerContents = innerBuilder.ToString(); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 762be15883..ff6c3799e3 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -2755,17 +2755,13 @@ public void GenerateContentForEntry_SubSubSensesWithNumberingStyle() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); - const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; - const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; - const string subSenseContent = senseContent + "/span[@class='sense']/span[@class='shares senses']/span[@class='sensecontent']"; - const string subSenseNumberTwoOne = subSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = subSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; - const string subSubSenseContent = subSenseContent + "/span[@class='share sense']/span[@class='shares senses']/span[@class='sensecontent']"; - const string subSubSenseNumberTwoOneOne = subSubSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='matte']"; - const string subSubSubSenseContent = subSubSenseContent + "/span[@class='share sense']/span[@class='shares senses']/span[@class='sensecontent']"; - const string subSubSubSenseNumberTwoOneOneOne = subSubSubSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='matte2.1']"; - const string subSubSubSenseNumberTwoOneOneTwo = subSubSubSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='matte2.2']"; + const string senseNumberOne = "//span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "//span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + const string subSenseNumberTwoOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string subSubSenseNumberTwoOneOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='matte']"; + const string subSubSubSenseNumberTwoOneOneOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='matte2.1']"; + const string subSubSubSenseNumberTwoOneOneTwo = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='matte2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2900,12 +2896,9 @@ public void GenerateContentForEntry_SubSensesOfSingleSenses_GetFullNumbers() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); - const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; - const string subSenseContent = senseContent + "/span[@class='sense']/span[@class='shares senses']/span[@class='sensecontent']"; - const string subSenseNumberOneOne = subSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]//span[@lang='en' and text()='subGloss']"; - const string subosoSenseContent = subSenseContent + "/span[@class='share sense']/span[@class='shares senses']/span[@class='sensecontent']"; - const string subosoSenseNumberOneOneOne = subosoSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]//span[@lang='en' and text()='subGloss2.1']"; - const string subosoSenseNumberOneOneTwo = subosoSenseContent + "/span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='b']]//span[@lang='en' and text()='subGloss2.2']"; + const string subSenseNumberOneOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]//span[@lang='en' and text()='subGloss']"; + const string subosoSenseNumberOneOneOne = "//span[@lang='en' and text()='subGloss2.1']"; + const string subosoSenseNumberOneOneTwo = "//span[@lang='en' and text()='subGloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subSenseNumberOneOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subosoSenseNumberOneOneOne, 1); @@ -3705,7 +3698,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferenc //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences refdrefs']/span[@class='minimallexreference refdref']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); + "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); } [Test] @@ -6893,10 +6886,10 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubsubentry([Valu //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); const string fwdNameXpath = - "//span[@class='subentries subentries']/span[@class='subentry subentry']/span[@class='subentries subentries']/span[@class='subentry subentry']" + "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries']/span[@class='subentry subentry']" + "/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; const string revNameXpath = - "//span[@class='subentries subentries']/span[@class='subentry subentry']/span[@class='subentries subentries']/span[@class='subentry subentry']" + "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries']/span[@class='subentry subentry']" + "/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(string.Format(fwdNameXpath, complexRefAbbr)); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(string.Format(revNameXpath, complexRefRevAbbr), 1); @@ -7446,7 +7439,7 @@ public void GenerateContentForEntry_ReferencedNode_GeneratesBothClasses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); - const string headwordXpath = "//span[@class='reffingsubs sharedsubentries']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; + const string headwordXpath = "//span[@class='reffingsubs']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 78aa37f124..fcc755ee97 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -110,7 +110,7 @@ public void GenerateLetterHeaderCss_CssUsesDefinedStyleInfo() var mediatorStyles = FontHeightAdjuster.StyleSheetFromPropertyTable(m_propertyTable); var styleSheet = new StyleSheet(); //SUT - CssGenerator.GenerateLetterHeaderCss(m_propertyTable, mediatorStyles, styleSheet); + styleSheet.Rules.AddRange(CssGenerator.GenerateLetterHeaderCss(m_propertyTable, mediatorStyles)); // verify that the css result contains boilerplate rules and the text-align center expected from the letHeadStyle test style Assert.IsTrue(Regex.Match(styleSheet.ToString(), @"\.letHead\s*{\s*-moz-column-count:1;\s*-webkit-column-count:1;\s*column-count:1;\s*clear:both;\s*width:100%;.*text-align:center").Success, "GenerateLetterHeaderCss did not generate the expected css rules"); @@ -138,17 +138,17 @@ public void GenerateCssForConfiguration_SimpleConfigurationGeneratesValidCss() //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // verify that the css result contains a line similar to: .lexentry {clear:both;white-space:pre;} - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry\s*{\s*clear:both;\s*white-space:pre-wrap;").Success, + VerifyRegex(cssResult, @"\.lexentry\s*{\s*clear:both;\s*white-space:pre-wrap;", "Css for root node(lexentry) did not generate 'clear' and 'white-space' rules match"); - // verify that the css result contains a line similar to: .lexentry .headword { - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.mainheadword>\s*span\s*{.*").Success, + // verify that the css result contains a line similar to: .mainheadword span { { + VerifyRegex(cssResult, @"^\s*\.mainheadword>\s*span\s*{.*", "Css for child node(headword) did not generate a specific match"); } [Test] public void GenerateCssForConfiguration_SharedConfigurationGeneratesValidCss() { - var headwordNode = new ConfigurableDictionaryNode + var subEntryHeadwordNode = new ConfigurableDictionaryNode { FieldDescription = "MLHeadWord", CSSClassNameOverride = "mainheadword", @@ -158,24 +158,35 @@ public void GenerateCssForConfiguration_SharedConfigurationGeneratesValidCss() var sharedNode = new ConfigurableDictionaryNode { Label = "SharedSubentries", - Children = new List { headwordNode }, + Children = new List { subEntryHeadwordNode }, FieldDescription = "Subentries", CSSClassNameOverride = "sharedsubentries" }; var subentriesNode = new ConfigurableDictionaryNode { FieldDescription = "Subentries", ReferenceItem = "SharedSubentries" }; + var mainHeadwordNode = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + CSSClassNameOverride = "mainheadword", + DictionaryNodeOptions = ConfiguredXHTMLGeneratorTests.GetWsOptionsForLanguages(new[] { "fr" }), + Before = " " + }; var mainEntryNode = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", - Children = new List { subentriesNode } + Children = new List { subentriesNode, mainHeadwordNode } }; var model = DictionaryConfigurationModelTests.CreateSimpleSharingModel(mainEntryNode, sharedNode); PopulateFieldsForTesting(model); + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); + cssGenerator.AddStyles(mainHeadwordNode); + cssGenerator.AddStyles(subEntryHeadwordNode); + var cssResult = cssGenerator.GetStylesString(); // verify that the css result contains a line similar to: .sharedsubentries .sharedsubentry .headword span{ - Assert.IsTrue(Regex.Match(cssResult, @"\.sharedsubentries\s*\.sharedsubentry>\s*\.mainheadword>\s*span\s*{.*").Success, - "Css for child node(headword) did not generate a match{0}{1}", Environment.NewLine, cssResult); + VerifyRegex(cssResult, @"^\s*\.sharedsubentries-mainheadword>\s*span\s*{.*", + "Css for child node(headword) did not generate a match"); } [Test] @@ -192,6 +203,20 @@ public void GenerateCssForConfiguration_LinksLookLikePlainText() Assert.IsTrue(Regex.Match(cssResult, @"\s*a\s*{[^}]*color:inherit;").Success, "Links should inherit color."); } + [Test] + public void GenerateCssForConfiguration_AddStyleDoesNotGenerateEmptyStyle() + { + var emptyNode = new ConfigurableDictionaryNode { FieldDescription = "Nothing"}; + var mainEntryNode = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", Children = new List { emptyNode } }; + PopulateFieldsForTesting(mainEntryNode); + + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); + //SUT + cssGenerator.AddStyles(emptyNode); + Assert.That(cssGenerator.GetStylesString(), Is.EqualTo(string.Empty)); + } + [Test] public void GenerateCssForConfiguration_GeneratesShimForBidirectionalText() { @@ -257,11 +282,11 @@ public void GenerateCssForConfiguration_BeforeAfterSpanConfigGeneratesApostrophe //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // Check result for before, between and after rules - Assert.IsTrue(Regex.Match(cssResult, @"\.mainheadword>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'\\\s*'beforeText\\'\s*';\s*}").Success, + VerifyRegex(cssResult, @"\.mainheadword>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'\\\s*'beforeText\\'\s*';\s*}", "css before rule with 'beforeText' content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.mainheadword>\s*.mainheadwor\s*\+\s*.mainheadwor:before\s*{\s*content\s*:\s*'\\\s*'betweenText\\'\s*';\s*}").Success, + VerifyRegex(cssResult, @"\.mainheadword>\s*.mainheadwor\s*\+\s*.mainheadwor:before\s*{\s*content\s*:\s*'\\\s*'betweenText\\'\s*';\s*}", "css before rule with 'betweenText' content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.mainheadword>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'\\\s*'afterText\\'\s*';\s*}").Success, + VerifyRegex(cssResult, @"\.mainheadword>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'\\\s*'afterText\\'\s*';\s*}", "css after rule with 'afterText' content not found on headword"); } @@ -285,24 +310,37 @@ public void GenerateCssForConfiguration_BeforeAfterGroupingSpanWorks() After = "}", DictionaryNodeOptions = new DictionaryNodeGroupingOptions() }; + var mainHeadword = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + Label = "Headword", + CSSClassNameOverride = "mh", + DictionaryNodeOptions = ConfiguredXHTMLGeneratorTests.GetWsOptionsForLanguages(new[] { "fr" }), + After = " " + }; var mainEntryNode = new ConfigurableDictionaryNode { - Children = new List { groupingNode }, + Children = new List { groupingNode, mainHeadword }, FieldDescription = "LexEntry" }; PopulateFieldsForTesting(mainEntryNode); var model = new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }; + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); + cssGenerator.AddStyles(mainHeadword); + cssGenerator.AddStyles(groupingNode); + cssGenerator.AddStyles(headwordNode); + var cssResult = cssGenerator.GetStylesString(); // Check the result for before and after rules for the group Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:before\s*{\s*content\s*:\s*'{';\s*}").Success, "css before rule for the grouping node was not generated"); Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, "css after rule for the grouping node was not generated"); // Check result for before and after rules equivalent to .headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg>\s*\.mh>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-mh>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, "css before rule with Z content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg>\s*\.mh>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-mh>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, "css after rule with A content not found on headword"); } @@ -359,8 +397,8 @@ public void GenerateCssForConfiguration_BetweenSpaceIsNotAddedAfterSingleHeadwor //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // Check result for between rule equivalent to lexentry> .mainheadword> span.writingsystemprefix + span:not(:last-child):after{content:' ';} - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry\s*>\s*\.mainheadword>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before{.*content:' ';.*}", RegexOptions.Singleline).Success, - "Between selector not generated."); + VerifyRegex(cssResult, @".*\.mainheadword>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before{.*content:' ';.*}", + "Between selector not generated."); } [Test] @@ -380,19 +418,30 @@ public void GenerateCssForConfiguration_BeforeAfterConfigGeneratesBeforeAfterCss Children = new List {headwordNode}, FieldDescription = "Subentries" }; + var mainEntryHeadword = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + Label = "Headword", + CSSClassNameOverride = "headword", + DictionaryNodeOptions = ConfiguredXHTMLGeneratorTests.GetWsOptionsForLanguages(new[] { "fr" }), + After = " " + }; var mainEntryNode = new ConfigurableDictionaryNode { - Children = new List { subentryNode }, + Children = new List { mainEntryHeadword, subentryNode }, FieldDescription = "LexEntry" }; PopulateFieldsForTesting(mainEntryNode); - var model = new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }; //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - // Check result for before and after rules equivalent to .subentries .subentry .headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - Assert.IsTrue(Regex.Match(cssResult, @"\.subentries\s*\.subentry>\s*\.headword>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); + cssGenerator.AddStyles(mainEntryHeadword); + cssGenerator.AddStyles(headwordNode); + var cssResult = cssGenerator.GetStylesString(); + // Check result for before and after rules equivalent to .subentries-headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} + VerifyRegex(cssResult, @"\.subentries-headword>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", "css before rule with Z content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.subentries\s*\.subentry>\s\.headword>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, + VerifyRegex(cssResult, @"\.subentries-headword>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", "css after rule with A content not found on headword"); } @@ -452,9 +501,9 @@ public void GenerateCssForConfiguration_BeforeAfterConfigGeneratesBeforeAfterCss var model = new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses:after")); - Assert.That(cssResult, Does.Not.Contain(".lexentry> .senses .sense:after")); - Assert.That(cssResult, Does.Not.Contain(".lexentry> .senses .sense:last-child:after")); + Assert.That(cssResult, Contains.Substring(".senses:after")); + Assert.That(cssResult, Does.Not.Contain(".senses .sense:after")); + Assert.That(cssResult, Does.Not.Contain(".senses .sense:last-child:after")); } } @@ -494,12 +543,9 @@ public void GenerateCssForConfiguration_DefinitionOrGlossBeforeAfterConfigGenera var model = new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense> .definitionorgloss> span:first-child:before{.*content:'<';.*}", - RegexOptions.Singleline).Success, "Before not generated."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense> .definitionorgloss> span\+span\[lang\|=\'en\']:before{.*content:',';.*}", - RegexOptions.Singleline).Success, "Between not generated."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense> .definitionorgloss> span:last-child:after{.*content:'>';.*}", - RegexOptions.Singleline).Success, "After not generated."); + VerifyRegex(cssResult, @".definitionorgloss> span:first-child:before{.*content:'<';.*}", "Before not generated."); + VerifyRegex(cssResult, @".definitionorgloss> span\+span\[lang\|=\'en\']:before{.*content:',';.*}", "Between not generated."); + VerifyRegex(cssResult, @".definitionorgloss> span:last-child:after{.*content:'>';.*}", "After not generated."); } [Test] @@ -544,7 +590,7 @@ public void GenerateCssForConfiguration_WithBulletStyleOnNoteAndWritingSystemsCs DictionaryNodeOptions = wsOpts, Style = "Bulleted List" }; - var definitionOrGloss = new ConfigurableDictionaryNode + var bibliography = new ConfigurableDictionaryNode { FieldDescription = "Bibliography", Before = "<", @@ -557,7 +603,7 @@ public void GenerateCssForConfiguration_WithBulletStyleOnNoteAndWritingSystemsCs { FieldDescription = "SensesOS", CSSClassNameOverride = "Senses", - Children = new List { anthroNote, definitionOrGloss } + Children = new List { anthroNote, bibliography } }; var mainEntryNode = new ConfigurableDictionaryNode { @@ -570,18 +616,16 @@ public void GenerateCssForConfiguration_WithBulletStyleOnNoteAndWritingSystemsCs var model = new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense>.*.anthronote{.*font-size:12pt;.*color:#F00;.*content:'\\25A0';.*}", - RegexOptions.Singleline).Success, "AnthoNote content not generated."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense>.*.anthronote>.*span.writingsystemprefix.~.span.writingsystemprefix:before", - RegexOptions.Singleline).Success, "AnthoNote between content not generated."); - Assert.IsFalse(Regex.Match(cssResult, @".lexentry> .senses .sense> .anthronote:after", - RegexOptions.Singleline).Success, "AnthoNote after content should not generated."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .senses .sense> .bibliography> span{.*font-size:12pt;.*color:#F00;.*content:'\\25A0';.*}", - RegexOptions.Singleline).Success, "Bibliography content not generated."); - Assert.IsFalse(Regex.Match(cssResult, @".lexentry> .senses .sense>.*.bibliography>.*span.writingsystemprefix.~.span.writingsystemprefix:before", - RegexOptions.Singleline).Success, "Bibliography between content should not generated."); - Assert.IsFalse(Regex.Match(cssResult, @".lexentry> .senses .sense>.*.bibliography:after", - RegexOptions.Singleline).Success, "Bibliography after content should not generated."); + VerifyRegex(cssResult, @"\s*\.anthronote:before{.*font-size:12pt;.*color:#F00;.*content:'\\25A0';.*}", + "AnthroNote content not generated."); + VerifyRegex(cssResult, @"\s*\.anthronote>.*span.writingsystemprefix.~.span.writingsystemprefix:before", + "AnthroNote between content not generated."); + Assert.That(Regex.Match(cssResult, @".*\.anthronote:after").Success, Is.False, "AnthroNote after content should not generated."); + VerifyRegex(cssResult, @"\s*\.bibliography> span{.*font-size:12pt;.*color:#F00;.*content:'\\25A0';.*}", + "Bibliography content not generated."); + Assert.That(Regex.Match(cssResult, @"\s*\.bibliography>.*span.writingsystemprefix.~.span.writingsystemprefix:before").Success, Is.False, + "Bibliography between content should not generated."); + Assert.That(Regex.Match(cssResult, @"\s*\.bibliography:after").Success, Is.False, "Bibliography after content should not generated."); } [Test] @@ -609,8 +653,8 @@ public void GenerateCssForCustomBulletStyleForSenses() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regExPected = @".lexentry>\s.senses\s>\s.sensecontent:before.*{.*content:'@';.*font-size:14pt;.*color:Green;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Custom bullet content not generated."); + const string regExPected = @"\s*.senses\s>\s.sensecontent:before.*{.*content:'@';.*font-size:14pt;.*color:Green;.*}"; + VerifyRegex(cssResult, regExPected, "Custom bullet content not generated."); } [Test] @@ -1279,7 +1323,7 @@ public void CssAndXhtmlMatchOnSenseCollectionItems() sense.Gloss.set_String(wsEn, TsStringUtils.MakeString("gloss", wsEn)); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .gloss")); + Assert.That(cssResult, Contains.Substring(".gloss")); var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings); const string positiveTest = "/*[@class='lexentry']/span[@class='senses']/span[@class='sense']/span[@class='gloss']"; @@ -1534,10 +1578,9 @@ public void GenerateCssForConfiguration_GramInfoFieldsWork() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .morphosyntaxanalysisra> .mlpartofspeech")); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .morphosyntaxanalysisra> .mlinflectionclass")); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .morphosyntaxanalysisra> .slots .slot> .name")); - Assert.False(Regex.Match(cssResult, @"{\s*}").Success); // make sure we filter out empty rules + Assert.That(cssResult, Contains.Substring(".mlpartofspeech")); + Assert.That(cssResult, Contains.Substring(".mlinflectionclass")); + Assert.That(cssResult, Contains.Substring(".name")); } [Test] @@ -1567,7 +1610,7 @@ public void GenerateCssForConfiguration_VariantPronunciationFormWorks() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .variantformentrybackrefs .variantformentrybackref> .pronunciations .pronunciation> .form")); + VerifyRegex(cssResult, @"\s*\.form", "No form style generated"); } [Test] @@ -1600,7 +1643,7 @@ public void GenerateCssForConfiguration_SubentryTypeWorks() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .subentries .subentry> .complexformtypes .complexformtype> .reverseabbr> span")); + VerifyRegex(cssResult, @"\s*\.reverseabbr> span", "No reverse abbreviation span style generated"); } [Test] @@ -1639,11 +1682,11 @@ public void GenerateCssForConfiguration_GeneratesComplexFormTypesBeforeBetweenAf PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs> .complexformtypes .complexformtype> .name:before{\s*content:'<';\s*}", + VerifyRegex(cssResult, @".name:before{\s*content:'<';\s*}", "Before not generated:"); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs> .complexformtypes .complexformtype> .name> .nam\+ .nam:before{\s*content:',';\s*}", + VerifyRegex(cssResult, @".name> .nam \+ .nam:before{\s*content:',';\s*}", "Between not generated:"); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs> .complexformtypes .complexformtype> .name:after{\s*content:'>';\s*}", + VerifyRegex(cssResult, @".name:after{\s*content:'>';\s*}", "After not generated:"); } @@ -1686,12 +1729,10 @@ public void GenerateCssForConfiguration_GeneratesComplexFormTypesBeforeBetweenAf PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs .visiblecomplexformbackref> .complexformtypes .complexformtype> .name:before{\s*content:'<';\s*}", - "Before not generated:"); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs .visiblecomplexformbackref> .complexformtypes .complexformtype> .name> .nam\+ .nam:before{\s*content:',';\s*}", + VerifyRegex(cssResult, @"^\s*\.name:before{\s*content:'<';\s*}", "Before not generated:"); + VerifyRegex(cssResult, @"^\s*.name> .nam \+ .nam:before{\s*content:',';\s*}", "Between not generated:"); - VerifyRegex(cssResult, @".lexentry> .visiblecomplexformbackrefs .visiblecomplexformbackref> .complexformtypes .complexformtype> .name:after{\s*content:'>';\s*}", - "After not generated:"); + VerifyRegex(cssResult, @"^\s*\.name:after{\s*content:'>';\s*}", "After not generated:"); } [Test] @@ -1733,20 +1774,16 @@ public void GenerateCssForConfiguration_GeneratesVariantTypesBeforeBetweenAfter( PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs:before{.*content:'\[';.*}", - RegexOptions.Singleline).Success, "Before not generated for Variant Entry."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs> .variantformentrybackref\+ .variantformentrybackref:before{.*content:'\; ';.*}", - RegexOptions.Singleline).Success, "Between not generated Variant Entry."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs:after{.*content:'\]';.*}", - RegexOptions.Singleline).Success, "After not generated Variant Entry."); - Assert.False(Regex.Match(cssResult, @".lexentry .variantformentrybackrefs> .span\+ .span:before").Success); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs .variantformentrybackref> .variantentrytypes .variantentrytype> .name:before{.*content:'<';.*}", - RegexOptions.Singleline).Success, "Before not generated Variant Entry Type."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs .variantformentrybackref> .variantentrytypes .variantentrytype> .name> .nam\+ .nam:before{.*content:',';.*}", - RegexOptions.Singleline).Success, "Between not generated Variant Entry Type."); - Assert.IsTrue(Regex.Match(cssResult, @".lexentry> .variantformentrybackrefs .variantformentrybackref> .variantentrytypes .variantentrytype> .name:after{.*content:'>';.*}", - RegexOptions.Singleline).Success, "After not generated Variant Entry Type."); - } + VerifyRegex(cssResult, @".variantformentrybackrefs:before{.*content:'\[';.*}", + "Before not generated for Variant Entry."); + VerifyRegex(cssResult, @".variantformentrybackrefs>\s+.variantformentrybackref\s*\+\s*.variantformentrybackref:before{.*content:'\; ';.*}", + "Between not generated Variant Entry."); + VerifyRegex(cssResult, @".variantformentrybackrefs:after{.*content:'\]';.*}", "After not generated Variant Entry."); + // Review: Was this assert correct before? VerifyRegex(cssResult, @".variantformentrybackrefs> .span \+ .span:before"); + VerifyRegex(cssResult, @"^\.name:before{.*content:'<';.*}", "Before not generated Variant Entry Type."); + VerifyRegex(cssResult, @"^\.name:after{.*content:'>';.*}", "After not generated Variant Entry Type."); + VerifyRegex(cssResult, @"^\.name> .nam \+ .nam:before{.*content:',';.*}", "Between not generated Variant Entry Type."); + } [Test] public void GenerateCssForConfiguration_GeneratesVariantNameSuffixBeforeBetweenAfter() @@ -1791,19 +1828,19 @@ public void GenerateCssForConfiguration_GeneratesVariantNameSuffixBeforeBetweenA PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants:before{.*content:'\[';.*}", + VerifyRegex(cssResult, @".variantformentrybackrefs_inflectional-variants:before{.*content:'\[';.*}", "Before not generated for Variant Entry."); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants> .variantformentrybackref_inflectional-variants\+ .variantformentrybackref_inflectional-variants:before{.*content:'\; ';.*}", + VerifyRegex(cssResult, @"^\.variantformentrybackrefs_inflectional-variants>\s+\.variantformentrybackref_inflectional-variants\s*\+\s*\.variantformentrybackref_inflectional-variants:before{.*content:'\; ';.*}", "Between should have been generated using class selectors because this element has type factoring."); Assert.False(Regex.Match(cssResult, @".lexentry>? .variantformentrybackrefs_inflectional-variants>? span\+ span:before").Success, "Between should not have been generated using generic spans because this element has type factoring." + Environment.NewLine + cssResult); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants:after{.*content:'\]';.*}", + VerifyRegex(cssResult, @".variantformentrybackrefs_inflectional-variants:after{.*content:'\]';.*}", "After not generated Variant Entry."); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants> .variantentrytypes .variantentrytype> .name:before{.*content:'<';.*}", + VerifyRegex(cssResult, @"^\s*\.name:before{.*content:'<';.*}", "Before not generated Variant Entry Type:"); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants> .variantentrytypes .variantentrytype> .name> .nam\+ .nam:before{.*content:',';.*}", + VerifyRegex(cssResult, @"^\s*\.name> .nam \+ .nam:before{.*content:',';.*}", "Between not generated Variant Entry Type:"); - VerifyRegex(cssResult, @".lexentry> .variantformentrybackrefs_inflectional-variants> .variantentrytypes .variantentrytype> .name:after{.*content:'>';.*}", + VerifyRegex(cssResult, @"^\s*\.name:after{.*content:'>';.*}", "After not generated Variant Entry Type:"); } @@ -1829,19 +1866,30 @@ public void GenerateCssForConfiguration_SenseComplexFormsNotSubEntriesHeadWord() CSSClassNameOverride = "Senses", Children = new List { complexformsnotsubentries } }; + var headwordMain = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadword", + CSSClassNameOverride = "HeadWord", + After = " " + }; var entry = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", CSSClassNameOverride = "lexentry", - Children = new List { senses } + Children = new List { senses, headwordMain } }; var model = new DictionaryConfigurationModel(); model.Parts = new List { entry }; PopulateFieldsForTesting(entry); + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .otherreferencedcomplexforms .otherreferencedcomplexform> .headword")); + cssGenerator.AddGlobalStyles(model, m_propertyTable); + cssGenerator.AddStyles(headwordMain); + cssGenerator.AddStyles(form); + var cssResult = cssGenerator.GetStylesString(); + VerifyRegex(cssResult, @"^\s*\.otherreferencedcomplexforms-headword", "Headword node not generated for non subentry headword"); } [Test] @@ -1872,38 +1920,8 @@ public void GenerateCssForConfiguration_ComplexFormsEachInOwnParagraph() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .complexforms .complexform> .headword")); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.complexforms\s*\.complexform{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); - } - - [Test] - public void GenerateCssForConfiguration_SenseSubEntriesHeadWord() - { - var form = new ConfigurableDictionaryNode { FieldDescription = "HeadWord", Style = "FooStyle"}; - var subentries = new ConfigurableDictionaryNode - { - FieldDescription = "Subentries", - Children = new List { form } - }; - var senses = new ConfigurableDictionaryNode - { - FieldDescription = "SensesOS", - CSSClassNameOverride = "Senses", - Children = new List { subentries } - }; - var entry = new ConfigurableDictionaryNode - { - FieldDescription = "LexEntry", - CSSClassNameOverride = "lexentry", - Children = new List { senses } - }; - - var model = new DictionaryConfigurationModel(); - model.Parts = new List { entry }; - PopulateFieldsForTesting(entry); - //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses .sense> .subentries .subentry> .headword")); + Assert.That(cssResult, Contains.Substring(".headword")); // Make sure that the headword style was generated + Assert.IsTrue(Regex.Match(cssResult, @"\.complexforms\s*\.complexform{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); } [Test] @@ -1937,13 +1955,17 @@ public void GenerateCssForConfiguration_SenseShowGramInfoFirstWorks() var model = new DictionaryConfigurationModel(); model.Parts = new List { entry }; PopulateFieldsForTesting(entry); + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); + cssGenerator.AddGlobalStyles(model, m_propertyTable); //SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses > .sensecontent > .sense> .gloss")); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses > .sensecontent > .sense> .morphosyntaxanalysisra")); - Assert.IsTrue(Regex.Match(cssResult, - @"\.lexentry>\s*\.senses\s*>\s*\.sharedgrammaticalinfo\s*>\s*\.morphosyntaxanalysisra\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", - RegexOptions.Singleline).Success, "Style for sharedgrammaticalinfo not placed correctly"); + cssGenerator.AddStyles(senses); + cssGenerator.AddStyles(gramInfo); + var cssResult = cssGenerator.GetStylesString(); + VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra", "Style for non-shared grammatical info not generated"); + VerifyRegex(cssResult, + @"^\s*\.senses\s*>\s*\.sharedgrammaticalinfo\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", + "Style for sharedgrammaticalinfo not placed correctly"); } [Test] @@ -1980,9 +2002,7 @@ public void GenerateCssForConfiguration_GramInfoFirstHasNoBetweenMaterialWorks() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, - @"\.entry>\s*\.senses>\s*span.sensecontent\+\s*span\:before\{\s*content\:\'\*\'\;", - RegexOptions.Singleline).Success, "Between Material for Senses not placed correctly"); + VerifyRegex(cssResult, @"^\s*\.senses>\s*span\.sensecontent \+\s*span\:before\{\s*content\:\'\*\'\;", "Between Material for Senses not placed correctly"); } [Test] @@ -2008,9 +2028,9 @@ public void GenerateCssForConfiguration_WritingSystemAudioWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .lexemeformoa> span[lang|=\"en-Zxxx-x-audio\"]{")); - Assert.IsTrue(Regex.Match(cssResult, @"a.en-Zxxx-x-audio{.*text-decoration:none;.*}", RegexOptions.Singleline).Success, - "Audio not generated."); + // Not using regex to avoid figuring out all the escapes necessary + Assert.That(cssResult, Contains.Substring(".lexemeformoa> span[lang|=\"en-Zxxx-x-audio\"]{")); + VerifyRegex(cssResult, @"a.en-Zxxx-x-audio{.*text-decoration:none;.*}", "Audio not generated."); } [Test] @@ -2035,9 +2055,8 @@ public void GenerateCssForConfiguration_SenseDisplayInParaWorks() PopulateFieldsForTesting(model); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses > .sensecontent > .sense> .gloss")); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent(\s*\+\s*\.sensecontent)?\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); - Assert.False(Regex.Match(cssResult, @"{\s*}").Success); // make sure we filter out empty rules + VerifyRegex(cssResult, @"^\.gloss", "gloss missing"); + VerifyRegex(cssResult, @"^\s*\.senses\s*>\s*\.sensecontent\s*{\s*display\s*:\s*block;.*}"); } [Test] @@ -2068,7 +2087,7 @@ public void GenerateCssForConfiguration_ExampleDisplayInParaWorks() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*>\s*\.sense>\s*\.examples\s*\.example\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); + Assert.IsTrue(Regex.Match(cssResult, @"\.example\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); } [Test] @@ -2099,7 +2118,7 @@ public void GenerateCssForConfiguration_ExampleUncheckedDisplayInParaWorks() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsFalse(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*\.sense>\s*\.examples\s*\.example\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); + Assert.IsFalse(Regex.Match(cssResult, @"\.example\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); } [Test] @@ -2125,9 +2144,8 @@ public void GenerateCssForConfiguration_SenseParaStyleNotAppliedToInLineFirstSen PopulateFieldsForTesting(model); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses > .sensecontent > .sense> .gloss")); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*\+\s*\.sensecontent\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*>\s*\.sense\s*{.*font-style\s*:\s*italic;.*}", RegexOptions.Singleline).Success); + VerifyRegex(cssResult, @"^\s*\.senses\s*>\s*\.sensecontent\s*\+\s*\.sensecontent\s*{.*display\s*:\s*block;.*}", "First sense inline style not generated"); + VerifyRegex(cssResult, @"^\s*\.senses\s*>\s*\.sensecontent\s*>\s*\.sense\s*{.*font-style\s*:\s*italic;.*}", "Style for each sense not generated"); } [Test] @@ -2153,9 +2171,8 @@ public void GenerateCssForConfiguration_SenseParaStyleAppliedToFirstSense() PopulateFieldsForTesting(model); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".lexentry> .senses > .sensecontent > .sense> .gloss")); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*{.*display\s*:\s*block;.*}", RegexOptions.Singleline).Success); - Assert.IsTrue(Regex.Match(cssResult, @"\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*>\s*\.sense\s*{.*font-style\s*:\s*italic;.*}", RegexOptions.Singleline).Success); + VerifyRegex(cssResult, @"\s*\.senses\s*>\s*\.sensecontent\s*{.*display\s*:\s*block;.*}", "Block display not applied to all senses"); + VerifyRegex(cssResult, @"\s*\.senses\s*>\s*\.sensecontent\s*>\s*\.sense\s*{.*font-style\s*:\s*italic;.*}", "Font style missing"); } [Test] @@ -2179,8 +2196,7 @@ public void GenerateCssForConfiguration_SenseNumberCharStyleWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber", RegexOptions.Singleline).Success, - "sense number style selector was not generated."); + VerifyRegex(cssResult, @"\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber", "sense number style selector was not generated."); VerifyFontInfoInCss(FontColor, FontBGColor, FontName, FontBold, FontItalic, FontSize, cssResult); } @@ -2233,9 +2249,8 @@ public void GenerateCssForConfiguration_ReversalSenseNumberWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(cssResult, Contains.Substring(".reversalindexentry> .refdsenses > .sensecontent > .refdsense> .gloss")); - Assert.IsTrue(Regex.Match(cssResult, @"\.reversalindexentry>\s*\.refdsenses\s*>\s*\.sensecontent\s*\.sensenumber\s*{.*font-style\s*:\s*italic;.*}", RegexOptions.Singleline).Success); - Assert.False(Regex.Match(cssResult, @"{\s*}").Success); // make sure we filter out empty rules + VerifyRegex(cssResult, @"^.gloss\s*{\s*font-family", "Gloss with style was not generated from reversal sense"); + VerifyRegex(cssResult, @"^\.refdsenses\s*>\s*\.sensecontent\s*\.sensenumber\s*{.*font-style\s*:\s*italic;.*}", "Sense Number missing"); } [Test] @@ -2258,10 +2273,8 @@ public void GenerateCssForConfiguration_SenseNumberBeforeAndAfterWork() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber:before{.*content:'\['.*}", RegexOptions.Singleline).Success, - "Before content not applied to the sense number selector."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber:after{.*content:'\]'.*}", RegexOptions.Singleline).Success, - "After content not applied to the sense number selector."); + VerifyRegex(cssResult, @"\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber:before{.*content:'\['.*}", "Before content not applied to the sense number selector."); + VerifyRegex(cssResult, @"\s*\.senses\s*>\s*\.sensecontent\s*\.sensenumber:after{.*content:'\]'.*}", "After content not applied to the sense number selector."); } [Test] @@ -2309,17 +2322,15 @@ public void GenerateCssForConfiguration_PrimaryEntryReferencesTypeContextWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string thisMainSense = @"\.reversalindexentry>\s*\.sensesrs\s*\.sensesr>\s*\.entryrefswiththismainsense"; - VerifyRegex(cssResult, thisMainSense + @">\s*\.entrytypes:before{\s*content:'b4';\s*}"); // TODO? (Hasso) 2016.10: put on .types .type first-child - VerifyRegex(cssResult, thisMainSense + @">\s*\.entryrefswiththismainsens\s*\+\s*.entrytypes:before{\s*content:'twixt';\s*}", + VerifyRegex(cssResult, @"\s*\.entrytypes:before{\s*content:'b4';\s*}"); // TODO? (Hasso) 2016.10: put on .types .type first-child + VerifyRegex(cssResult, @"\s*\.entryrefswiththismainsens\s*\+\s*.entrytypes:before\s*{\s*content:'twixt';\s*}", "Until everything else is restructured under the yet-to-be-added Targets node, Factoring Type.Between goes between typed factions"); - VerifyRegex(cssResult, thisMainSense + @">\s*\.entrytypes:after{\s*content:'farther back';\s*}"); - VerifyRegex(cssResult, thisMainSense + @"\s*\.entryrefswiththismainsens>\s*\.testheadword:after{\s*content:'ah';\s*}", + VerifyRegex(cssResult, @"\s*\.entrytypes:after{\s*content:'farther back';\s*}"); + VerifyRegex(cssResult, @"^\.testheadword:after{\s*content:'ah';\s*}", "Headword's selector should *not* have changed due to factoring"); - const string entryType = thisMainSense + @">\s*\.entrytypes \.entrytype"; - VerifyRegex(cssResult, entryType + @">\s*\.reversename>\s*span:first-child:before{\s*content:'beef';\s*}"); - VerifyRegex(cssResult, entryType + @">\s*\.reversename>\s*span+span\[lang|='" + lang2 + @"'\]:before{\s*content:'viet';\s*}"); - VerifyRegex(cssResult, entryType + @">\s*\.reversename>\s*span:last-child:after{\s*content:'aft';\s*}"); + VerifyRegex(cssResult, @"\s*\.reversename>\s*span:first-child:before{\s*content:'beef';\s*}"); + VerifyRegex(cssResult, @"\s*\.reversename>\s*span+span\[lang|='" + lang2 + @"'\]:before{\s*content:'viet';\s*}"); + VerifyRegex(cssResult, @"\s*\.reversename>\s*span:last-child:after{\s*content:'aft';\s*}"); } [Test] @@ -2341,7 +2352,7 @@ public void GenerateCssForConfiguration_BetweenWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".*\.lexentry>\s*\.senses>\s*\.sense\s*\+\s*\.sense:before{.*content:','.*}", "Between selector not generated."); + VerifyRegex(cssResult, @".*\.senses>\s*\.sense\s*\+\s*\.sense:before{.*content:','.*}", "Between selector not generated."); } [Test] @@ -2364,8 +2375,8 @@ public void GenerateCssForConfiguration_BetweenSingleWsWithAbbrSpanWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before{.*content:','.*}", RegexOptions.Singleline).Success, - "Between span selector not generated."); + VerifyRegex(cssResult, @".*\.lexemeform>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before{.*content:','.*}", + "Between span selector not generated."); } [Test] @@ -2397,7 +2408,7 @@ public void GenerateCssForConfiguration_BetweenMultiWsWithoutAbbrSpanWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\+span\[lang\|=\'fr\'\]:before{.*content:','.*}", RegexOptions.Singleline).Success, + VerifyRegex(cssResult, @".*\.lexemeform>\s*span\+span\[lang\|\='fr'\]:before{.*content:','.*}", "Between Multi-WritingSystem without Abbr selector not generated."); } @@ -2437,13 +2448,13 @@ public void GenerateCssForConfiguration_BetweenMultiWsWithAbbrSpanWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before\s*{.*content:','.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.lexemeform>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before\s*{.*content:','.*}", RegexOptions.Singleline).Success, "Between Multi-WritingSystem with Abbr selector not generated for LexemeForm."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.headword>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before\s*{.*content:','.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.headword>\s*span\.writingsystemprefix\s*\~\s*span\.writingsystemprefix:before\s*{.*content:','.*}", RegexOptions.Singleline).Success, "Between Multi-WritingSystem with Abbr selector not generated for HeadWord."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.lexemeform>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, "writingsystemprefix:after not generated for headword."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.headword>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.headword>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, "writingsystemprefix:after not generated for lexemeform."); } @@ -2484,13 +2495,13 @@ public void GenerateCssForConfiguration_BetweenMultiWsWithAbbrSpan_NotEnabled_De wsOpts.Options[1].IsEnabled = false; // uncheck French ws // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsFalse(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\.writingsystemprefix\s*\+\s*span:not\(:last-child\):after\s*{.*content:','.*}", RegexOptions.Singleline).Success, + Assert.IsFalse(Regex.Match(cssResult, @".*\.lexemeform>\s*span\.writingsystemprefix\s*\+\s*span:not\(:last-child\):after\s*{.*content:','.*}", RegexOptions.Singleline).Success, "Between Multi-WritingSystem selector should not be generated for LexemeForm (only 1 ws checked)."); - Assert.IsFalse(Regex.Match(cssResult, @".*\.lexentry>\s*\.headword>\s*span\.writingsystemprefix\s*\+\s*span:not\(:last-child\):after\s*{.*content:','.*}", RegexOptions.Singleline).Success, + Assert.IsFalse(Regex.Match(cssResult, @".*\.headword>\s*span\.writingsystemprefix\s*\+\s*span:not\(:last-child\):after\s*{.*content:','.*}", RegexOptions.Singleline).Success, "Between Multi-WritingSystem selector should not be generated for HeadWord (only 1 ws checked)."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.lexemeform>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.lexemeform>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, "writingsystemprefix:after not generated for headword."); - Assert.IsTrue(Regex.Match(cssResult, @".*\.lexentry>\s*\.headword>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, + Assert.IsTrue(Regex.Match(cssResult, @".*\.headword>\s*span\.writingsystemprefix:after\s*{.*content:' '.*}", RegexOptions.Singleline).Success, "writingsystemprefix:after not generated for lexemeform."); } @@ -2515,7 +2526,7 @@ public void GenerateCssForConfiguration_BetweenWorksWithFormatCss() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".*\.lexentry>\s*\.senses>\s*\.sense\s*\+\s*\.sense:before{.*content:',';.*font-size:10pt;.*color:#00F.*}", + VerifyRegex(cssResult, @".*\.senses>\s*\.sense\s*\+\s*\.sense:before{.*content:',';.*font-size:10pt;.*color:#00F.*}", "Between selector with format not generated."); } } @@ -2640,16 +2651,11 @@ public void GenerateCssForConfiguration_PictureCssIsGenerated() // SUT var cssWithPictureRules = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*picture.*{.*float:right.*}", RegexOptions.Singleline).Success, - "picture not floated right"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*picture.*img.*{.*max-width:1in;.*}", RegexOptions.Singleline).Success, - "css for image did not contain height contraint attribute"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*pictures.*picture.*{.*margin:\s*0pt\s*0pt\s*4pt\s*4pt.*;.*}", RegexOptions.Singleline).Success, - "css for image did not contain valid margin attribute"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*{.*clear:both.*}", RegexOptions.Singleline).Success, - "float not cleared at entry"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry*\>\s*.*pictures.*picture*\>\s*.captionContent\s*.caption*\{.*margin-left:\s*24pt", RegexOptions.Singleline).Success, - "css for caption did not contain valid margin attribute"); + VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*{.*float:right.*}", "picture not floated right"); + VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*img.*{.*max-width:1in;.*}", "css for image did not contain height contraint attribute"); + VerifyRegex(cssWithPictureRules, @"^\s*\.pictures.*picture.*{.*margin:\s*0pt\s*0pt\s*4pt\s*4pt.*;.*}", "css for image did not contain valid margin attribute"); + VerifyRegex(cssWithPictureRules, @"^\s*\.entry\s*{.*clear:both.*}", "float not cleared at entry"); + VerifyRegex(cssWithPictureRules, @"^\s*\s*.captionContent\s*.caption*\{.*margin-left:\s*24pt", "css for caption did not contain valid margin attribute"); } /// @@ -2704,23 +2710,23 @@ public void GenerateCssForConfiguration_PictureSubfieldsBeforeBetweenAfterIsAreG // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); - var senseNumberBefore = @".entry> .pictures .picture> .captionContent .sensenumbertss:before\{\s*content:'\[';"; - Assert.IsTrue(Regex.Match(cssResult, senseNumberBefore, RegexOptions.Singleline).Success, "expected Sense Number before rule is generated"); + var senseNumberBefore = @"^.captionContent .sensenumbertss:before\{\s*content:'\[';"; + VerifyRegex(cssResult, senseNumberBefore, "expected Sense Number before rule is generated"); - var senseNumberAfter = @".entry> .pictures .picture> .captionContent .sensenumbertss:after\{\s*content:'\]';"; - Assert.IsTrue(Regex.Match(cssResult, senseNumberAfter, RegexOptions.Singleline).Success, "expected Sense Number after rule is generated"); + var senseNumberAfter = @"^.captionContent .sensenumbertss:after\{\s*content:'\]';"; + VerifyRegex(cssResult, senseNumberAfter, "expected Sense Number after rule is generated"); - var senseNumberBetween = @".entry> .pictures .picture> .captionContent .sensenumbertss> .sensenumberts\+ .sensenumberts:before\{\s*content:', ';"; - Assert.IsTrue(Regex.Match(cssResult, senseNumberBetween, RegexOptions.Singleline).Success, "expected Sense Number between rule is generated"); + var senseNumberBetween = @"^.captionContent .sensenumbertss>\s*\.sensenumberts \+\s*\.sensenumberts:before\{\s*content:', ';"; + VerifyRegex(cssResult, senseNumberBetween, "expected Sense Number between rule is generated"); - var captionBefore = @".entry> .pictures .picture> .captionContent .caption:before\{\s*content:'\{';"; - Assert.IsTrue(Regex.Match(cssResult, captionBefore, RegexOptions.Singleline).Success, "expected Caption before rule is generated"); + var captionBefore = @"^.captionContent .caption:before\{\s*content:'\{';"; + VerifyRegex(cssResult, captionBefore, "expected Caption before rule is generated"); - var captionAfter = @".entry> .pictures .picture> .captionContent .caption:after\{\s*content:'\}';"; - Assert.IsTrue(Regex.Match(cssResult, captionAfter, RegexOptions.Singleline).Success, "expected Caption after rule is generated"); + var captionAfter = @"^.captionContent .caption:after\{\s*content:'\}';"; + VerifyRegex(cssResult, captionAfter, "expected Caption after rule is generated"); - var captionBetween = @".entry> .pictures .picture> .captionContent .caption> .captio\+ .captio:before\{\s*content:' ';"; - Assert.IsTrue(Regex.Match(cssResult, captionBetween, RegexOptions.Singleline).Success, "expected Caption between rule is generated"); + var captionBetween = @"^.captionContent .caption>\s*\.captio \+\s*\.captio:before\{\s*content:' ';"; + VerifyRegex(cssResult, captionBetween, "expected Caption between rule is generated"); } /// @@ -2762,14 +2768,14 @@ public void GenerateCssForConfiguration_PictureBeforeBetweenAfterIsAreGenerated( // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); - var pictureBefore = @".entry> .pictures> div:first-child:before\{\s*content:'\[';"; - Assert.IsTrue(Regex.Match(cssResult, pictureBefore, RegexOptions.Singleline).Success, "expected Picture before rule is generated"); + var pictureBefore = @".pictures> div:first-child:before\{\s*content:'\[';"; + VerifyRegex(cssResult, pictureBefore, "expected Picture before rule is generated"); - var pictureAfter = @".entry> .pictures> div:last-child:after\{\s*content:'\]';"; - Assert.IsTrue(Regex.Match(cssResult, pictureAfter, RegexOptions.Singleline).Success, "expected Picture after rule is generated"); + var pictureAfter = @".pictures> div:last-child:after\{\s*content:'\]';"; + VerifyRegex(cssResult, pictureAfter, "expected Picture after rule is generated"); - var pictureBetween = @".entry> .pictures> div\+ div:before\{\s*content:', ';"; - Assert.IsTrue(Regex.Match(cssResult, pictureBetween, RegexOptions.Singleline).Success, "expected Picture between rule is generated"); + var pictureBetween = @".*\.pictures>\s*div\s*\+\s*div:before\{\s*content:', ';"; + VerifyRegex(cssResult, pictureBetween, "expected Picture between rule is generated"); } @@ -2820,12 +2826,9 @@ public void GenerateCssForConfiguration_PictureWritesRulesForHeadwordAndGlossInC // SUT var cssWithPictureRules = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*pictures.*picture> .captionContent .caption", RegexOptions.Singleline).Success, - "css for image did not contain expected rule"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*pictures.*picture> .captionContent .headword", RegexOptions.Singleline).Success, - "css for image did not contain expected headword rule"); - Assert.IsTrue(Regex.Match(cssWithPictureRules, @".*\.entry.*pictures.*picture> .captionContent .owner_gloss", RegexOptions.Singleline).Success, - "css for image did not contain expected gloss rule"); + VerifyRegex(cssWithPictureRules, @"^\.captionContent .caption", "css for image did not contain expected rule"); + VerifyRegex(cssWithPictureRules, @"^\.captionContent .headword", "css for image did not contain expected headword rule"); + VerifyRegex(cssWithPictureRules, @"^\.captionContent .owner_gloss", "css for image did not contain expected gloss rule"); } [Test] @@ -2860,9 +2863,9 @@ public void GenerateCssForConfiguration_GlossWithMultipleWs() sense.Gloss.set_String(wsEn, TsStringUtils.MakeString("gloss", wsEn)); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(".lexentry> .senses .sense> .gloss> span.writingsystemprefix" + + Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(".gloss> span.writingsystemprefix" + "{font-family:\'foofoo\',serif;font-size:10pt;font-weight:bold;font-style:italic;color:#00F;")); - Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(".lexentry> .senses .sense> .gloss> span.writingsystemprefix:after{content:' ';}")); + Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(".gloss> span.writingsystemprefix:after{content:' ';}")); } [Test] @@ -2950,13 +2953,13 @@ public void GenerateCssForConfiguration_NormalStyleForWsDoesNotOverrideNodeStyle PopulateFieldsForTesting(entryNode); // Default (no ws) style info const string englishGeneralStyle = "span[lang|=\"en\"]{font-family:'english',serif;color:#F00;}"; - const string definitionSelector = ".lexentry> .senses .sense> .definition"; - const string englishSpecificStyle = " span[lang|=\"en\"]{color:#FF0;}"; + const string definitionSelector = ".definition span[lang|=\"en\"]{color:#FF0;}"; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); + // Using substring instead of regex to avoid spending all the time figuring out which regex characters to escape in this css Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(englishGeneralStyle)); - Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(definitionSelector + englishSpecificStyle)); - } + Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(definitionSelector)); + } [Test] public void GenerateCssForConfiguration_GenerateMainEntryParagraphStyle() @@ -3039,11 +3042,11 @@ public void GenerateCssForConfiguration_GenerateDictionaryMinorParagraphStyle() model.Parts.ForEach(PopulateFieldsForTesting); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - Assert.IsTrue(Regex.Match(cssResult, @"div.minorentry{\s*margin-left:24pt;\s*padding-right:48pt;\s*}", RegexOptions.Singleline).Success, + VerifyRegex(cssResult, @"div.minorentry{\s*margin-left:24pt;\s*padding-right:48pt;\s*}", "Dictionary-Minor Paragraph Style not generated."); - Assert.IsTrue(Regex.Match(cssResult, @"div.specialminorentry{\s*padding-right:32pt;\s*}", RegexOptions.Singleline).Success, + VerifyRegex(cssResult, @"div.specialminorentry{\s*padding-right:32pt;\s*}", "Dictionary-Minor Paragraph Style for node with style attribute not generated."); - Assert.IsTrue(Regex.Match(cssResult, @"div.optionsminorentry{\s*padding-right:16pt;\s*}", RegexOptions.Singleline).Success, + VerifyRegex(cssResult, @"div.optionsminorentry{\s*padding-right:16pt;\s*}", "Dictionary-Minor Paragraph Style for node with paragraph options not generated."); } @@ -3110,8 +3113,8 @@ public void GenerateCssForBulletStyleForSenses() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regExPected = @".lexentry>\s.senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Bulleted style not generated."); + const string regExPected = @".senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + VerifyRegex(cssResult, regExPected, "Bulleted style not generated."); } [Test] @@ -3140,8 +3143,8 @@ public void GenerateCssForBulletStyleForSensesWithDisplayFirstSenseInline() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regExPected = @".lexentry>\s.senses\s>\s.sensecontent\s.\s.sensecontent:not\(:first-child\):before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Bulleted style not generated."); + const string regExPected = @"^\.senses\s+>\s*\.sensecontent:not\(:first-child\):before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + VerifyRegex(cssResult, regExPected, "Bulleted style not generated."); } [Test] @@ -3169,9 +3172,11 @@ public void GenerateCssForNumberingStyleForSenses() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected = @".lexentry>\s.sensesos{.*counter-reset:\ssensesos;.*}.*.lexentry>\s.sensesos\s>\s.sensecontent:before{.*counter-increment:\ssensesos;.*content:\scounter.sensesos,\sdecimal.\s'\s';.*font-size:14pt;.*color:Green;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected, RegexOptions.Singleline).Success, "Numbering style not generated for Senses."); - } + const string sensesCounterReset = @"\s*.sensesos\s*{\s*counter-reset:\ssensesos;.*}"; + const string sensesCounterInc = @".*\s.sensesos\s>\s.sensecontent:before{.*counter-increment:\ssensesos;.*content:\scounter.sensesos,\sdecimal.\s'\s';.*font-size:14pt;.*color:Green;.*}"; + VerifyRegex(cssResult, sensesCounterReset, "Numbering style counter reset not generated for Senses."); + VerifyRegex(cssResult, sensesCounterInc, "Numbering style counter-increment not generated for Senses."); + } [Test] public void GenerateCssForNumberingStyleForSubentries() @@ -3199,10 +3204,11 @@ public void GenerateCssForNumberingStyleForSubentries() PopulateFieldsForTesting(entryConfig); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected = @".lexentry>\s.subentries{.*counter-reset:[\s]subentries;.*}.*.lexentry>\s.subentries\s.subentry:before{.*counter-increment:[\s]subentries;.*content:\scounter.subentries,\supper-roman.\s'\s';.*font-size:14pt;.*color:Green;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected, RegexOptions.Singleline).Success, - "Numbering style not generated for Subentry."); - } + const string regexExpected = @"\s*\.subentries{.*counter-reset:[\s]subentries;.*}"; + const string counterIncrement = @"\s*\.subentries\s.subentry:before{.*counter-increment:[\s]subentries;.*content:\scounter.subentries,\supper-roman.\s'\s';.*font-size:14pt;.*color:Green;.*}"; + VerifyRegex(cssResult, regexExpected, "counter-reset style not generated for Subentry."); + VerifyRegex(cssResult, regexExpected, "counter-increment style not generated for Subentry."); + } [Test] public void GenerateCssForNumberingStyleForExamples() @@ -3239,8 +3245,8 @@ public void GenerateCssForNumberingStyleForExamples() PopulateFieldsForTesting(entry); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected = @".lexentry>\s.senses\s>\s.sensecontent\s>\s.sense>\s.examplesos{.*counter-reset:[\s]examplesos;.*}.*.lexentry>\s.senses\s>\s.sensecontent\s>\s.sense>\s.examplesos\s.exampleso:before{.*counter-increment:[\s]examplesos;.*content:[\s]counter.examplesos,[\s]upper-alpha.\s'\s';.*font-size:14pt;.*color:Green;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected, RegexOptions.Singleline).Success, "Numbering style not generated for Examples."); + const string regexExpected = @"\s.examplesos{.*counter-reset:[\s]examplesos;.*}.*\s*\s\.examplesos\s\.exampleso:before{.*counter-increment:[\s]examplesos;.*content:[\s]counter.examplesos,[\s]upper-alpha.\s'\s';.*font-size:14pt;.*color:Green;.*}"; + VerifyRegex(cssResult, regexExpected, "Numbering style not generated for Examples."); } [Test] @@ -3301,16 +3307,16 @@ public void GenerateCssForNonBulletStyleForSenses() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regExpected = @".lexentry>\s.senses\s>\s.sensecontent"; - Assert.IsTrue(Regex.Match(cssResult, regExpected, RegexOptions.Singleline).Success, "Sense List style should generate a match."); - const string regExNotExpected = regExpected + @"(\s\+\s.sensecontent)?:not\(:first-child\):before"; - Assert.IsFalse(Regex.Match(cssResult, regExNotExpected, RegexOptions.Singleline).Success, - "Sense List style should not generate a match, since it is not a bulleted style."); + const string regExpected = @"\s.senses\s>\s.sensecontent"; + VerifyRegex(cssResult, regExpected, "Sense List style should generate a match."); + const string regExNotExpected = regExpected + @"(\s*\.sensecontent)?:not\(:first-child\):before"; + Assert.IsFalse(Regex.Match(cssResult, regExNotExpected, RegexOptions.Singleline).Success, "Sense List style should not generate a match, since it is not a bulleted style."); } [Test] - public void GenerateCssForBulletStyleForSubSenses() + public void GenerateCssForBulletStyle_OneStyleWhenSubSenseMatchesSense() { + var cssGenerator = new CssGenerator(); GenerateBulletStyle("Bulleted List"); var subsenses = new ConfigurableDictionaryNode { @@ -3343,9 +3349,62 @@ public void GenerateCssForBulletStyleForSubSenses() var model = new DictionaryConfigurationModel { Parts = new List { entry } }; PopulateFieldsForTesting(entry); // SUT - var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regExPected = @".lexentry>\s.senses\s*>\s*.sensecontent\s*>\s*.sense>\s.senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; - Assert.IsTrue(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Bulleted style for SubSenses not generated."); + // Add the sense and subsense styles + cssGenerator.AddGlobalStyles(model, m_propertyTable); // Gets the bullet information prepped + cssGenerator.Init(m_propertyTable); + cssGenerator.AddStyles(senses); + cssGenerator.AddStyles(subsenses); + var cssResult = cssGenerator.GetStylesString(); + const string regExPected = @".*senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + Assert.That(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Bulleted style for SubSenses not generated."); + Assert.That(!Regex.Match(cssResult, regExPected, RegexOptions.Singleline).NextMatch().Success, "Bulleted style for SubSenses not generated."); + } + + [Test] + public void GenerateCssForBulletStyle_TwoStylesWhenSubSensesAreDifferent() + { + var cssGenerator = new CssGenerator(); + GenerateBulletStyle("Bulleted List"); + var subsenses = new ConfigurableDictionaryNode + { + FieldDescription = "SensesOS", + CSSClassNameOverride = "Senses", + DictionaryNodeOptions = new DictionaryNodeSenseOptions + { + NumberStyle = "Dictionary-SenseNum", DisplayEachSenseInAParagraph = true + }, + Style = "Bulleted List" + }; + var senses = new ConfigurableDictionaryNode + { + FieldDescription = "SensesOS", + CSSClassNameOverride = "Senses", + DictionaryNodeOptions = new DictionaryNodeSenseOptions + { + NumberStyle = "Dictionary-SenseNum", DisplayEachSenseInAParagraph = true + }, + Children = new List { subsenses } + }; + + var entry = new ConfigurableDictionaryNode + { + FieldDescription = "LexEntry", + CSSClassNameOverride = "lexentry", + Children = new List { senses } + }; + var model = new DictionaryConfigurationModel { Parts = new List { entry } }; + PopulateFieldsForTesting(entry); + // SUT + // Add the sense and subsense styles + cssGenerator.AddGlobalStyles(model, m_propertyTable); // Gets the bullet information prepped + cssGenerator.Init(m_propertyTable); + cssGenerator.AddStyles(senses); + cssGenerator.AddStyles(subsenses); + var cssResult = cssGenerator.GetStylesString(); + const string regExPectedForSub = @"\.senses-senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + VerifyRegex(cssResult, regExPectedForSub, "Bulleted style for SubSenses not generated."); + const string regExPectedForSense = @"\.senses\s>\s\.sensecontent"; // Make sure there is a .sense > .sensecontent rule as well as the bulletted sub-sense + VerifyRegex(cssResult, regExPectedForSense, "Non-bulleted style for Senses not generated."); } [Test] @@ -3373,21 +3432,21 @@ public void GenerateCssForBulletStyleForRootSubentries() PopulateFieldsForTesting(entryConfig); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - var regexExpected1 = @"\.lexentry>\s\.subentries\s\.subentry{[^}]*\sfont-size:12pt;[^}]*\scolor:#F00;[^}]*\sdisplay:block;[^}]*}"; + var regexExpected1 = @"\.subentries\s\.subentry{[^}]*\sfont-size:12pt;[^}]*\scolor:#F00;[^}]*\sdisplay:block;[^}]*}"; Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, "expected subentry rule not generated"); - var regexExpected2 = @"\.lexentry>\s\.subentries\s\.subentry:before{[^}]*\scontent:'\\25A0';[^}]*font-size:14pt;[^}]*color:Green;[^}]*}"; + var regexExpected2 = @"\.subentries\s\.subentry:before{[^}]*\scontent:'\\25A0';[^}]*font-size:14pt;[^}]*color:Green;[^}]*}"; Assert.IsTrue(Regex.Match(cssResult, regexExpected2, RegexOptions.Singleline).Success, "expected subentry:before rule not generated"); // Check that the bullet info values occur only in the :before section, and that the primary values // do not occur in the :before section. - var regexUnwanted1 = @"\.lexentry>\s\.subentries\s\.subentry{[^}]*\scontent:'\\25A0';[^}]*}"; + var regexUnwanted1 = @"\.subentries\s\.subentry{[^}]*\scontent:'\\25A0';[^}]*}"; Assert.IsFalse(Regex.Match(cssResult, regexUnwanted1, RegexOptions.Singleline).Success, "subentry rule has unwanted content value"); - var regexUnwanted2 = @"\.lexentry>\s\.subentries\s\.subentry{[^}]*\sfont-size:14pt;[^}]*}"; + var regexUnwanted2 = @".subentries\s\.subentry{[^}]*\sfont-size:14pt;[^}]*}"; Assert.IsFalse(Regex.Match(cssResult, regexUnwanted2, RegexOptions.Singleline).Success, "subentry rule has unwanted font-size value"); - var regexUnwanted3 = @"\.lexentry>\s\.subentries\s\.subentry{[^}]*\scolor:Green;[^}]*}"; + var regexUnwanted3 = @".subentries\s\.subentry{[^}]*\scolor:Green;[^}]*}"; Assert.IsFalse(Regex.Match(cssResult, regexUnwanted3, RegexOptions.Singleline).Success, "subentry rule has unwanted color value"); var regexUnwanted4 = @"\.lexentry>\s\.subentries\s\.subentry:before{[^}]*\sfont-size:12pt;[^}]*}"; @@ -3430,8 +3489,8 @@ public void GenerateCssForCustomFieldUnderISenseOrEntry() { // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected1 = @"\.lexentry>\s.mlrs\s\.mlr>\s\.configtargets\s\.configtarget>\s\.costume{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, "expected costume rule not generated"); + const string regexExpected1 = @"\s*\.costume{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "expected costume rule not generated"); } } @@ -3456,8 +3515,8 @@ public void GenerateCssForCustomFieldStartsWithNumber() { // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected1 = @"\.lexentry>\s.cf12costume{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, "Class name started with number"); + const string regexExpected1 = @"\s*.cf12costume{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "Class name started with number"); } } @@ -3494,15 +3553,12 @@ public void GenerateCssForCustomFieldWithSpaces() { // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexExpected1 = @"\.lexentry>\s\.custom-location \.custom-locatio{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, - "expected custom-location rule not generated"); - const string regexExpected2 = @"\.lexentry>\s\.custom-location \.custom-locatio>\s\.name{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected2, RegexOptions.Singleline).Success, - "expected custom-location>name rule not generated"); - const string regexExpected3 = @"\.lexentry>\s\.custom-location \.custom-locatio>\s\.abbreviation{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected3, RegexOptions.Singleline).Success, - "expected custom-location>abbreviation rule not generated"); + const string regexExpected1 = @"\s*\.custom-location \.custom-locatio{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "expected custom-location rule not generated"); + const string regexExpected2 = @"\s*\.name{[^}]*}"; + VerifyRegex(cssResult, regexExpected2, "expected custom-location name rule not generated"); + const string regexExpected3 = @"\s*\.abbreviation{[^}]*}"; + VerifyRegex(cssResult, regexExpected3, "expected custom-location>abbreviation rule not generated"); } } @@ -3526,9 +3582,8 @@ public void GenerateCssForDuplicateConfigNodeWithSpaces() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - var regexExpected1 = @"\.lexentry>\s\.note_test-one{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, - "expected duplicated config node rename rule not generated"); + var regexExpected1 = @"\s\.note_test-one{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "expected duplicated config node rename rule not generated"); } [Test] @@ -3551,9 +3606,8 @@ public void GenerateCssForDuplicateConfigNodeWithPunc() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - var regexExpected1 = @"\.lexentry>\s\.note_-test{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, - "expected duplicated config node rename rule not generated"); + var regexExpected1 = @"^\s*\.note_-test{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "expected duplicated config node rename rule not generated"); } [Test] @@ -3576,9 +3630,8 @@ public void GenerateCssForDuplicateConfigNodeWithMultiPunc() PopulateFieldsForTesting(model); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - var regexExpected1 = @"\.lexentry>\s\.note_-test-{[^}]*}"; - Assert.IsTrue(Regex.Match(cssResult, regexExpected1, RegexOptions.Singleline).Success, - "expected duplicated config node rename rule not generated"); + var regexExpected1 = @"\s*\.note_-test-{[^}]*}"; + VerifyRegex(cssResult, regexExpected1, "expected duplicated config node rename rule not generated"); } [Test] @@ -3627,21 +3680,21 @@ public void GenerateCssForCollectionBeforeAndAfter() //var regexItem1 = @".entry> .pronunciations .pronunciation> .form> span\+ span:before\{\s*content:' ';\s*\}"; //Assert.IsTrue(Regex.Match(cssResult, regexItem1, RegexOptions.Singleline).Success, "expected collection item between rule is generated"); - var regexItem2 = @".entry> .pronunciations .pronunciation> .form> span:first-child:before\{\s*content:'\[';\s*\}"; + var regexItem2 = @".form> span:first-child:before\{\s*content:'\[';\s*\}"; Assert.IsTrue(Regex.Match(cssResult, regexItem2, RegexOptions.Singleline).Success, "expected collection item before rule is generated"); - var regexItem3 = @".entry> .pronunciations .pronunciation> .form> span:last-child:after\{\s*content:'\]';\s*\}"; + var regexItem3 = @".form> span:last-child:after\{\s*content:'\]';\s*\}"; Assert.IsTrue(Regex.Match(cssResult, regexItem3, RegexOptions.Singleline).Success, "expected collection item after rule is generated"); - var regexCollection1 = @".entry> .pronunciations> .pronunciation\+ .pronunciation:before\{\s*content:', ';\s*\}"; - Assert.IsTrue(Regex.Match(cssResult, regexCollection1, RegexOptions.Singleline).Success, "expected collection between rule is generated"); + var regexCollection1 = @"^\.pronunciations>\s+.pronunciation\s+\+\s+\.pronunciation:before\{\s*content:', ';\s*\}"; + VerifyRegex(cssResult, regexCollection1, "expected collection between rule is generated"); // The following two checks test the fix for LT-17048. The preceding four checks should be the same before and after the fix. - var regexCollection2 = @".entry> .pronunciations:before\{\s*content:'\{Pron: ';\s*\}"; - Assert.IsTrue(Regex.Match(cssResult, regexCollection2, RegexOptions.Singleline).Success, "expected collection before rule is generated"); + var regexCollection2 = @".pronunciations:before\{\s*content:'\{Pron: ';\s*\}"; + VerifyRegex(cssResult, regexCollection2, "expected collection before rule is generated"); - var regexCollection3 = @".entry> .pronunciations:after\{\s*content:'\} ';\s*\}"; - Assert.IsTrue(Regex.Match(cssResult, regexCollection3, RegexOptions.Singleline).Success, "expected collection after rule is generated"); + var regexCollection3 = @".pronunciations:after\{\s*content:'\} ';\s*\}"; + VerifyRegex(cssResult, regexCollection3, "expected collection after rule is generated"); } [Test] @@ -3676,12 +3729,12 @@ public void GenerateCssForConfiguration_NoBeforeAfterForSenseParagraphs() ((DictionaryNodeSenseOptions)sensesConfig.DictionaryNodeOptions).DisplayEachSenseInAParagraph = false; var cssInline = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - const string regexBefore = @"^\.lexentry> \.senses:before\{"; - const string regexAfter = @"^\.lexentry> \.senses:after\{"; + const string regexBefore = @"^\.senses:before\{"; + const string regexAfter = @"^\.senses:after\{"; Assert.AreNotEqual(cssPara, cssInline, "The css should change depending on senses showing in a paragraph"); - Assert.IsTrue(Regex.IsMatch(cssInline, regexBefore, RegexOptions.Multiline), "The css for inline senses should have a senses:before rule"); - Assert.IsTrue(Regex.IsMatch(cssInline, regexAfter, RegexOptions.Multiline), "The css for inline senses should have a senses:after rule"); + VerifyRegex(cssInline, regexBefore, "The css for inline senses should have a senses:before rule"); + VerifyRegex(cssInline, regexAfter, "The css for inline senses should have a senses:after rule"); Assert.IsFalse(Regex.IsMatch(cssPara, regexBefore, RegexOptions.Multiline), "The css for paragraphed senses should not have a senses:before rule"); Assert.IsFalse(Regex.IsMatch(cssPara, regexAfter, RegexOptions.Multiline), "The css for paragraphed senses should not have a senses:after rule"); } @@ -3726,11 +3779,11 @@ public void GenerateCssForConfiguration_SpecificLanguageColorIsNotOverridenByPar //SUT var result = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // default (analysis ws) rule - const string regexPrimary = @"^\.lexentry> \.extendednotecontents\{\s*color:#008000;"; + const string regexPrimary = @"^\.extendednotecontents\{\s*color:#008000;"; // specific (embedded vernacular ws) rule affecting any span inside .extendednotecontents (at any level) - const string regexSpecific = @"^\.lexentry> \.extendednotecontents span\[lang|='fr']\{\s*color:#00F"; - Assert.IsTrue(Regex.IsMatch(result, regexPrimary, RegexOptions.Multiline), "The css for the default color should be there."); - Assert.IsTrue(Regex.IsMatch(result, regexSpecific, RegexOptions.Multiline), "The css for the specific language color should be there."); + const string regexSpecific = @"^\.extendednotecontents span\[lang|='fr']\{\s*color:#00F"; + VerifyRegex(result, regexPrimary, "The css for the default color should be there."); + VerifyRegex(result, regexSpecific, "The css for the specific language color should be there."); } [Test] @@ -4184,7 +4237,7 @@ private static void VerifyParagraphBorderInCss(Color color, int leading, int tra Assert.That(css, Contains.Substring("border-right-width:" + trailing / 1000 + "pt")); } - private static void VerifyRegex(string input, string pattern, string message = null, RegexOptions options = RegexOptions.Singleline) + private static void VerifyRegex(string input, string pattern, string message = null, RegexOptions options = RegexOptions.Singleline | RegexOptions.Multiline) { Assert.IsTrue(Regex.Match(input, pattern, options).Success, string.Format("{3}Expected{0}{1}{0}but got{0}{2}", Environment.NewLine, pattern, input, From 6b966d466d38af1a4441b6d6df19ddca3df87609 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Fri, 3 Nov 2023 15:55:46 -0400 Subject: [PATCH 012/415] Update FW build with new palaso version Change-Id: Id109cbe1608b17f21376fb0442fc29f7d1bedac5 --- Build/mkall.targets | 2 +- Build/nuget-common/packages.config | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Build/mkall.targets b/Build/mkall.targets index 08e2458ccf..6106ffae17 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -283,7 +283,7 @@ 5.2.0-beta0003 - 13.0.0-beta0074 + 13.0.0-beta0076 9.4.0.1-beta 10.2.0-beta0075 70.1.123 diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index f5e0f05591..96cd1c5591 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -38,17 +38,17 @@ - + - + - + @@ -59,21 +59,21 @@ - + - - + + - - - - - - - - + + + + + + + + From d9301e97726c6880562d658f7eecf9f8874aae52 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 3 Nov 2023 14:24:13 -0700 Subject: [PATCH 013/415] Correct the css before, between, and after behavior for Senses Change-Id: I1a3dad42aea755d4f6389816721e9cbd62622086 --- Src/xWorks/CssGenerator.cs | 27 +++++++++--- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 47 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index df13a07648..e6fa9cad36 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -563,7 +563,7 @@ private static List GenerateCssFromListAndParaOptions(ConfigurableDic Value = baseSelection }; styleRules.Add(blockRule); - styleRules.AddRange(GenerateCssForCounterReset(SelectBareClassName(configNode, baseSelection, cache), declaration)); + styleRules.AddRange(GenerateCssForCounterReset(SelectCollectionClassName(configNode, baseSelection, cache), declaration)); var bulletRule = AdjustRuleIfParagraphNumberScheme(blockRule, configNode, propertyTable); // REVIEW (Hasso) 2016.10: could these two lines be moved outside the loop? // REVIEW (Hasso) 2016.10: both of these following lines add all rules but BeforeAfter (so if the condition in the first line @@ -760,7 +760,7 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN string pictCaptionContent = ".captionContent "; if (configNode.Parent == null) { - collectionSelector = SelectBareClassName(configNode, baseSelection); + collectionSelector = SelectCollectionClassName(configNode, baseSelection); baseSelection = SelectClassName(configNode, baseSelection); GenerateFlowResetForBaseNode(baseSelection, rules); } @@ -769,12 +769,12 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN // Headword, Gloss, and Caption are contained in a captionContent area. if (configNode.Parent.DictionaryNodeOptions is DictionaryNodePictureOptions) { - collectionSelector = pictCaptionContent + SelectBareClassName(configNode, baseSelection, cache); + collectionSelector = pictCaptionContent + SelectCollectionClassName(configNode, baseSelection, cache); baseSelection = pictCaptionContent + SelectClassName(configNode, baseSelection, cache); } else { - collectionSelector = SelectBareClassName(configNode, baseSelection, cache); + collectionSelector = SelectCollectionClassName(configNode, baseSelection, cache); baseSelection = SelectClassName(configNode, baseSelection, cache); } collectionItemSelector = $".{GetClassAttributeForCollectionItem(configNode)}"; @@ -807,7 +807,7 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN } else { - betweenSelector = $""; + betweenSelector = $"{collectionSelector}> .sensecontent + .sensecontent:before"; } break; } @@ -829,7 +829,7 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN for (var i = enabledWsOptions.Length - 1; i > 0; i--) { betweenSelector = (i == enabledWsOptions.Length - 1 ? string.Empty : betweenSelector + ",") + - string.Format("{0} span+span[lang|='{1}']:before", selectorOfWsOptOwner, enabledWsOptions[i].Id); + $"{selectorOfWsOptOwner} span+span[lang|='{enabledWsOptions[i].Id}']:before"; } } break; @@ -1010,11 +1010,24 @@ internal static string GetClassAttributeForCollectionItem(ConfigurableDictionary /// output of this method for :before and :after rules in the css is sufficient to fix the bug reported in /// LT-17048. A better name might be nice, but this one is fairly descriptive. /// - private static string SelectBareClassName(ConfigurableDictionaryNode configNode, string adjustedClassName, LcmCache cache = null) + private static string SelectCollectionClassName(ConfigurableDictionaryNode configNode, string adjustedClassName, LcmCache cache = null) { var type = ConfiguredLcmGenerator.GetPropertyTypeForConfigurationNode(configNode, cache); if (type == ConfiguredLcmGenerator.PropertyType.CollectionType) + { + // collection selectors typically follow the form of '.collection .collectionItem' or '.collection' and we want to return '.collection' + var collectionSelectorParts = adjustedClassName.Split(' '); + if (collectionSelectorParts.Length == 1) + { + return adjustedClassName; + } + if (collectionSelectorParts.Length == 2) + { + return collectionSelectorParts[0]; + } + Debug.Fail("Unexpected adjustedClassName input for a collection type"); return "." + GetClassAttributeForConfig(configNode); + } return SelectClassName(configNode, adjustedClassName, type); } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index fcc755ee97..82e381bfc0 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -777,6 +777,53 @@ public void GenerateCssForStyleName_ParagraphMarginIsAbsolute_GrandParentAndPare Assert.That(childDeclaration.ToString(), Contains.Substring("margin-left:8pt"), "Child margin incorrectly generated"); } + [Test] + public void GenerateCssForStyleName_SensesAndSubSenses_BeforeBetweenAfterWork() + { + var gloss = new ConfigurableDictionaryNode { FieldDescription = "Gloss" }; + var subSenses = new ConfigurableDictionaryNode + { + FieldDescription = "SensesOS", + CSSClassNameOverride = "Senses", + DictionaryNodeOptions = new DictionaryNodeSenseOptions(), + Children = new List { gloss }, + Before = "^", + Between = ",", + After = ":" + }; + var senses = new ConfigurableDictionaryNode + { + FieldDescription = "SensesOS", + CSSClassNameOverride = "Senses", + DictionaryNodeOptions = new DictionaryNodeSenseOptions(), + Children = new List { subSenses }, + Before = "#", + Between = ";", + After = "." + }; + var entry = new ConfigurableDictionaryNode + { + FieldDescription = "LexEntry", + Children = new List { senses } + }; + PopulateFieldsForTesting(entry); + // In order to generate the correct indentation at each level we should see 5pt margin for each style + //SUT + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); + cssGenerator.AddStyles(entry); + var senseClassName = cssGenerator.AddStyles(senses); + var subsenseClassName = cssGenerator.AddStyles(subSenses); + Assert.That(senseClassName, Is.Not.EqualTo(subsenseClassName)); + var styleResults = cssGenerator.GetStylesString(); + Assert.That(styleResults, Contains.Substring($"{senseClassName}:before")); + Assert.That(styleResults, Contains.Substring($"{subsenseClassName}:before")); + Assert.That(styleResults, Contains.Substring($"{senseClassName}:after")); + Assert.That(styleResults, Contains.Substring($"{subsenseClassName}:after")); + Assert.That(styleResults, Contains.Substring($"{senseClassName}> .sensecontent + .sensecontent:before")); + Assert.That(styleResults, Contains.Substring($"{subsenseClassName}> .sensecontent + .sensecontent:before")); + } + [Test] public void GenerateCssForStyleName_HangingIndentWithExistingMargin_NoParentWorks() { From 58bc693763b0757fec6ebb328e8609a70816ccb1 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 7 Nov 2023 14:36:08 -0800 Subject: [PATCH 014/415] Fix bug in unique css style name generation Change-Id: I8eefe0c020cc6b67c275f456af8f096b29f08144 --- Src/xWorks/CssGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index e6fa9cad36..07aa6fc479 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -95,7 +95,7 @@ public string AddStyles(ConfigurableDictionaryNode node) return className; } // Otherwise get a unique but useful class name and re-generate the style with the new name - className = $".{GetBestUniqueNameForNode(_styleDictionary, node)}"; + className = GetBestUniqueNameForNode(_styleDictionary, node); _styleDictionary[className] = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); return className; } @@ -117,7 +117,7 @@ public static string GetBestUniqueNameForNode(Dictionary { Guard.AgainstNull(node.Parent, "There should not be duplicate class names at the top of tree."); // first try pre-pending the parent node classname - var className = $"{GetClassAttributeForConfig(node.Parent)}-{GetClassAttributeForConfig(node)}"; + var className = $".{GetClassAttributeForConfig(node.Parent)}-{GetClassAttributeForConfig(node)}"; int counter = 0; while (styles.ContainsKey(className)) { From cc26f72010b768a8c7ce96f3a2987ff2245c7267 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 7 Nov 2023 14:41:13 -0800 Subject: [PATCH 015/415] Re-add the letter heading and default styles to the stylesheet * These were inadvertently left off in the recent refactor Change-Id: I6d746d051628094e4d28b466d28a21853d468ce8 --- Src/xWorks/CssGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 07aa6fc479..f4b613cdac 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -67,8 +67,8 @@ public void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyProperty var propStyleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); LoadBulletUnicodes(); LoadNumberingStyles(); - GenerateLetterHeaderCss(propertyTable, propStyleSheet); - GenerateCssForDefaultStyles(propertyTable, propStyleSheet, model); + _styleSheet.Rules.AddRange(GenerateLetterHeaderCss(propertyTable, propStyleSheet)); + _styleSheet.Rules.AddRange(GenerateCssForDefaultStyles(propertyTable, propStyleSheet, model)); MakeLinksLookLikePlainText(_styleSheet); GenerateBidirectionalCssShim(_styleSheet); GenerateCssForAudioWs(_styleSheet, cache); From 5b79d6d393a8c754e1c5e1a7e4a2639efe3f5f79 Mon Sep 17 00:00:00 2001 From: Ken Zook Date: Tue, 7 Nov 2023 11:15:43 -0600 Subject: [PATCH 016/415] LT-21668 Fix broken SFM import with \rf If ref is needed in extended note examples, it should use \enrf and id enrf but that requires C# code change, and data entry does not support this anyway, so I'm removing it for now to avoid the current crash. Change-Id: I77bdef3b4684e548d656d00aa0729dd495111c3c --- .../Language Explorer/Import/ImportFields.xml | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/DistFiles/Language Explorer/Import/ImportFields.xml b/DistFiles/Language Explorer/Import/ImportFields.xml index 58f0458bdf..6cbedd4727 100644 --- a/DistFiles/Language Explorer/Import/ImportFields.xml +++ b/DistFiles/Language Explorer/Import/ImportFields.xml @@ -1035,28 +1035,6 @@ - - - When you have a marker that indicates the reference for this extended note example - sentence. - - Set the Language Descriptor to the language of this - field. - - Yes - - Yes, appends field contents into a single field. - - No - - No - - - \rf Bear and Fly 7.02 - - - From 65516a111f9ef5320696e157f7bcf8bbac875bf0 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Thu, 9 Nov 2023 13:27:44 -0800 Subject: [PATCH 017/415] Fix missing spaces in minor entry before/after Change-Id: I134d109ea0a8e0ce129e6f08bf44759b45a17120 --- Src/xWorks/ConfiguredLcmGenerator.cs | 3 ++- Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index cc83eb4fdc..6b37009d9b 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -306,8 +306,9 @@ internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDict using (var xw = settings.ContentGenerator.CreateWriter(bldr)) { var clerk = settings.PropertyTable.GetValue("ActiveClerk", null); + var entryClassName = settings.StylesGenerator.AddStyles(configuration).Trim('.'); settings.ContentGenerator.StartEntry(xw, - GetClassNameAttributeForConfig(configuration), entry.Guid, index, clerk); + entryClassName, entry.Guid, index, clerk); settings.ContentGenerator.AddEntryData(xw, pieces); settings.ContentGenerator.EndEntry(xw); xw.Flush(); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index ff6c3799e3..2dfbe96618 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -7474,7 +7474,10 @@ public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() //SUT var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); - + var css = ((CssGenerator)settings.StylesGenerator).GetStylesString(); + // verify that the flow reset css is generated + Assert.That(css, Contains.Substring("white-space:pre-wrap")); + Assert.That(css, Contains.Substring("clear:both")); var complexOptions = (DictionaryNodeListOptions)mainEntryNode.DictionaryNodeOptions; complexOptions.Options[0].IsEnabled = false; result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 1); From b4cbfc512f393c554aa97795a331e530a11a59e0 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 15 Nov 2023 08:36:55 -0500 Subject: [PATCH 018/415] LT-21648: Use lcm 11.0.0-beta0081 Change-Id: Icfba3db3245b38f6155a774876f6caa745ecbeed --- Build/mkall.targets | 2 +- Build/nuget-common/packages.config | 18 +++++++++--------- Src/AppForTests.config | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Build/mkall.targets b/Build/mkall.targets index 6106ffae17..8b4024de05 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -285,7 +285,7 @@ 5.2.0-beta0003 13.0.0-beta0076 9.4.0.1-beta - 10.2.0-beta0075 + 11.0.0-beta0081 70.1.123 2.5.13 diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index 96cd1c5591..3eda2bb8f0 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -50,15 +50,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/Src/AppForTests.config b/Src/AppForTests.config index 4e524462f8..6641ea1f45 100644 --- a/Src/AppForTests.config +++ b/Src/AppForTests.config @@ -4,7 +4,7 @@ - From 5b6167f8f51c5c39de98fed401287bfdb4c50e4f Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 10 Nov 2023 14:24:36 -0800 Subject: [PATCH 019/415] First draft of the code for adding a Word export option * Add export menu item and stubs for calling our word export code Change-Id: Ie488274342882cbad46be8ced5b23df86492f872 --- .../Export Templates/MicrosoftWord.xml | 4 + Src/xWorks/DictionaryExportService.cs | 30 ++++++- Src/xWorks/ExportDialog.cs | 83 ++++++++++++------- 3 files changed, 88 insertions(+), 29 deletions(-) create mode 100644 DistFiles/Language Explorer/Export Templates/MicrosoftWord.xml diff --git a/DistFiles/Language Explorer/Export Templates/MicrosoftWord.xml b/DistFiles/Language Explorer/Export Templates/MicrosoftWord.xml new file mode 100644 index 0000000000..4e07146b5b --- /dev/null +++ b/DistFiles/Language Explorer/Export Templates/MicrosoftWord.xml @@ -0,0 +1,4 @@ + + diff --git a/Src/xWorks/DictionaryExportService.cs b/Src/xWorks/DictionaryExportService.cs index 729315647b..2f8aaf7ef6 100644 --- a/Src/xWorks/DictionaryExportService.cs +++ b/Src/xWorks/DictionaryExportService.cs @@ -77,7 +77,35 @@ internal int CountReversalIndexEntries(IReversalIndex ri) return entries.Length; } - public void ExportDictionaryContent(string xhtmlPath, DictionaryConfigurationModel configuration = null, IThreadedProgress progress = null) + public void ExportDictionaryForWord(string filePath, DictionaryConfigurationModel configuration = null, IThreadedProgress progress = null) + { + using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_propertyTable, m_mediator)) + { + configuration = configuration ?? new DictionaryConfigurationModel(DictionaryConfigurationListener.GetCurrentConfiguration(m_propertyTable, "Dictionary"), m_cache); + var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, DictionaryType); + if (progress != null) + progress.Maximum = entriesToSave.Length; + // TODO: Create and add call to our Word content generator LcmWordGenerator(entriesToSave, publication, configuration, filePath, progress); + } + } + + public void ExportReversalForWord(string filePath, string reversalWs, DictionaryConfigurationModel configuration = null, IThreadedProgress progress = null) + { + Guard.AgainstNullOrEmptyString(reversalWs, nameof(reversalWs)); + using (ClerkActivator.ActivateClerkMatchingExportType(ReversalType, m_propertyTable, m_mediator)) + using (ReversalIndexActivator.ActivateReversalIndex(reversalWs, m_propertyTable, m_cache)) + { + configuration = configuration ?? new DictionaryConfigurationModel( + DictionaryConfigurationListener.GetCurrentConfiguration(m_propertyTable, "ReversalIndex"), m_cache); + var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, ReversalType); + if (progress != null) + progress.Maximum = entriesToSave.Length; + + // TODO: Create and add call to our Word content generator LcmWordGenerator(entriesToSave, publication, configuration, filePath, progress); + } + } + + public void ExportDictionaryContent(string xhtmlPath, DictionaryConfigurationModel configuration = null, IThreadedProgress progress = null) { using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_propertyTable, m_mediator)) { diff --git a/Src/xWorks/ExportDialog.cs b/Src/xWorks/ExportDialog.cs index acfd69d18f..98898db732 100644 --- a/Src/xWorks/ExportDialog.cs +++ b/Src/xWorks/ExportDialog.cs @@ -83,7 +83,8 @@ protected internal enum FxtTypes kftGrammarSketch, kftClassifiedDict, kftSemanticDomains, - kftWebonary + kftWebonary, + kftWordOpenXml } // ReSharper restore InconsistentNaming protected internal struct FxtType @@ -638,6 +639,7 @@ private void btnExport_Click(object sender, EventArgs e) case FxtTypes.kftWebonary: ProcessWebonaryExport(); return; + case FxtTypes.kftWordOpenXml: default: using (var dlg = new SaveFileDialogAdapter()) { @@ -680,7 +682,7 @@ private void btnExport_Click(object sender, EventArgs e) m_propertyTable.SetPropertyPersistence("ExportDlgShowInFolder", true); } } - } + } private static void OpenExportFolder(string sDirectory, string sFileName) { @@ -838,39 +840,61 @@ protected void DoExport(string outPath, bool fLiftOutput) progressDlg.Restartable = true; progressDlg.RunTask(true, ExportGrammarSketch, outPath, ft.m_sDataType, ft.m_sXsltFiles); break; - } - TrackingHelper.TrackExport(m_areaOrig, exportType, ImportExportStep.Succeeded); + case FxtTypes.kftWordOpenXml: + progressDlg.Minimum = 0; + progressDlg.Maximum = 1000; + progressDlg.AllowCancel = true; + progressDlg.Restartable = true; + progressDlg.RunTask(true, ExportWordOpenXml, outPath, ft.m_sDataType, ft.m_sXsltFiles); + break; + } - catch (WorkerThreadException e) + TrackingHelper.TrackExport(m_areaOrig, exportType, ImportExportStep.Succeeded); + } + catch (WorkerThreadException e) + { + TrackingHelper.TrackExport(m_areaOrig, exportType, ImportExportStep.Failed); + if (e.InnerException is CancelException) { - TrackingHelper.TrackExport(m_areaOrig, exportType, ImportExportStep.Failed); - if (e.InnerException is CancelException) - { - MessageBox.Show(this, e.InnerException.Message); - m_ce = null; - } - else if (e.InnerException is LiftFormatException) - { - // Show the pretty yellow semi-crash dialog box, with instructions for the - // user to report the bug. - var app = m_propertyTable.GetValue("App"); - ErrorReporter.ReportException(new Exception(xWorksStrings.ksLiftExportBugReport, e.InnerException), - app.SettingsKey, m_propertyTable.GetValue("FeedbackInfoProvider").SupportEmailAddress, this, false); - } - else - { - string msg = xWorksStrings.ErrorExporting_ProbablyBug + Environment.NewLine + e.InnerException.Message; - MessageBox.Show(this, msg); - } + MessageBox.Show(this, e.InnerException.Message); + m_ce = null; + } + else if (e.InnerException is LiftFormatException) + { + // Show the pretty yellow semi-crash dialog box, with instructions for the + // user to report the bug. + var app = m_propertyTable.GetValue("App"); + ErrorReporter.ReportException(new Exception(xWorksStrings.ksLiftExportBugReport, e.InnerException), + app.SettingsKey, m_propertyTable.GetValue("FeedbackInfoProvider").SupportEmailAddress, this, false); } - finally + else { - m_progressDlg = null; - m_dumper = null; - Close(); + string msg = xWorksStrings.ErrorExporting_ProbablyBug + Environment.NewLine + e.InnerException.Message; + MessageBox.Show(this, msg); } } + finally + { + m_progressDlg = null; + m_dumper = null; + Close(); + } + } + } + + private object ExportWordOpenXml(IThreadedProgress progress, object[] args) + { + if (args.Length < 1) + return null; + var filePath = (string)args[0]; + var exportService = new DictionaryExportService(m_propertyTable, m_mediator); + exportService.ExportDictionaryForWord(filePath, null, progress); + foreach (var reversal in m_cache.ServiceLocator.GetInstance().AllInstances()) + { + exportService.ExportReversalForWord(filePath, reversal.WritingSystem, new DictionaryConfigurationModel()); } + return null; + } private object ExportConfiguredXhtml(IThreadedProgress progress, object[] args) { @@ -1261,6 +1285,9 @@ protected virtual void ConfigureItem(XmlDocument document, ListViewItem item, Xm case "webonary": ft.m_ft = FxtTypes.kftWebonary; break; + case "wordOpenXml": + ft.m_ft = FxtTypes.kftWordOpenXml; + break; case "LIFT": ft.m_ft = FxtTypes.kftLift; break; From 3a9900526bcda1919e33e4ba75b4993bf5e1a95d Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 15 Nov 2023 13:48:30 -0500 Subject: [PATCH 020/415] LT-21671: Add OpenXml nuget package Change-Id: I299c324a979245138fa1c3275fa13055255608a5 --- Build/mkall.targets | 1 + Build/nuget-common/packages.config | 1 + Src/xWorks/xWorks.csproj | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/Build/mkall.targets b/Build/mkall.targets index 8b4024de05..5ce41487c4 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -649,6 +649,7 @@ + diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index 3eda2bb8f0..c3a295c2ac 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -8,6 +8,7 @@ + diff --git a/Src/xWorks/xWorks.csproj b/Src/xWorks/xWorks.csproj index c1fd6b8d9e..a4d4c12beb 100644 --- a/Src/xWorks/xWorks.csproj +++ b/Src/xWorks/xWorks.csproj @@ -152,6 +152,10 @@ False ..\..\Output\Debug\DesktopAnalytics.dll + + False + ..\..\Output\Debug\DocumentFormat.OpenXml.dll + ..\..\packages\DotNetZip.1.13.7\lib\net40\DotNetZip.dll @@ -280,6 +284,7 @@ False ..\..\Output\Debug\UIAdapterInterfaces.dll + ..\..\Output\Debug\xCore.dll False From 6042763d417c248a432051a944c5e59d6eac6b15 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 15 Nov 2023 13:58:26 -0800 Subject: [PATCH 021/415] Fix LT-21677 - Use exact matching for lang tags * Also generates consistent quote usage for css ' instead of " - As best as I can tell our changes to merge styles and generate less specific selectors have caused the lang selection to become an issue. |= matches 'ur' or 'ur-x-latn' but if a user has both the wrong match might win - Also some of our tests were apparently broken before by the '|=' in the regular expression matching and were generating false negative results Change-Id: Iee46321ce036ae34e7349265d8bcf6b191368719 --- Src/xWorks/CssGenerator.cs | 8 +++--- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 32 ++++++++++----------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index f4b613cdac..af9eda414d 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -288,7 +288,7 @@ private static List GenerateCssForWritingSystems(string selector, str { // We want only the character type settings from the styleName style since we're applying them // to a span. - var wsRule = new StyleRule { Value = selector + String.Format("[lang|=\"{0}\"]", aws.LanguageTag) }; + var wsRule = new StyleRule { Value = selector + String.Format("[lang=\'{0}\']", aws.LanguageTag) }; var styleDecls = GenerateCssStyleFromLcmStyleSheet(styleName, aws.Handle, propertyTable); wsRule.Declarations.Properties.AddRange(GetOnlyCharacterStyle(styleDecls)); styleRules.Add(wsRule); @@ -666,7 +666,7 @@ private static List GenerateCssFromWsOptions(ConfigurableDictionaryNo // if the writing system isn't a magic name just use it otherwise find the right one from the magic list var wsIdString = possiblyMagic == 0 ? ws.Id : WritingSystemServices.GetWritingSystemList(cache, possiblyMagic, true).First().Id; var wsId = cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(wsIdString); - var wsRule = new StyleRule {Value = baseSelection + String.Format("[lang|=\"{0}\"]", wsIdString)}; + var wsRule = new StyleRule {Value = baseSelection + String.Format("[lang=\'{0}\']", wsIdString)}; if (!string.IsNullOrEmpty(configNode.Style)) wsRule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(configNode.Style, wsId, propertyTable)); if (!IsEmptyRule(wsRule)) @@ -829,7 +829,7 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN for (var i = enabledWsOptions.Length - 1; i > 0; i--) { betweenSelector = (i == enabledWsOptions.Length - 1 ? string.Empty : betweenSelector + ",") + - $"{selectorOfWsOptOwner} span+span[lang|='{enabledWsOptions[i].Id}']:before"; + $"{selectorOfWsOptOwner} span+span[lang='{enabledWsOptions[i].Id}']:before"; } } break; @@ -1436,7 +1436,7 @@ private static void AddFontInfoCss(BaseStyleInfo projectStyle, StyleDeclaration // fontName still null means not set in Normal Style, then get default fonts from WritingSystems configuration. // Comparison, projectStyle.Name == "Normal", required to limit the font-family definition to the - // empty span (ie span[lang|="en"]{}. If not included, font-family will be added to many more spans. + // empty span (ie span[lang="en"]{}. If not included, font-family will be added to many more spans. if (fontName == null && projectStyle.Name == "Normal") { var lgWritingSysytem = cache.ServiceLocator.WritingSystemManager.get_EngineOrNull(wsId); diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 82e381bfc0..6bb1391a9f 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -544,7 +544,7 @@ public void GenerateCssForConfiguration_DefinitionOrGlossBeforeAfterConfigGenera //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); VerifyRegex(cssResult, @".definitionorgloss> span:first-child:before{.*content:'<';.*}", "Before not generated."); - VerifyRegex(cssResult, @".definitionorgloss> span\+span\[lang\|=\'en\']:before{.*content:',';.*}", "Between not generated."); + VerifyRegex(cssResult, @".definitionorgloss> span\+span\[lang\=\'en\']:before{.*content:',';.*}", "Between not generated."); VerifyRegex(cssResult, @".definitionorgloss> span:last-child:after{.*content:'>';.*}", "After not generated."); } @@ -1223,7 +1223,7 @@ public void GenerateCssForStyleName_DefaultVernMagicConfigResultsInRealLanguageC //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); //Verify that vernacular was converted into french to match the vernholder node - Assert.That(cssResult, Contains.Substring(".vernholder> span[lang|=\"fr\"]")); + Assert.That(cssResult, Contains.Substring(".vernholder> span[lang='fr']")); } [Test] @@ -1247,7 +1247,7 @@ public void GenerateCssForStyleName_DefaultAnalysisMagicConfigResultsInRealLangu //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); //Verify that analysis was converted into english to match the analyholder node - Assert.That(cssResult, Contains.Substring(".analyholder> span[lang|=\"en\"]")); + Assert.That(cssResult, Contains.Substring(".analyholder> span[lang='en']")); } [Test] @@ -1400,8 +1400,8 @@ public void GenerateCssForConfiguration_CharStyleSubscriptWorks() var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); //make sure that fontinfo with the subscript overrides made it into css VerifyExtraFontInfoInCss(0, FwSuperscriptVal.kssvSub, FwUnderlineType.kuntNone, Color.Black, cssResult); - Assert.IsTrue(Regex.Match(cssResult, @".*\.sil*\.fieldworks.xworks.testrootclass>\s*span\[lang|='fr']\{.*position\:relative\*top\:-0.2em.*", RegexOptions.Singleline).Success, - "Subscript's positiion not generated properly"); + Assert.IsTrue(Regex.Match(cssResult, @".*\.sil*\.fieldworks.xworks.testrootclass>\s*span\[lang='fr'\]\{.*position\:relative;\s*top\:0.3em.*", RegexOptions.Singleline).Success, + "Subscript's position not generated properly"); } [Test] @@ -1427,8 +1427,8 @@ public void GenerateCssForConfiguration_CharStyleSuperscriptWorks() var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); //make sure that fontinfo with the superscript overrides made it into css VerifyExtraFontInfoInCss(0, FwSuperscriptVal.kssvSuper, FwUnderlineType.kuntNone, Color.Black, cssResult); - Assert.IsTrue(Regex.Match(cssResult, @".*\.sil*\.fieldworks.xworks.testrootclass>\s*span\[lang|='fr']\{.*position\:relative\*top\:\0.2em.*", RegexOptions.Singleline).Success, - "Superscript's positiion not generated properly"); + Assert.IsTrue(Regex.Match(cssResult, @".*\.sil*\.fieldworks.xworks.testrootclass>\s*span\[lang='fr']\{.*position\:relative;\s*top\:-0.6em.*", RegexOptions.Singleline).Success, + "Superscript's position not generated properly"); } [Test] @@ -2076,7 +2076,7 @@ public void GenerateCssForConfiguration_WritingSystemAudioWorks() // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // Not using regex to avoid figuring out all the escapes necessary - Assert.That(cssResult, Contains.Substring(".lexemeformoa> span[lang|=\"en-Zxxx-x-audio\"]{")); + Assert.That(cssResult, Contains.Substring(".lexemeformoa> span[lang='en-Zxxx-x-audio']{")); VerifyRegex(cssResult, @"a.en-Zxxx-x-audio{.*text-decoration:none;.*}", "Audio not generated."); } @@ -2376,7 +2376,7 @@ public void GenerateCssForConfiguration_PrimaryEntryReferencesTypeContextWorks() VerifyRegex(cssResult, @"^\.testheadword:after{\s*content:'ah';\s*}", "Headword's selector should *not* have changed due to factoring"); VerifyRegex(cssResult, @"\s*\.reversename>\s*span:first-child:before{\s*content:'beef';\s*}"); - VerifyRegex(cssResult, @"\s*\.reversename>\s*span+span\[lang|='" + lang2 + @"'\]:before{\s*content:'viet';\s*}"); + VerifyRegex(cssResult, @"\s*\.reversename>\s*span\+span\[lang='" + lang2 + @"'\]:before{\s*content:'viet';\s*}"); VerifyRegex(cssResult, @"\s*\.reversename>\s*span:last-child:after{\s*content:'aft';\s*}"); } @@ -2455,7 +2455,7 @@ public void GenerateCssForConfiguration_BetweenMultiWsWithoutAbbrSpanWorks() PopulateFieldsForTesting(entry); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @".*\.lexemeform>\s*span\+span\[lang\|\='fr'\]:before{.*content:','.*}", + VerifyRegex(cssResult, @".*\.lexemeform>\s*span\+span\[lang\='fr'\]:before{.*content:','.*}", "Between Multi-WritingSystem without Abbr selector not generated."); } @@ -2951,8 +2951,8 @@ public void GenerateCssForConfiguration_WsSpanWithNormalStyle() PopulateFieldsForTesting(testEntryNode); // Default (no ws) style info const string defaultStyle = "body{font-size:10pt;}"; - const string englishStyle = "span[lang|=\"en\"]{font-family:'english',serif;color:#F00;}"; - const string frenchStyle = "span[lang|=\"fr\"]{font-family:'french',serif;color:#008000;}"; + const string englishStyle = "span[lang='en']{font-family:'english',serif;color:#F00;}"; + const string frenchStyle = "span[lang='fr']{font-family:'french',serif;color:#008000;}"; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(defaultStyle + englishStyle + frenchStyle)); @@ -2999,8 +2999,8 @@ public void GenerateCssForConfiguration_NormalStyleForWsDoesNotOverrideNodeStyle }; PopulateFieldsForTesting(entryNode); // Default (no ws) style info - const string englishGeneralStyle = "span[lang|=\"en\"]{font-family:'english',serif;color:#F00;}"; - const string definitionSelector = ".definition span[lang|=\"en\"]{color:#FF0;}"; + const string englishGeneralStyle = "span[lang='en']{font-family:'english',serif;color:#F00;}"; + const string definitionSelector = ".definition span[lang='en']{color:#FF0;}"; //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); // Using substring instead of regex to avoid spending all the time figuring out which regex characters to escape in this css @@ -3131,7 +3131,7 @@ public void GenerateCssForConfiguration_DictionaryMinorUnusedDoesNotOverride() // The problem we are testing for occurred in the section of CssGenerator labeled: // "Then generate the rules for all the writing system overrides" // So I chose to check specifically for one of the default writing systems; DefaultAnalWs would have worked too. - var vernStyle = "span[lang|=\"" + vernWs + "\"]{color:#008000;}"; + var vernStyle = "span[lang='" + vernWs + "']{color:#008000;}"; Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(@"div.minorentryvariant " + vernStyle), "Dictionary-Secondary Paragraph Style should be generated."); } @@ -3828,7 +3828,7 @@ public void GenerateCssForConfiguration_SpecificLanguageColorIsNotOverridenByPar // default (analysis ws) rule const string regexPrimary = @"^\.extendednotecontents\{\s*color:#008000;"; // specific (embedded vernacular ws) rule affecting any span inside .extendednotecontents (at any level) - const string regexSpecific = @"^\.extendednotecontents span\[lang|='fr']\{\s*color:#00F"; + const string regexSpecific = @"^\.extendednotecontents span\[lang='fr']\{\s*color:#00F"; VerifyRegex(result, regexPrimary, "The css for the default color should be there."); VerifyRegex(result, regexSpecific, "The css for the specific language color should be there."); } From 9b49e37a189f759e21c23a818e1975a01b28c713 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 15 Nov 2023 09:13:23 -0500 Subject: [PATCH 022/415] =?UTF-8?q?LT-21648:=20Add=20=E2=80=9CCaption=20(o?= =?UTF-8?q?r=20Headword)=E2=80=9D=20to=20Dictionary=20Config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Dictionary Configuration->Pictures add an option that will first try to get the Caption for a writing system. If there isn’t a caption then try to get the Headword for the writing system. Note: In the fwdictconfig files we also made changes to Pictures->Headword so that they display the Headwords for the specified writing system. Previously we were always displaying the headword for the default ws, regardless of which ws the user selected. If multipe ws’s were selected we were displaying the default headword multiple times. Change-Id: I91d66eebaba69a832a30c2fafe6c452e9b6dbf27 --- .../Dictionary/Hybrid.fwdictconfig | 21 ++- .../Dictionary/Lexeme.fwdictconfig | 14 +- .../Dictionary/Root.fwdictconfig | 28 +++- Src/xWorks/ConfiguredLcmGenerator.cs | 31 ++++ .../ConfiguredXHTMLGeneratorTestHelpers.cs | 2 +- .../ConfiguredXHTMLGeneratorTests.cs | 133 ++++++++++++++++++ 6 files changed, 219 insertions(+), 10 deletions(-) diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig index 55b3c99278..7ae9d0e438 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig @@ -1112,13 +1112,18 @@ + + + + - + @@ -2111,13 +2116,18 @@ + + + + - + @@ -3621,13 +3631,18 @@ + + + + - + diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig index e43fae4152..2c37665ca9 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig @@ -1001,13 +1001,18 @@ + + + + - + @@ -1947,13 +1952,18 @@ + + + + - + diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig index 9836860cb1..95b314eee0 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig @@ -868,13 +868,18 @@ + + + + - + @@ -1917,13 +1922,18 @@ + + + + - + @@ -2949,13 +2959,18 @@ + + + + - + @@ -4393,13 +4408,18 @@ + + + + - + diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 6b37009d9b..bcd2f95d33 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -378,6 +378,13 @@ internal static string GenerateContentForFieldByReflection(object field, Configu return ret.ToString(); } } + if (config.FieldDescription == "CaptionOrHeadword") + { + if (field is ICmPicture) + { + return GenerateContentForCaptionOrHeadword(field as ICmPicture, config, settings); + } + } if (config.IsCustomField && config.SubField == null) { // REVIEW: We have overloaded terms here, this is a C# class not a css class, consider a different name @@ -864,6 +871,30 @@ private static string GenerateContentForDefOrGloss(ILexSense sense, Configurable return string.Empty; } + private static string GenerateContentForCaptionOrHeadword(ICmPicture picture, ConfigurableDictionaryNode config, GeneratorSettings settings) + { + var wsOption = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; + if (wsOption == null) + throw new ArgumentException(@"Configuration nodes for MultiString fields should have WritingSystemOptions", "config"); + var bldr = new StringBuilder(); + foreach (var option in wsOption.Options) + { + if (option.IsEnabled) + { + int wsId; + ITsString bestString = picture.GetCaptionOrHeadword(option.Id, out wsId); + if (bestString != null) + { + var contentItem = GenerateWsPrefixAndString(config, settings, wsOption, wsId, bestString, Guid.Empty); + bldr.Append(contentItem); + } + } + } + if (bldr.Length > 0) + return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); + return String.Empty; + } + internal static string CopyFileSafely(GeneratorSettings settings, string source, string relativeDestination) { if (!File.Exists(source)) diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTestHelpers.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTestHelpers.cs index 166c691e9b..64039baab7 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTestHelpers.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTestHelpers.cs @@ -448,7 +448,7 @@ internal static ICmPicture CreatePicture(LcmCache cache, bool exists = true, str if (caption != null) { var wsHandle = cache.WritingSystemFactory.GetWsFromStr(ws); - pic.Caption.set_String(wsHandle, TsStringUtils.MakeString("caption", wsHandle)); + pic.Caption.set_String(wsHandle, TsStringUtils.MakeString(caption, wsHandle)); } var file = cache.ServiceLocator.GetInstance().Create(); if (cache.LangProject.MediaOC.Any()) diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 2dfbe96618..f35c7879a3 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -1277,6 +1277,139 @@ public void GenerateContentForEntry_DontDisplayNotSure() AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramName2, 1); } + [Test] + public void GenerateContentForEntry_CaptionOrHeadwordGetsCaption() + { + var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); + var headwordNode = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + CSSClassNameOverride = "headword", + DictionaryNodeOptions = wsOpts + }; + + var captionOrHeadwordNode = new ConfigurableDictionaryNode { FieldDescription = "CaptionOrHeadword", DictionaryNodeOptions = wsOpts }; + var pictureNode = new ConfigurableDictionaryNode + { + DictionaryNodeOptions = new DictionaryNodePictureOptions(), + FieldDescription = "PicturesOfSenses", + CSSClassNameOverride = "Pictures", + Children = new List { captionOrHeadwordNode } + }; + + var sensesNode = new ConfigurableDictionaryNode + { + FieldDescription = "Senses", + }; + var mainEntryNode = new ConfigurableDictionaryNode + { + Children = new List { sensesNode, pictureNode, headwordNode }, + FieldDescription = "LexEntry" + }; + CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var entryOne = CreateInterestingLexEntry(Cache); + AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); + var sense = entryOne.SensesOS[0]; + sense.PicturesOS.Add(CreatePicture(Cache, true, "captionEn", "en")); + + var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); + //SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + const string captionOrHeadwordContainsCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; + //This assert is dependent on the specific entry data created in CreateInterestingLexEntry + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaption, 1); + } + + [Test] + public void GenerateContentForEntry_CaptionOrHeadwordGetsHeadword() + { + var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); + var headwordNode = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + CSSClassNameOverride = "headword", + DictionaryNodeOptions = wsOpts + }; + + var captionOrHeadwordNode = new ConfigurableDictionaryNode { FieldDescription = "CaptionOrHeadword", DictionaryNodeOptions = wsOpts }; + var pictureNode = new ConfigurableDictionaryNode + { + DictionaryNodeOptions = new DictionaryNodePictureOptions(), + FieldDescription = "PicturesOfSenses", + CSSClassNameOverride = "Pictures", + Children = new List { captionOrHeadwordNode } + }; + + var sensesNode = new ConfigurableDictionaryNode + { + FieldDescription = "Senses", + }; + var mainEntryNode = new ConfigurableDictionaryNode + { + Children = new List { sensesNode, pictureNode, headwordNode }, + FieldDescription = "LexEntry" + }; + CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var entryOne = CreateInterestingLexEntry(Cache); + AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); + var sense = entryOne.SensesOS[0]; + sense.PicturesOS.Add(CreatePicture(Cache, true, null, "en")); // Create the picture with a null caption. + + var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); + //SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; + //This assert is dependent on the specific entry data created in CreateInterestingLexEntry + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadword, 1); + } + + [Test] + public void GenerateContentForEntry_CaptionOrHeadword_HandlePerWs() + { + var wsOpts = GetWsOptionsForLanguages(new[] { "en", "fr" }); + var headwordNode = new ConfigurableDictionaryNode + { + FieldDescription = "MLHeadWord", + CSSClassNameOverride = "headword", + DictionaryNodeOptions = wsOpts + }; + + var captionOrHeadwordNode = new ConfigurableDictionaryNode { FieldDescription = "CaptionOrHeadword", DictionaryNodeOptions = wsOpts }; + var pictureNode = new ConfigurableDictionaryNode + { + DictionaryNodeOptions = new DictionaryNodePictureOptions(), + FieldDescription = "PicturesOfSenses", + CSSClassNameOverride = "Pictures", + Children = new List { captionOrHeadwordNode } + }; + + var sensesNode = new ConfigurableDictionaryNode + { + FieldDescription = "Senses", + }; + var mainEntryNode = new ConfigurableDictionaryNode + { + Children = new List { sensesNode, pictureNode, headwordNode }, + FieldDescription = "LexEntry" + }; + CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var entryOne = CreateInterestingLexEntry(Cache); + AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); + AddHeadwordToEntry(entryOne, "HeadwordFr", m_wsFr); + var sense = entryOne.SensesOS[0]; + sense.PicturesOS.Add(CreatePicture(Cache, true, "captionEn", "en")); // Create the picture with a en caption, but no fr caption. + + var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); + //SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; + const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; + //This assert is dependent on the specific entry data created in CreateInterestingLexEntry + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaptionEn, 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadwordFr, 1); + } + + [Test] public void GenerateContentForEntry_DefinitionOrGlossWorks() { From 819712c111e56629e222389d27116f00454222ae Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Thu, 16 Nov 2023 10:41:36 -0800 Subject: [PATCH 023/415] Fix LT-20890 - Get SenseNumber styling working * Use the WritingSystem from the HomographConfiguration in the xhtml span for the SenseNumber as well as in the style generation Change-Id: I69d1922961c4087cb4baf9b92a43209e0d06fea9 --- Src/xWorks/ConfiguredLcmGenerator.cs | 7 ++++++- Src/xWorks/CssGenerator.cs | 12 +++++++++++- Src/xWorks/ILcmContentGenerator.cs | 2 +- Src/xWorks/LcmJsonGenerator.cs | 2 +- Src/xWorks/LcmXhtmlGenerator.cs | 3 ++- .../xWorksTests/ConfiguredXHTMLGeneratorTests.cs | 11 +++++++---- 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index bcd2f95d33..b13382363a 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -26,6 +26,7 @@ using SIL.FieldWorks.Common.FwUtils; using SIL.FieldWorks.Common.Widgets; using SIL.LCModel; +using SIL.LCModel.DomainImpl; using SIL.LCModel.DomainServices; using SIL.LCModel.Infrastructure; using SIL.LCModel.Utils; @@ -1650,6 +1651,7 @@ private static string GenerateContentForSenses(ConfigurableDictionaryNode config if (senseNode != null) info.ParentSenseNumberingStyle = senseNode.ParentSenseNumberingStyle; + info.HomographConfig = settings.Cache.ServiceLocator.GetInstance(); // Calculating isThisSenseNumbered may make sense to do for each item in the foreach loop below, but because of how the answer // is determined, the answer for all sibling senses is the same as for the first sense in the collection. // So calculating outside the loop for performance. @@ -2094,9 +2096,11 @@ private static string GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryNode var senseOptions = senseConfigNode.DictionaryNodeOptions as DictionaryNodeSenseOptions; var formattedSenseNumber = GetSenseNumber(senseOptions.NumberingStyle, ref info); + info.HomographConfig = settings.Cache.ServiceLocator.GetInstance(); + var senseNumberWs = string.IsNullOrEmpty(info.HomographConfig.WritingSystem) ? "en" : info.HomographConfig.WritingSystem; if (string.IsNullOrEmpty(formattedSenseNumber)) return string.Empty; - return settings.ContentGenerator.GenerateSenseNumber(formattedSenseNumber); + return settings.ContentGenerator.GenerateSenseNumber(formattedSenseNumber, senseNumberWs); } private static string GetSenseNumber(string numberingStyle, ref SenseInfo info) @@ -3103,6 +3107,7 @@ internal struct SenseInfo public int SenseCounter { get; set; } public string SenseOutlineNumber { get; set; } public string ParentSenseNumberingStyle { get; set; } + public HomographConfiguration HomographConfig { get; set; } } } diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index af9eda414d..de049a4492 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -23,6 +23,9 @@ using XCore; using Property = ExCSS.Property; using SIL.FieldWorks.Common.FwUtils; +using SIL.FieldWorks.FwCoreDlgControls; +using SIL.LCModel.Core.WritingSystems; +using SIL.LCModel.DomainImpl; namespace SIL.FieldWorks.XWorks { @@ -35,6 +38,7 @@ public class CssGenerator : ILcmStylesGenerator internal const string BeforeAfterBetweenStyleName = "Dictionary-Context"; internal const string LetterHeadingStyleName = "Dictionary-LetterHeading"; + internal const string SenseNumberStyleName = "Dictionary-SenseNumber"; internal const string DictionaryNormal = "Dictionary-Normal"; internal const string DictionaryMinor = "Dictionary-Minor"; internal const string WritingSystemPrefix = "writingsystemprefix"; @@ -416,7 +420,13 @@ private static List GenerateCssForSenses(ConfigurableDictionaryNode c if (senseOptions.DisplayEachSenseInAParagraph) selectors = new List(RemoveBeforeAfterSelectorRules(selectors)); styleRules.AddRange(CheckRangeOfRulesForEmpties(selectors)); + + var cache = propertyTable.GetValue("cache"); + var senseNumberLanguage = cache.ServiceLocator.GetInstance().WritingSystem; + senseNumberLanguage = string.IsNullOrEmpty(senseNumberLanguage) ? "en" : senseNumberLanguage; + var senseNumberWsId = cache.WritingSystemFactory.GetWsFromStr(senseNumberLanguage); var senseNumberRule = new StyleRule(); + // Not using SelectClassName here; sense and sensenumber are siblings and the configNode is for the Senses collection. // Select the base plus the node's unmodified class attribute and append the sensenumber matcher. var senseNumberSelector = string.Format("{0} .sensenumber", senseContentSelector); @@ -424,7 +434,7 @@ private static List GenerateCssForSenses(ConfigurableDictionaryNode c senseNumberRule.Value = senseNumberSelector; if(!String.IsNullOrEmpty(senseOptions.NumberStyle)) { - senseNumberRule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(senseOptions.NumberStyle, DefaultStyle, propertyTable)); + senseNumberRule.Declarations.Properties.AddRange(GenerateCssStyleFromLcmStyleSheet(senseOptions.NumberStyle, senseNumberWsId, propertyTable)); } if (!IsEmptyRule(senseNumberRule)) styleRules.Add(senseNumberRule); diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 8eb9d40657..3c2067076b 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -55,7 +55,7 @@ string GenerateGroupingNode(object field, string className, ConfigurableDictiona void WriteProcessedContents(IFragmentWriter writer, string contents); string AddImage(string classAttribute, string srcAttribute, string pictureGuid); string AddImageCaption(string captionContent); - string GenerateSenseNumber(string formattedSenseNumber); + string GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs); string AddLexReferences(bool generateLexType, string lexTypeContent, string className, string referencesContent, bool typeBefore); void BeginCrossReference(IFragmentWriter writer, bool isBlockProperty, string className); void EndCrossReference(IFragmentWriter writer); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 24d7150dfb..64eb9e56a3 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -324,7 +324,7 @@ public string AddImageCaption(string captionContent) return captionContent; } - public string GenerateSenseNumber(string formattedSenseNumber) + public string GenerateSenseNumber(string formattedSenseNumber, string wsId) { return formattedSenseNumber; } diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index 7445826f60..201ac843fc 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -913,13 +913,14 @@ public string AddImageCaption(string captionContent) } } - public string GenerateSenseNumber(string formattedSenseNumber) + public string GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs) { var bldr = new StringBuilder(); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sensenumber"); + xw.WriteAttributeString("lang", senseNumberWs); xw.WriteString(formattedSenseNumber); xw.WriteEndElement(); xw.Flush(); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index f35c7879a3..e60a8a0679 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -1968,9 +1968,12 @@ public void GenerateContentForEntry_LexemeBasedConsidersComplexFormsMainEntries( /// if this is not the only sense, then number it. /// (See LT-17906.) /// - [Test] - public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses() + [TestCase("en")] + [TestCase("fr")] + public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(string homographWs) { + var homographConfig = Cache.ServiceLocator.GetInstance(); + homographConfig.WritingSystem = homographWs; var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; var sensesNode = new ConfigurableDictionaryNode @@ -1991,8 +1994,8 @@ public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + string senseNumberOne = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; + string senseNumberTwo = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); From b6c0e01b9d29f6b7c2f5346b16389874de1b95b5 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Thu, 16 Nov 2023 14:11:20 -0800 Subject: [PATCH 024/415] Fix LT-21670: Use the CustomHomographNumbers for Sense Numbers * Note: When LT-21669 is completed it will need to fill in the CustomHomographNumbers in the HomographConfiguration for this fix to continue working Change-Id: I14ddf1421ba049f7ea0f0225ddbe1e2d333af880 --- Src/xWorks/ConfiguredLcmGenerator.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index b13382363a..35f48688f3 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2117,7 +2117,16 @@ private static string GetSenseNumber(string numberingStyle, ref SenseInfo info) nextNumber = GetRomanSenseCounter(numberingStyle, info.SenseCounter); break; default: // handles %d and %O. We no longer support "%z" (1 b iii) because users can hand-configure its equivalent - nextNumber = info.SenseCounter.ToString(); + // Use the digits from the CustomHomographNumbers if they are defined + if (info.HomographConfig.CustomHomographNumbers.Count == 10) + { + nextNumber = info.HomographConfig.CustomHomographNumbers[info.SenseCounter]; + } + else + { + // If the writing system somehow has no NumberingSystem then default to the sense counter number in Ascii + nextNumber = info.SenseCounter.ToString(); + } break; } info.SenseOutlineNumber = GenerateSenseOutlineNumber(info, nextNumber); From cd0f97348c6cd671657857433388125a944ae2c5 Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Thu, 9 Nov 2023 08:59:17 +0700 Subject: [PATCH 025/415] Added cmd capability to clone and open a project When launched from CMD with the proper arguments, flex will now interface with flexbridge to obtain a specific project with minimal user interaction. Currently, flexbridge confirms with the user before downloading from the given URI using a simple prompt. This change required a number of new parameters to be passed to flexbridge, and we opted to overload the launching method, to help preserve functionality for any external callers. I also changed the Process.Start logic slightly in order to deliver the user/pass to the process over environment variable, as opposed to less secure solutions. This work is mostly done, but it doesn't support custom project names yet. And I still need to consider input checking for the new CMD arguments. Ticket link for FB: https://github.com/sillsdev/flexbridge/pull/388 Change-Id: Ib4a85a628216f382780aaa50f2e70bd720ef00a2 --- .../FwControls/ObtainProjectMethod.cs | 16 ++++++ Src/Common/FieldWorks/FieldWorks.cs | 11 ++++ Src/Common/FwUtils/FLExBridgeHelper.cs | 55 ++++++++++++++++-- Src/Common/FwUtils/FwLinkArgs.cs | 56 +++++++++++++++++++ 4 files changed, 134 insertions(+), 4 deletions(-) diff --git a/Src/Common/Controls/FwControls/ObtainProjectMethod.cs b/Src/Common/Controls/FwControls/ObtainProjectMethod.cs index 629071b711..c150ea52dc 100644 --- a/Src/Common/Controls/FwControls/ObtainProjectMethod.cs +++ b/Src/Common/Controls/FwControls/ObtainProjectMethod.cs @@ -2,6 +2,7 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -63,6 +64,21 @@ public static string ObtainProjectFromAnySource(Form parent, IHelpTopicProvider return projectFileFullPath; } + /// + /// Get a new FW project from the specified Mercurial repository. + /// + /// Null if the operation was cancelled or otherwise did not work. The full pathname of an fwdata file, if it did work. + public static string ObtainProject(Uri repoUri, string repoName, string userName, string passWord, string repoIdentifier, out ObtainedProjectType obtainedProjectType) + { + var result = FLExBridgeHelper.LaunchFieldworksBridge(FwDirectoryFinder.ProjectsDirectory, userName, FLExBridgeHelper.Obtain, null, + LcmCache.ModelVersion, FLExBridgeHelper.LiftVersion, null, null, out _, out var projectFileFullPath, repoUri, repoName, passWord, repoIdentifier); + + //Assume project type + obtainedProjectType = result ? ObtainedProjectType.FieldWorks : ObtainedProjectType.None; + + return projectFileFullPath; + } + private static void EnsureLinkedFoldersExist(string fwdataFileFullPathname) { var projectFolder = Path.GetDirectoryName(fwdataFileFullPathname); diff --git a/Src/Common/FieldWorks/FieldWorks.cs b/Src/Common/FieldWorks/FieldWorks.cs index b34f6b3777..a1f8713e9a 100644 --- a/Src/Common/FieldWorks/FieldWorks.cs +++ b/Src/Common/FieldWorks/FieldWorks.cs @@ -1352,6 +1352,17 @@ private static ProjectId DetermineProject(FwAppArgs args) if (TryCommandLineOption(projId, out projectOpenError)) return projId; + if (!string.IsNullOrEmpty(args.Password) && !string.IsNullOrEmpty(args.ProjectUri) && !string.IsNullOrEmpty(args.Username)) + { + var projectFile = ObtainProjectMethod.ObtainProject(new Uri(args.ProjectUri), args.Database, args.Username, args.Password, args.RepoIdentifier, out _); + if (!string.IsNullOrEmpty(projectFile)) + { + var projectName = Path.GetFileNameWithoutExtension(projectFile); + projId = new ProjectId(args.DatabaseType, projectName); + return projId; + } + } + // If this app hasn't been run before, ask user about opening sample DB. var app = GetOrCreateApplication(args); if (app.RegistrySettings.FirstTimeAppHasBeenRun) diff --git a/Src/Common/FwUtils/FLExBridgeHelper.cs b/Src/Common/FwUtils/FLExBridgeHelper.cs index c36a0285e4..8fb8669dc5 100644 --- a/Src/Common/FwUtils/FLExBridgeHelper.cs +++ b/Src/Common/FwUtils/FLExBridgeHelper.cs @@ -215,6 +215,35 @@ static FLExBridgeHelper() public static bool LaunchFieldworksBridge(string projectFolder, string userName, string command, string projectGuid, int fwmodelVersionNumber, string liftModelVersionNumber, string writingSystemId, Action onNonBlockerCommandComplete, out bool changesReceived, out string projectName) + { + return LaunchFieldworksBridge(projectFolder, userName, command, projectGuid, fwmodelVersionNumber, liftModelVersionNumber, writingSystemId, onNonBlockerCommandComplete, + out changesReceived, out projectName, null, null, null, null); + } + + /// + /// Launches the FLExBridge application with the given commands and locks out the FLEx interface until the bridge + /// is closed. + /// + /// The entire FieldWorks project folder path. + /// Must include the project folder and project name with "fwdata" extension. + /// Empty is OK if not send_receive command. + /// the username to use in Chorus commits + /// obtain, start, send_receive, view_notes + /// Optional Lang Project guid, that is only used with the 'move_lift' command + /// Version of LIFT schema that is supported by FLEx. + /// The id of the first vernacular writing system + /// Current FDO model version number + /// Callback called when a non-blocker command has completed + /// true if S/R made changes to the project. + /// Name of the project to be opened after launch returns. + /// Full URI of the project, if known beforehand. + /// The name of the project, if known beforehand. + /// The authentication credentials which will allow access to the repo to clone, if known beforehand. + /// The authentication credentials which will allow access to the repo to clone, if known beforehand. + /// true if successful, false otherwise + public static bool LaunchFieldworksBridge(string projectFolder, string userName, string command, string projectGuid, + int fwmodelVersionNumber, string liftModelVersionNumber, string writingSystemId, Action onNonBlockerCommandComplete, + out bool changesReceived, out string projectName, Uri projectUri, string name, string credentialsPassword, string repoIdentifier) { _pipeID = string.Format(@"SendReceive{0}{1}", projectFolder, command); _flexBridgeTerminated = false; @@ -222,6 +251,17 @@ public static bool LaunchFieldworksBridge(string projectFolder, string userName, var args = ""; projectName = ""; _projectName = ""; + string userCredentials = null; + if (projectUri != null) + { + var uriWithoutCredentials = projectUri.AbsoluteUri.Replace(projectUri.UserInfo + "@", ""); + AddArg(ref args, "-uri", uriWithoutCredentials); + AddArg(ref args, "-project", name); + AddArg(ref args, "-user", userName); + AddArg(ref args, "-repositoryIdentifier", repoIdentifier); + userCredentials = string.Join(":", userName, credentialsPassword); + } + var userNameActual = userName; if (string.IsNullOrEmpty(userName)) userNameActual = Environment.UserName; // default so we can always pass something. @@ -279,13 +319,13 @@ public static bool LaunchFieldworksBridge(string projectFolder, string userName, if (!host.Initialize("FLExBridgeEndpoint" + _pipeID, AlertFlex, CleanupHost)) return false; - LaunchFlexBridge(host, command, args, onNonBlockerCommandComplete, ref changesReceived, ref projectName); + LaunchFlexBridge(host, command, args, onNonBlockerCommandComplete, userCredentials, ref changesReceived, ref projectName); return true; } private static void LaunchFlexBridge(IIPCHost host, string command, string args, Action onNonBlockerCommandComplete, - ref bool changesReceived, ref string projectName) + string userPass, ref bool changesReceived, ref string projectName) { string flexbridgeLauncher = FullFieldWorksBridgePath(); if (Platform.IsUnix) @@ -298,8 +338,16 @@ private static void LaunchFlexBridge(IIPCHost host, string command, string args, } // Launch the bridge process. - using (Process.Start(flexbridgeLauncher, args)) + using (var process = new Process()) { + var startInfo = new ProcessStartInfo(); + if (userPass != null) startInfo.EnvironmentVariables["CHORUS_CREDENTIALS"] = userPass; + startInfo.UseShellExecute = false; + startInfo.FileName = flexbridgeLauncher; + startInfo.Arguments = args; + + process.StartInfo = startInfo; + process.Start(); } var nonFlexBlockers = new HashSet @@ -380,7 +428,6 @@ private static void AddArg(ref string extant, string flag, string value) extant += flag; if (!string.IsNullOrEmpty(value)) { - bool hasWhitespace; if (value.Any(Char.IsWhiteSpace)) { extant += " \"" + value + "\""; diff --git a/Src/Common/FwUtils/FwLinkArgs.cs b/Src/Common/FwUtils/FwLinkArgs.cs index 009e51cff6..c83ff3b1ed 100644 --- a/Src/Common/FwUtils/FwLinkArgs.cs +++ b/Src/Common/FwUtils/FwLinkArgs.cs @@ -444,6 +444,14 @@ public class FwAppArgs : FwLinkArgs public const string kAppServerMode = "appServerMode"; /// Command-line argument: flag that tells FW to bring up a dialog to set an associated project. public const string kChooseProject = "chooseProject"; + /// Command-line argument: authentication username + public const string kUser = "user"; + /// Command-line argument: authentication password + public const string kPass = "password"; + /// Command-line argument: remote URI of the desired project + public const string kUri = "projectUri"; + /// Command-line argument: The ID used to identify repositories, regardless of their source URL, i.e. "log -r0 --template " + SurroundWithQuotes("{node}") + public const string kIdentifier = "repositoryIdentifier"; #endregion #region Member variables @@ -454,6 +462,10 @@ public class FwAppArgs : FwLinkArgs private string m_backupFile = string.Empty; private string m_restoreOptions = string.Empty; private string m_chooseProjectFile = string.Empty; + private string m_user = string.Empty; + private string m_pass = string.Empty; + private string m_uri = string.Empty; + private string m_repoIdentifier = string.Empty; #endregion #region Properties @@ -589,6 +601,46 @@ public string ChooseProjectFile { get { return m_chooseProjectFile; } } + + /// ------------------------------------------------------------------------------------ + /// + /// Gets the Send/Receive username. + /// + /// ------------------------------------------------------------------------------------ + public string Username + { + get { return m_user; } + } + + /// ------------------------------------------------------------------------------------ + /// + /// Gets the Send/Receive password. + /// + /// ------------------------------------------------------------------------------------ + public string Password + { + get { return m_pass; } + } + + /// ------------------------------------------------------------------------------------ + /// + /// Gets the remote URI for the target project. + /// + /// ------------------------------------------------------------------------------------ + public string ProjectUri + { + get { return m_uri; } + } + + /// ------------------------------------------------------------------------------------ + /// + /// Gets the ID used to identify the repository. + /// + /// ------------------------------------------------------------------------------------ + public string RepoIdentifier + { + get { return m_repoIdentifier; } + } #endregion #region Constructor @@ -922,6 +974,10 @@ private void ProcessArg(string name, string value) case kAppServerMode: AppServerMode = true; break; case kTag: m_tag = value; break; case kTool: m_toolName = value; break; + case kUser: m_user = value; break; + case kPass: m_pass = value; break; + case kUri: m_uri = value; break; + case kIdentifier: m_repoIdentifier = value; break; case kGuid: if (value != "null") TargetGuid = new Guid(value); From a9b0c06ab00c237a9bd08c514709deb00fd3d419 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 17 Nov 2023 14:07:35 -0800 Subject: [PATCH 026/415] Fix LT-21679 - Make custom numbers work for more than 9 senses Change-Id: I5441798f86b6e3d7be6d46197f3b9cc8455ce579 --- Src/xWorks/ConfiguredLcmGenerator.cs | 11 +++++----- .../ConfiguredXHTMLGeneratorTests.cs | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 35f48688f3..a855bb83a0 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2117,15 +2117,14 @@ private static string GetSenseNumber(string numberingStyle, ref SenseInfo info) nextNumber = GetRomanSenseCounter(numberingStyle, info.SenseCounter); break; default: // handles %d and %O. We no longer support "%z" (1 b iii) because users can hand-configure its equivalent + nextNumber = info.SenseCounter.ToString(); // Use the digits from the CustomHomographNumbers if they are defined if (info.HomographConfig.CustomHomographNumbers.Count == 10) { - nextNumber = info.HomographConfig.CustomHomographNumbers[info.SenseCounter]; - } - else - { - // If the writing system somehow has no NumberingSystem then default to the sense counter number in Ascii - nextNumber = info.SenseCounter.ToString(); + for (var digit = 0; digit < 10; ++digit) + { + nextNumber = nextNumber.Replace(digit.ToString(), info.HomographConfig.CustomHomographNumbers[digit]); + } } break; } diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index e60a8a0679..6c9a7fb08b 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -1967,13 +1967,20 @@ public void GenerateContentForEntry_LexemeBasedConsidersComplexFormsMainEntries( /// If the numbering style for Senses says to number it, and /// if this is not the only sense, then number it. /// (See LT-17906.) + /// Also verify that custom homograph numbers are used and we can count past 9 with them /// - [TestCase("en")] - [TestCase("fr")] - public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(string homographWs) + [TestCase("en", null)] + [TestCase("fr", null)] + [TestCase("fr", new [] { "y", "1", "2", "3", "4", "5", "6", "7", "8", "9" })] + public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(string homographWs, string[] customHomographs) { var homographConfig = Cache.ServiceLocator.GetInstance(); + var tenthSenseNumber = customHomographs == null ? "10" : customHomographs[1] + customHomographs[0]; homographConfig.WritingSystem = homographWs; + if (customHomographs != null) + { + homographConfig.CustomHomographNumbers = new List(customHomographs); + } var wsOpts = GetWsOptionsForLanguages(new[] { "en" }); var glossNode = new ConfigurableDictionaryNode { FieldDescription = "Gloss", DictionaryNodeOptions = wsOpts }; var sensesNode = new ConfigurableDictionaryNode @@ -1991,11 +1998,20 @@ public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(strin CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var testEntry = CreateInterestingLexEntry(Cache); AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); + AddSenseToEntry(testEntry, "3", m_wsEn, Cache); + AddSenseToEntry(testEntry, "4", m_wsEn, Cache); + AddSenseToEntry(testEntry, "5", m_wsEn, Cache); + AddSenseToEntry(testEntry, "6", m_wsEn, Cache); + AddSenseToEntry(testEntry, "7", m_wsEn, Cache); + AddSenseToEntry(testEntry, "8", m_wsEn, Cache); + AddSenseToEntry(testEntry, "9", m_wsEn, Cache); + AddSenseToEntry(testEntry, "10", m_wsEn, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); string senseNumberOne = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; string senseNumberTwo = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; + string senseNumberTen = $"//span[@class='sensecontent']/spansenses/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='{tenthSenseNumber}']]//span[@lang='en' and text()='10']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); From 4f6b99b226aef008aed6ef3b94aa3a6fef638270 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 22 Nov 2023 13:58:24 -0500 Subject: [PATCH 027/415] LT-21438: Webonary - generate entries before headings Previously we were generating the letter headings based on what we anticipated would be the list of entries. This submission changes the order so we now generate the entries first, then generate the corresponding letter headings. Change-Id: I086a2023b597be89ef871f593d4badfae42aab52 --- Src/xWorks/DictionaryExportService.cs | 14 +++++------ Src/xWorks/LcmJsonGenerator.cs | 7 +++++- Src/xWorks/UploadToWebonaryController.cs | 12 ++++------ .../xWorksTests/LcmJsonGeneratorTests.cs | 23 ++++++++++++++++--- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/Src/xWorks/DictionaryExportService.cs b/Src/xWorks/DictionaryExportService.cs index 2f8aaf7ef6..fa6c5981bd 100644 --- a/Src/xWorks/DictionaryExportService.cs +++ b/Src/xWorks/DictionaryExportService.cs @@ -135,14 +135,14 @@ private void ExportConfiguredXhtml(string xhtmlPath, DictionaryConfigurationMode LcmXhtmlGenerator.SavePublishedHtmlWithStyles(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_propertyTable, xhtmlPath, progress); } - public List ExportConfiguredJson(string folderPath, DictionaryConfigurationModel configuration) + public List ExportConfiguredJson(string folderPath, DictionaryConfigurationModel configuration, out int[] entryIds) { using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_propertyTable, m_mediator)) { var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, DictionaryType); return LcmJsonGenerator.SavePublishedJsonWithStyles(entriesToSave, publicationDecorator, BatchSize, configuration, m_propertyTable, - Path.Combine(folderPath, "configured.json"), null); + Path.Combine(folderPath, "configured.json"), null, out entryIds); } } @@ -154,9 +154,9 @@ public List ExportConfiguredReversalJson(string folderPath, string rever using (ReversalIndexActivator.ActivateReversalIndex(reversalWs, m_propertyTable, m_cache)) { var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, - out entryIds, ReversalType); - return LcmJsonGenerator.SavePublishedJsonWithStyles(entryIds, publicationDecorator, BatchSize, - configuration, m_propertyTable, Path.Combine(folderPath, $"reversal_{reversalWs}.json"), null); + out var entriesToSave, ReversalType); + return LcmJsonGenerator.SavePublishedJsonWithStyles(entriesToSave, publicationDecorator, BatchSize, + configuration, m_propertyTable, Path.Combine(folderPath, $"reversal_{reversalWs}.json"), null, out entryIds); } } @@ -351,13 +351,13 @@ public void ActivatePublication(string publication) public JObject ExportDictionaryContentJson(string siteName, IEnumerable templateFileNames, IEnumerable reversals, + int[] entryIds, string exportPath = null) { using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_propertyTable, m_mediator)) { - ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, DictionaryType); var clerk = m_propertyTable.GetValue("ActiveClerk", null); - return LcmJsonGenerator.GenerateDictionaryMetaData(siteName, templateFileNames, reversals, entriesToSave, exportPath, m_cache, clerk); + return LcmJsonGenerator.GenerateDictionaryMetaData(siteName, templateFileNames, reversals, entryIds, exportPath, m_cache, clerk); } } } diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 64eb9e56a3..e99dfc3819 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -519,11 +519,13 @@ public void InsertRawJson(string jsonContent) /// could index the entries after upload and before processing. /// public static List SavePublishedJsonWithStyles(int[] entriesToSave, DictionaryPublicationDecorator publicationDecorator, int batchSize, - DictionaryConfigurationModel configuration, PropertyTable propertyTable, string jsonPath, IThreadedProgress progress) + DictionaryConfigurationModel configuration, PropertyTable propertyTable, string jsonPath, IThreadedProgress progress, out int[] entryIds) { var entryCount = entriesToSave.Length; var cssPath = Path.ChangeExtension(jsonPath, "css"); var cache = propertyTable.GetValue("cache", null); + var entryIdsList = new List(); + // Don't display letter headers if we're showing a preview in the Edit tool or we're not sorting by headword using (var cssWriter = new StreamWriter(cssPath, false, Encoding.UTF8)) { @@ -581,6 +583,7 @@ public static List SavePublishedJsonWithStyles(int[] entriesToSave, Dict entryObject.displayXhtml = entryData.Item3.ToString(); jsonWriter.WriteRaw(entryObject.ToString()); jsonWriter.WriteRaw(","); + entryIdsList.Add(entryData.Item1.Hvo); } jsonWriter.WriteEndArray(); jsonWriter.Flush(); @@ -594,6 +597,8 @@ public static List SavePublishedJsonWithStyles(int[] entriesToSave, Dict cssWriter.Write(CssGenerator.GenerateCssFromConfiguration(configuration, readOnlyPropertyTable)); cssWriter.Flush(); + + entryIds = entryIdsList.ToArray(); return generatedEntries; } } diff --git a/Src/xWorks/UploadToWebonaryController.cs b/Src/xWorks/UploadToWebonaryController.cs index 105c4bf49b..7362a4760f 100644 --- a/Src/xWorks/UploadToWebonaryController.cs +++ b/Src/xWorks/UploadToWebonaryController.cs @@ -103,12 +103,12 @@ private void ExportDictionaryContent(string tempDirectoryToCompress, UploadToWeb webonaryView.UpdateStatus(xWorksStrings.ExportingEntriesToWebonaryCompleted); } - private JObject GenerateDictionaryMetadataContent(UploadToWebonaryModel model, + private JObject GenerateDictionaryMetadataContent(UploadToWebonaryModel model, int[] entryIds, IEnumerable templateFileNames, string tempDirectoryForExport) { return m_exportService.ExportDictionaryContentJson(model.SiteName, templateFileNames, model.Reversals.Where(kvp => model.SelectedReversals.Contains(kvp.Key)).Select(kvp => kvp.Value), - tempDirectoryForExport); + entryIds, tempDirectoryForExport); } internal static void CompressExportedFiles(string tempDirectoryToCompress, string zipFileToUpload, IUploadToWebonaryView webonaryView) @@ -572,18 +572,16 @@ public void UploadToWebonary(UploadToWebonaryModel model, IUploadToWebonaryView GenerateConfigurationTemplates(configuration, m_cache, tempDirectoryForExport); view.UpdateStatus(xWorksStrings.ksPreparingDataForWebonary); - var metadataContent = GenerateDictionaryMetadataContent(model, + int[] entryIds; + var entries = m_exportService.ExportConfiguredJson(tempDirectoryForExport, configuration, out entryIds); + var metadataContent = GenerateDictionaryMetadataContent(model, entryIds, templateFileNames, tempDirectoryForExport); view.UpdateStatus(xWorksStrings.ksWebonaryFinishedDataPrep); - var entries = - m_exportService.ExportConfiguredJson(tempDirectoryForExport, - configuration); var allRequestsSucceeded = PostEntriesToWebonary(model, view, entries, false); var reversalClerk = RecordClerk.FindClerk(m_propertyTable, "AllReversalEntries"); foreach (var selectedReversal in model.SelectedReversals) { - int[] entryIds; var writingSystem = model.Reversals[selectedReversal].WritingSystem; entries = m_exportService.ExportConfiguredReversalJson( tempDirectoryForExport, writingSystem, out entryIds, diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index 18e21fd0d1..52e1e68f99 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -1102,7 +1102,7 @@ public void SavePublishedJsonWithStyles_DisplayXhtmlPopulated() var results = LcmJsonGenerator.SavePublishedJsonWithStyles(new[] { testEntry.Hvo }, DefaultDecorator, 1, new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, - m_propertyTable, "test.json", null); + m_propertyTable, "test.json", null, out int[] _); var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""homographnumber"":""0"",""citationform"":[{""lang"":""fr"",""value"":""Citation""}], ""displayXhtml"":""
0Citation
""}"; @@ -1133,7 +1133,7 @@ public void SavePublishedJsonWithStyles_BatchingWorks() var results = LcmJsonGenerator.SavePublishedJsonWithStyles(new[] { testEntry.Hvo, testEntry2.Hvo, testEntry3.Hvo }, DefaultDecorator, testBatchSize, new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, - m_propertyTable, "test.json", null); + m_propertyTable, "test.json", null, out int[] _); Assert.That(results.Count, Is.EqualTo(2)); // 3 entries makes 2 batches at batchSize of 2 Assert.That(results[0].Count, Is.EqualTo(testBatchSize)); // one full batch of 2 Assert.That(results[1].Count, Is.EqualTo(1)); // one lonely entry in the last batch @@ -1156,12 +1156,29 @@ public void SavePublishedJsonWithStyles_HiddenMinorEntry_DoesNotThrow() ConfiguredXHTMLGeneratorTests.SetPublishAsMinorEntry(minorEntry, false); var result = LcmJsonGenerator.SavePublishedJsonWithStyles(new[] { minorEntry.Hvo }, - DefaultDecorator, 1, configModel, m_propertyTable, "test.json", null); + DefaultDecorator, 1, configModel, m_propertyTable, "test.json", null, out int[] _); Assert.AreEqual(1, result.Count, "batches"); Assert.AreEqual(0, result[0].Count, "entries"); } + [Test] + public void SavePublishedJsonWithStyles_MinorEntryNotPublished() + { + var configModel = ConfiguredXHTMLGeneratorTests.CreateInterestingConfigurationModel(Cache, m_propertyTable); + var mainEntry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); + var minorEntry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); + ConfiguredXHTMLGeneratorTests.CreateVariantForm(Cache, mainEntry, minorEntry); + ConfiguredXHTMLGeneratorTests.SetPublishAsMinorEntry(minorEntry, false); + + var result = LcmJsonGenerator.SavePublishedJsonWithStyles(new[] { mainEntry.Hvo, minorEntry.Hvo }, + DefaultDecorator, 10, configModel, m_propertyTable, "test.json", null, out int[] entryIds); + + Assert.AreEqual(1, result.Count, "batches"); + Assert.AreEqual(1, result[0].Count, "entries"); + Assert.AreEqual(result[0].Count, entryIds.Length); + } + [Test] public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGeneratesCorrectResult() { From 1da61dce6889570018fac700d4a9f52feed06b63 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 7 Dec 2023 16:35:07 -0500 Subject: [PATCH 028/415] LT-21424: Do lowercase matching for all occurrence indexes When trying to get a best guess do lowercase matching regardless of where the occurrence is in the segment. Also use LCM 11.0.0-beta0083. Change-Id: I7bb6f233bf74ab72bfec5e911bfd39e4959fa713 --- Build/mkall.targets | 2 +- Build/nuget-common/packages.config | 18 +++++++++--------- Src/LexText/Interlinear/InterlinVc.cs | 3 +-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Build/mkall.targets b/Build/mkall.targets index 5ce41487c4..6d50c20bb3 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -285,7 +285,7 @@ 5.2.0-beta0003 13.0.0-beta0076 9.4.0.1-beta - 11.0.0-beta0081 + 11.0.0-beta0083 70.1.123 2.5.13 diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index c3a295c2ac..be5a41057b 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -51,15 +51,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/Src/LexText/Interlinear/InterlinVc.cs b/Src/LexText/Interlinear/InterlinVc.cs index 7bb9ef5683..16ebcc3e8f 100644 --- a/Src/LexText/Interlinear/InterlinVc.cs +++ b/Src/LexText/Interlinear/InterlinVc.cs @@ -2487,8 +2487,7 @@ private void RecordGuessIfAvailable(AnalysisOccurrence occurrence) IAnalysis wag = occurrence.Analysis; IAnalysis wagGuess; // now record the guess in the decorator. - // Todo JohnT: if occurrence.Indx is 0, record using DefaultStartSentenceFlid. - if (GuessServices.TryGetBestGuess(occurrence, out wagGuess)) + if (GuessServices.TryGetBestGuess(occurrence, out wagGuess, false)) { SetObjProp(wag.Hvo, InterlinViewDataCache.AnalysisMostApprovedFlid, wagGuess.Hvo); SetInt(wagGuess.Analysis.Hvo, InterlinViewDataCache.OpinionAgentFlid, (int)GuessServices.GetOpinionAgent(wagGuess.Analysis)); From e3c50d36c91a6522a66a128c7b090787e321dbfd Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 29 Nov 2023 13:54:35 -0800 Subject: [PATCH 029/415] Modify the buildLocalLibraries.sh to work with the new Nuget world Change-Id: Idbbcf3ccabd4ad1201f3a53c769e89d61aec0944 --- Build/buildLocalLibraries.sh | 339 ++++++++++++++++++++++++----------- Build/mkall.targets | 59 +++--- 2 files changed, 269 insertions(+), 129 deletions(-) diff --git a/Build/buildLocalLibraries.sh b/Build/buildLocalLibraries.sh index 03471d3721..aaaeb21a1a 100755 --- a/Build/buildLocalLibraries.sh +++ b/Build/buildLocalLibraries.sh @@ -1,116 +1,253 @@ #!/bin/bash # script for building libpalaso, liblcm and chorus libraries locally for debugging FLEx -# You must also indicate that you are using local libraries or edit the LibraryDevelopment.properties file -# with the path to the library outputs (i.e. C:/libpalaso/output/Debug) - -########## Parameters ############ -# Edit these parameters according to the configurations on your machine -buildcommand="C:/Program Files (x86)/MSBuild/14.0/Bin/MSBuild.exe" -BUILD_CONFIG=Debug -########### End Parameters ############# - -set -e -o pipefail -PROGRAM="$(basename "$0")" - -copy_curl() { - echo "curl $2 <= $1" - curl -# -L -o $2 $1 +# Review: Do we also need to delete the nuget files out of packages + +libpalaso_dir="" +liblcm_dir="" +chorus_dir="" +mkall_targets_file="mkall.targets" +packages_dir="../packages" + +# dotnet pack result version regex +version_regex="\s*Successfully created package.*\.([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?)\.nupkg" + +# Function to display usage information +function display_usage { + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -p, --libpalaso Specify libpalaso directory path and delete specified files, then run 'dotnet pack'" + echo " -l, --liblcm Specify liblcm directory path and run 'dotnet pack'" + echo " -c, --chorus Specify chorus directory path and delete specified files, then run 'dotnet pack'" + echo " -v, --version Set version numbers for the selected library in the mkall.targets and packages.config (does not delete packages or run pack)" + echo " -h, --help Display this help message" + exit 1 } -printUsage() { - echo "buildLocalLibraries x86|x64 [PALASOROOT] [LIBLCMROOT] [CHORUSROOT] [BUILDCOMMAND]" +# Function to run 'dotnet pack' in the liblcm directory +function delete_and_pack_liblcm { + if [ -n "$liblcm_dir" ]; then + + # Check if the specified directory exists + if [ ! -d "$packages_dir" ]; then + echo "Error: The specified packages directory does not exist: $packages_dir" + exit 1 + fi + + if [ "$use_manual_version" == true ]; then + version_number="$manual_version" + else + echo "Deleting files starting with 'SIL.LCModel' in $packages_dir" + find "$packages_dir" -name 'SIL.LCModel*' -exec rm -f -r {} \; + + echo "Running 'dotnet pack' in the liblcm directory: $liblcm_dir" + pack_output=$(cd "$liblcm_dir" && dotnet pack) + + # Extract version number using regex + if [[ $pack_output =~ $version_regex ]]; then + version_number=${BASH_REMATCH[1]} + echo "Version number extracted from dotnet pack output: $version_number" + else + echo "Error: Unable to extract version number from dotnet pack output. (Maybe build failure or nothing needed building?)" + exit 1 + fi + copy_pdb_files "$liblcm_dir/artifacts/Debug/net461" + fi + + # Update LcmNugetVersion in mkall.targets + update_mkall_targets "LcmNugetVersion" "$version_number" + + # Update packages.config with extracted version + update_packages_config "SIL.LCModel" "$version_number" + + fi } -osName=`uname -s` - -if [ "$5" != "" ] -then - buildcommand="$5" -fi - -if [ "$1" == "x86" ] -then - libpalasoPlatform="Mixed Platforms" - liblcmPlatform="x86" - ICUBuildType="Win32" -elif [ "$1" == "x64" ] -then - libpalasoPlatform="x64" - liblcmPlatform="x64" - ICUBuildType="Win64" -else - printUsage - exit -fi +# Function to delete specified files in the chorus directory and run 'dotnet pack' +function delete_and_pack_chorus { + if [ -n "$chorus_dir" ]; then + + # Check if the specified directory exists + if [ ! -d "$packages_dir" ]; then + echo "Error: The specified packages directory does not exist: $packages_dir" + exit 1 + fi + + if [ "$use_manual_version" == true ]; then + version_number="$manual_version" + else + prefix="SIL.Chorus" + echo "Deleting files starting with specified prefix in $packages_dir" + + find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; + + echo "Running 'dotnet pack' in the chorus directory: $chorus_dir" + pack_output=$(cd "$chorus_dir" && dotnet pack) + + # Extract version number using regex + if [[ $pack_output =~ $version_regex ]]; then + version_number=${BASH_REMATCH[1]} + echo "Version number extracted from dotnet pack output: $version_number" + else + echo "Error: Unable to extract version number from dotnet pack output." + exit 1 + fi + copy_pdb_files "$chorus_dir/Output/Debug/net461" + fi + + # Update ChorusNugetVersion in mkall.targets + update_mkall_targets "ChorusNugetVersion" "$version_number" + # Update packages.config with extracted version + update_packages_config "SIL.Chorus" "$version_number" + fi +} -# Get the path to the libpalaso, chorus and LCModel cloned repositories on your machine -# Repositories are available at github.com/sillsdev -if [ "$2" == "" ] -then - read -p "Enter the full path to your local libpalaso repo: " libpalasoRepo -else - libpalasoRepo="$2" -fi -if [ "$3" == "" ] -then - read -p "Enter the full path to your local liblcm repo: " liblcmRepo -else - liblcmRepo="$3" -fi -if [ "$4" == "" ] -then - read -p "Enter the full path to your local chorus repo: " chorusRepo -else - chorusRepo="$4" -fi +# Function to delete specified files in the libpalaso directory and run 'dotnet pack' +function delete_and_pack_libpalaso { + if [ -n "$libpalaso_dir" ]; then + + # Check if the specified directory exists + if [ ! -d "$packages_dir" ]; then + echo "Error: The specified packages directory does not exist: $packages_dir" + exit 1 + fi + prefixes=("SIL.Core" "SIL.Windows" "SIL.DblBundle" "SIL.WritingSystems" "SIL.Dictionary" "SIL.Lift" "SIL.Lexicon" "SIL.Archiving") + if [ "$use_manual_version" == true ]; then + version_number="$manual_version" + else + echo "Deleting files starting with specified prefixes in $packages_dir" + + for prefix in "${prefixes[@]}"; do + find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; + done + + echo "Running 'dotnet pack' in the libpalaso directory: $libpalaso_dir" + pack_output=$(cd "$libpalaso_dir" && dotnet pack) + + # Extract version number using regex + if [[ $pack_output =~ $version_regex ]]; then + version_number=${BASH_REMATCH[1]} + echo "Version number extracted from dotnet pack output: $version_number" + else + echo "Error: Unable to extract version number from dotnet pack output." + exit 1 + fi + copy_pdb_files "$libpalaso_dir/output/Debug/net461" + fi + + # Update PalasoNugetVersion in mkall.targets + update_mkall_targets "PalasoNugetVersion" "$version_number" + + # Update packages.config with extracted version for each prefix + for prefix in "${prefixes[@]}"; do + update_packages_config "$prefix" "$version_number" + done + fi +} -############### build libpalaso ############# -cd ${libpalasoRepo}/build -if [[ ${osName} == "Linux" ]] -then - ./buildupdate.mono.sh - MONO=Mono - (. ../environ && "${buildcommand}" /target:build /verbosity:quiet /property:Configuration=$BUILD_CONFIG$MONO /property:Platform="${libpalasoPlatform}" Palaso.proj) -else - ./buildupdate.win.sh - MONO= - "${buildcommand}" /target:build /verbosity:quiet /property:Configuration=$BUILD_CONFIG /property:Platform="${libpalasoPlatform}" Palaso.proj -fi +# Function to update specified element in mkall.targets +function update_mkall_targets { + local element="$1" + local version_number="$2" + if [ -f "$mkall_targets_file" ]; then + echo "Updating $element in $mkall_targets_file to $version_number" + sed -i "s/<$element>.*<\/$element>/<${element}>$version_number<\/${element}>/" "$mkall_targets_file" + else + echo "Error: $mkall_targets_file not found." + exit 1 + fi +} +# Function to update packages.config with extracted version for a given package ID prefix +function update_packages_config { + local id_prefix="$1" + local version_number="$2" + local packages_config_file="nuget-common/packages.config" + if [ -f "$packages_config_file" ]; then + echo "Updating $packages_config_file with version $version_number for packages with ID starting with $id_prefix" + + # Use sed to modify lines starting with the specified package ID + sed -i 's/\(package id="'$id_prefix'[\.a-zA-Z0-9]*" \)version="[0-9\.]*[-a-zA-Z0-9]*"/\1version="'$version_number'"/' "$packages_config_file" + else + echo "Error: $packages_config_file not found." + exit 1 + fi +} +# Function to copy .pdb files from artifacts directory to the specified output directory +function copy_pdb_files { + local artifacts_dir="$1" + local output_dir="../Output/Debug" + local downloads_dir="../Downloads" + + # Check if the artifacts directory exists + if [ ! -d "$artifacts_dir" ]; then + echo "Error: The specified artifacts directory does not exist: $artifacts_dir" + exit 1 + fi + + if [ ! -d "$output_dir" ]; then + echo "Error: The output directory does not exist: $output_dir" + exit 1 + fi + + if [ ! -d "$downloads_dir" ]; then + echo "Error: The downloads directory does not exist: $downloads_dir" + exit 1 + fi + + # Copy .pdb files to the output directory + find "$artifacts_dir" -name '*.pdb' -exec cp {} "$output_dir" \; -exec cp {} "$downloads_dir" + + echo ".pdb files copied from $artifacts_dir to $output_dir and $downloads_dir" +} -copy_curl http://build.palaso.org/guestAuth/repository/download/Libraries_Icu4c${ICUBuildType}FieldWorksContinuous/latest.lastSuccessful/icudt54.dll ../output/${BUILD_CONFIG}$MONO/${Platform}/icudt54.dll -copy_curl http://build.palaso.org/guestAuth/repository/download/Libraries_Icu4c${ICUBuildType}FieldWorksContinuous/latest.lastSuccessful/icuin54.dll ../output/${BUILD_CONFIG}$MONO/${Platform}/icuin54.dll -copy_curl http://build.palaso.org/guestAuth/repository/download/Libraries_Icu4c${ICUBuildType}FieldWorksContinuous/latest.lastSuccessful/icuuc54.dll ../output/${BUILD_CONFIG}$MONO/${Platform}/icuuc54.dll -copy_curl http://build.palaso.org/guestAuth/repository/download/Libraries_Icu4c${ICUBuildType}FieldWorksContinuous/latest.lastSuccessful/icutu54.dll ../output/${BUILD_CONFIG}$MONO/${Platform}/icutu54.dll -copy_curl http://build.palaso.org/guestAuth/repository/download/Libraries_Icu4c${ICUBuildType}FieldWorksContinuous/latest.lastSuccessful/gennorm2.exe ../output/${BUILD_CONFIG}$MONO/${Platform}/gennorm2.exe - - -############### build liblcm ############## -cd $liblcmRepo -mkdir -p ${liblcmRepo}/lib/downloads -cp -r ${libpalasoRepo}/output/${BUILD_CONFIG}/* lib/downloads -if [[ ${osName} == "Linux" ]] -then - (. environ && "${buildcommand}" /target:Build /property:Configuration=$BUILD_CONFIG /property:Platform="${liblcmPlatform}" /property:UseLocalFiles=True LCM.sln) -else - "${buildcommand}" /target:Build /property:Configuration=$BUILD_CONFIG /property:Platform="${liblcmPlatform}" /property:UseLocalFiles=True LCM.sln +# Parse command-line options +while [[ $# -gt 0 ]]; do + case "$1" in + -p|--libpalaso) + libpalaso_dir="$2" + shift 2 + ;; + -l|--liblcm) + liblcm_dir="$2" + shift 2 + ;; + -c|--chorus) + chorus_dir="$2" + shift 2 + ;; + -v|--version) + manual_version="$2" + use_manual_version=true + shift 2 + ;; + -h|--help) + display_usage + ;; + *) + echo "Error: Unknown option '$1'" + display_usage + ;; + esac +done + +# Display usage if no options are provided +if [ -z "$libpalaso_dir" ] && [ -z "$liblcm_dir" ] && [ -z "$chorus_dir" ]; then + display_usage fi +# Display the provided directory paths +echo "libpalaso directory: $libpalaso_dir" +echo "liblcm directory: $liblcm_dir" +echo "chorus directory: $chorus_dir" +# Delete specified files in the libpalaso directory and run 'dotnet pack' +delete_and_pack_libpalaso -############### build chorus ############## -cd ${chorusRepo}/build -cp -a ${libpalasoRepo}/output/${BUILD_CONFIG}$MONO/* ../lib/${BUILD_CONFIG}$MONO -cp -a ${libpalasoRepo}/output/${BUILD_CONFIG}$MONO/${Platform}/* ../lib/${BUILD_CONFIG}$MONO -if [[ ${osName} == "Linux" ]] -then - ./TestBuild.sh $BUILD_CONFIG -else - ./buildupdate.win.sh - "${buildcommand}" /target:Compile /verbosity:quiet /property:Configuration=$BUILD_CONFIG Chorus.proj -fi - - +# Delete specified files in the liblcm directory and run 'dotnet pack' +delete_and_pack_liblcm -echo $(date +"%F %T") $PROGRAM: "Finished" +# Delete specified files in the chorus directory and run 'dotnet pack' +delete_and_pack_chorus -#End Script +echo $(date +"%F %T") "Local build and pack finished" +# print a hint for how to use local .pdb files in cyan +tput setaf 6; echo "Build FLEx with /p:UsingLocalLibraryBuild=true to keep the local .pdb files" \ No newline at end of file diff --git a/Build/mkall.targets b/Build/mkall.targets index 6d50c20bb3..e7c267f83f 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -479,17 +479,20 @@ $(IcuNugetVersion)runtimes/**/*.*true $(IcuNugetVersion)build/native/**/*.*true - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)contentFiles/**/*.* - $(LcmNugetVersion)tools/net461/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)contentFiles/**/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)lib/net461/*.* - $(LcmNugetVersion)lib/net461/*.* + + + + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)contentFiles/**/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)tools/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)contentFiles/**/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(LcmNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) 1.4.0lib/net45/*.*true 7.1.0-final.1.21458.1lib/net45/*.*true 1.2.5554lib/net/*.*true @@ -499,27 +502,27 @@ 4.7.3lib/net45/*.*true 4.4.0lib/netstandard2.0/*.*true - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) $(PalasoNugetVersion)contentFiles/any/any/*.*true - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) $(PalasoNugetVersion)build/**/*.*true - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) $(PalasoNugetVersion)contentFiles/any/any/*.*true - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) $(PalasoNugetVersion)build/Interop.WIA.dlltrue $(PalasoNugetVersion)build/x64/Interop.WIA.dlltrue - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) $(PalasoNugetVersion)build/*.*true $(PalasoNugetVersion)contentFiles/any/any/*.*true - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* - $(PalasoNugetVersion)lib/net461/*.* + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(PalasoNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) 4.5.4lib/net461/*.*true 4.6.0lib/netstandard2.0/*.*true 6.0.0lib/net461/*.* @@ -531,9 +534,9 @@ 1.0.0.39lib/net461/*.*true 1.0.0.39lib/net461/*.*true - $(ChorusNugetVersion)lib/net461/*.* - $(ChorusNugetVersion)lib/net461/*.* - $(ChorusNugetVersion)lib/net461/*.* + $(ChorusNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(ChorusNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) + $(ChorusNugetVersion)lib/net461/*.*$(UsingLocalLibraryBuild) 4.9.4lib/net45/*.*true 1.0.16lib/net461/*.* From 09665623872b2a36498d1bbd3835d2379c9783bb Mon Sep 17 00:00:00 2001 From: mark-sil Date: Tue, 12 Dec 2023 11:19:13 -0500 Subject: [PATCH 030/415] buildLocalLibraries.sh: fixed problem with copying pdb files The command to copy the pdb files was failing with the following error: find: missing argument to `-exec' Change-Id: I0cb868a87c80e468e24c96c4568d1cec9898a0e0 --- Build/buildLocalLibraries.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build/buildLocalLibraries.sh b/Build/buildLocalLibraries.sh index aaaeb21a1a..925db030e3 100755 --- a/Build/buildLocalLibraries.sh +++ b/Build/buildLocalLibraries.sh @@ -194,7 +194,7 @@ function copy_pdb_files { fi # Copy .pdb files to the output directory - find "$artifacts_dir" -name '*.pdb' -exec cp {} "$output_dir" \; -exec cp {} "$downloads_dir" + find "$artifacts_dir" -name '*.pdb' -exec cp {} "$output_dir" \; -exec cp {} "$downloads_dir" \; echo ".pdb files copied from $artifacts_dir to $output_dir and $downloads_dir" } From 3b7d4a9d7cfcee1edec5ee13168685781e02b293 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 4 Jan 2024 09:11:10 -0500 Subject: [PATCH 031/415] =?UTF-8?q?LT-21424:=20Check=20lowercase=20for=20p?= =?UTF-8?q?arsing=20and=20=E2=80=98Try=20a=20word=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we don’t get results for a word that contains uppercase characters, then try the lowercase word. Change-Id: I53c6ca897bdd76434ac37d3b4148fa4b9d877847 --- .../ParserCoreTests/ParseWorkerTests.cs | 230 ++++++++++++++++++ .../ParserCoreTests/ParserCoreTests.csproj | 1 + Src/LexText/ParserCore/ParserWorker.cs | 52 +++- 3 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs diff --git a/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs b/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs new file mode 100644 index 0000000000..46c86ce6a4 --- /dev/null +++ b/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs @@ -0,0 +1,230 @@ +// Copyright (c) 2024 SIL International +// This software is licensed under the LGPL, version 2.1 or later +// (http://www.gnu.org/licenses/lgpl-2.1.html) + +using System; +using NUnit.Framework; +using SIL.LCModel.Core.Text; +using SIL.LCModel.Core.WritingSystems; +using SIL.LCModel; +using SIL.LCModel.Infrastructure; +using XCore; +using System.Xml.Linq; +using SIL.ObjectModel; +using System.Collections.Generic; +using System.Linq; + +namespace SIL.FieldWorks.WordWorks.Parser +{ + [TestFixture] + public class ParseWorkerTests : MemoryOnlyBackendProviderTestBase + { + #region Data Members + private String m_taskDetailsString; + private IdleQueue m_idleQueue; + private CoreWritingSystemDefinition m_vernacularWS; + #endregion Data Members + + #region Non-test methods + private IWfiWordform FindOrCreateWordform(string form) + { + ILcmServiceLocator servLoc = Cache.ServiceLocator; + IWfiWordform wf = servLoc.GetInstance().GetMatchingWordform(m_vernacularWS.Handle, form); + if (wf == null) + { + UndoableUnitOfWorkHelper.Do("Undo create", "Redo create", m_actionHandler, + () => wf = servLoc.GetInstance().Create(TsStringUtils.MakeString(form, m_vernacularWS.Handle))); + } + return wf; + } + + protected IWfiWordform CheckAnalysisSize(string form, int expectedSize, bool isStarting) + { + IWfiWordform wf = FindOrCreateWordform(form); + int actualSize = wf.AnalysesOC.Count; + string msg = String.Format("Wrong number of {0} analyses for: {1}", isStarting ? "starting" : "ending", form); + Assert.AreEqual(expectedSize, actualSize, msg); + return wf; + } + + protected void ExecuteIdleQueue() + { + foreach (var task in m_idleQueue) + task.Delegate(task.Parameter); + m_idleQueue.Clear(); + } + + private void HandleTaskUpdate(TaskReport task) + { + m_taskDetailsString = task?.Details?.ToString(); + } + #endregion // Non-tests + + #region Setup and TearDown + public override void FixtureSetup() + { + base.FixtureSetup(); + m_vernacularWS = Cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem; + m_idleQueue = new IdleQueue {IsPaused = true}; + } + + public override void FixtureTeardown() + { + m_vernacularWS = null; + m_idleQueue.Dispose(); + m_idleQueue = null; + + base.FixtureTeardown(); + } + + public override void TestTearDown() + { + UndoAll(); + base.TestTearDown(); + } + + /// ------------------------------------------------------------------------------------ + /// + /// End the undoable UOW and Undo everything. + /// + /// ------------------------------------------------------------------------------------ + protected void UndoAll() + { + // Undo the UOW (or more than one of them, if the test made new ones). + while (m_actionHandler.CanUndo()) + m_actionHandler.Undo(); + + // Need to 'Commit' to clear out redo stack, + // since nothing is really saved. + m_actionHandler.Commit(); + } + #endregion Setup and TearDown + + #region Tests + [Test] + public void TryAWord() + { + XDocument lowerXDoc = new XDocument(new XComment("cats")); + var parserWorker = new ParserWorker(Cache, HandleTaskUpdate, m_idleQueue, null); + parserWorker.Parser = new TestParserClass(null, lowerXDoc); + + // SUT + parserWorker.TryAWord("Cats", false, null); + Assert.AreEqual(m_taskDetailsString, lowerXDoc.ToString()); + m_taskDetailsString = null; + + // SUT + parserWorker.TryAWord("cats", false, null); + Assert.AreEqual(m_taskDetailsString, lowerXDoc.ToString()); + } + + [Test] + public void UpdateWordform() + { + IWfiWordform catsLowerTest = CheckAnalysisSize("cats", 0, true); + IWfiWordform catsUpperTest = CheckAnalysisSize("Cats", 0, true); + ILexDb ldb = Cache.LanguageProject.LexDbOA; + + ParseResult lowerResult = null; + UndoableUnitOfWorkHelper.Do("Undo stuff", "Redo stuff", m_actionHandler, () => + { + // Noun + ILexEntry catN = Cache.ServiceLocator.GetInstance().Create(); + IMoStemAllomorph catNForm = Cache.ServiceLocator.GetInstance().Create(); + catN.AlternateFormsOS.Add(catNForm); + catNForm.Form.VernacularDefaultWritingSystem = TsStringUtils.MakeString("catn", m_vernacularWS.Handle); + IMoStemMsa catNMsa = Cache.ServiceLocator.GetInstance().Create(); + catN.MorphoSyntaxAnalysesOC.Add(catNMsa); + + lowerResult = new ParseResult(new[] + { + new ParseAnalysis(new[] + { + new ParseMorph(catNForm, catNMsa), + }) + }); + }); + + var parserWorker = new ParserWorker(Cache, HandleTaskUpdate, m_idleQueue, null); + parserWorker.Parser = new TestParserClass(lowerResult, null); + + // SUT + var bVal = parserWorker.UpdateWordform(catsUpperTest, ParserPriority.Low); + ExecuteIdleQueue(); + Assert.IsTrue(bVal); + CheckAnalysisSize("Cats", 1, false); + CheckAnalysisSize("cats", 0, false); + + // SUT + bVal = parserWorker.UpdateWordform(catsLowerTest, ParserPriority.Low); + ExecuteIdleQueue(); + Assert.IsTrue(bVal); + CheckAnalysisSize("Cats", 1, false); + CheckAnalysisSize("cats", 1, false); + } + #endregion // Tests + } + + /// + /// IParser class used for testing the ParseWorker. + /// This test class only returns results for lowercase words. Uppercase + /// words return empty results. + /// + public class TestParserClass : DisposableBase, IParser + { + private readonly ParseResult m_lowerResult; + private readonly XDocument m_lowerXDoc; + + public TestParserClass(ParseResult lowerResult, XDocument lowerXDoc) + { + m_lowerResult = lowerResult; + m_lowerXDoc = lowerXDoc; + } + + public bool IsUpToDate() { return true; } + + public void Update() { } + + public void Reset() { } + + /// + /// If the input word is lowercase then return the lowercase results. + /// Else return empty results. + /// + public ParseResult ParseWord(string word) + { + if (word == word.ToLower()) + { + return m_lowerResult; + } + else + { + return new ParseResult(Enumerable.Empty()); + } + } + + /// + /// If the input word is lowercase then return the lowercase results. + /// Else return empty results. + /// + public XDocument ParseWordXml(string word) + { + if (word == word.ToLower()) + { + return m_lowerXDoc; + } + else + { + return new XDocument(); + } + } + + /// + /// Not implemented. + /// + public XDocument TraceWordXml(string word, IEnumerable selectTraceMorphs) + { + return null; + } + } +} diff --git a/Src/LexText/ParserCore/ParserCoreTests/ParserCoreTests.csproj b/Src/LexText/ParserCore/ParserCoreTests/ParserCoreTests.csproj index d7738d63a2..fef7275253 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/ParserCoreTests.csproj +++ b/Src/LexText/ParserCore/ParserCoreTests/ParserCoreTests.csproj @@ -208,6 +208,7 @@ AssemblyInfoForTests.cs + Code diff --git a/Src/LexText/ParserCore/ParserWorker.cs b/Src/LexText/ParserCore/ParserWorker.cs index 1980597681..7015212615 100644 --- a/Src/LexText/ParserCore/ParserWorker.cs +++ b/Src/LexText/ParserCore/ParserWorker.cs @@ -109,7 +109,33 @@ public void TryAWord(string sForm, bool fDoTrace, int[] sSelectTraceMorphs) using (var task = new TaskReport(string.Format(ParserCoreStrings.ksTraceWordformX, sForm), m_taskUpdateHandler)) { string normForm = CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFD).Normalize(sForm); - task.Details = fDoTrace ? m_parser.TraceWordXml(normForm, sSelectTraceMorphs) : m_parser.ParseWordXml(normForm); + + // Get the lowercase word. + var cf = new CaseFunctions(m_cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem); + string normFormLower = CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFD).Normalize(cf.ToLower(sForm)); + + // The word is already lowercase, just return the xml. + if (normForm == normFormLower) + { + task.Details = fDoTrace ? m_parser.TraceWordXml(normForm, sSelectTraceMorphs) : m_parser.ParseWordXml(normForm); + } + // The word is uppercase, make a ParseWord() call for the uppercase word to determine if we should try to get + // the xml for the uppercase word or for the lowercase word. + else + { + ParseResult result = m_parser.ParseWord(normForm); + + // Parse of uppercase word was successful, get it's xml. + if (result.Analyses.Count > 0 && result.ErrorMessage == null) + { + task.Details = fDoTrace ? m_parser.TraceWordXml(normForm, sSelectTraceMorphs) : m_parser.ParseWordXml(normForm); + } + // Parse of uppercase word was not successful, try to get the xml for the lowercase word. + else + { + task.Details = fDoTrace ? m_parser.TraceWordXml(normFormLower, sSelectTraceMorphs) : m_parser.ParseWordXml(normFormLower); + } + } } } @@ -137,6 +163,22 @@ public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority) ParseResult result = m_parser.ParseWord( CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFD) .Normalize(form.Text.Replace(' ', '.'))); + + // If the parse of the original word was not successful,then try to parse the lowercase word. + if (result.Analyses.Count == 0 || result.ErrorMessage != null) + { + var cf = new CaseFunctions(m_cache.ServiceLocator.WritingSystemManager.Get(form.get_WritingSystemAt(0))); + string sLower = cf.ToLower(form.Text); + + // Try parsing the lowercase word if it is different from the original word. + if (sLower != form.Text) + { + result = m_parser.ParseWord( + CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFD) + .Normalize(sLower.Replace(' ', '.'))); + } + } + if (wordformHash == result.GetHashCode()) return false; @@ -159,5 +201,13 @@ public void ReloadGrammarAndLexicon() m_parser.Reset(); CheckNeedsUpdate(); } + + /// ------------------------------------------------------------------------------------ + /// + /// Should only be used for tests! + /// + /// ------------------------------------------------------------------------------------ + internal IParser Parser { set => m_parser = value; } + } } From 813ccd863fce743a7fcdbe49d84178d114e6f51a Mon Sep 17 00:00:00 2001 From: mark-sil Date: Tue, 9 Jan 2024 13:18:24 -0500 Subject: [PATCH 032/415] =?UTF-8?q?LT-21424:=20Fix=20=E2=80=98Approve=20Th?= =?UTF-8?q?roughout=20this=20Text=E2=80=99=20for=20capitals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An additional change was needed to make ‘Approve Throughout this Text’ do lowercase matching regardless of where the occurrence is in the segment. Change-Id: I649104e9292d0f89baf2c2224298b3a95325cb2d --- .../Interlinear/FocusBoxController.ApproveAndMove.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index 15b638ab1b..95c62f3aea 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -507,8 +507,6 @@ public void ApproveGuessOrChangesForWholeTextAndMoveNext(Command cmd) return; } SaveAnalysisForAnnotation(SelectedOccurrence, newAnalysisTree); - // determine if we confirmed on a sentence initial wordform to its lowercased form - bool fIsSentenceInitialCaseChange = oldWf != wf; if (wf != null) { ApplyAnalysisToInstancesOfWordform(newAnalysisTree.Analysis, oldWf, wf); @@ -535,10 +533,8 @@ private void ApplyAnalysisToInstancesOfWordform(IAnalysis newAnalysis, IWfiWordf foreach (var occ in navigator.GetAnalysisOccurrencesAdvancingInStText().ToList()) { // We certainly want to update any occurrence that exactly matches the wordform of the analysis we are confirming. - // If oldWordform is different, we are confirming a different case form from what occurred in the text, - // and we only confirm these if SelectedOccurrence and occ are both sentence-initial. - // We want to do that only for sentence-initial occurrences. - if (occ.Analysis == newWordform || (occ.Analysis == oldWordform && occ.Index == 0 && SelectedOccurrence.Index == 0)) + // If oldWordform is different, we are confirming a different case form from what occurred in the text. + if (occ.Analysis == newWordform || occ.Analysis == oldWordform) occ.Segment.AnalysesRS[occ.Index] = newAnalysis; } } From 94e5bc6963e0508fa781c8cbe11da71ce8aea8fe Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Tue, 21 Nov 2023 18:12:56 -0500 Subject: [PATCH 033/415] Refactored export + Initial word export w/o styles * Added an IFragment interface. * Refactored ConfiguredLcmGenerator, LcmJsonGenerator, and LcmXhtmlGenerator to pass IFragments rather than strings. * Setup an initial Word Generator without styles. * Generator tests expect results to be strings, not IFragments. Updated the generator tests to call ToString on all results. Change-Id: Iff4bd970ecfd5c181f07db04852ce583da5deac1 --- Src/xWorks/ConfiguredLcmGenerator.cs | 421 +++++----- Src/xWorks/DictionaryExportService.cs | 13 +- Src/xWorks/ExportDialog.cs | 2 +- Src/xWorks/ILcmContentGenerator.cs | 49 +- Src/xWorks/LcmJsonGenerator.cs | 158 ++-- Src/xWorks/LcmWordGenerator.cs | 759 ++++++++++++++++++ Src/xWorks/LcmXhtmlGenerator.cs | 185 +++-- Src/xWorks/StringFragment.cs | 70 ++ Src/xWorks/XhtmlDocView.cs | 4 +- Src/xWorks/xWorks.csproj | 2 + .../ConfiguredLcmUsfmGeneratorTests.cs | 44 +- .../ConfiguredXHTMLGeneratorReversalTests.cs | 24 +- .../ConfiguredXHTMLGeneratorTests.cs | 372 ++++----- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 6 +- .../xWorksTests/LcmJsonGeneratorTests.cs | 46 +- 15 files changed, 1554 insertions(+), 601 deletions(-) create mode 100644 Src/xWorks/LcmWordGenerator.cs create mode 100644 Src/xWorks/StringFragment.cs diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index a855bb83a0..6c4a9a5059 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2,36 +2,37 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Web.UI.WebControls; using ExCSS; +using Icu.Collation; using SIL.Code; -using SIL.LCModel.Core.Cellar; -using SIL.LCModel.Core.Text; -using SIL.LCModel.Core.WritingSystems; using SIL.FieldWorks.Common.Controls; -using SIL.FieldWorks.Filters; using SIL.FieldWorks.Common.Framework; -using SIL.LCModel.Core.KernelInterfaces; using SIL.FieldWorks.Common.FwUtils; using SIL.FieldWorks.Common.Widgets; +using SIL.FieldWorks.Filters; using SIL.LCModel; +using SIL.LCModel.Core.Cellar; +using SIL.LCModel.Core.KernelInterfaces; +using SIL.LCModel.Core.Text; +using SIL.LCModel.Core.WritingSystems; using SIL.LCModel.DomainImpl; using SIL.LCModel.DomainServices; using SIL.LCModel.Infrastructure; using SIL.LCModel.Utils; using SIL.PlatformUtilities; using SIL.Reporting; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Web.UI.WebControls; using XCore; using FileUtils = SIL.LCModel.Utils.FileUtils; using UnitType = ExCSS.UnitType; @@ -112,11 +113,47 @@ private static bool IsCanceling(IThreadedProgress progress) return progress != null && progress.IsCanceling; } + internal static StringBuilder GenerateLetterHeaderIfNeeded(ICmObject entry, + ref string lastHeader, Collator headwordWsCollator, + ConfiguredLcmGenerator.GeneratorSettings settings, RecordClerk clerk = null) + { + // If performance is an issue these dummies can be stored between calls + var dummyOne = + new Dictionary>(); + var dummyTwo = new Dictionary>(); + var dummyThree = new Dictionary>(); + var cache = settings.Cache; + var wsString = ConfiguredLcmGenerator.GetWsForEntryType(entry, cache); + var firstLetter = ConfiguredExport.GetLeadChar( + ConfiguredLcmGenerator.GetSortWordForLetterHead(entry, clerk), wsString, dummyOne, + dummyTwo, dummyThree, + headwordWsCollator, cache); + if (firstLetter != lastHeader && !string.IsNullOrEmpty(firstLetter)) + { + var headerTextBuilder = new StringBuilder(); + var upperCase = + new CaseFunctions(cache.ServiceLocator.WritingSystemManager.Get(wsString)) + .ToTitle(firstLetter); + var lowerCase = firstLetter.Normalize(); + headerTextBuilder.Append(upperCase); + if (lowerCase != upperCase) + { + headerTextBuilder.Append(' '); + headerTextBuilder.Append(lowerCase); + } + lastHeader = firstLetter; + + return headerTextBuilder; + } + + return new StringBuilder(""); + } + /// - /// This method uses a ThreadPool to execute the given individualActions in parallel. - /// It waits for all the individualActions to complete and then returns. - /// - internal static void SpawnEntryGenerationThreadsAndWait(List individualActions, IThreadedProgress progress) + /// This method uses a ThreadPool to execute the given individualActions in parallel. + /// It waits for all the individualActions to complete and then returns. + ///
+ internal static void SpawnEntryGenerationThreadsAndWait(List individualActions, IThreadedProgress progress) { var actionCount = individualActions.Count; //Note that our COM classes all implement the STA threading model, while the ThreadPool always uses MTA model threads. @@ -218,7 +255,7 @@ internal static string GetWsForEntryType(ICmObject entry, LcmCache cache) /// If it is a Minor Entry, first checks whether the entry should be published as a Minor Entry; then, generates XHTML for each applicable /// Minor Entry configuration node. ///
- public static string GenerateContentForEntry(ICmObject entryObj, DictionaryConfigurationModel configuration, + public static IFragment GenerateContentForEntry(ICmObject entryObj, DictionaryConfigurationModel configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index = -1) { if (IsMainEntry(entryObj, configuration)) @@ -227,23 +264,23 @@ public static string GenerateContentForEntry(ICmObject entryObj, DictionaryConfi var entry = (ILexEntry)entryObj; return entry.PublishAsMinorEntry ? GenerateContentForMinorEntry(entry, configuration, publicationDecorator, settings, index) - : string.Empty; + : settings.ContentGenerator.CreateFragment(); } - public static string GenerateContentForMainEntry(ICmObject entry, ConfigurableDictionaryNode configuration, + public static IFragment GenerateContentForMainEntry(ICmObject entry, ConfigurableDictionaryNode configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index) { if (configuration.DictionaryNodeOptions != null && ((ILexEntry)entry).ComplexFormEntryRefs.Any() && !IsListItemSelectedForExport(configuration, entry)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); return GenerateContentForEntry(entry, configuration, publicationDecorator, settings, index); } - private static string GenerateContentForMinorEntry(ICmObject entry, DictionaryConfigurationModel configuration, + private static IFragment GenerateContentForMinorEntry(ICmObject entry, DictionaryConfigurationModel configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index) { // LT-15232: show minor entries using only the last applicable Minor Entry node (not more than once) var applicablePart = configuration.Parts.Skip(1).LastOrDefault(part => IsListItemSelectedForExport(part, entry)); - return applicablePart == null ? string.Empty : GenerateContentForEntry(entry, applicablePart, publicationDecorator, settings, index); + return applicablePart == null ? settings.ContentGenerator.CreateFragment() : GenerateContentForEntry(entry, applicablePart, publicationDecorator, settings, index); } /// @@ -265,7 +302,7 @@ internal static bool IsMainEntry(ICmObject entry, DictionaryConfigurationModel c /// Generates content with the GeneratorSettings.ContentGenerator for an ICmObject for a specific ConfigurableDictionaryNode /// the configuration node must match the entry type - internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDictionaryNode configuration, + internal static IFragment GenerateContentForEntry(ICmObject entry, ConfigurableDictionaryNode configuration, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, int index = -1) { Guard.AgainstNull(settings, nameof(settings)); @@ -293,17 +330,18 @@ internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDict if (!configuration.IsEnabled) { - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } + var pieces = configuration.ReferencedOrDirectChildren .Select(config => GenerateContentForFieldByReflection(entry, config, publicationDecorator, settings)) - .Where(content => !string.IsNullOrEmpty(content)).ToList(); + .Where(content => content!=null && !string.IsNullOrEmpty(content.ToString())).ToList(); if (pieces.Count == 0) - return string.Empty; - var bldr = new StringBuilder(); + return settings.ContentGenerator.CreateFragment(); + var bldr = settings.ContentGenerator.CreateFragment(); using (var xw = settings.ContentGenerator.CreateWriter(bldr)) { var clerk = settings.PropertyTable.GetValue("ActiveClerk", null); @@ -313,8 +351,12 @@ internal static string GenerateContentForEntry(ICmObject entry, ConfigurableDict settings.ContentGenerator.AddEntryData(xw, pieces); settings.ContentGenerator.EndEntry(xw); xw.Flush(); - return CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFC) - .Normalize(bldr.ToString()); // All content should be in NFC (LT-18177) + + // Do not normalize the string if exporting to word doc--it is not needed and will cause loss of document styles + if (bldr is LcmWordGenerator.DocFragment) + return bldr; + + return settings.ContentGenerator.CreateFragment(CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFC).Normalize(bldr.ToString())); // All content should be in NFC (LT-18177) } } catch (ArgumentException) @@ -348,13 +390,13 @@ public static string GetClassNameAttributeForConfig(ConfigurableDictionaryNode c /// write out appropriate content using the settings parameter. /// /// We use a significant amount of boilerplate code for fields and subfields. Make sure you update both. - internal static string GenerateContentForFieldByReflection(object field, ConfigurableDictionaryNode config, + internal static IFragment GenerateContentForFieldByReflection(object field, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings, SenseInfo info = new SenseInfo(), bool fUseReverseSubField = false) { if (!config.IsEnabled) { - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } var cache = settings.Cache; var entryType = field.GetType(); @@ -371,12 +413,12 @@ internal static string GenerateContentForFieldByReflection(object field, Configu } if (field is ILexEntryRef) { - var ret = new StringBuilder(); + var ret = settings.ContentGenerator.CreateFragment(); foreach (var sense in (((field as ILexEntryRef).Owner as ILexEntry).AllSenses)) { ret.Append(GenerateContentForDefOrGloss(sense, config, settings)); } - return ret.ToString(); + return ret; } } if (config.FieldDescription == "CaptionOrHeadword") @@ -391,7 +433,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu // REVIEW: We have overloaded terms here, this is a C# class not a css class, consider a different name var customFieldOwnerClassName = GetClassNameForCustomFieldParent(config, settings.Cache); if (!GetPropValueForCustomField(field, config, cache, customFieldOwnerClassName, config.FieldDescription, ref propertyValue)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } else { @@ -413,7 +455,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu var msg = string.Format("Issue with finding {0} for {1}", config.FieldDescription, entryType); ShowConfigDebugInfo(msg, config); #endif - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } propertyValue = GetValueFromMember(property, field); GetSortedReferencePropertyValue(config, ref propertyValue, field); @@ -421,7 +463,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu // If the property value is null there is nothing to generate if (propertyValue == null) { - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } if (!string.IsNullOrEmpty(config.SubField)) { @@ -431,7 +473,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu if (!GetPropValueForCustomField(propertyValue, config, cache, ((ICmObject)propertyValue).ClassName, config.SubField, ref propertyValue)) { - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } } else @@ -445,14 +487,14 @@ internal static string GenerateContentForFieldByReflection(object field, Configu var msg = String.Format("Issue with finding (subField) {0} for (subType) {1}", subField, subType); ShowConfigDebugInfo(msg, config); #endif - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } propertyValue = subProp.GetValue(propertyValue, new object[] { }); GetSortedReferencePropertyValue(config, ref propertyValue, field); } // If the property value is null there is nothing to generate if (propertyValue == null) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } ICmFile fileProperty; ICmObject fileOwner; @@ -462,7 +504,7 @@ internal static string GenerateContentForFieldByReflection(object field, Configu switch (typeForNode) { case PropertyType.CollectionType: - return !IsCollectionEmpty(propertyValue) ? GenerateContentForCollection(propertyValue, config, publicationDecorator, field, settings, info) : string.Empty; + return !IsCollectionEmpty(propertyValue) ? GenerateContentForCollection(propertyValue, config, publicationDecorator, field, settings, info) : settings.ContentGenerator.CreateFragment(); case PropertyType.MoFormType: return GenerateContentForMoForm(propertyValue as IMoForm, config, settings); @@ -501,9 +543,10 @@ internal static string GenerateContentForFieldByReflection(object field, Configu : GenerateContentForAudioFile(fileProperty.ClassName, fileOwner.Guid.ToString(), srcAttr, LoudSpeaker, settings); } } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } - var bldr = new StringBuilder(GenerateContentForValue(field, propertyValue, config, settings)); + + var bldr = GenerateContentForValue(field, propertyValue, config, settings); if (config.ReferencedOrDirectChildren != null) { foreach (var child in config.ReferencedOrDirectChildren) @@ -511,10 +554,10 @@ internal static string GenerateContentForFieldByReflection(object field, Configu bldr.Append(GenerateContentForFieldByReflection(propertyValue, child, publicationDecorator, settings)); } } - return bldr.ToString(); + return bldr; } - private static string GenerateContentForGroupingNode(object field, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForGroupingNode(object field, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { if (config.ReferencedOrDirectChildren != null && config.ReferencedOrDirectChildren.Any(child => child.IsEnabled)) @@ -523,7 +566,7 @@ private static string GenerateContentForGroupingNode(object field, ConfigurableD return settings.ContentGenerator.GenerateGroupingNode(field, className, config, publicationDecorator, settings, (f, c, p, s) => GenerateContentForFieldByReflection(f, c, p, s)); } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } /// @@ -615,10 +658,10 @@ private static bool GetPropValueForCustomField(object fieldOwner, ConfigurableDi return true; } - private static string GenerateContentForVideoFile(string className, string mediaId, string srcAttribute, string caption, GeneratorSettings settings) + private static IFragment GenerateContentForVideoFile(string className, string mediaId, string srcAttribute, string caption, GeneratorSettings settings) { if (string.IsNullOrEmpty(srcAttribute) && string.IsNullOrEmpty(caption)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); // This creates a link that will open the video in the same window as the dictionary view/preview // refreshing will bring it back to the dictionary return settings.ContentGenerator.GenerateVideoLinkContent(className, GetSafeXHTMLId(mediaId), srcAttribute, caption); @@ -749,43 +792,45 @@ private static string GetClassNameForCustomFieldParent(ConfigurableDictionaryNod return parentNodeType.Name; } - private static string GenerateContentForPossibility(object propertyValue, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForPossibility(object propertyValue, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { if (config.ReferencedOrDirectChildren == null || !config.ReferencedOrDirectChildren.Any(node => node.IsEnabled)) - return string.Empty; - var bldr = new StringBuilder(); + return settings.ContentGenerator.CreateFragment(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var child in config.ReferencedOrDirectChildren) { var content = GenerateContentForFieldByReflection(propertyValue, child, publicationDecorator, settings); bldr.Append(content); } - if (bldr.Length > 0) + if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), className); + return settings.ContentGenerator.WriteProcessedObject(false, bldr, className); } - return string.Empty; + + // bldr is a fragment that is empty of text, since length = 0 + return bldr; } - private static string GenerateContentForPictureCaption(object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForPictureCaption(object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // todo: get sense numbers and captions into the same div and get rid of this if else - string content; + IFragment content; if (config.DictionaryNodeOptions != null) content = GenerateContentForStrings(propertyValue as IMultiString, config, settings); else content = GenerateContentForString(propertyValue as ITsString, config, settings); - if (!string.IsNullOrEmpty(content)) + if (!content.IsNullOrEmpty()) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); return settings.ContentGenerator.WriteProcessedObject(true, content, className); } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } - private static string GenerateContentForPicture(ICmFile pictureFile, ConfigurableDictionaryNode config, ICmObject owner, + private static IFragment GenerateContentForPicture(ICmFile pictureFile, ConfigurableDictionaryNode config, ICmObject owner, GeneratorSettings settings) { var srcAttribute = GenerateSrcAttributeFromFilePath(pictureFile, settings.UseRelativePaths ? "pictures" : null, settings); @@ -797,7 +842,7 @@ private static string GenerateContentForPicture(ICmFile pictureFile, Configurabl var ownerGuid = owner.Guid.ToString(); return settings.ContentGenerator.AddImage(className, srcAttribute, ownerGuid); } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } /// @@ -844,12 +889,12 @@ private static string GenerateSrcAttributeForMediaFromFilePath(string filename, return settings.UseRelativePaths ? filePath : new Uri(filePath).ToString(); } - private static string GenerateContentForDefOrGloss(ILexSense sense, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForDefOrGloss(ILexSense sense, ConfigurableDictionaryNode config, GeneratorSettings settings) { var wsOption = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; if (wsOption == null) throw new ArgumentException(@"Configuration nodes for MultiString fields whould have WritingSystemOptions", "config"); - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var option in wsOption.Options) { if (option.IsEnabled) @@ -864,20 +909,21 @@ private static string GenerateContentForDefOrGloss(ILexSense sense, Configurable } } - if (bldr.Length > 0) + if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); } - return string.Empty; + // bldr is a fragment that is empty of text, since length = 0 + return bldr; } - private static string GenerateContentForCaptionOrHeadword(ICmPicture picture, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForCaptionOrHeadword(ICmPicture picture, ConfigurableDictionaryNode config, GeneratorSettings settings) { var wsOption = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; if (wsOption == null) throw new ArgumentException(@"Configuration nodes for MultiString fields should have WritingSystemOptions", "config"); - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var option in wsOption.Options) { if (option.IsEnabled) @@ -891,9 +937,10 @@ private static string GenerateContentForCaptionOrHeadword(ICmPicture picture, Co } } } - if (bldr.Length > 0) - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), GetClassNameAttributeForConfig(config)); - return String.Empty; + if (bldr.Length() > 0) + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, GetClassNameAttributeForConfig(config)); + // bldr is a fragment that is empty of text, since length = 0 + return bldr; } internal static string CopyFileSafely(GeneratorSettings settings, string source, string relativeDestination) @@ -1302,11 +1349,11 @@ private static MemberInfo GetProperty(Type lookupType, ConfigurableDictionaryNod return propInfo; } - private static string GenerateContentForMoForm(IMoForm moForm, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForMoForm(IMoForm moForm, ConfigurableDictionaryNode config, GeneratorSettings settings) { // Don't export if there is no such data if (moForm == null) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); if (config.ReferencedOrDirectChildren != null && config.ReferencedOrDirectChildren.Any()) { throw new NotImplementedException("Children for MoForm types not yet supported."); @@ -1317,12 +1364,12 @@ private static string GenerateContentForMoForm(IMoForm moForm, ConfigurableDicti /// /// This method will generate the XHTML that represents a collection and its contents /// - private static string GenerateContentForCollection(object collectionField, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForCollection(object collectionField, ConfigurableDictionaryNode config, DictionaryPublicationDecorator pubDecorator, object collectionOwner, GeneratorSettings settings, SenseInfo info = new SenseInfo()) { // To be used for things like shared grammatical info - var sharedCollectionInfo = string.Empty; - var bldr = new StringBuilder(); + var sharedCollectionInfo = settings.ContentGenerator.CreateFragment(); + var bldr = settings.ContentGenerator.CreateFragment(); IEnumerable collection; if (collectionField is IEnumerable) { @@ -1396,14 +1443,14 @@ private static string GenerateContentForCollection(object collectionField, Confi } } - if (bldr.Length > 0 || sharedCollectionInfo.Length > 0) + if (bldr.Length() > 0 || sharedCollectionInfo.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; return config.DictionaryNodeOptions is DictionaryNodeSenseOptions ? - settings.ContentGenerator.WriteProcessedSenses(false, bldr.ToString(), className, sharedCollectionInfo) : - settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); + settings.ContentGenerator.WriteProcessedSenses(false, bldr, className, sharedCollectionInfo) : + settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } private static bool IsLexReferenceCollection(ConfigurableDictionaryNode config) @@ -1464,10 +1511,10 @@ private static bool IsPrimaryEntryReference(ConfigurableDictionaryNode config, o return false; } - private static string GenerateContentForEntryRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, + private static IFragment GenerateContentForEntryRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, ConfigurableDictionaryNode typeNode, bool isComplex) { - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); var lerCollection = collection.Cast().ToList(); // ComplexFormsNotSubentries is a filtered version of VisibleComplexFormBackRefs, so it doesn't have it's own VirtualOrdering. @@ -1495,11 +1542,11 @@ private static string GenerateContentForEntryRefCollection(ConfigurableDictionar foreach (var item in lerCollection) bldr.Append(GenerateCollectionItemContent(config, pubDecorator, item, collectionOwner, settings)); } - return bldr.ToString(); + return bldr; } private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryNode config, List lerCollection, object collectionOwner, DictionaryPublicationDecorator pubDecorator, - GeneratorSettings settings, StringBuilder bldr, ConfigurableDictionaryNode typeNode, bool isComplex) + GeneratorSettings settings, IFragment bldr, ConfigurableDictionaryNode typeNode, bool isComplex) { var lexEntryTypes = isComplex ? settings.Cache.LangProject.LexDbOA.ComplexEntryTypesOA.ReallyReallyAllPossibilities @@ -1543,7 +1590,7 @@ private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryN } private static void GenerateContentForSubentries(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject collectionOwner, - DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, StringBuilder bldr) + DictionaryPublicationDecorator pubDecorator, GeneratorSettings settings, IFragment bldr) { var listOptions = config.DictionaryNodeOptions as DictionaryNodeListOptions; var typeNode = config.ReferencedOrDirectChildren.FirstOrDefault(n => n.FieldDescription == LookupComplexEntryType); @@ -1622,8 +1669,8 @@ private static bool IsCollectionInNeedOfSorting(string fieldDescr) /// /// This method will generate the Content that represents a senses collection and its contents /// - private static string GenerateContentForSenses(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, - GeneratorSettings settings, IEnumerable senseCollection, SenseInfo info, ref string sharedGramInfo) + private static IFragment GenerateContentForSenses(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + GeneratorSettings settings, IEnumerable senseCollection, SenseInfo info, ref IFragment sharedGramInfo) { // Check whether all the senses have been excluded from publication. See https://jira.sil.org/browse/LT-15697. var filteredSenseCollection = new List(); @@ -1635,7 +1682,7 @@ private static string GenerateContentForSenses(ConfigurableDictionaryNode config filteredSenseCollection.Add(item); } if (filteredSenseCollection.Count == 0) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); var isSubsense = config.Parent != null && config.FieldDescription == config.Parent.FieldDescription; string lastGrammaticalInfo, langId; var isSameGrammaticalInfo = IsAllGramInfoTheSame(config, filteredSenseCollection, isSubsense, out lastGrammaticalInfo, out langId); @@ -1656,14 +1703,14 @@ private static string GenerateContentForSenses(ConfigurableDictionaryNode config // is determined, the answer for all sibling senses is the same as for the first sense in the collection. // So calculating outside the loop for performance. var isThisSenseNumbered = ShouldThisSenseBeNumbered(filteredSenseCollection[0], config, filteredSenseCollection); - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var item in filteredSenseCollection) { info.SenseCounter++; bldr.Append(GenerateSenseContent(config, publicationDecorator, item, isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); } settings.StylesGenerator.AddStyles(config); - return bldr.ToString(); + return bldr; } /// @@ -1714,12 +1761,12 @@ child.DictionaryNodeOptions is DictionaryNodeSenseOptions && !string.IsNullOrEmpty(((DictionaryNodeSenseOptions)child.DictionaryNodeOptions).NumberingStyle)); } - private static string InsertGramInfoBeforeSenses(ILexSense item, ConfigurableDictionaryNode gramInfoNode, + private static IFragment InsertGramInfoBeforeSenses(ILexSense item, ConfigurableDictionaryNode gramInfoNode, DictionaryPublicationDecorator publicationDecorator, GeneratorSettings settings) { var content = GenerateContentForFieldByReflection(item, gramInfoNode, publicationDecorator, settings); - if (string.IsNullOrEmpty(content)) - return string.Empty; + if (content.IsNullOrEmpty()) + return settings.ContentGenerator.CreateFragment(); return settings.ContentGenerator.GenerateGramInfoBeforeSensesContent(content); } @@ -1805,11 +1852,11 @@ private static bool CheckIfAllGramInfoTheSame(ConfigurableDictionaryNode config, return true; } - private static string GenerateSenseContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static IFragment GenerateSenseContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, object item, bool isThisSenseNumbered, GeneratorSettings settings, bool isSameGrammaticalInfo, SenseInfo info) { var senseNumberSpan = GenerateSenseNumberSpanIfNeeded(config, isThisSenseNumbered, ref info, settings); - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); if (config.ReferencedOrDirectChildren != null) { foreach (var child in config.ReferencedOrDirectChildren) @@ -1820,23 +1867,22 @@ private static string GenerateSenseContent(ConfigurableDictionaryNode config, Di } } } - if (bldr.Length == 0) - return string.Empty; + if (bldr.Length() == 0) + return bldr; var senseContent = bldr.ToString(); - bldr.Clear(); return settings.ContentGenerator.AddSenseData(senseNumberSpan, IsBlockProperty(config), ((ICmObject)item).Owner.Guid, senseContent, GetCollectionItemClassAttribute(config)); } - private static string GeneratePictureContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static IFragment GeneratePictureContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, object item, GeneratorSettings settings) { if (item is ICmPicture cmPic && !File.Exists(cmPic.PictureFileRA?.AbsoluteInternalPath)) { Logger.WriteEvent($"Skipping generating picture because there is no file at {cmPic.PictureFileRA?.AbsoluteInternalPath ?? "all"}"); - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); var contentGenerator = settings.ContentGenerator; using (var writer = contentGenerator.CreateWriter(bldr)) { @@ -1855,7 +1901,8 @@ private static string GeneratePictureContent(ConfigurableDictionaryNode config, // Note: this SenseNumber comes from a field in the FDO model (not generated based on a DictionaryNodeSenseOptions). // Should we choose in the future to generate the Picture's sense number using ConfiguredLcmGenerator based on a SenseOption, // we will need to pass the SenseOptions to this point in the call tree. - var captionBldr = new StringBuilder(); + + var captionBldr = settings.ContentGenerator.CreateFragment(); foreach (var child in config.ReferencedOrDirectChildren) { if (child.FieldDescription != "PictureFileRA") @@ -1865,26 +1912,26 @@ private static string GeneratePictureContent(ConfigurableDictionaryNode config, } } - if (captionBldr.Length != 0) + if (captionBldr.Length() != 0) { contentGenerator.WriteProcessedContents(writer, settings.ContentGenerator.AddImageCaption(captionBldr.ToString())); } writer.Flush(); } - return bldr.ToString(); + return bldr; } - private static string GenerateCollectionItemContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, object item, object collectionOwner, GeneratorSettings settings, ConfigurableDictionaryNode factoredTypeField = null) { if (item is IMultiStringAccessor) return GenerateContentForStrings((IMultiStringAccessor)item, config, settings); if ((config.DictionaryNodeOptions is DictionaryNodeListOptions && !IsListItemSelectedForExport(config, item, collectionOwner)) || config.ReferencedOrDirectChildren == null) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); var listOptions = config.DictionaryNodeOptions as DictionaryNodeListOptions; if (listOptions is DictionaryNodeListAndParaOptions) { @@ -1907,16 +1954,15 @@ private static string GenerateCollectionItemContent(ConfigurableDictionaryNode c bldr.Append(GenerateContentForFieldByReflection(item, child, publicationDecorator, settings)); } } - if (bldr.Length == 0) - return string.Empty; - var collectionContent = bldr.ToString(); - bldr.Clear(); + if (bldr.Length() == 0) + return bldr; + var collectionContent = bldr; return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), collectionContent); } private static void GenerateContentForLexRefCollection(ConfigurableDictionaryNode config, IEnumerable collection, ICmObject cmOwner, DictionaryPublicationDecorator pubDecorator, - GeneratorSettings settings, StringBuilder bldr) + GeneratorSettings settings, IFragment bldr) { // The collection of ILexReferences has already been sorted by type, // so we'll now group all the targets by LexRefType and sort their targets alphabetically before generating XHTML @@ -2001,23 +2047,23 @@ private static int CompareLexRefTargets(Tuple lhs, } /// Content for Targets and nodes, except Type, which is returned in ref string typeXHTML - private static string GenerateCrossReferenceChildren(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, List> referenceList, object collectionOwner, GeneratorSettings settings) { if (config.ReferencedOrDirectChildren == null) - return string.Empty; - var xBldr = new StringBuilder(); + return settings.ContentGenerator.CreateFragment(); + var xBldr = settings.ContentGenerator.CreateFragment(); using (var xw = settings.ContentGenerator.CreateWriter(xBldr)) { settings.ContentGenerator.BeginCrossReference(xw, IsBlockProperty(config), GetCollectionItemClassAttribute(config)); var targetInfo = referenceList.FirstOrDefault(); if (targetInfo == null) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); var reference = targetInfo.Item2; if (LexRefTypeTags.IsUnidirectional((LexRefTypeTags.MappingTypes)reference.OwnerType.MappingType) && LexRefDirection(reference, collectionOwner) == ":r") { - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } foreach (var child in config.ReferencedOrDirectChildren.Where(c => c.IsEnabled)) { @@ -2065,18 +2111,18 @@ private static string GenerateCrossReferenceChildren(ConfigurableDictionaryNode settings.ContentGenerator.EndCrossReference(xw); // config xw.Flush(); } - return xBldr.ToString(); + return xBldr; } - private static string GenerateSubentryTypeChild(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, + private static IFragment GenerateSubentryTypeChild(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ILexEntry subEntry, object mainEntryOrSense, GeneratorSettings settings) { if (!config.IsEnabled) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); var complexEntryRef = EntryRefForSubentry(subEntry, mainEntryOrSense); return complexEntryRef == null - ? string.Empty + ? settings.ContentGenerator.CreateFragment() : GenerateContentForCollection(complexEntryRef.ComplexEntryTypesRS, config, publicationDecorator, subEntry, settings); } @@ -2088,10 +2134,10 @@ private static ILexEntryRef EntryRefForSubentry(ILexEntry subEntry, object mainE return complexEntryRef; } - private static string GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryNode senseConfigNode, bool isThisSenseNumbered, ref SenseInfo info, GeneratorSettings settings) + private static IFragment GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryNode senseConfigNode, bool isThisSenseNumbered, ref SenseInfo info, GeneratorSettings settings) { if (!isThisSenseNumbered) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); var senseOptions = senseConfigNode.DictionaryNodeOptions as DictionaryNodeSenseOptions; @@ -2099,7 +2145,7 @@ private static string GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryNode info.HomographConfig = settings.Cache.ServiceLocator.GetInstance(); var senseNumberWs = string.IsNullOrEmpty(info.HomographConfig.WritingSystem) ? "en" : info.HomographConfig.WritingSystem; if (string.IsNullOrEmpty(formattedSenseNumber)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); return settings.ContentGenerator.GenerateSenseNumber(formattedSenseNumber, senseNumberWs); } @@ -2163,24 +2209,24 @@ private static string GetRomanSenseCounter(string numberingStyle, int senseNumbe return roman; } - private static string GenerateContentForICmObject(ICmObject propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForICmObject(ICmObject propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // Don't export if there is no such data if (propertyValue == null || config.ReferencedOrDirectChildren == null || !config.ReferencedOrDirectChildren.Any(node => node.IsEnabled)) - return string.Empty; - var bldr = new StringBuilder(); + return settings.ContentGenerator.CreateFragment(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var child in config.ReferencedOrDirectChildren) { var content = GenerateContentForFieldByReflection(propertyValue, child, null, settings); bldr.Append(content); } - if (bldr.Length > 0) + if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedObject(false, bldr.ToString(), className); + return settings.ContentGenerator.WriteProcessedObject(false, bldr, className); } - return string.Empty; + return bldr; } /// Write the class element in the span for an individual item in the collection @@ -2355,7 +2401,7 @@ private static bool IsCollectionEmpty(object collection) /// data to generate xhtml for /// /// - private static string GenerateContentForValue(object field, object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) + private static IFragment GenerateContentForValue(object field, object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { // If we're working with a headword, either for this entry or another one (Variant or Complex Form, etc.), store that entry's GUID // so we can generate a link to the main or minor entry for this headword. @@ -2403,13 +2449,13 @@ private static string GenerateContentForValue(object field, object propertyValue if (!TsStringUtils.IsNullOrEmpty((ITsString)propertyValue)) { var content = GenerateContentForString((ITsString)propertyValue, config, settings, guid); - if (!string.IsNullOrEmpty(content)) + if (!content.IsNullOrEmpty()) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; return settings.ContentGenerator.WriteProcessedCollection(false, content, className); } } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } if (propertyValue is IMultiStringAccessor) { @@ -2445,22 +2491,23 @@ private static string GenerateContentForValue(object field, object propertyValue } else if (propertyValue is IStText) { - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var para in (propertyValue as IStText).ParagraphsOS) { var stp = para as IStTxtPara; if (stp == null) continue; var contentPara = GenerateContentForString(stp.Contents, config, settings, guid); - if (!string.IsNullOrEmpty(contentPara)) + if (!contentPara.IsNullOrEmpty()) { bldr.Append(contentPara); - bldr.AppendLine(); + bldr.AppendBreak(); } } - if (bldr.Length > 0) - return settings.ContentGenerator.WriteProcessedCollection(true, bldr.ToString(), GetClassNameAttributeForConfig(config)); - return string.Empty; + if (bldr.Length() > 0) + return settings.ContentGenerator.WriteProcessedCollection(true, bldr, GetClassNameAttributeForConfig(config)); + // bldr is empty of text + return bldr; } else { @@ -2472,11 +2519,11 @@ private static string GenerateContentForValue(object field, object propertyValue { Debug.WriteLine(String.Format("What do I do with {0}?", propertyValue.GetType().Name)); } - return String.Empty; + return settings.ContentGenerator.CreateFragment(); } } - private static string WriteElementContents(object propertyValue, + private static IFragment WriteElementContents(object propertyValue, ConfigurableDictionaryNode config, GeneratorSettings settings) { var content = propertyValue.ToString(); @@ -2485,10 +2532,10 @@ private static string WriteElementContents(object propertyValue, return settings.ContentGenerator.AddProperty(GetClassNameAttributeForConfig(config), IsBlockProperty(config), content); } - return String.Empty; + return settings.ContentGenerator.CreateFragment(); } - private static string GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings) { return GenerateContentForStrings(multiStringAccessor, config, settings, Guid.Empty); @@ -2498,7 +2545,7 @@ private static string GenerateContentForStrings(IMultiStringAccessor multiString /// This method will generate an XHTML span with a string for each selected writing system in the /// DictionaryWritingSystemOptions of the configuration that also has data in the given IMultiStringAccessor /// - private static string GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid guid) { var wsOptions = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; @@ -2509,8 +2556,8 @@ private static string GenerateContentForStrings(IMultiStringAccessor multiString // TODO pH 2014.12: this can generate an empty span if no checked WS's contain data // gjm 2015.12 but this will help some (LT-16846) if (multiStringAccessor == null || multiStringAccessor.StringCount == 0) - return String.Empty; - var bldr = new StringBuilder(); + return settings.ContentGenerator.CreateFragment(); + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var option in wsOptions.Options) { if (!option.IsEnabled) @@ -2539,22 +2586,23 @@ private static string GenerateContentForStrings(IMultiStringAccessor multiString } var contentItem = GenerateWsPrefixAndString(config, settings, wsOptions, wsId, bestString, guid); - if (!String.IsNullOrEmpty(contentItem)) + if (!String.IsNullOrEmpty(contentItem.ToString())) bldr.Append(contentItem); } - if (bldr.Length > 0) + if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); } - return string.Empty; + // bldr is empty of text + return bldr; } /// /// This method will generate an XHTML span with a string for each selected writing system in the /// DictionaryWritingSystemOptions of the configuration that also has data in the given IMultiAccessorBase /// - private static string GenerateContentForVirtualStrings(ICmObject owningObject, IMultiAccessorBase multiStringAccessor, + private static IFragment GenerateContentForVirtualStrings(ICmObject owningObject, IMultiAccessorBase multiStringAccessor, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid guid) { var wsOptions = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; @@ -2562,7 +2610,8 @@ private static string GenerateContentForVirtualStrings(ICmObject owningObject, I { throw new ArgumentException(@"Configuration nodes for MultiString fields should have WritingSystemOptions", "config"); } - var bldr = new StringBuilder(); + + var bldr = settings.ContentGenerator.CreateFragment(); foreach (var option in wsOptions.Options) { if (!option.IsEnabled) @@ -2585,39 +2634,40 @@ private static string GenerateContentForVirtualStrings(ICmObject owningObject, I var requestedString = multiStringAccessor.get_String(wsId); bldr.Append(GenerateWsPrefixAndString(config, settings, wsOptions, wsId, requestedString, guid)); } - if (bldr.Length > 0) + if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.WriteProcessedCollection(false, bldr.ToString(), className); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); } - return String.Empty; + // bldr is empty of text + return bldr; } - private static string GenerateWsPrefixAndString(ConfigurableDictionaryNode config, GeneratorSettings settings, + private static IFragment GenerateWsPrefixAndString(ConfigurableDictionaryNode config, GeneratorSettings settings, DictionaryNodeWritingSystemOptions wsOptions, int wsId, ITsString requestedString, Guid guid) { if (String.IsNullOrEmpty(requestedString.Text)) { - return String.Empty; + return settings.ContentGenerator.CreateFragment(); } var wsName = settings.Cache.WritingSystemFactory.get_EngineOrNull(wsId).Id; var content = GenerateContentForString(requestedString, config, settings, guid, wsName); - if (String.IsNullOrEmpty(content)) - return String.Empty; + if (String.IsNullOrEmpty(content.ToString())) + return settings.ContentGenerator.CreateFragment(); return settings.ContentGenerator.GenerateWsPrefixWithString(settings, wsOptions.DisplayWritingSystemAbbreviations, wsId, content); } - private static string GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem = null) { return GenerateContentForString(fieldValue, config, settings, Guid.Empty, writingSystem); } - private static string GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, + private static IFragment GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, GeneratorSettings settings, Guid linkTarget, string writingSystem = null) { if (TsStringUtils.IsNullOrEmpty(fieldValue)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); if (writingSystem != null && writingSystem.Contains("audio")) { var fieldText = fieldValue.Text; @@ -2627,7 +2677,7 @@ private static string GenerateContentForString(ITsString fieldValue, Configurabl var srcAttr = GenerateSrcAttributeForMediaFromFilePath(fieldText, "AudioVisual", settings); var fileContent = GenerateContentForAudioFile(writingSystem, audioId, srcAttr, string.Empty, settings); var content = GenerateAudioWsContent(writingSystem, linkTarget, fileContent, settings); - if (!string.IsNullOrEmpty(content)) + if (!content.IsNullOrEmpty()) return settings.ContentGenerator.WriteProcessedObject(false, content, null); } } @@ -2640,7 +2690,7 @@ private static string GenerateContentForString(ITsString fieldValue, Configurabl { // use the passed in writing system unless null // otherwise use the first option from the DictionaryNodeWritingSystemOptions or english if the options are null - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); try { using (var writer = settings.ContentGenerator.CreateWriter(bldr)) @@ -2686,7 +2736,7 @@ private static string GenerateContentForString(ITsString fieldValue, Configurabl } writer.Flush(); - return bldr.ToString(); + return bldr; } } catch (Exception e) @@ -2711,11 +2761,11 @@ private static string GenerateContentForString(ITsString fieldValue, Configurabl return settings.ContentGenerator.GenerateErrorContent(badStrBuilder); } } - return string.Empty; + return settings.ContentGenerator.CreateFragment(); } - private static string GenerateAudioWsContent(string wsId, - Guid linkTarget, string fileContent, GeneratorSettings settings) + private static IFragment GenerateAudioWsContent(string wsId, + Guid linkTarget, IFragment fileContent, GeneratorSettings settings) { return settings.ContentGenerator.AddAudioWsContent(wsId, linkTarget, fileContent); } @@ -2776,11 +2826,11 @@ private static void GenerateRunWithPossibleLink(GeneratorSettings settings, stri /// Source location path for audio file /// Inner text for hyperlink (unicode icon for audio) /// - private static string GenerateContentForAudioFile(string classname, + private static IFragment GenerateContentForAudioFile(string classname, string audioId, string srcAttribute, string audioIcon, GeneratorSettings settings) { if (string.IsNullOrEmpty(audioId) && string.IsNullOrEmpty(srcAttribute) && string.IsNullOrEmpty(audioIcon)) - return string.Empty; + return settings.ContentGenerator.CreateFragment(); var safeAudioId = GetSafeXHTMLId(audioId); return settings.ContentGenerator.GenerateAudioLinkContent(classname, srcAttribute, audioIcon, safeAudioId); } @@ -2801,7 +2851,7 @@ private static bool IsUSFM(string candidate) return USFMTableStart.IsMatch(candidate); } - private static string GenerateTablesFromUSFM(ITsString usfm, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem) + private static IFragment GenerateTablesFromUSFM(ITsString usfm, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem) { var delimiters = new Regex(@"\\d\s").Matches(usfm.Text); @@ -2811,7 +2861,7 @@ private static string GenerateTablesFromUSFM(ITsString usfm, ConfigurableDiction return GenerateTableFromUSFM(usfm, config, settings, writingSystem); } - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); // If there is a table before the first title, generate it if (delimiters[0].Index > 0) { @@ -2824,12 +2874,12 @@ private static string GenerateTablesFromUSFM(ITsString usfm, ConfigurableDiction bldr.Append(GenerateTableFromUSFM(usfm.GetSubstring(delimiters[i].Index, lim), config, settings, writingSystem)); } - return bldr.ToString(); + return bldr; } - private static string GenerateTableFromUSFM(ITsString usfm, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem) + private static IFragment GenerateTableFromUSFM(ITsString usfm, ConfigurableDictionaryNode config, GeneratorSettings settings, string writingSystem) { - var bldr = new StringBuilder(); + var bldr = settings.ContentGenerator.CreateFragment(); using (var writer = settings.ContentGenerator.CreateWriter(bldr)) { // Regular expression to match the end of a string or a table row marker at the end of a title or row @@ -2867,7 +2917,7 @@ select match.Groups["rowcontents"] into rowContentsGroup settings.ContentGenerator.EndTable(writer); writer.Flush(); } - return bldr.ToString(); + return bldr; // TODO (Hasso) 2021.06: impl for JSON } @@ -3133,4 +3183,17 @@ public interface IFragmentWriter : IDisposable { void Flush(); } + + /// + /// A document fragment + /// + public interface IFragment + { + void Append(IFragment frag); + void AppendBreak(); + string ToString(); + int Length(); + bool IsNullOrEmpty(); + void Clear(); + } } diff --git a/Src/xWorks/DictionaryExportService.cs b/Src/xWorks/DictionaryExportService.cs index fa6c5981bd..d67d537df1 100644 --- a/Src/xWorks/DictionaryExportService.cs +++ b/Src/xWorks/DictionaryExportService.cs @@ -81,11 +81,12 @@ public void ExportDictionaryForWord(string filePath, DictionaryConfigurationMode { using (ClerkActivator.ActivateClerkMatchingExportType(DictionaryType, m_propertyTable, m_mediator)) { - configuration = configuration ?? new DictionaryConfigurationModel(DictionaryConfigurationListener.GetCurrentConfiguration(m_propertyTable, "Dictionary"), m_cache); - var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, DictionaryType); - if (progress != null) + configuration = configuration ?? new DictionaryConfigurationModel(DictionaryConfigurationListener.GetCurrentConfiguration(m_propertyTable, "Dictionary"), m_cache); + var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, DictionaryType); + if (progress != null) progress.Maximum = entriesToSave.Length; - // TODO: Create and add call to our Word content generator LcmWordGenerator(entriesToSave, publication, configuration, filePath, progress); + + LcmWordGenerator.SavePublishedDocx(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_propertyTable, filePath, progress); } } @@ -101,7 +102,9 @@ public void ExportReversalForWord(string filePath, string reversalWs, Dictionary if (progress != null) progress.Maximum = entriesToSave.Length; - // TODO: Create and add call to our Word content generator LcmWordGenerator(entriesToSave, publication, configuration, filePath, progress); + string reversalFilePath = filePath.Split(new string[] { ".docx"}, StringSplitOptions.None)[0] + "-" + reversalWs + ".docx"; + + LcmWordGenerator.SavePublishedDocx(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_propertyTable, reversalFilePath, progress); } } diff --git a/Src/xWorks/ExportDialog.cs b/Src/xWorks/ExportDialog.cs index 98898db732..7f63aedc80 100644 --- a/Src/xWorks/ExportDialog.cs +++ b/Src/xWorks/ExportDialog.cs @@ -891,7 +891,7 @@ private object ExportWordOpenXml(IThreadedProgress progress, object[] args) exportService.ExportDictionaryForWord(filePath, null, progress); foreach (var reversal in m_cache.ServiceLocator.GetInstance().AllInstances()) { - exportService.ExportReversalForWord(filePath, reversal.WritingSystem, new DictionaryConfigurationModel()); + exportService.ExportReversalForWord(filePath, reversal.WritingSystem); } return null; } diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 3c2067076b..0a75535915 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -14,18 +14,19 @@ namespace SIL.FieldWorks.XWorks /// public interface ILcmContentGenerator { - string GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, string content); - string GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId); - string WriteProcessedObject(bool isBlock, string elementContent, string className); - string WriteProcessedCollection(bool isBlock, string elementContent, string className); - string GenerateGramInfoBeforeSensesContent(string content); - string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, - Func childContentGenerator); - string AddSenseData(string senseNumberSpan, bool isBlockProperty, Guid ownerGuid, string senseContent, string className); - string AddCollectionItem(bool isBlock, string collectionItemClass, string content); - string AddProperty(string className, bool isBlockProperty, string content); - - IFragmentWriter CreateWriter(StringBuilder bldr); + IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content); + IFragment GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId); + IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className); + IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className); + IFragment GenerateGramInfoBeforeSensesContent(IFragment content); + IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, + Func childContentGenerator); + IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, string senseContent, string className); + IFragment AddCollectionItem(bool isBlock, string collectionItemClass, IFragment content); + IFragment AddProperty(string className, bool isBlockProperty, string content); + IFragment CreateFragment(); + IFragment CreateFragment(string str); + IFragmentWriter CreateWriter(IFragment fragment); void StartMultiRunString(IFragmentWriter writer, string writingSystem); void EndMultiRunString(IFragmentWriter writer); void StartBiDiWrapper(IFragmentWriter writer, bool rightToLeft); @@ -39,30 +40,30 @@ string GenerateGroupingNode(object field, string className, ConfigurableDictiona void AddToRunContent(IFragmentWriter writer, string txtContent); void AddLineBreakInRunContent(IFragmentWriter writer); void StartTable(IFragmentWriter writer); - void AddTableTitle(IFragmentWriter writer, string content); + void AddTableTitle(IFragmentWriter writer, IFragment content); void StartTableBody(IFragmentWriter writer); void StartTableRow(IFragmentWriter writer); - void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, string content); + void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, IFragment content); void EndTableRow(IFragmentWriter writer); void EndTableBody(IFragmentWriter writer); void EndTable(IFragmentWriter writer); void StartEntry(IFragmentWriter writer, string className, Guid entryGuid, int index, RecordClerk clerk); - void AddEntryData(IFragmentWriter writer, List pieces); + void AddEntryData(IFragmentWriter writer, List pieces); void EndEntry(IFragmentWriter writer); void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, string content); void BeginObjectProperty(IFragmentWriter writer, bool isBlockProperty, string getCollectionItemClassAttribute); void EndObject(IFragmentWriter writer); - void WriteProcessedContents(IFragmentWriter writer, string contents); - string AddImage(string classAttribute, string srcAttribute, string pictureGuid); - string AddImageCaption(string captionContent); - string GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs); - string AddLexReferences(bool generateLexType, string lexTypeContent, string className, string referencesContent, bool typeBefore); + void WriteProcessedContents(IFragmentWriter writer, IFragment contents); + IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid); + IFragment AddImageCaption(string captionContent); + IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs); + IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, string className, string referencesContent, bool typeBefore); void BeginCrossReference(IFragmentWriter writer, bool isBlockProperty, string className); void EndCrossReference(IFragmentWriter writer); - string WriteProcessedSenses(bool isBlock, string senseContent, string className, string sharedCollectionInfo); - string AddAudioWsContent(string wsId, Guid linkTarget, string fileContent); - string GenerateErrorContent(StringBuilder badStrBuilder); - string GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, + IFragment WriteProcessedSenses(bool isBlock, IFragment senseContent, string className, IFragment sharedCollectionInfo); + IFragment AddAudioWsContent(string wsId, Guid linkTarget, IFragment fileContent); + IFragment GenerateErrorContent(StringBuilder badStrBuilder); + IFragment GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, string caption); } } \ No newline at end of file diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index e99dfc3819..1b16b330b4 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -2,6 +2,13 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) +using Icu.Collation; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SIL.FieldWorks.Common.Controls; +using SIL.FieldWorks.Common.FwUtils; +using SIL.LCModel; +using SIL.LCModel.Utils; using System; using System.Collections.Generic; using System.Diagnostics; @@ -10,13 +17,6 @@ using System.Text; using System.Threading; using System.Web.UI.WebControls; -using Icu.Collation; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using SIL.FieldWorks.Common.Controls; -using SIL.FieldWorks.Common.FwUtils; -using SIL.LCModel; -using SIL.LCModel.Utils; using XCore; namespace SIL.FieldWorks.XWorks @@ -38,13 +38,13 @@ public LcmJsonGenerator(LcmCache cache) Cache = cache; } - public string GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, - bool displayAbbreviation, int wsId, string content) + public IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, + bool displayAbbreviation, int wsId, IFragment content) { return content; } - public string GenerateAudioLinkContent(string classname, string srcAttribute, string caption, + public IFragment GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId) { /*"audio": { @@ -55,10 +55,10 @@ public string GenerateAudioLinkContent(string classname, string srcAttribute, st dynamic audioObject = new JObject(); audioObject.id = safeAudioId; audioObject.src = srcAttribute.Replace("\\", "/"); // expecting relative paths only - return WriteProcessedObject(false, audioObject.ToString(), "value"); + return WriteProcessedObject(false, new StringFragment(audioObject.ToString()), "value"); } - public string GenerateVideoLinkContent(string className, string mediaId, + public IFragment GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, string caption) { @@ -66,64 +66,83 @@ public string GenerateVideoLinkContent(string className, string mediaId, dynamic videoObject = new JObject(); videoObject.id = mediaId; videoObject.src = srcAttribute.Replace("\\", "/"); // expecting relative paths only - return WriteProcessedObject(false, videoObject.ToString(), "value"); + return WriteProcessedObject(false, new StringFragment(videoObject.ToString()), "value"); } - public string WriteProcessedObject(bool isBlock, string elementContent, string className) + public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className) { - if (elementContent.StartsWith("{")) + if (elementContent.ToString().StartsWith("{")) return WriteProcessedContents(elementContent, className, string.Empty, ","); - return WriteProcessedContents(elementContent.TrimEnd(','), className, "{", "},"); + + ((StringFragment)elementContent).TrimEnd(','); + return WriteProcessedContents(elementContent, className, "{", "},"); } - public string WriteProcessedCollection(bool isBlock, string elementContent, string className) + public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className) { - return WriteProcessedContents(elementContent.TrimEnd(','), className, "[", "],"); + ((StringFragment)elementContent).TrimEnd(','); + return WriteProcessedContents(elementContent, className, "[", "],"); } - private string WriteProcessedContents(string elementContent, string className, string begin, string end) + private IFragment WriteProcessedContents(IFragment elementContent, string className, string begin, string end) { - if (string.IsNullOrEmpty(elementContent)) - return string.Empty; + if (elementContent.IsNullOrEmpty()) + return new StringFragment(); + var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); + if (!string.IsNullOrEmpty(className)) { bldr.Append($"\"{className}\": "); } bldr.Append(begin); - bldr.Append(elementContent.TrimEnd(',')); + bldr.Append(elementContent.ToString().TrimEnd(',')); bldr.Append(end); - return bldr.ToString(); + return fragment; } - public string GenerateGramInfoBeforeSensesContent(string content) + public IFragment GenerateGramInfoBeforeSensesContent(IFragment content) { // The grammatical info is generated as a json property on 'senses' - return $"{content}"; + return content; } - public string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, + public IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, - Func childContentGenerator) + Func childContentGenerator) { //TODO: Decide how to handle grouping nodes in the json api - return string.Empty; + return new StringFragment(); + } + + public IFragment AddCollectionItem(bool isBlock, string className, IFragment content) + { + var fragment = new StringFragment(); + fragment.StrBuilder.Append(content.IsNullOrEmpty() ? string.Empty : $"{{{content}}},"); + return fragment; + } + + public IFragment AddProperty(string className, bool isBlockProperty, string content) + { + var fragment = new StringFragment($"\"{className}\": \"{content}\","); + return fragment; } - public string AddCollectionItem(bool isBlock, string className, string content) + public IFragment CreateFragment() { - return string.IsNullOrEmpty(content)? string.Empty : $"{{{content}}},"; + return new StringFragment(); } - public string AddProperty(string className, bool isBlockProperty, string content) + public IFragment CreateFragment(string str) { - return $"\"{className}\": \"{content}\","; + return new StringFragment(str); } - public IFragmentWriter CreateWriter(StringBuilder bldr) + public IFragmentWriter CreateWriter(IFragment bldr) { - return new JsonFragmentWriter(bldr); + return new JsonFragmentWriter(((StringFragment)bldr).StrBuilder); } public void StartMultiRunString(IFragmentWriter writer, string writingSystem) @@ -192,7 +211,7 @@ public void StartTable(IFragmentWriter writer) // TODO: decide on a useful json representation for tables } - public void AddTableTitle(IFragmentWriter writer, string content) + public void AddTableTitle(IFragmentWriter writer, IFragment content) { // TODO: decide on a useful json representation for tables } @@ -207,7 +226,7 @@ public void StartTableRow(IFragmentWriter writer) // TODO: decide on a useful json representation for tables } - public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, string content) + public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, IFragment content) { // TODO: decide on a useful json representation for tables } @@ -255,7 +274,7 @@ public void StartEntry(IFragmentWriter xw, string className, Guid entryGuid, int jsonWriter.InsertRawJson(","); } - public void AddEntryData(IFragmentWriter xw, List pieces) + public void AddEntryData(IFragmentWriter xw, List pieces) { pieces.ForEach(((JsonFragmentWriter)xw).InsertRawJson); } @@ -269,7 +288,7 @@ public void AddCollection(IFragmentWriter writer, bool isBlockProperty, string c { ((JsonFragmentWriter)writer).InsertPropertyName(className); BeginArray(writer); - WriteProcessedContents(writer, content); + WriteProcessedContents(writer, new StringFragment(content)); EndArray(writer); } @@ -295,18 +314,25 @@ public void EndObject(IFragmentWriter writer) ((JsonFragmentWriter)writer).EndObject(); } - public void WriteProcessedContents(IFragmentWriter writer, string contents) + public void WriteProcessedContents(IFragmentWriter writer, IFragment contents) { - if (!string.IsNullOrEmpty(contents)) + if (!contents.IsNullOrEmpty()) { // Try not to double up, but do try to end content with a ',' for building up objects - ((JsonFragmentWriter)writer).InsertRawJson(contents.TrimEnd(',') + ","); + string curStr = contents.ToString(); + StringBuilder bldr = ((StringFragment)contents).StrBuilder; + bldr.Clear(); + bldr.Append(curStr.TrimEnd(',') + ","); + ((JsonFragmentWriter)writer).InsertRawJson(contents); } } - public string AddImage(string classAttribute, string srcAttribute, string pictureGuid) + public IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid) { var bldr = new StringBuilder(); + var fragment = new StringFragment(); + fragment.StrBuilder = bldr; + var sw = new StringWriter(bldr); using (var xw = new JsonTextWriter(sw)) { @@ -315,24 +341,26 @@ public string AddImage(string classAttribute, string srcAttribute, string pictur xw.WritePropertyName("src"); xw.WriteValue(srcAttribute.Replace("\\", "/")); // expecting relative paths only xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string AddImageCaption(string captionContent) + public IFragment AddImageCaption(string captionContent) { - return captionContent; + return new StringFragment(captionContent); } - public string GenerateSenseNumber(string formattedSenseNumber, string wsId) + public IFragment GenerateSenseNumber(string formattedSenseNumber, string wsId) { - return formattedSenseNumber; + return new StringFragment(formattedSenseNumber); } - public string AddLexReferences(bool generateLexType, string lexTypeContent, string className, + public IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, string className, string referencesContent, bool typeBefore) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); + var sw = new StringWriter(bldr); using (var xw = new JsonTextWriter(sw)) { @@ -341,7 +369,7 @@ public string AddLexReferences(bool generateLexType, string lexTypeContent, stri if (generateLexType && typeBefore) { xw.WritePropertyName("referenceType"); - xw.WriteValue(lexTypeContent); + xw.WriteValue(lexTypeContent.ToString()); } // Write an array with the references. xw.WritePropertyName("references"); @@ -352,13 +380,13 @@ public string AddLexReferences(bool generateLexType, string lexTypeContent, stri if (generateLexType && !typeBefore) { xw.WritePropertyName("referenceType"); - xw.WriteValue(lexTypeContent); + xw.WriteValue(lexTypeContent.ToString()); } xw.WriteEndObject(); xw.WriteRaw(","); xw.Flush(); - return bldr.ToString(); + return fragment; } } @@ -378,35 +406,37 @@ public void EndCrossReference(IFragmentWriter writer) /// /// Generates data for all senses of an entry. For better processing of json add sharedGramInfo as a separate property object /// - public string WriteProcessedSenses(bool isBlock, string sensesContent, string classAttribute, string sharedGramInfo) + public IFragment WriteProcessedSenses(bool isBlock, IFragment sensesContent, string classAttribute, IFragment sharedGramInfo) { - return $"{sharedGramInfo}{WriteProcessedCollection(isBlock, sensesContent, classAttribute)}"; + return new StringFragment($"{sharedGramInfo.ToString()}{WriteProcessedCollection(isBlock, sensesContent, classAttribute)}"); } - public string AddAudioWsContent(string wsId, Guid linkTarget, string fileContent) + public IFragment AddAudioWsContent(string wsId, Guid linkTarget, IFragment fileContent) { - return $"{{\"guid\":\"g{linkTarget}\",\"lang\":\"{wsId}\",{fileContent}}}"; + return new StringFragment($"{{\"guid\":\"g{linkTarget}\",\"lang\":\"{wsId}\",{fileContent}}}"); } - public string GenerateErrorContent(StringBuilder badStrBuilder) + public IFragment GenerateErrorContent(StringBuilder badStrBuilder) { // We can't generate comments in json - But adding unicode tofu in front of the cleaned bad string should help // highlight the problem content without crashing the user or blocking the rest of the export - return $"\\u+0FFF\\u+0FFF\\u+0FFF{badStrBuilder}"; + return new StringFragment($"\\u+0FFF\\u+0FFF\\u+0FFF{badStrBuilder}"); } - public string AddSenseData(string senseNumberSpan, bool isBlock, Guid ownerGuid, + public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlock, Guid ownerGuid, string senseContent, string className) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); + var sw = new StringWriter(bldr); using (var xw = new JsonTextWriter(sw)) { xw.WriteStartObject(); - if (!string.IsNullOrEmpty(senseNumberSpan)) + if (!senseNumberSpan.IsNullOrEmpty()) { xw.WritePropertyName("senseNumber"); - xw.WriteValue(senseNumberSpan); + xw.WriteValue(senseNumberSpan.ToString()); } xw.WritePropertyName("guid"); xw.WriteValue("g" + ownerGuid); @@ -414,7 +444,8 @@ public string AddSenseData(string senseNumberSpan, bool isBlock, Guid ownerGuid, xw.WriteEndObject(); xw.WriteRaw(","); xw.Flush(); - return bldr.ToString(); + + return fragment; } } @@ -503,6 +534,11 @@ public void InsertRawJson(string jsonContent) { jsonWriter.WriteRaw(jsonContent); } + + public void InsertRawJson(IFragment jsonContent) + { + jsonWriter.WriteRaw(jsonContent.ToString()); + } } /// diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs new file mode 100644 index 0000000000..31b1ec3635 --- /dev/null +++ b/Src/xWorks/LcmWordGenerator.cs @@ -0,0 +1,759 @@ +// Copyright (c) 2014-$year$ SIL International +// This software is licensed under the LGPL, version 2.1 or later +// (http://www.gnu.org/licenses/lgpl-2.1.html) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Web.UI.WebControls; +using DocumentFormat.OpenXml; +using Icu.Collation; +using SIL.FieldWorks.Common.Controls; +using SIL.FieldWorks.Common.FwUtils; +using SIL.LCModel; +using SIL.LCModel.Core.Text; +using SIL.LCModel.Utils; +using XCore; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; + +namespace SIL.FieldWorks.XWorks +{ + // This alias is to be used when creating Wordprocessing Text objects, + // since there are multiple different Text types across the packages we are using. + using WP = DocumentFormat.OpenXml.Wordprocessing; + + public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator + { + private LcmCache Cache { get; } + public LcmWordGenerator(LcmCache cache) + { + Cache = cache; + } + + public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecorator publicationDecorator, int batchSize, DictionaryConfigurationModel configuration, + XCore.PropertyTable propertyTable, string filePath, IThreadedProgress progress = null) + { + using (MemoryStream mem = new MemoryStream()) + { + DocFragment fragment = new DocFragment(mem); + + var entryCount = entryHvos.Length; + var cssPath = System.IO.Path.ChangeExtension(filePath, "css"); + var clerk = propertyTable.GetValue("ActiveClerk", null); + var cache = propertyTable.GetValue("cache", null); + + + var readOnlyPropertyTable = new ReadOnlyPropertyTable(propertyTable); + var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, true, true, System.IO.Path.GetDirectoryName(filePath), + ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropertyTable, configuration), System.IO.Path.GetFileName(cssPath) == "configured.css") + { ContentGenerator = new LcmWordGenerator(cache) }; + string lastHeader = null; + var entryContents = new Tuple[entryCount]; + var entryActions = new List(); + + // For every entry generate an action that will produce the doc fragment for that entry + for (var i = 0; i < entryCount; ++i) + { + var hvo = entryHvos.ElementAt(i); + var entry = cache.ServiceLocator.GetObject(hvo); + var entryStringBuilder = new DocFragment(); + entryContents[i] = new Tuple(entry, entryStringBuilder); + + var generateEntryAction = new Action(() => + { + var entryContent = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, publicationDecorator, settings); + entryStringBuilder.Append(entryContent); + if (progress != null) + progress.Position++; + }); + + entryActions.Add(generateEntryAction); + } + + // Generate all the document fragments (in parallel) + if (progress != null) + progress.Message = xWorksStrings.ksGeneratingDisplayFragments; + ConfiguredLcmGenerator.SpawnEntryGenerationThreadsAndWait(entryActions, progress); + + // Generate the letter headers and insert the document fragments into the full file + if (progress != null) + progress.Message = xWorksStrings.ksArrangingDisplayFragments; + var wsString = entryContents.Length > 0 ? ConfiguredLcmGenerator.GetWsForEntryType(entryContents[0].Item1, settings.Cache) : null; + var col = FwUtils.GetCollatorForWs(wsString); + + foreach (var entry in entryContents) + { + if (!entry.Item2.IsNullOrEmpty()) + { + IFragment letterHeader = GenerateLetterHeaderIfNeeded(entry.Item1, + ref lastHeader, col, settings, clerk); + + // If needed, append letter header to the word doc + if (!letterHeader.IsNullOrEmpty()) + fragment.Append(letterHeader); + + // TODO: when/how are styles applied to the letter headers? + // Append the entry to the word doc + fragment.Append(entry.Item2); + } + } + col?.Dispose(); + + if (progress != null) + progress.Message = xWorksStrings.ksGeneratingStyleInfo; + + // TODO: Generate styles + + fragment.DocFrag.Dispose(); + + // Create mode will overwrite any existing document at the given filePath; + // this is expected behavior that the user is warned about + // if they choose to export to an existing file. + using (FileStream fileStream = new FileStream(filePath, System.IO.FileMode.Create)) + { + mem.WriteTo(fileStream); + } + + } + } + + internal static IFragment GenerateLetterHeaderIfNeeded(ICmObject entry, ref string lastHeader, Collator headwordWsCollator, ConfiguredLcmGenerator.GeneratorSettings settings, RecordClerk clerk = null) + { + StringBuilder headerTextBuilder = ConfiguredLcmGenerator.GenerateLetterHeaderIfNeeded(entry, ref lastHeader, + headwordWsCollator, settings, clerk); + + return new DocFragment(headerTextBuilder.ToString()); + + } + + // ILcmStylesGenerator functions to implement + public void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyPropertyTable propertyTable) + { + //TODO + return; + } + + public string AddStyles(ConfigurableDictionaryNode node) + { + // TODO + return "TODO: AddStyles"; + } + + public void Init(ReadOnlyPropertyTable propertyTable) + { + // TODO + return; + } + + // ILcmContentGenerator functions to implement + public IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, + bool displayAbbreviation, int wsId, IFragment content) + { + return content; + } + + public IFragment GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId) + { + // TODO + return new DocFragment("TODO: generate audio link content"); + } + public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className) + { + return WriteProcessedContents(elementContent, className); + } + public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className) + { + return WriteProcessedContents(elementContent, className); + } + + private IFragment WriteProcessedContents(IFragment elementContent, string className) + { + // TODO: + // Currently we don't use the class name here. + // We don't want to write the class name to the document, + // but we may use it to set styles. + // Do we need write it here, for it to be used when determining style? + + if (elementContent.IsNullOrEmpty()) + return new DocFragment(); + + return elementContent; + } + + public IFragment GenerateGramInfoBeforeSensesContent(IFragment content) + { + return content; + } + public IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, + Func childContentGenerator) + { + //TODO: handle grouping nodes + return new DocFragment("TODO: handle grouping nodes"); + } + + public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, string senseContent, string className) + { + var senseCont = new DocFragment(senseContent); + // Add sense numbers if needed + if (!senseNumberSpan.IsNullOrEmpty()) + { + senseNumberSpan.Append(senseCont); + return senseNumberSpan; + } + + return senseCont; + } + public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, IFragment content) + { + return content.IsNullOrEmpty() ? new DocFragment() : content; + } + public IFragment AddProperty(string className, bool isBlockProperty, string content) + { + return new DocFragment(content); + } + + public IFragment CreateFragment() + { + return new DocFragment(); + } + + public IFragment CreateFragment(string str) + { + return new DocFragment(str); + } + + public class DocFragment : IFragment + { + internal MemoryStream MemStr { get; } + internal WordprocessingDocument DocFrag { get; } + internal Body DocBody { get; } + + /// + /// Constructs a new memory stream and creates an empty doc fragment + /// that writes to that stream. + /// + public DocFragment() + { + MemStr = new MemoryStream(); + DocFrag = WordprocessingDocument.Open(MemStr, true); + + // Initialize the document and body. + MainDocumentPart mainDocPart = DocFrag.AddMainDocumentPart(); + mainDocPart.Document = new WP.Document(); + DocBody = mainDocPart.Document.AppendChild(new WP.Body()); + } + + /// + /// Initializes the memory stream from the argument and creates + /// an empty doc fragment that writes to that stream. + /// + public DocFragment(MemoryStream str) + { + MemStr = str; + DocFrag = WordprocessingDocument.Open(str, true); + + // Initialize the document and body. + MainDocumentPart mainDocPart = DocFrag.AddMainDocumentPart(); + mainDocPart.Document = new WP.Document(); + DocBody = mainDocPart.Document.AppendChild(new WP.Body()); + } + + /// + /// Constructs a new memory stream and creates a non-empty doc fragment, + /// containing the given string, that writes to that stream. + /// + public DocFragment(string str) : this() + { + // Add text to the fragment + Paragraph para = DocBody.AppendChild(new Paragraph()); + Run run = para.AppendChild(new Run()); + + if (!string.IsNullOrEmpty(str)) + { + // For spaces to show correctly, set preserve spaces on the text element + WP.Text txt = new WP.Text(str); + txt.Space = SpaceProcessingModeValues.Preserve; + run.AppendChild(txt); + } + else + { + // For spaces to show correctly, set preserve spaces on the text element + WP.Text txt = new WP.Text(String.Empty); + txt.Space = SpaceProcessingModeValues.Preserve; + run.AppendChild(txt); + } + } + + /// + /// Returns content of the doc fragment as a string. + /// Be careful using this as document styles won't be preserved in a string. + /// This function is primarily used inside the Length() function + /// to check the length of text in a doc fragment. + /// + public override string ToString() + { + if (IsNullOrEmpty()) + { + return string.Empty; + } + + return ToString(DocBody); + } + + private string ToString(OpenXmlElement textBody) + { + var FragStr = new StringBuilder(); + foreach (var docSection in textBody.Elements()) + { + switch (docSection.LocalName) + { + // Text + case "t": + FragStr.Append(docSection.InnerText); + break; + + // Carriage return/page break + case "cr": + case "br": + FragStr.AppendLine(); + break; + + // Tab + case "tab": + FragStr.Append("\t"); + break; + + // Paragraph + case "p": + FragStr.Append(ToString(docSection)); + FragStr.AppendLine(); + break; + + default: + FragStr.Append(ToString(docSection)); + break; + } + } + + return FragStr.ToString(); + } + + public int Length() + { + string str = ToString(); + return str.Length; + } + + /// + /// Appends one doc fragment to another. + /// Use this if styles have already been applied + /// and if not attempting to append within the same paragraph. + /// + public void Append(IFragment frag) + { + + foreach (Paragraph para in ((DocFragment)frag).DocBody.OfType().ToList()) + { + // Append each paragraph. It is necessary to deep clone the node to maintain its tree of document properties + // and to ensure its styles will be maintained in the copy. + this.DocBody.AppendChild(para.CloneNode(true)); + } + } + + /// + /// Appends a new run inside the last paragraph of the doc fragment. + /// The run will be added to the end of the paragraph. + /// + public void Append(Run run) + { + // Deep clone the run b/c of its tree of properties and to maintain styles. + Paragraph lastPar = GetLastParagraph(); + lastPar.AppendChild(run.CloneNode(true)); + } + + public void AppendBreak() + { + // Breaks are automatically added between different paragraphs. + // A null op here is sufficient, unless we want line breaks within a paragraph or run. + } + + public void AppendSpace() + { + Run lastRun = GetLastRun(); + WP.Text txt = new WP.Text(" "); + // For spaces to show correctly, set preserve spaces on the text element + txt.Space = SpaceProcessingModeValues.Preserve; + lastRun.AppendChild(txt); + } + + public bool IsNullOrEmpty() + { + // A docbody with no children is an empty document. + if (MemStr == null || DocFrag == null || DocBody == null || !DocBody.HasChildren) + { + return true; + } + return false; + } + + public void Clear() + { + // Clear() method is not used for the word generator. + throw new NotImplementedException(); + } + + /// + /// Returns last paragraph in the document if it contains any, + /// else creates and returns a new paragraph. + /// + public Paragraph GetLastParagraph() + { + List parList = DocBody.OfType().ToList(); + if (parList.Any()) + return parList.Last(); + return GetNewParagraph(); + } + + /// + /// Creates and returns a new paragraph. + /// + public Paragraph GetNewParagraph() + { + Paragraph newPar = DocBody.AppendChild(new Paragraph()); + return newPar; + } + + /// + /// Returns last run in the document if it contains any, + /// else creates and returns a new run. + /// + private Run GetLastRun() + { + Paragraph lastPara = GetLastParagraph(); + List runList = lastPara.OfType().ToList(); + if (runList.Any()) + return runList.Last(); + + return lastPara.AppendChild(new Run()); + } + } + + public IFragmentWriter CreateWriter(IFragment frag) + { + return new WordFragmentWriter((DocFragment)frag); + } + + public class WordFragmentWriter : IFragmentWriter + { + public DocFragment WordFragment { get; } + private bool isDisposed; + internal Dictionary collatorCache = new Dictionary(); + + public WordFragmentWriter(DocFragment frag) + { + WordFragment = frag; + } + + public void Dispose() + { + foreach (var cachEntry in collatorCache.Values) + { + cachEntry?.Dispose(); + } + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + ". ****** "); + if (!isDisposed) + { + WordFragment.DocFrag.Dispose(); + WordFragment.MemStr.Dispose(); + isDisposed = true; + } + } + + public void Flush() + { + WordFragment.MemStr.Flush(); + } + + public void Insert(IFragment frag) + { + WordFragment.Append(frag); + } + + public void Insert(Run run) + { + WordFragment.Append(run); + } + + /// + /// Gets and returns the last run in the document, if one exists. + /// Otherwise, creates and returns a new run. + /// + public Run GetCurrentRun() + { + List runList = WordFragment.DocBody.Descendants().ToList(); + if (runList.Any()) + return runList.Last(); + + // If there is no run, create one + Run lastRun = WordFragment.DocBody.AppendChild(new Run()); + return lastRun; + } + + /// + /// Get the last paragraph in the doc if it contains any, + /// and add a new run to it. + /// Else, create and add the run to a new paragraph. + /// + public void CreateRun() + { + Paragraph curPar = WordFragment.GetLastParagraph(); + curPar.AppendChild(new Run()); + } + + /*public void AddStyleToRun() + { + // Grab the latest run and add a style + Run lastRun = GetCurrentRun(); + + // TODO: add style + + }*/ + } + + public void StartMultiRunString(IFragmentWriter writer, string writingSystem) + { + return; + } + public void EndMultiRunString(IFragmentWriter writer) + { + return; + } + public void StartBiDiWrapper(IFragmentWriter writer, bool rightToLeft) + { + return; + } + public void EndBiDiWrapper(IFragmentWriter writer) + { + return; + } + + /// + /// Creates a new run that is appended to the doc's last paragraph, + /// if one exists, or to a new paragraph otherwise. + /// + /// + /// + public void StartRun(IFragmentWriter writer, string writingSystem) + { + ((WordFragmentWriter)writer).CreateRun(); + } + public void EndRun(IFragmentWriter writer) + { + // Ending the run should be a null op for word writer + // Beginning a new run is sufficient to end the old run + // and to ensure new styles/content are applied to the new run. + } + public void SetRunStyle(IFragmentWriter writer, string css) + { + // Grab the current run and set its style + Run currentRun = ((WordFragmentWriter)writer).GetCurrentRun(); + + // TODO: get the style indicated by the string css class + // For now, use bold as a default style in order to test setting styles + + // If run already has properties, append the new style to run properties + if (currentRun.RunProperties != null) + currentRun.RunProperties.Append(new WP.Bold()); + + // Otherwise create run properties and append the style + else + { + currentRun.RunProperties = new WP.RunProperties(); + currentRun.RunProperties.Append(new WP.Bold()); + } + } + public void StartLink(IFragmentWriter writer, Guid destination) + { + return; + } + public void StartLink(IFragmentWriter writer, string externalDestination) + { + return; + } + public void EndLink(IFragmentWriter writer) + { + return; + } + + /// + /// Adds text to the last run in the doc, if one exists. + /// Creates a new run from the text otherwise. + /// + public void AddToRunContent(IFragmentWriter writer, string txtContent) + { + // For spaces to show correctly, set preserve spaces on the new text element + WP.Text txt = new WP.Text(txtContent); + txt.Space = SpaceProcessingModeValues.Preserve; + ((WordFragmentWriter)writer).GetCurrentRun() + .AppendChild(txt); + } + public void AddLineBreakInRunContent(IFragmentWriter writer) + { + ((WordFragmentWriter)writer).GetCurrentRun() + .AppendChild(new WP.Break()); + } + public void StartTable(IFragmentWriter writer) + { + return; + } + public void AddTableTitle(IFragmentWriter writer, IFragment content) + { + return; + } + public void StartTableBody(IFragmentWriter writer) + { + return; + } + public void StartTableRow(IFragmentWriter writer) + { + return; + } + public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, IFragment content) + { + return; + } + public void EndTableRow(IFragmentWriter writer) + { + return; + } + public void EndTableBody(IFragmentWriter writer) + { + return; + } + public void EndTable(IFragmentWriter writer) + { + return; + } + + public void StartEntry(IFragmentWriter writer, string className, Guid entryGuid, int index, RecordClerk clerk) + { + // Each entry starts a new paragraph, and any entry data added will be added within the same paragraph. + // Create a new paragraph for the entry. + DocFragment wordDoc = ((WordFragmentWriter)writer).WordFragment; + Paragraph entryPar = wordDoc.GetNewParagraph(); + + // TODO: paragraph-level styles can be set here. + } + public void AddEntryData(IFragmentWriter writer, List pieces) + { + // TODO: In theory the pieces in the list here are already styled--where are run-level styles first set? + foreach (IFragment piece in pieces) + { + WordFragmentWriter wordWriter = ((WordFragmentWriter)writer); + + // Each piece contains one run. These runs should reside in the same paragraph. + // So we append each run instead of the IFragments directly. + // Character formatting & style of each run will be preserved. + List runs = ((DocFragment)piece).DocBody.Descendants().ToList(); + foreach (Run run in runs) + { + // For spaces to show correctly, set preserve spaces on the text element + WP.Text txt = new WP.Text(" "); + txt.Space = SpaceProcessingModeValues.Preserve; + run.AppendChild(txt); + wordWriter.Insert(run); + } + } + } + public void EndEntry(IFragmentWriter writer) + { + return; + } + public void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, string content) + { + return; + } + public void BeginObjectProperty(IFragmentWriter writer, bool isBlockProperty, string getCollectionItemClassAttribute) + { + return; + } + public void EndObject(IFragmentWriter writer) + { + return; + } + public void WriteProcessedContents(IFragmentWriter writer, IFragment contents) + { + if (contents.IsNullOrEmpty()) + { + ((WordFragmentWriter)writer).Insert(contents); + } + } + public IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid) + { + return new DocFragment("TODO: add image"); + } + public IFragment AddImageCaption(string captionContent) + { + return new DocFragment("TODO: add image caption"); + } + public IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs) + { + // TODO: for styles, do we need to do something with the writing system? + return new DocFragment(formattedSenseNumber); + } + public IFragment AddLexReferences(bool generateLexType,IFragment lexTypeContent, string className, string referencesContent, bool typeBefore) + { + var fragment = new DocFragment(); + // Generate the factored ref types element (if before). + if (generateLexType && typeBefore) + { + fragment.Append(WriteProcessedObject(false, lexTypeContent, className)); + } + // Then add all the contents for the LexReferences (e.g. headwords) + fragment.Append(new DocFragment(referencesContent)); + // Generate the factored ref types element (if after). + if (generateLexType && !typeBefore) + { + fragment.Append(WriteProcessedObject(false, lexTypeContent, className)); + } + + return fragment; + } + public void BeginCrossReference(IFragmentWriter writer, bool isBlockProperty, string className) + { + return; + } + public void EndCrossReference(IFragmentWriter writer) + { + return; + } + public IFragment WriteProcessedSenses(bool isBlock, IFragment senseContent, string className, IFragment sharedGramInfo) + { + sharedGramInfo.Append(senseContent); + return sharedGramInfo; + } + public IFragment AddAudioWsContent(string wsId, Guid linkTarget, IFragment fileContent) + { + return new DocFragment("TODO: add audiows content"); + } + public IFragment GenerateErrorContent(StringBuilder badStrBuilder) + { + return new DocFragment($"Error generating content for string: '{badStrBuilder}'"); + } + public IFragment GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, + string caption) + { + return new DocFragment("TODO: generate video link content"); + } + } +} diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index 201ac843fc..6da6f8ded9 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -2,14 +2,6 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Web.UI.WebControls; -using System.Xml; using Icu.Collation; using SIL.FieldWorks.Common.Controls; using SIL.FieldWorks.Common.FwUtils; @@ -18,6 +10,14 @@ using SIL.LCModel.Core.WritingSystems; using SIL.LCModel.DomainServices; using SIL.LCModel.Utils; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Web.UI.WebControls; +using System.Xml; using XCore; namespace SIL.FieldWorks.XWorks @@ -162,39 +162,27 @@ private static bool IsExport(ConfiguredLcmGenerator.GeneratorSettings settings) internal static void GenerateLetterHeaderIfNeeded(ICmObject entry, ref string lastHeader, XmlWriter xhtmlWriter, Collator headwordWsCollator, ConfiguredLcmGenerator.GeneratorSettings settings, RecordClerk clerk = null) { - // If performance is an issue these dummy's can be stored between calls - var dummyOne = new Dictionary>(); - var dummyTwo = new Dictionary>(); - var dummyThree = new Dictionary>(); + StringBuilder headerTextBuilder = ConfiguredLcmGenerator.GenerateLetterHeaderIfNeeded(entry, ref lastHeader, + headwordWsCollator, settings, clerk); + var cache = settings.Cache; - var wsString = ConfiguredLcmGenerator.GetWsForEntryType(entry, settings.Cache); - var firstLetter = ConfiguredExport.GetLeadChar(ConfiguredLcmGenerator.GetSortWordForLetterHead(entry, clerk), wsString, dummyOne, dummyTwo, dummyThree, - headwordWsCollator, cache); - if (firstLetter != lastHeader && !string.IsNullOrEmpty(firstLetter)) + var wsString = ConfiguredLcmGenerator.GetWsForEntryType(entry, cache); + + if (headerTextBuilder.Length > 0) { - var headerTextBuilder = new StringBuilder(); - var upperCase = new CaseFunctions(cache.ServiceLocator.WritingSystemManager.Get(wsString)).ToTitle(firstLetter); - var lowerCase = firstLetter.Normalize(); - headerTextBuilder.Append(upperCase); - if (lowerCase != upperCase) - { - headerTextBuilder.Append(' '); - headerTextBuilder.Append(lowerCase); - } xhtmlWriter.WriteStartElement("div"); xhtmlWriter.WriteAttributeString("class", "letHead"); xhtmlWriter.WriteStartElement("span"); xhtmlWriter.WriteAttributeString("class", "letter"); xhtmlWriter.WriteAttributeString("lang", wsString); - var wsRightToLeft = cache.WritingSystemFactory.get_Engine(wsString).RightToLeftScript; + var wsRightToLeft = + cache.WritingSystemFactory.get_Engine(wsString).RightToLeftScript; if (wsRightToLeft != settings.RightToLeft) xhtmlWriter.WriteAttributeString("dir", wsRightToLeft ? "rtl" : "ltr"); xhtmlWriter.WriteString(TsStringUtils.Compose(headerTextBuilder.ToString())); xhtmlWriter.WriteEndElement(); xhtmlWriter.WriteEndElement(); xhtmlWriter.WriteWhitespace(Environment.NewLine); - - lastHeader = firstLetter; } } @@ -229,7 +217,7 @@ public static string GenerateEntryHtmlWithStyles(ICmObject entry, DictionaryConf exportSettings.StylesGenerator.AddGlobalStyles(configuration, new ReadOnlyPropertyTable(propertyTable)); GenerateOpeningHtml(previewCssPath, custCssPath, exportSettings, writer); var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, configuration, pubDecorator, exportSettings); - writer.WriteRaw(content); + writer.WriteRaw(content.ToString()); GenerateClosingHtml(writer); writer.Flush(); cssWriter.Write(((CssGenerator)exportSettings.StylesGenerator).GetStylesString()); @@ -349,13 +337,13 @@ private static void GenerateBottomOfPageButtonsIfNeeded(ConfiguredLcmGenerator.G GeneratePageButtons(settings, entryHvos, pageRanges, currentPageBounds, xhtmlWriter); } - public static List GenerateNextFewEntries(DictionaryPublicationDecorator publicationDecorator, int[] entryHvos, + public static List GenerateNextFewEntries(DictionaryPublicationDecorator publicationDecorator, int[] entryHvos, string currentConfigPath, ConfiguredLcmGenerator.GeneratorSettings settings, Tuple oldCurrentPageRange, Tuple oldAdjacentPageRange, int entriesToAddCount, out Tuple currentPage, out Tuple adjacentPage) { GenerateAdjustedPageButtons(entryHvos, settings, oldCurrentPageRange, oldAdjacentPageRange, entriesToAddCount, out currentPage, out adjacentPage); - var entries = new List(); + var entries = new List(); DictionaryConfigurationModel currentConfig = new DictionaryConfigurationModel(currentConfigPath, settings.Cache); if (oldCurrentPageRange.Item1 > oldAdjacentPageRange.Item1) { @@ -550,9 +538,10 @@ private static List> GetPageRanges(int[] entryHvos, int entriesP return pageRanges; } - public string GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, string content) + public IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { if (displayAbbreviation) @@ -563,15 +552,16 @@ public string GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSetting xw.WriteString(prefix); xw.WriteEndElement(); } - xw.WriteRaw(content); + xw.WriteRaw(content.ToString()); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId) + public IFragment GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("audio"); @@ -591,58 +581,62 @@ public string GenerateAudioLinkContent(string classname, string srcAttribute, st xw.WriteRaw(""); xw.WriteFullEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string WriteProcessedObject(bool isBlock, string elementContent, string className) + public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className) { return WriteProcessedContents(isBlock, elementContent, className); } - public string WriteProcessedCollection(bool isBlock, string elementContent, string className) + public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className) { return WriteProcessedContents(isBlock, elementContent, className); } - private string WriteProcessedContents(bool asBlock, string xmlContent, string className) + private IFragment WriteProcessedContents(bool asBlock, IFragment xmlContent, string className) { - if (!String.IsNullOrEmpty(xmlContent)) + if (!xmlContent.IsNullOrEmpty()) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement(asBlock ? "div" : "span"); if (!String.IsNullOrEmpty(className)) xw.WriteAttributeString("class", className); - xw.WriteRaw(xmlContent); + xw.WriteRaw(xmlContent.ToString()); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - return String.Empty; + return new StringFragment(); } - public string GenerateGramInfoBeforeSensesContent(string content) + public IFragment GenerateGramInfoBeforeSensesContent(IFragment content) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sharedgrammaticalinfo"); - xw.WriteRaw(content); + xw.WriteRaw(content.ToString()); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, + public IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, - Func childContentGenerator) + Func childContentGenerator) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); + using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("span"); @@ -656,17 +650,28 @@ public string GenerateGroupingNode(object field, string className, ConfigurableD } var innerContents = innerBuilder.ToString(); if (String.IsNullOrEmpty(innerContents)) - return String.Empty; + new StringFragment(); xw.WriteRaw(innerContents); xw.WriteEndElement(); // xw.Flush(); } - return bldr.ToString(); + return fragment; } - public IFragmentWriter CreateWriter(StringBuilder bldr) + public IFragment CreateFragment() { - return new XmlFragmentWriter(XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })); + return new StringFragment(); + } + + public IFragment CreateFragment(string str) + { + return new StringFragment(str); + } + + public IFragmentWriter CreateWriter(IFragment bldr) + { + var strbldr = (StringFragment)bldr; + return new XmlFragmentWriter(XmlWriter.Create(strbldr.StrBuilder, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })); } public class XmlFragmentWriter : IFragmentWriter @@ -769,11 +774,11 @@ public void StartTable(IFragmentWriter writer) ((XmlFragmentWriter)writer).Writer.WriteStartElement("table"); } - public void AddTableTitle(IFragmentWriter writer, string content) + public void AddTableTitle(IFragmentWriter writer, IFragment content) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("caption"); - xw.WriteRaw(content); + xw.WriteRaw(content.ToString()); xw.WriteEndElement(); // } @@ -791,7 +796,7 @@ public void StartTableRow(IFragmentWriter writer) /// Adds a <td> element (or <th> if isHead is true). /// If isRightAligned is true, adds the appropriate style element. /// - public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, string content) + public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, HorizontalAlign alignment, IFragment content) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement(isHead ? "th" : "td"); @@ -815,7 +820,7 @@ public void AddTableCell(IFragmentWriter writer, bool isHead, int colSpan, Horiz default: throw new ArgumentOutOfRangeException(nameof(alignment), alignment, null); } - xw.WriteRaw(content); + xw.WriteRaw(content.ToString()); // WriteFullEndElement in case there is no content xw.WriteFullEndElement(); // or } @@ -844,9 +849,12 @@ public void StartEntry(IFragmentWriter writer, string className, Guid entryGuid, xw.WriteAttributeString("id", "g" + entryGuid); } - public void AddEntryData(IFragmentWriter writer, List pieces) + public void AddEntryData(IFragmentWriter writer, List pieces) { - pieces.ForEach(((XmlFragmentWriter)writer).Writer.WriteRaw); + foreach (IFragment frag in pieces) + { + ((XmlFragmentWriter)writer).Writer.WriteRaw(frag.ToString()); + } } public void EndEntry(IFragmentWriter writer) @@ -877,16 +885,17 @@ public void EndObject(IFragmentWriter writer) ((XmlFragmentWriter)writer).Writer.WriteEndElement(); //
or } - public void WriteProcessedContents(IFragmentWriter writer, string contents) + public void WriteProcessedContents(IFragmentWriter writer, IFragment contents) { - ((XmlFragmentWriter)writer).Writer.WriteRaw(contents); + ((XmlFragmentWriter)writer).Writer.WriteRaw(contents.ToString()); } /// /// This is used as an id in the xhtml and must be unique. - public string AddImage(string classAttribute, string srcAttribute, string pictureGuid) + public IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("img"); @@ -895,13 +904,14 @@ public string AddImage(string classAttribute, string srcAttribute, string pictur xw.WriteAttributeString("id", "g" + pictureGuid); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string AddImageCaption(string captionContent) + public IFragment AddImageCaption(string captionContent) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("div"); @@ -909,13 +919,14 @@ public string AddImageCaption(string captionContent) xw.WriteRaw(captionContent); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs) + public IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement("span"); @@ -924,14 +935,15 @@ public string GenerateSenseNumber(string formattedSenseNumber, string senseNumbe xw.WriteString(formattedSenseNumber); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string AddLexReferences(bool generateLexType, string lexTypeContent, string className, + public IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, string className, string referencesContent, bool typeBefore) { var bldr = new StringBuilder(100); + var fragment = new StringFragment(bldr); // Generate the factored ref types element (if before). if (generateLexType && typeBefore) { @@ -945,7 +957,7 @@ public string AddLexReferences(bool generateLexType, string lexTypeContent, stri bldr.Append(WriteProcessedObject(false, lexTypeContent, className)); } - return bldr.ToString(); + return fragment; } public void BeginCrossReference(IFragmentWriter writer, bool isBlockProperty, string classAttribute) @@ -958,27 +970,31 @@ public void EndCrossReference(IFragmentWriter writer) EndObject(writer); } - public string WriteProcessedSenses(bool isBlock, string sensesContent, string classAttribute, string sharedGramInfo) + public IFragment WriteProcessedSenses(bool isBlock, IFragment sensesContent, string classAttribute, IFragment sharedGramInfo) { - return WriteProcessedObject(isBlock, sharedGramInfo + sensesContent, classAttribute); + sharedGramInfo.Append(sensesContent); + return WriteProcessedObject(isBlock, sharedGramInfo, classAttribute); } - public string AddAudioWsContent(string className, Guid linkTarget, string fileContent) + public IFragment AddAudioWsContent(string className, Guid linkTarget, IFragment fileContent) { // No additional wrapping required for the xhtml return fileContent; } - public string GenerateErrorContent(StringBuilder badStrBuilder) + public IFragment GenerateErrorContent(StringBuilder badStrBuilder) { - return $"\u0FFF\u0FFF\u0FFF"; + var fragment = new StringFragment(message); + return fragment; } - public string GenerateVideoLinkContent(string className, string mediaId, + public IFragment GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, string caption) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { // This creates a link that will open the video in the same window as the dictionary view/preview @@ -993,27 +1009,29 @@ public string GenerateVideoLinkContent(string className, string mediaId, xw.WriteRaw(""); xw.WriteFullEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string AddCollectionItem(bool isBlock, string collectionItemClass, string content) + public IFragment AddCollectionItem(bool isBlock, string collectionItemClass, IFragment content) { var bldr = new StringBuilder(); + var builder = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", collectionItemClass); - xw.WriteRaw(content); + xw.WriteRaw(content.ToString()); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return builder; } } - public string AddProperty(string className, bool isBlockProperty, string content) + public IFragment AddProperty(string className, bool isBlockProperty, string content) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { @@ -1022,20 +1040,21 @@ public string AddProperty(string className, bool isBlockProperty, string content xw.WriteString(content); xw.WriteEndElement(); xw.Flush(); - return bldr.ToString(); + return fragment; } } - public string AddSenseData(string senseNumberSpan, bool isBlock, Guid ownerGuid, + public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlock, Guid ownerGuid, string senseContent, string className) { var bldr = new StringBuilder(); + var fragment = new StringFragment(bldr); using (var xw = XmlWriter.Create(bldr, new XmlWriterSettings { ConformanceLevel = ConformanceLevel.Fragment })) { // Wrap the number and sense combination in a sensecontent span so that both can be affected by DisplayEachSenseInParagraph xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sensecontent"); - xw.WriteRaw(senseNumberSpan); + xw.WriteRaw(senseNumberSpan?.ToString()); xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", className); xw.WriteAttributeString("entryguid", "g" + ownerGuid); @@ -1043,7 +1062,7 @@ public string AddSenseData(string senseNumberSpan, bool isBlock, Guid ownerGuid, xw.WriteEndElement(); // element name for property xw.WriteEndElement(); // xw.Flush(); - return bldr.ToString(); + return fragment; } } diff --git a/Src/xWorks/StringFragment.cs b/Src/xWorks/StringFragment.cs new file mode 100644 index 0000000000..eda4916f7d --- /dev/null +++ b/Src/xWorks/StringFragment.cs @@ -0,0 +1,70 @@ +using SIL.FieldWorks.XWorks; +using System; +using System.Text; + +public class StringFragment : IFragment +{ + public StringBuilder StrBuilder { get; set; } + + public StringFragment() + { + StrBuilder = new StringBuilder(); + } + + // Create a new string fragment linked to an existing string builder. + public StringFragment(StringBuilder bldr) + { + StrBuilder = bldr; + } + + // Create a new string fragment containing the given string. + public StringFragment(string str) : this() + { + // Add text to the fragment + StrBuilder.Append(str); + } + + public override string ToString() + { + if (StrBuilder == null) + return String.Empty; + return StrBuilder.ToString(); + } + + public int Length() + { + if (StrBuilder == null) + return 0; + return StrBuilder.Length; + } + + public void Append(IFragment frag) + { + if (frag != null) + StrBuilder.Append(frag.ToString()); + } + + public void AppendBreak() + { + StrBuilder.AppendLine(); + } + + public void TrimEnd(char c) + { + string curString = StrBuilder.ToString(); + StrBuilder.Clear(); + StrBuilder.Append(curString.TrimEnd(c)); + } + + public bool IsNullOrEmpty() + { + if ((StrBuilder != null) && (!String.IsNullOrEmpty(StrBuilder.ToString()))) + return false; + return true; + } + + public void Clear() + { + StrBuilder?.Clear(); + } +} diff --git a/Src/xWorks/XhtmlDocView.cs b/Src/xWorks/XhtmlDocView.cs index a222d2e0f0..6f28d26888 100644 --- a/Src/xWorks/XhtmlDocView.cs +++ b/Src/xWorks/XhtmlDocView.cs @@ -370,7 +370,7 @@ private void AddMoreEntriesToPage(bool goingUp, GeckoWebBrowser browser) foreach (var entry in entries) { var entryElement = browserElement.OwnerDocument.CreateHtmlElement("div"); - var entryDoc = XDocument.Parse(entry); + var entryDoc = XDocument.Parse(entry.ToString()); foreach (var attribute in entryDoc.Root.Attributes()) { entryElement.SetAttribute(attribute.Name.ToString(), attribute.Value); @@ -402,7 +402,7 @@ private void AddMoreEntriesToPage(bool goingUp, GeckoWebBrowser browser) // Load entries above the lower navigation buttons foreach (var entry in entries) { - var entryElement = browserElement.OwnerDocument.CreateHtmlElement("div"); var entryDoc = XDocument.Parse(entry); + var entryElement = browserElement.OwnerDocument.CreateHtmlElement("div"); var entryDoc = XDocument.Parse(entry.ToString()); foreach (var attribute in entryDoc.Root.Attributes()) { entryElement.SetAttribute(attribute.Name.ToString(), attribute.Value); diff --git a/Src/xWorks/xWorks.csproj b/Src/xWorks/xWorks.csproj index a4d4c12beb..909643cc39 100644 --- a/Src/xWorks/xWorks.csproj +++ b/Src/xWorks/xWorks.csproj @@ -429,6 +429,7 @@ + @@ -514,6 +515,7 @@ Form + diff --git a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs index 5b5b060a16..ab07598513 100644 --- a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs @@ -140,7 +140,7 @@ public void NoUSFM_GeneratesPlainText() const string plainText = "Plain Text"; var entry = CreateInterestingLexEntry(plainText); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToUSFMField + "/span[@lang='en' and text()='" + plainText + "']", 1); AssertThatXmlIn.String(result).HasNoMatchForXpath("//table"); @@ -153,7 +153,7 @@ public void NoLeadingUSFM_GeneratesPlainText() const string plainText = "Plain Text\n\\d ignore me"; var entry = CreateInterestingLexEntry(plainText); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasNoMatchForXpath("//table"); AssertIsGood(result); } @@ -165,7 +165,7 @@ public void LeadingTitle_GeneratesTable() const string titleUSFM = @"\d " + title; var entry = CreateInterestingLexEntry(titleUSFM); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToTitle + "[@lang='en' and text()='" + title + "']", 1); AssertIsGood(result); @@ -176,7 +176,7 @@ public void LeadingTableRow_GeneratesTable() { var entry = CreateInterestingLexEntry("\\tr\n"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertIsGood(result); } @@ -186,7 +186,7 @@ public void TitleAndTableRow_GeneratesBoth() { var entry = CreateInterestingLexEntry(@"\d title \tr \tc "); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( XPathToTitle + "[@lang='en' and text()='title']", 1); @@ -202,7 +202,7 @@ public void NoGapNoContentTitleAndRow_DoesNotThrow() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = (ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)).ToString()); // Verify that the field is in the results AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField, 1); @@ -218,7 +218,7 @@ public void WhitespaceOnlyBetweenTitleAndRow_DoesNotThrow() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = (ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)).ToString()); // Verify that the partially-typed table is in the results AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToTitle + "[text()='" + TR + "']", 1); @@ -233,7 +233,7 @@ public void MissingSpaces_NoCells() var entry = CreateInterestingLexEntry(almostTable); var result = string.Empty; // SUT - Assert.DoesNotThrow(() => result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)); + Assert.DoesNotThrow(() => result = (ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings)).ToString()); // Verify that the field is in the results AssertThatXmlIn.String(result).HasNoMatchForXpath("//th"); @@ -256,7 +256,7 @@ public void ManyRowsAndCells() var entry = CreateInterestingLexEntry($"{TR} {TC}1 {a1}\t{TC}2 {a2} \t \r\n{TC}3 {a3} {TR}" + $"\t{TC}1 {b1} {TC}2 {b2} {TC}3 {b3} {TR} {TC}1 {c1} {TC}2 {c2} {TC}3 {c3}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(XPathToTitle); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToRow, 3); @@ -287,7 +287,7 @@ public void EmptyCell() const string a3 = "ty"; var entry = CreateInterestingLexEntry($"{TR} {TC}1 {a1}\t{TC}2 {TC}3 {a3}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); const string xpathToA = XPathToRow + "/td[span[@lang='en' and text()='" + a1 + "']]/following-sibling::td[not(node())]/following-sibling::td[span[text()='" + a3 + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -300,7 +300,7 @@ public void EmptyCells() { var entry = CreateInterestingLexEntry($"{TR} {TC}1 {TC}2\r\n{TC}3\t{TC}4 "); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); const string xpathToA = XPathToRow + "/td[not(node())]/following-sibling::td[not(node())]/following-sibling::td[not(node())]/following-sibling::td[not(node())]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -316,7 +316,7 @@ public void TableHeading_GeneratesTableHeader() const string b2h = "not normally expected, but not forbidden"; var entry = CreateInterestingLexEntry($"{TR} {TH}1 {a1h} {TC}2\r{a2c}\n{TR} {TC}1 {b1c} {TH}2 {b2h}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); const string xpathToA = XPathToRow + "/th[span[@lang='en' and text()='" + a1h + "']]/following-sibling::td[span[@lang='en' and text()='" + a2c + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -340,7 +340,7 @@ public void TableCellAlignment() var entry = CreateInterestingLexEntry($"{TR} {TH}r1 {a1} {TH}2\r{a2}\n{TR} {TC}1 {b1} {TC}r2 {b2} " + $"{TR} {TH}c1 {c1} {TH}l2\r{c2}\n{TR} {TC}l1 {d1} {TC}c2 {d2}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); const string xpathToA = XPathToRow + "/th[@style='text-align: right;' and span[@lang='en' and text()='" + a1 + "']]/following-sibling::th[not(@style) and span[@lang='en' and text()='" + a2 + "']]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathToA, 1); @@ -371,7 +371,7 @@ public void MultipleTables() $@"\d {title1} {TR} {TC}1 {la1} \d {title2} {TR} {TH}1 {za1} {TH}2 {za2} {TR} {TC}1 {zb1} {TC}2 {zb2} \d {TR} {TH}1 {loneCell}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "/table", 3); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToTitle, 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToRow, 4); // 1 + 2 + 1 = 4 @@ -385,7 +385,7 @@ public void CellRange(int min, int lim) { var entry = CreateInterestingLexEntry($"{TR} {TC}{min}-{lim} home on the range"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); // +1 because the range includes both ends AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath($"{XPathToRow}/td[@colspan='{lim - min + 1}']", 1); AssertIsGood(result); @@ -401,7 +401,7 @@ public void CellRange_None_NoColSpan(string range) const string content = "seldom"; var entry = CreateInterestingLexEntry($"{TR} {TC}{range} {content}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath($"{XPathToRow}/td[not(@colspan)]/span[text()='{content}']", 1); AssertIsGood(result); } @@ -412,7 +412,7 @@ public void BadUSFM_RowWithoutCells() const string junk = "no cells"; var entry = CreateInterestingLexEntry($"{TR} {junk}"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); var expected = string.Format(xWorksStrings.InvalidUSFM_TextAfterTR, junk); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + $"//span[{StyleBigRed} and text() = '{expected}']", 1); @@ -426,7 +426,7 @@ public void BadUSFM_RowWithTextBeforeCells() const string junk = "oops"; var entry = CreateInterestingLexEntry($"{TR} {junk} {TC}1 data"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); var expected = string.Format(xWorksStrings.InvalidUSFM_TextAfterTR, junk); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + @@ -441,7 +441,7 @@ public void BadUSFM_PartiallyTyped_NoErrors(string usfm, string xpath) { var entry = CreateInterestingLexEntry(usfm); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); Assert.That(result, Does.EndWith("")); AssertIsGood(result); @@ -452,7 +452,7 @@ public void BadUSFM_PartiallyTyped_NoErrors([Values(@"\t", TC, TH + "1", TH + "l { var entry = CreateInterestingLexEntry($@"{TR} {marker}\th2 exists"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + $"//span[{StyleBigRed} and text()='{marker}']", 1); Assert.That(result, Does.EndWith("")); @@ -464,7 +464,7 @@ public void BadUSFM_PartiallyTyped_NoError() { var entry = CreateInterestingLexEntry(TR + @" \t"); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToUSFMField + "//span[" + StyleBigRed + @" and text()='\t']", 1); Assert.That(result, Does.EndWith("")); @@ -478,7 +478,7 @@ public void MistypedMarker([Values("tre", "tcp", "t", "td", "tl", "t2", "h", "th const string cellAfter = "after"; var cellWithBadMark = $@"in \{misMark} between"; var entry = CreateInterestingLexEntry($"{TR} {TC} {cellBefore} {TC} {cellWithBadMark} {TC} {cellAfter}"); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, m_configNode, null, m_settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell, 3); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell + "[text()='" + cellBefore + "']", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(XPathToCell + "[text()='" + cellAfter + "']", 1); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs index 1fa28b8559..e43d64b0c4 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs @@ -119,7 +119,7 @@ public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var entry = CreateInterestingEnglishReversalEntry(); //SUT - string result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); + string result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); const string frenchLexForm = "/div[@class='reversalindexentry']/span[@class='reversalform']/span[@lang='en' and text()='ReversalForm']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } @@ -146,7 +146,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry( paroleEntry.SummaryDefinition.SetAnalysisDefaultWritingSystem("summDefn"); CXGTests.CreateComplexForm(Cache, paroleEntry, sense.Owner as ILexEntry, true); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); const string headwordXpath = senseXpath + "/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']"; @@ -165,7 +165,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfSense( var paroleEntry = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech"); CXGTests.CreateComplexForm(Cache, paroleEntry.SensesOS[0], sense.Owner as ILexEntry, true); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); 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); @@ -182,7 +182,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfSense( var paroleEntry = CXGTests.CreateInterestingLexEntry(Cache, "parole", "speech"); CXGTests.CreateVariantForm(Cache, paroleEntry.SensesOS[0], variantEntry, "Spelling Variant"); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); 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); @@ -200,7 +200,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfEntry( paroleEntry.SummaryDefinition.SetAnalysisDefaultWritingSystem("summDefn"); CXGTests.CreateVariantForm(Cache, paroleEntry, variantEntry, "Spelling Variant"); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); 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); @@ -226,7 +226,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferences_Ordered() 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 = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings); // SUT + var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); // 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}']"; @@ -401,7 +401,7 @@ public void GenerateXHTMLForEntry_ReversalStringGeneratesContent() var entryHeadWord = rie.SensesRS.First().Entry.HeadWord; //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(rie, reversalNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(rie, reversalNode, null, DefaultSettings).ToString(); 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); @@ -461,7 +461,7 @@ public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleReferencedSens var testEntry = CreateInterestingEnglishReversalEntry(); AddSenseToReversaEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); const string senseNumberOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' 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 @@ -523,7 +523,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSenses() var testEntry = CreateInterestingEnglishReversalEntry(); AddSingleSubSenseToSense(testEntry, "second gloss", m_wsEn, Cache); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); // 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='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); @@ -585,7 +585,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSensesinReversalSubEntry( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var testEntry = CreateInterestingEnglishSubReversalEntryWithSubSense(); //SUT - var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -703,7 +703,7 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() testEntry.SensesRS.Add(entry1.SensesOS.First()); testEntry.SensesRS.Add(entry2.SensesOS.First()); - var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); // check that the sense gram info appears once before the rest of the sense information. Assert.That(xhtml, Is.Not.Null.Or.Empty); const string sharedGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en' and text()='n']"; @@ -715,7 +715,7 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() entry2.MorphoSyntaxAnalysesOC.Add(msa2a); msa2a.PartOfSpeechRA = verb; entry2.SensesOS.First().MorphoSyntaxAnalysisRA = msa2a; - xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); // check that the sense gram info appears separately for both senses. AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfo, 0); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(separateGramInfo, 2); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 6c9a7fb08b..88a5ba1435 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -189,7 +189,7 @@ public void GenerateContentForEntry_HeadwordConfigurationGeneratesCorrectResult( AddHeadwordToEntry(entry, "HeadWordTest", m_wsFr); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[@lang='fr']/a[text()='HeadWordTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchHeadwordOfHeadwordTest, 1); } @@ -212,7 +212,7 @@ public void GenerateContentForEntry_InvalidUnicodeHeadword_GeneratesErrorResult( var entry = CreateInterestingLexEntry(Cache, "\uD900"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string invalidCharsHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[text()='\u0fff\u0fff\u0fff']"; // change Headword back to something legal so that we don't crash trying to save bad data into the cache. AddHeadwordToEntry(entry, "notbadanymore", Cache.DefaultVernWs); @@ -279,7 +279,7 @@ public void GenerateContentForEntry_LexemeFormConfigurationGeneratesCorrectResul morph.Form.set_String(wsFr, TsStringUtils.MakeString("LexemeFormTest", wsFr)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string frenchLexForm = "/div[@class='lexentry']/span[@class='lexemeformoa']/span[@lang='fr']/a[text()='LexemeFormTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } @@ -324,7 +324,7 @@ public void GenerateContentForEntry_PronunciationLocationGeneratesCorrectResult( pronunciation.LocationRA = location; var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string hereLocation = "/div[@class='lexentry']/span[@class='pronunciations']/span[@class='pronunciation']/span[@class='location']/span[@class='name']/span[@lang='fr' and text()='Here!']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(hereLocation, 1); } @@ -402,7 +402,7 @@ public void GenerateContentForEntry_PronunciationVideoFileGeneratesAnchorTag() const string mediaFileAnchor2 = entryPart + variantsPart + varPronPart + mediaFilePart + movieCamSearch; //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); Assert.That(result, Contains.Substring(videoFileUrl1)); Assert.That(result, Contains.Substring(videoFileUrl2)); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(mediaFileAnchor1, 1); @@ -456,7 +456,7 @@ public void GenerateContentForEntry_NoEnabledConfigurationsWritesNothing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); Assert.IsEmpty(result, "Should not have generated anything for a disabled node"); } @@ -476,9 +476,9 @@ public void GenerateContentForEntry_HomographNumbersGeneratesCorrectResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); XHTMLStringBuilder.AppendLine(""); //keep the xml valid (single root element) //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); XHTMLStringBuilder.Append(result); - result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings).ToString(); XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); @@ -502,7 +502,7 @@ public void GenerateContentForEntry_HeadwordRefConfigurationGeneratesWithTwoWS() var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(crossRefOwnerTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordWsInCrossRefsXpath("en", "bEN"), 1); @@ -530,7 +530,7 @@ public void GenerateContentForEntry_OneSenseWithGlossGeneratesCorrectResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string oneSenseWithGlossOfGloss = xpathThruSense + "//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithGlossOfGloss, 1); @@ -579,9 +579,9 @@ public void GenerateContentForEntry_OneEntryWithSenseAndOneWithoutWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); XHTMLStringBuilder.AppendLine(""); //keep the xml valid (single root element) //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); XHTMLStringBuilder.Append(result); - result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, settings).ToString(); XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); result = XHTMLStringBuilder.ToString(); @@ -602,7 +602,7 @@ public void GenerateContentForEntry_DefaultRootGeneratesResult() var dictionaryModel = new DictionaryConfigurationModel(defaultRoot, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings).ToString(); var entryExists = "/div[@class='entry' and @id='g" + entry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryExists, 1); } @@ -639,7 +639,7 @@ public void GenerateContentForEntry_DoesNotDescendThroughDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string sensesThatShouldNotBe = "/div[@class='entry']/span[@class='senses']"; const string headwordThatShouldNotBe = "//span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(sensesThatShouldNotBe, 0); @@ -670,7 +670,7 @@ public void GenerateContentForEntry_ProduceNothingWithOnlyDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); Assert.IsEmpty(result, "With only one subnode that is disabled, there should be nothing generated!"); } @@ -728,7 +728,7 @@ public void GenerateContentForEntry_TwoSensesWithSameInfoShowGramInfoFirst() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -813,7 +813,7 @@ public void GenerateContentForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished Cache.ServiceLocator.GetInstance().LexDbEntries, mainDict); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings).ToString(); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -878,7 +878,7 @@ public void GenerateContentForEntry_TwoSensesWithDifferentGramInfoShowInfoInSens var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 2); @@ -927,7 +927,7 @@ public void GenerateContentForEntry_TwoSensesWithNoGramInfoDisplaysNothingForSha var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); @@ -987,7 +987,7 @@ public void GenerateContentForEntry_MorphemeType() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string morphTypePath = "//span[@class='morphosyntaxanalysis']/span[@class='morphtypes']/span[@class='morphtype']/span[@class='abbreviation']/span[@lang='en' and text()='sfx']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(morphTypePath, 1); } @@ -1027,7 +1027,7 @@ public void GenerateContentForEntry_MakesSpanForRA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); } @@ -1078,7 +1078,7 @@ public void GenerateContentForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1112,7 +1112,7 @@ public void GenerateContentForEntry_DoesNotMakeSpanForRAIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1146,7 +1146,7 @@ public void GenerateContentForEntry_DoesNotMakeSpanForTSStringIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string scientificCatPath = xpathThruSense + "/span[@class='scientificname']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(scientificCatPath, 0); } @@ -1202,7 +1202,7 @@ public void GenerateContentForEntry_SupportsGramAbbrChildOfMSARA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; @@ -1265,7 +1265,7 @@ public void GenerateContentForEntry_DontDisplayNotSure() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='']"; const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; @@ -1314,7 +1314,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsCaption() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string captionOrHeadwordContainsCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaption, 1); @@ -1357,7 +1357,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsHeadword() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadword, 1); @@ -1401,7 +1401,7 @@ public void GenerateContentForEntry_CaptionOrHeadword_HandlePerWs() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -1432,7 +1432,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } @@ -1462,7 +1462,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks_WithAbbrev() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); @@ -1491,7 +1491,7 @@ public void GenerateContentForEntry_DefinitionOrGloss_HandlePerWS() entryOne.SensesOS.First().Definition.set_String(wsEs, "definition"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss' and span[1]='gloss' and span[2]='definition']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGlossTwoWs, 1); } @@ -1550,7 +1550,7 @@ public void GenerateContentForEntry_ReferencedComplexFormDefinitionOrGloss_Handl CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); // SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); // set of xpaths and required number of matches. var checkthis = new Dictionary() @@ -1623,7 +1623,7 @@ public void GenerateContentForEntry_OtherReferencedComplexForms() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -1660,7 +1660,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithSpaceWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithHyphenSuffix = "//span[@class='senses_test-one']/span[@class='sense_test-one']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1691,7 +1691,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithHyphenSuffix = "//span[@class='senses_-test']/span[@class='sense_-test']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1722,7 +1722,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithMultiPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithHyphenSuffix = "//span[@class='senses_-test-']/span[@class='sense_-test-']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1781,7 +1781,7 @@ public void GenerateContentForEntry_HeadWordRefVirtualPropWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); var headwordMatch = string.Format("//span[@class='{0}']//span[@class='{1}']/span[text()='{2}']", nters, headWord, entryThreeForm); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordMatch, 1); @@ -1835,7 +1835,7 @@ public void GenerateContentForEntry_EtymologyLanguageWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languages']/span[@class='language']/span[@class='abbreviation']/span[@lang='en' and text()='ar']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithArabicSrcLanguage, 1); const string etymologyWithGeorgianNotes = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languagenotes']/span[@lang='en' and text()='Georgian']"; @@ -2008,7 +2008,7 @@ public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(strin AddSenseToEntry(testEntry, "10", m_wsEn, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); string senseNumberOne = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; string senseNumberTwo = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; string senseNumberTen = $"//span[@class='sensecontent']/spansenses/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='{tenthSenseNumber}']]//span[@lang='en' and text()='10']"; @@ -2170,7 +2170,7 @@ public void GenerateContentForEntry_SingleSenseGetsNoSenseNumber() Assert.That(testEntry.AllSenses.Count, Is.EqualTo(1), "Test set up incorrectly. There should just be one sense."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(1), "Test not set up correctly. There should be no subsenses."); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberOne); @@ -2238,7 +2238,7 @@ public void GenerateContentForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSin Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. @@ -2305,7 +2305,7 @@ public void GenerateContentForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSi Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. @@ -2372,7 +2372,7 @@ public void GenerateContentForEntry_SubsenseStyleInfluencesSenseNumberShown() Assert.That(testEntry.AllSenses.Count, Is.EqualTo(3), "Test set up incorrectly."); Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 1); // Should have sense number on top sense. @@ -2422,7 +2422,7 @@ public void GenerateContentForEntry_NumberingSingleSenseAlsoCountsSubSense() AddSingleSubSenseToSense("gloss", testEntry.SensesOS.First()); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string SenseOneSubSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(SenseOneSubSense, 1); } @@ -2479,7 +2479,7 @@ public void GenerateContentForEntry_SensesAndSubSensesWithDifferentNumberingStyl AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2544,7 +2544,7 @@ public void GenerateContentForEntry_SensesAndSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2596,7 +2596,7 @@ public void GenerateContentForEntry_NoSenseNumberFIfStyleSaysNoNumbering() AddSenseToEntry(testEntry, "gloss", m_wsEn, Cache); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 0); // Should not have produced sense number if style said not to number it. @@ -2656,7 +2656,7 @@ public void GenerateContentForEntry_SensesNoneAndSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string subSensesNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; const string subSenseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -2698,7 +2698,7 @@ public void GenerateContentForEntry_SensesGeneratedForMultipleSubSenses() AddSenseAndTwoSubsensesToEntry(testEntry, "second gloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; @@ -2753,7 +2753,7 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleJoined() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; @@ -2807,7 +2807,7 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleSeparatedBy AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; @@ -2861,7 +2861,7 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleNone() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; @@ -2906,7 +2906,7 @@ public void GenerateContentForEntry_SubSubSensesWithNumberingStyle() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[1].SensesOS[0], "matte"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseNumberOne = "//span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string senseNumberTwo = "//span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; const string subSenseNumberTwoOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; @@ -3047,7 +3047,7 @@ public void GenerateContentForEntry_SubSensesOfSingleSenses_GetFullNumbers() AddSenseAndTwoSubsensesToEntry(testEntry.SensesOS[0], "subGloss"); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string subSenseNumberOneOne = "//span[@class='share sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]//span[@lang='en' and text()='subGloss']"; const string subosoSenseNumberOneOneOne = "//span[@lang='en' and text()='subGloss2.1']"; const string subosoSenseNumberOneOneTwo = "//span[@lang='en' and text()='subGloss2.2']"; @@ -3097,7 +3097,7 @@ public void GenerateContentForEntry_SubentriesSensesDontGetMainEntrySensesNumber CreateComplexForm(Cache, testEntry.SensesOS[0], subEntry, true); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='senses']/span[@class='sensecontent']"; @@ -3137,7 +3137,7 @@ public void GenerateContentForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseO var testEntry = CreateInterestingLexEntry(Cache); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -3167,7 +3167,7 @@ public void GenerateContentForEntry_SenseContentWithGuid() var testEntry = CreateInterestingLexEntry(Cache); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); const string senseEntryGuid = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuid, 1); @@ -3220,7 +3220,7 @@ public void GenerateContentForEntry_ExampleAndTranslationAreGenerated() AddExampleToSense(testEntry.SensesOS[0], example, Cache, m_wsFr, m_wsEn, translation); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents']/span[@class='examplescontent']"; var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3274,7 +3274,7 @@ public void GenerateContentForEntry_ExampleSentenceAndTranslationAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3328,7 +3328,7 @@ public void GenerateContentForEntry_LineSeperatorUnicodeCharBecomesBrElement() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); @@ -3403,7 +3403,7 @@ public void GenerateContentForEntry_ExtendedNoteChildrenAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); @@ -3485,7 +3485,7 @@ public void GenerateContentForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); @@ -3561,7 +3561,7 @@ public void GenerateContentForEntry_EnvironmentsAndAllomorphsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string xPathThruAllomorph = "/div[@class='lexentry']/span[@class='alternateformsos']/span[@class='alternateformso']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( xPathThruAllomorph + "/span[@class='form']/span[@lang='fr' and text()='Allomorph']", 1); @@ -3598,7 +3598,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsIncludesSubentriesAndO var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } @@ -3650,7 +3650,7 @@ public void GenerateContentForEntry_GeneratesLinksForReferencedForms() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( @@ -3749,7 +3749,7 @@ public void GenerateContentForEntry_GeneratesLinksForPrimaryEntryReferences() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href]", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( @@ -3796,7 +3796,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferences() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); } @@ -3848,7 +3848,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferenc var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); } @@ -3904,7 +3904,7 @@ public void GenerateContentForEntry_GeneratesCrossReferencesOnUnCheckConfigTarge var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT- - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasNoMatchForXpath( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']"); } @@ -3944,7 +3944,7 @@ public void GenerateContentForEntry_GeneratesForwardNameForSymmetricCrossReferen var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); const string anyNameXpath = @@ -3989,7 +3989,7 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardCrossReference var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -4034,7 +4034,7 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseCrossReference var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -4084,7 +4084,7 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardLexicalRelatio var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -4135,7 +4135,7 @@ public void GenerateContentForEntry_GeneratesLexicalRelationsLabelWithNoRepetiti var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); @@ -4182,7 +4182,7 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseLexicalRelatio var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( @@ -4237,7 +4237,7 @@ public void GenerateContentForEntry_LexicalRelationsSortbyNodeOptionsOrder() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string NameXpath = "//span[@class='minimallexreferences']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name']/span[@lang='en' and text()='{1}']"; var fwdNameFirstXpath = string.Format(NameXpath, "1", etyRefTypeName); var fwdNameSecondXpath = string.Format(NameXpath, "2", comRefTypeName); @@ -4249,7 +4249,7 @@ public void GenerateContentForEntry_LexicalRelationsSortbyNodeOptionsOrder() Options = DictionaryDetailsControllerTests.ListOfEnabledDNOsFromStrings(new[] { comRefType.Guid + ":f", etyRefType.Guid + ":f" }) }; - var resultAfterChange = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var resultAfterChange = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameChangedFirstXpath = string.Format(NameXpath, "1", comRefTypeName); var fwdNameChangedSecondXpath = string.Format(NameXpath, "2", etyRefTypeName); AssertThatXmlIn.String(resultAfterChange).HasSpecifiedNumberOfMatchesForXpath(fwdNameChangedFirstXpath, 1); @@ -4316,7 +4316,7 @@ public void GenerateContentForEntry_GeneratesAsymmetricRelationsProperly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(armEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(armEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(output).HasNoMatchForXpath(fwdNameXpath); @@ -4394,7 +4394,7 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForSubSenseProperly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings).ToString(); var goodTarget = string.Format( "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstHeadword); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); @@ -4463,7 +4463,7 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForTreeBetweenSenses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings).ToString(); var goodTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget1, 1); @@ -4896,7 +4896,7 @@ public void GenerateContentForEntry_NoncheckedListItemsAreNotGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); } @@ -4947,7 +4947,7 @@ public void GenerateContentForEntry_CheckedListItemsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); } @@ -5004,7 +5004,7 @@ public void GenerateContentForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='referencedentries']" + "/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); @@ -5046,7 +5046,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsUnderSensesIncludesSub var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( xpathThruSense + "/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } @@ -5249,7 +5249,7 @@ public void GenerateContentForEntry_OneSenseWithSinglePicture() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']//span[text()='caption']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -5287,8 +5287,8 @@ public void GenerateContentForEntry_PictureFileMissing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); - Assert.That(result, Is.Empty); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); + Assert.IsEmpty(result); } /// LT-21573: PictureFileRA can be null after an incomplete SFM import @@ -5323,7 +5323,7 @@ public void GenerateContentForEntry_PictureFileRAMissing() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); Assert.That(result, Is.Empty); } @@ -5356,7 +5356,7 @@ public void GenerateContentForEntry_PictureWithCreator() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='creator' and text()='Jason Naylor']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -5388,7 +5388,7 @@ public void GenerateContentForEntry_PictureWithNonUnicodePathLinksCorrectly() // generates a src attribute with an absolute file path var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); } @@ -5416,7 +5416,7 @@ public void GenerateContentForEntry_PictureCopiedAndRelativePathUsed() try { //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5453,7 +5453,7 @@ public void GenerateContentForEntry_MissingPictureFileDoesNotCrashOnCopy() try { //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5508,7 +5508,7 @@ public void GenerateContentForEntry_TwoDifferentFilesGetTwoDifferentResults() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5563,7 +5563,7 @@ public void GenerateContentForEntry_UniqueIdsForSameFile() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); const string pictureXPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img"; var pictureWithComposedPath = pictureXPath + "[contains(@src, '" + pictureRelativePath + "')]"; @@ -5705,7 +5705,7 @@ public void GenerateContentForEntry_TwoDifferentLinksToTheSamefileWorks() { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, tempFolder.FullName); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) @@ -5747,7 +5747,7 @@ public void GenerateContentForEntry_StringCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -5784,7 +5784,7 @@ public void GenerateContentForEntry_CustomFieldInGroupingNodeGeneratesContent() Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = $"/div[@class='lexentry']/span[@class='grouping_customgroup']/span[@class='customstring']/span[text()='" + customData + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -5833,7 +5833,7 @@ public void GenerateContentForEntry_CustomFieldInNestedGroupingNodeGeneratesCont Cache.MainCacheAccessor.SetString(testEntry.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string grpXPath = "/span[@class='grouping_customgroup']"; var customDataPath = $"/div[@class='lexentry']{grpXPath}{grpXPath}{grpXPath}/span[@class='customstring']/span[text()='{customData}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -5901,7 +5901,7 @@ public void GenerateContentForEntry_StringCustomFieldOnSenseGeneratesContent() Cache.MainCacheAccessor.SetString(testSence.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='l']/span[@class='es']/span[@class='e']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -5947,7 +5947,7 @@ public void GenerateContentForEntry_StringCustomFieldOnExampleGeneratesContent() Cache.MainCacheAccessor.SetString(exampleSentence.Hvo, customField.Flid, TsStringUtils.MakeString(customData, wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( "/div[@class='l']/span[@class='es']//span[@class='xs']/span[@class='x']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -5986,7 +5986,7 @@ public void GenerateContentForEntry_StringCustomFieldOnAllomorphGeneratesContent Cache.MainCacheAccessor.SetString(allomorph.Hvo, customField.Flid, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( "/div[@class='l']/span[@class='as']/span[@class='a']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); @@ -6018,7 +6018,7 @@ public void GenerateContentForEntry_MultiStringCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetMultiStringAlt(testEntry.Hvo, customField.Flid, m_wsEn, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -6080,7 +6080,7 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo TsStringUtils.MakeString(senseCustomData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryDataPath, 1); var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); @@ -6144,7 +6144,7 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo TsStringUtils.MakeString(senseCustomData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath, message: "Ref is to Sense; should be no Entry Custom Data"); var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); @@ -6184,7 +6184,7 @@ public void GenerateContentForEntry_CustomFieldOnRefdLexEntryGeneratesContent() Cache.MainCacheAccessor.SetMultiStringAlt(refdEntry.Hvo, customField.Flid, m_wsEn, TsStringUtils.MakeString(customData, m_wsEn)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='vars']/span[@class='var']/span[@class='owningentry_customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -6220,7 +6220,7 @@ public void GenerateContentForEntry_MultiStringDefinition_GeneratesMultilingualS var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var definitionXpath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition']/span[@lang='en']"; var str1Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[0]); var str2Xpath = string.Format(definitionXpath + "/span[@lang='fr' and text()='{0}']", multirunContent[1]); @@ -6268,7 +6268,7 @@ public void GenerateContentForEntry_ListItemCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, possibilityItem.Hvo); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string customDataPath = "/div[@class='lexentry']/span[@class='customlistitem']/span[@class='name']/span[text()='Djbuti']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -6309,7 +6309,7 @@ public void GenerateContentForEntry_MultiListItemCustomFieldGeneratesContent() Cache.MainCacheAccessor.Replace(testEntry.Hvo, customField.Flid, 0, 0, new[] { possibilityItem1.Hvo, possibilityItem2.Hvo }, 2); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); const string customDataPath1 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Dallas']"; const string customDataPath2 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Barcelona']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath1, 1); @@ -6341,7 +6341,7 @@ public void GenerateContentForEntry_DateCustomFieldGeneratesContent() SilTime.SetTimeProperty(Cache.MainCacheAccessor, testEntry.Hvo, customField.Flid, customData); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customdate' and text()='{0}']", customData.ToLongDateString()); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -6371,7 +6371,7 @@ public void GenerateContentForEntry_IntegerCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetInt(testEntry.Hvo, customField.Flid, customData); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format("/div[@class='lexentry']/span[@class='custominteger' and text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } @@ -6402,7 +6402,7 @@ public void GenerateContentForEntry_MultiLineCustomFieldGeneratesContent() Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, text.Hvo); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, settings).ToString(); const string customDataPath = "/div[@class='lexentry']/div/span[text()='First para Custom string'] | /div[@class='lexentry']/div/span[text()='Second para Custom string']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 2); @@ -6440,7 +6440,7 @@ public void GenerateContentForEntry_VariantOfReferencedHeadWord() CreateVariantForm(Cache, variantForm, mainEntry); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string referencedEntries = "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result) @@ -6471,7 +6471,7 @@ public void GenerateContentForEntry_WsAudiowithHyperlink() senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string audioTagwithSource = "//audio/source/@src"; AssertThatXmlIn.String(result) @@ -6560,7 +6560,7 @@ public void GenerateContentForEntry_AudioConversionDestinationDoesNotExist(bool File.Copy(path, Path.Combine(destination, Path.GetFileName(path)), true); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); if (isWebExport) { Assert.That(result, Contains.Substring("abu2.mp3"), "The automatic audio conversion in the CopyFileSafely method failed"); @@ -6649,7 +6649,7 @@ public void GenerateContentForEntry_AudioConversionIdenticalFileExists(bool isWe File.Copy(path, Path.Combine(destination, Path.GetFileName(path)), true); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); if (isWebExport) { Assert.That(result, Contains.Substring("abu2.mp3"), "The automatic audio conversion in the CopyFileSafely method failed"); @@ -6745,7 +6745,7 @@ public void GenerateContentForEntry_AudioConversionNonIdenticalFileExists(bool i } //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); if (isWebExport) { Assert.That(result, Contains.Substring("abu21.mp3"), "The automatic audio conversion code in the CopyFileSafely method did not change the file name as it should have since a file with the same name but different contents already exists"); @@ -6781,7 +6781,7 @@ public void GenerateContentForEntry_WsAudiowithRelativePaths() senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, true, true, "//audio/source/@src"); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string safeAudioId = "gTest_Audi_o"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("//audio[contains(@id," + safeAudioId + ")]", 1); @@ -6917,7 +6917,7 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentryUnderSens var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -6965,7 +6965,7 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentry() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); @@ -7036,7 +7036,7 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubsubentry([Valu var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string fwdNameXpath = "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries']/span[@class='subentry subentry']" + "/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; @@ -7097,7 +7097,7 @@ public void GenerateContentForEntry_DoesntGeneratesComplexFormType_WhenDisabled( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); StringAssert.DoesNotContain(complexRefAbbr, result); @@ -7184,7 +7184,7 @@ public void GenerateContentForEntry_GeneratesComplexForm_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword']"; @@ -7226,7 +7226,7 @@ public void GenerateContentForEntry_GeneratesSubentry_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='headword']"; @@ -7272,13 +7272,13 @@ public void GenerateContentForEntry_ComplexFormDontGenerateReference() // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries and publishing to Webonary this should NOT generate the reference. settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, true); - result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withoutReference, 2); } @@ -7371,7 +7371,7 @@ public void GenerateContentForEntry_GeneratesVariant_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string refTypeXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); const string headwordXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']"; @@ -7409,19 +7409,19 @@ public void GenerateContentForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMa var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); - Assert.That(result, Is.Null.Or.Empty); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); + Assert.IsEmpty(result); // try with HideMinorEntry off variantEntryRef.HideMinorEntry = 0; - result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); // Should get the same results if in Root based view model.IsRootBased = true; - result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); variantEntryRef.HideMinorEntry = 1; - result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings); - Assert.That(result, Is.Null.Or.Empty); + result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); + Assert.IsEmpty(result); } // Variant: Continue to generate the reference even if we are hiding the minor entry (useful for preview). @@ -7477,13 +7477,13 @@ public void GenerateContentForEntry_VariantGenerateReferenceForHiddenEntry() // When not hiding minor entries this should generate the reference. variantEntryRef.HideMinorEntry = 0; - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries this should still generate the reference. variantEntryRef.HideMinorEntry = 1; - result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); } @@ -7541,13 +7541,13 @@ public void GenerateContentForEntry_VariantDontGenerateReference() // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 2); //SUT // When hiding minor entries and publishing to Webonary this should NOT generate the reference. settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, true); - result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, model, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withReference, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(withoutReference, 2); } @@ -7590,7 +7590,7 @@ public void GenerateContentForEntry_ReferencedNode_GeneratesBothClasses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string headwordXpath = "//span[@class='reffingsubs']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7624,7 +7624,7 @@ public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); var css = ((CssGenerator)settings.StylesGenerator).GetStylesString(); // verify that the flow reset css is generated @@ -7632,7 +7632,7 @@ public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() Assert.That(css, Contains.Substring("clear:both")); var complexOptions = (DictionaryNodeListOptions)mainEntryNode.DictionaryNodeOptions; complexOptions.Options[0].IsEnabled = false; - result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 1); + result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 1).ToString(); Assert.IsEmpty(result); } @@ -7696,7 +7696,7 @@ public void GenerateContentForEntry_GeneratesCorrectMinorEntries( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(minorEntry, model, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(minorEntry, model, null, settings).ToString(); var isComplexFormShowing = complexForm == FormType.Unspecified && isUnspecifiedComplexTypeEnabled; var isVariantFormShowing = variantForm == FormType.Unspecified && isUnspecifiedVariantTypeEnabled; @@ -7942,7 +7942,7 @@ public void GenerateContentForEntry_FilterByPublication() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubEverything, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubEverything, settings).ToString(); Console.WriteLine(output); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. @@ -7954,7 +7954,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 1); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubMain, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7968,7 +7968,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchBodyIsBig, 1); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, pubTest, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7982,7 +7982,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchCorpseIsDead, 1); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubEverything, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubEverything, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -7991,7 +7991,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 2); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubMain, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8002,7 +8002,7 @@ public void GenerateContentForEntry_FilterByPublication() //SUT // We can still produce test publication output for the entry since we have a copy of it. Its senses and // examples should not be displayed because the senses are separately hidden. - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryBras, mainEntryNode, pubTest, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test output doesn't display the senses and examples. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8011,7 +8011,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 0); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubEverything, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubEverything, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the unfiltered output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8022,7 +8022,7 @@ public void GenerateContentForEntry_FilterByPublication() //SUT // We can still produce main publication output for the entry since we have a copy of it. Its sense and // example should not be displayed because the sense is separately hidden. - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubMain, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test output doesn't display the sense and example. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8031,7 +8031,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 0); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryOreille, mainEntryNode, pubTest, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays everything. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8045,7 +8045,7 @@ public void GenerateContentForEntry_FilterByPublication() const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the main publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8056,7 +8056,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 1); //SUT - output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubTest, settings); + output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubTest, settings).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); // Verify that the test publication output displays what it should. AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchEntry, 1); @@ -8120,7 +8120,7 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesLabelWithNoRepetit using (CreateVariantForm(Cache, entryEntry, ve4, new Guid("00000000-0000-0000-0000-000000000004"), null)) // no Type; none generated { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 2); } @@ -8183,7 +8183,7 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesShowOnlySelectedLi CreateVariantForm(Cache, entryEntry, ve1, "Free Variant"); // unique Type; CreateVariantForm(Cache, entryEntry, ve2, "Spelling Variant"); // unique Type; UnChecked var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); const string matchFreeVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Free Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFreeVariantRef, 1); const string matchSpellingVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Spelling Variant']"; @@ -8246,7 +8246,7 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesLabelWithNoRep }; CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); const string matchComplexFormRef = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormRef, 1); } @@ -8309,7 +8309,7 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesAndNamesGroup( }; CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); const string matchComplexFormTypeCompound = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Compound']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeCompound, 1); const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Idiom']"; @@ -8362,7 +8362,7 @@ public void GenerateContentForEntry_ComplexFormAndSenseInPara() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, DefaultSettings).ToString(); const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; var paracontinuationxpath = string.Format( "div[@class='lexentry']//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", @@ -8421,19 +8421,19 @@ public void GenerateContentForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefi var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, settings).ToString(); const string complexFormEntryRefXpath = "div[@class='minorentrycomplex']/span[@class='complexformentryrefs']/span[@class='complexformentryref']"; const string referencedEntriesXpath = "/span[@class='referencedentries']/span[@class='referencedentry']"; const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntrySummaryDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath1, 1); //SUT - var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, settings); + var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, settings).ToString(); const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='gloss2']"; AssertThatXmlIn.String(result2).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath2, 1); //SUT - var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, settings); + var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, settings).ToString(); const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntryS3Defn']"; AssertThatXmlIn.String(result3).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath3, 1); } @@ -8464,7 +8464,7 @@ public void GenerateContentForEntry_ContinuationParagraphWithEmtpyContentDoesNot var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); Assert.That(result, Does.Not.Match(@"
"), @@ -9050,7 +9050,7 @@ public void GenerateContentForEntry_EmbeddedWritingSystemGeneratesCorrectResult( var multiRunString = frenchString.Insert(12, englishStr); entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='en']"; const string nestedFr = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); @@ -9077,7 +9077,7 @@ public void GenerateContentForEntry_EmbeddedWritingSystemOfOppositeDirectionGene var wsHe = Cache.ServiceLocator.WritingSystemManager.GetWsFromStr("he"); entry.Bibliography.set_String(wsHe, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; const string extraDirection = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; @@ -9107,7 +9107,7 @@ public void GenerateContentForEntry_WritingSystemOfSameDirectionGeneratesNoExtra entry.Bibliography.set_String(wsHe, multiRunString); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, true); // Right-to-Left //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']"; const string extraDirection0 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']"; @@ -9145,7 +9145,7 @@ public void GenerateContentForEntry_EmbeddedHyperlinkGeneratesAnchor() (char)FwObjDataTypes.kodtExternalPathName + testUrl); entry.Bibliography.set_String(m_wsFr, stringBldr.GetString()); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); string nestedLink = $"/div[@class='lexentry']/span[@class='bib']/span/span/a[@href='{testUrl}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedLink, 1); } @@ -9198,7 +9198,7 @@ public void GenerateContentForEntry_CompareRelations_SimpleSituations_SortByHead var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(crossRefOwnerTypeXpath, 1); // ensure there is only one AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); // ...the *correct* one AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "a"), 1); @@ -9223,7 +9223,7 @@ public void GenerateContentForEntry_CompareRelations_ComplexSituation_SortByHead var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "a"), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(2, "b"), 1); @@ -9249,7 +9249,7 @@ public void GenerateContentForEntry_CrossRefs_Sequences_SequencePreserved() var mainEntryNode = ModelForCrossReferences(new[] { colorType.Guid.ToString(), greekType.Guid.ToString() }); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(alphaEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(alphaEntry, mainEntryNode, null, DefaultSettings).ToString(); // first sequence: colors: ARGB AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(colorTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "alpha"), 2); // the first in both @@ -9274,7 +9274,7 @@ public void GenerateContentForEntry_CrossRefs_Unidirectional_SequencePreserved() var mainEntryNode = ModelForCrossReferences(new[] { characterType.Guid + ":f" }); // SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(stoogesEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(stoogesEntry, mainEntryNode, null, DefaultSettings).ToString(); // sequence of Stooges: AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(characterTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "Larry"), 1); @@ -9436,7 +9436,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr //SUT //Console.WriteLine(LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { manEntry.Hvo, familyEntry.Hvo, girlEntry.Hvo, individualEntry.Hvo }, null, // new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_mediator)); // full output for diagnostics - var manResult = ConfiguredLcmGenerator.GenerateContentForEntry(manEntry, mainEntryNode, null, settings); + var manResult = ConfiguredLcmGenerator.GenerateContentForEntry(manEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(manResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); // antonyms are grouped into one span var idxAntonymAbbr = manResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); var idxWhole = manResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9462,7 +9462,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr // Ignore if usingSubfield. Justification: Part-Whole direction is miscalculated for field=Entry, subfield=MinimalLexReferences (LT-17571) if (!usingSubfield) { - var familyResult = ConfiguredLcmGenerator.GenerateContentForEntry(familyEntry, mainEntryNode, null, settings); + var familyResult = ConfiguredLcmGenerator.GenerateContentForEntry(familyEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(familyResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); idxAntonymAbbr = familyResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = familyResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9475,7 +9475,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr Assert.Less(idxAntonymAbbr, idxAntonymName, "Antonym name should come after Antonym abbreviation"); // SUT: Ensure that both directions of part-whole are kept separate - var girlResult = ConfiguredLcmGenerator.GenerateContentForEntry(girlEntry, mainEntryNode, null, settings); + var girlResult = ConfiguredLcmGenerator.GenerateContentForEntry(girlEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(girlResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 2); // whole and part idxAntonymAbbr = girlResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = girlResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9488,7 +9488,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr Assert.AreEqual(-1, idxAntonymName, "Antonym name relation should not exist for fille (girl)"); } - var individualResult = ConfiguredLcmGenerator.GenerateContentForEntry(individualEntry, mainEntryNode, null, settings); + var individualResult = ConfiguredLcmGenerator.GenerateContentForEntry(individualEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(individualResult).HasSpecifiedNumberOfMatchesForXpath(xpathLexRef, 1); idxAntonymAbbr = individualResult.IndexOf(antAbbrSpan, StringComparison.Ordinal); idxWhole = individualResult.IndexOf(whSpan, StringComparison.Ordinal); @@ -9548,7 +9548,7 @@ public void GenerateContentForEntry_VariantsOfEntryAreOrdered() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); // Test that variantformentrybackref items are in alphabetical order Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), @@ -9610,7 +9610,7 @@ public void GenerateContentForEntry_TypeBeforeForm() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); // Test that variantentrytypes is before variantformentrybackref Assert.That(result.IndexOf("variantentrytypes", StringComparison.InvariantCulture), @@ -9678,7 +9678,7 @@ public void GenerateContentForEntry_ComplexFormsAreOrderedAsUserSpecified( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); // Test that variantformentrybackref items are in (alphabetical or) virtual order Assert.That(result.IndexOf(headwords[0], StringComparison.InvariantCulture), @@ -9742,7 +9742,7 @@ public void GenerateContentForFieldByReflection_VariantFormTypesAreOrderedBasedO CreateInterestingLexEntry(Cache, "headwordB").MakeVariantOf(lexentry, (ILexEntryType)finalTypeInOptionsList); // SUT1 - var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings).ToString(); Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordB", StringComparison.InvariantCulture)), "variant forms not appearing in an order corresponding to their type sorting"); @@ -9751,7 +9751,7 @@ public void GenerateContentForFieldByReflection_VariantFormTypesAreOrderedBasedO ((DictionaryNodeListOptions)variantFormNode.DictionaryNodeOptions).Options.Reverse(); // SUT2 - result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, variantFormNode, null, DefaultSettings).ToString(); Assert.That(result.IndexOf("headwordB", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordA", StringComparison.InvariantCulture)), "variant forms not appearing in an order corresponding to their type sorting"); @@ -9802,7 +9802,7 @@ public void GenerateContentForFieldByReflection_SubentryTypesAreOrderedBasedOnOp CreateComplexForm(Cache, lexentry, CreateInterestingLexEntry(Cache, "headwordB"), true, finalTypeInOptionsListGuid); // SUT1 - var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings).ToString(); Assert.That(result.IndexOf("headwordA", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordB", StringComparison.InvariantCulture)), "Subentries should be sorted by Type"); @@ -9811,7 +9811,7 @@ public void GenerateContentForFieldByReflection_SubentryTypesAreOrderedBasedOnOp ((DictionaryNodeListOptions)subentryNode.DictionaryNodeOptions).Options.Reverse(); // SUT2 - result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForFieldByReflection(lexentry, subentryNode, null, DefaultSettings).ToString(); Assert.That(result.IndexOf("headwordB", StringComparison.InvariantCulture), Is.LessThan(result.IndexOf("headwordA", StringComparison.InvariantCulture)), "Subentries should be sorted by Type"); @@ -10052,7 +10052,7 @@ public void GenerateNextFewEntries_UpReturnsRequestedEntries() var entries = LcmXhtmlGenerator.GenerateNextFewEntries(pubEverything, new[] { firstEntry.Hvo, secondEntry.Hvo, thirdEntry.Hvo, fourthEntry.Hvo }, configPath, settings, currentPage, adjPage, 1, out current, out adjacent); Assert.AreEqual(1, entries.Count, "No entries generated"); - Assert.That(entries[0], Does.Contain(thirdEntry.HeadWord.Text)); + Assert.That(entries[0].ToString(), Does.Contain(thirdEntry.HeadWord.Text)); } finally { @@ -10107,8 +10107,8 @@ public void GenerateNextFewEntries_DownReturnsRequestedEntries() var entries = LcmXhtmlGenerator.GenerateNextFewEntries(pubEverything, new[] { firstEntry.Hvo, secondEntry.Hvo, thirdEntry.Hvo, fourthEntry.Hvo }, configPath, settings, currentPage, adjPage, 2, out current, out adjacent); Assert.AreEqual(2, entries.Count, "Not enough entries generated"); - Assert.That(entries[0], Does.Contain(thirdEntry.HeadWord.Text)); - Assert.That(entries[1], Does.Contain(fourthEntry.HeadWord.Text)); + Assert.That(entries[0].ToString(), Does.Contain(thirdEntry.HeadWord.Text)); + Assert.That(entries[1].ToString(), Does.Contain(fourthEntry.HeadWord.Text)); Assert.That(adjacent, Is.Null); } finally @@ -10142,7 +10142,7 @@ public void GenerateContentForEntry_GroupingNodeGeneratesSpanAndInnerContentWork var testEntry = CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); const string oneSenseWithGlossOfGloss = "/div[@class='lexentry']/span[@class='grouping_sensegroup']" + "/span[@class='senses']/span[@class='sense']//span[@lang='en' and text()='gloss']"; @@ -10175,7 +10175,7 @@ public void GenerateContentForEntry_GeneratesNFC() entry.CitationForm.set_String(wsKo, headword); Assert.That(entry.CitationForm.get_String(wsKo).get_IsNormalizedForm(FwNormalizationMode.knmNFD), "Should be NFDecomposed in memory"); Assert.AreEqual(6, headword.Text.Length); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, node, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, node, null, DefaultSettings).ToString(); var tsResult = TsStringUtils.MakeString(result, Cache.DefaultAnalWs); Assert.False(TsStringUtils.IsNullOrEmpty(tsResult), "Results should have been generated"); Assert.That(tsResult.get_IsNormalizedForm(FwNormalizationMode.knmNFC), "Resulting XHTML should be NFComposed"); @@ -10204,7 +10204,7 @@ public void GenerateContentForEntry_CompareRelations_ComplexSituation_CustomSort Assert.That(comRefType, Is.Not.Null); var mainEntryNode = ModelForCrossReferences(new[] { comRefType.Guid.ToString() }); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(CrossRefOwnerTypeXpath(comRefTypeName), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(1, "atest"), 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(HeadwordOrderInCrossRefsXpath(2, "ctest"), 1); diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 6bb1391a9f..6af2b10723 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -1289,7 +1289,7 @@ public void ClassMappingOverrides_ApplyAtRoot() using (var XHTMLWriter = XmlWriter.Create(xhtmResult)) { XHTMLWriter.WriteStartElement("body"); - var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testNode, null, DefaultSettings); + var content = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testNode, null, DefaultSettings).ToString(); XHTMLWriter.WriteRaw(content); XHTMLWriter.WriteEndElement(); XHTMLWriter.Flush(); @@ -1331,7 +1331,7 @@ public void ClassMappingOverrides_ApplyToChildren() Assert.That(cssResult, Does.Not.Contain(".headword")); Assert.That(cssResult, Contains.Substring(".tailwind")); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testParentNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testParentNode, null, DefaultSettings).ToString(); const string positiveTest = "//*[@class='tailwind']"; const string negativeTest = "//*[@class='headword']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); @@ -1372,7 +1372,7 @@ public void CssAndXhtmlMatchOnSenseCollectionItems() var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); Assert.That(cssResult, Contains.Substring(".gloss")); - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings).ToString(); const string positiveTest = "/*[@class='lexentry']/span[@class='senses']/span[@class='sense']/span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index 52e1e68f99..6241df3c96 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -156,7 +156,7 @@ public void GenerateJsonForEntry_OneSenseWithGlossGeneratesCorrectResult() var entry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); Console.WriteLine(result); var expectedResult = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]},]}"; @@ -189,7 +189,7 @@ public void GenerateJsonForEntry_DefinitionOrGloss_HandlePerWS() var wsEs = ConfiguredXHTMLGeneratorTests.EnsureWritingSystemSetup(Cache, "es", false); entry.SensesOS.First().Definition.set_String(wsEs, "definition"); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""senses"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss"": [{""lang"":""en"",""value"":""gloss""}, @@ -255,7 +255,7 @@ public void GenerateJsonForEntry_TwoSensesWithSameInfoShowGramInfoFirst_Json() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null) { ContentGenerator = new LcmJsonGenerator(Cache) }; //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); Console.WriteLine(result); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""msas"": {""mlpartofspeech"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]}, @@ -296,7 +296,7 @@ public void GenerateJsonForEntry_OneSenseWithSinglePicture() var settings = DefaultSettings; //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", ""sensenumber"": [{""lang"":""en"",""value"":""1""}],""caption"": [{""lang"":""en"",""value"":""caption""}]}]}"; @@ -510,7 +510,7 @@ public void GenerateJsonForEntry_FilterByPublication() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); var expectedResults = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses\": [{\"guid\":\"g" + @@ -580,7 +580,7 @@ public void GenerateJsonForEntry_TypeAfterForm() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNodeTypeAfter); //SUT - var outputTypeAfter = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0); + var outputTypeAfter = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0).ToString(); Assert.That(outputTypeAfter, Is.Not.Null.Or.Empty); var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + @@ -615,7 +615,7 @@ public void GenerateJsonForEntry_WsAudiowithHyperlink() const string audioFileName = "Test Audi'o.wav"; senseaudio.Form.set_String(wsEnAudio.Handle, TsStringUtils.MakeString(audioFileName, wsEnAudio.Handle)); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"": ""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; @@ -672,7 +672,7 @@ public void GenerateJsonForEntry_SensibleJsonForVideoFiles() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0); + var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); var expectedResults = "{\"xhtmlTemplate\":\"lexentry\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + "\"entry\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + @@ -702,7 +702,7 @@ public void GenerateJsonForEntry_SenseNumbersGeneratedForMultipleSenses() var testEntry = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); ConfiguredXHTMLGeneratorTests.AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses"":[{""senseNumber"":""1"", ""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""gloss""}]}, {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; @@ -731,7 +731,7 @@ public void GenerateJsonForEntry_EmbeddedWritingSystemGeneratesCorrectResult() var multiRunString = frenchString.Insert(12, englishStr); entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib"": [{""lang"":""fr"",""value"":""French with ""}, {""lang"":""en"",""value"":""English""},{""lang"":""fr"",""value"":"" embedded""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -757,7 +757,7 @@ public void GenerateJsonForEntry_UnicodeLineBreak_GeneratesValidJson() var englishStr = TsStringUtils.MakeString("English\u2028with line break", m_wsEn); entry.Bibliography.set_String(m_wsFr, englishStr); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""bib"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -804,7 +804,7 @@ public void GenerateJsonForEntry_GeneratesForwardNameForForwardLexicalRelations( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""sensesos"": [{""lexsensereferences"": [{""ownertype_name"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -847,7 +847,7 @@ public void GenerateJsonForEntry_EmptyNameOnLexicalRelation_GeneratesEmptyButVal CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""sensesos"": [{""lexsensereferences"": [{}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -874,12 +874,12 @@ public void GenerateJsonForEntry_HomographNumbersGeneratesCorrectResult() var entryTwo = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""1"", ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); - result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0).ToString(); expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""2"", ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -905,13 +905,13 @@ public void GenerateJsonForEntry_GeneratesSpecifiedSortIndex() var entryOne = ConfiguredXHTMLGeneratorTests.CreateInterestingLexEntry(Cache); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36).ToString(); var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); // default value of -1 - result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings); + result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings).ToString(); expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -958,7 +958,7 @@ public void GenerateJsonForEntry_TwoDifferentPicturesGetUniqueWebFriendlyPaths() try { //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); // Bug: The second filename should be different after the export with relative path settings (fix later) var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, @@ -1024,21 +1024,21 @@ public void GenerateJsonForEntry_MinorComplexForm_TemplateTypeCorrect_GeneratesG CssGeneratorTests.PopulateFieldsForTesting(minorEntryNode); ; //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); //SUT - var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0); + var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0).ToString(); expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result2, expected); //SUT - var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0); + var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0).ToString(); expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -1200,7 +1200,7 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera entry.Bibliography.set_String(wsHe, multiRunString); //SUT - var json = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings); + var json = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, ""bib"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); @@ -1229,7 +1229,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() var text = ConfiguredXHTMLGeneratorTests.CreateMultiParaText("Custom string", Cache); Cache.MainCacheAccessor.SetObjProp(testEntry.Hvo, customField.Flid, text.Hvo); //SUT - var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, DefaultSettings, 0); + var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, DefaultSettings, 0).ToString(); var expectedResults = @"{""xhtmlTemplate"":""lexentry"", ""guid"":""g" + testEntry.Guid + @""", From 08e60c761eed7bfa024898b2626386aab910e5fe Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 10 Jan 2024 09:39:56 -0800 Subject: [PATCH 034/415] Possible fix to the root cause of LT-21371 * Change the copy flag on the Clipboard api call to eliminate any possible issues related to memory being released and reused User debugging showed that a COMException with additional info: Invalid FORMATETC structure. Copying data from our c++ views and switching windows could make it unsafe to have clipboard contents that isn't copied. We tested this change with a user who was encountering the issue frequently and they observed a significant reduction in crashes Change-Id: I331e80177eabaacbc8e61c469351212e079adb22 --- Src/Common/SimpleRootSite/EditingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Common/SimpleRootSite/EditingHelper.cs b/Src/Common/SimpleRootSite/EditingHelper.cs index 8b53bf2011..67167ed574 100644 --- a/Src/Common/SimpleRootSite/EditingHelper.cs +++ b/Src/Common/SimpleRootSite/EditingHelper.cs @@ -3246,7 +3246,7 @@ internal void CopyTssToClipboard(ITsString tss) // the user selected a footnote marker but the TextRepOfObj() method isn't // implemented. - SetTsStringOnClipboard(tss, false, WritingSystemFactory); + SetTsStringOnClipboard(tss, true, WritingSystemFactory); } /// From ae7b2dde4b9d860ae95973f30b1c280277aac2ea Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 10 Jan 2024 10:12:08 -0800 Subject: [PATCH 035/415] Fix LT-21666 - Default complex form type for phrase to N/A * Previous default of Unspecified Complex Form caused many problems for users who didn't notice or desire that behavior Change-Id: Ie79b223a246e089d68c94e01e7e8f7085c6b5233 --- Src/LexText/LexTextControls/InsertEntryDlg.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Src/LexText/LexTextControls/InsertEntryDlg.cs b/Src/LexText/LexTextControls/InsertEntryDlg.cs index 1a7e32bd69..7f81715219 100644 --- a/Src/LexText/LexTextControls/InsertEntryDlg.cs +++ b/Src/LexText/LexTextControls/InsertEntryDlg.cs @@ -1570,14 +1570,7 @@ private void EnableComplexFormTypeCombo() case MoMorphTypeTags.kMorphDiscontiguousPhrase: case MoMorphTypeTags.kMorphPhrase: m_cbComplexFormType.Enabled = true; - // default to "Unspecified Complex Form" if found, else set to "0" for "phrase" - if (m_cbComplexFormType.SelectedIndex == m_idxNotComplex) - { - int unSpecCompFormIndex = m_cbComplexFormType.FindStringExact(UnSpecifiedComplex); - m_cbComplexFormType.SelectedIndex = unSpecCompFormIndex != -1 - ? unSpecCompFormIndex - : 0; - } + // Do not attempt to change index. Should default to "Not Applicable" - At request of LT-21666 break; default: m_cbComplexFormType.SelectedIndex = 0; From 05703eaf68e304ebe3d23fa4e1f96924e70db5c3 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 17 Jan 2024 11:27:15 -0500 Subject: [PATCH 036/415] =?UTF-8?q?LT-21443:=20=E2=80=98Approve=20All?= =?UTF-8?q?=E2=80=99=20-=20mark=20as=20user-approved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marking the analysis as user-approved is a partial fix for this Defect. The following problems still exist when testing with the project ‘Parser-Test2 Approve all 2024-01-16’: Step 5: There are 2 analysis, both in User Approved. The first has *** for Lex Gloss, the second has ‘I + NOM’ for Lex Gloss. (Note: This may be an issue because the capital word Sayana is also in the text.) Step 7: Both Analysis are shown as ‘User Opinion Unknown’ Step 9: Both Analysis are displayed in the correct section, but the one in ‘User Approved’ displays ‘***’ instead of displaying ‘he’ for the Lex Gloss. (Note: If in the ‘Word Analyses’ view you left click on the Lex Gloss that is displaying the ***, then it populates with ‘he + NOM’ and if you go back to the text, then ‘he + NOM’ is displayed in the Lex Gloss field for both ‘diana’ words, even if it’s not selected.) Step 10: When not selected Lex Gloss is displayed as ‘***’, Except for the first word in row 1. On row 1 the capital word ‘Sayana’ shows the Lex Gloss value even when not selected (I + NOM). NOTE: If I Use ‘Approve Throughout this Text’ for all the words, then none of these problems exist. Everything displays as described in the defects Acceptance Tests. Change-Id: Ic19d9391aa364016c63e2ab766ba4b2eab69965f --- Src/LexText/Interlinear/InterlinDocForAnalysis.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs index 9313c12f54..aafa827de4 100644 --- a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs +++ b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs @@ -2331,11 +2331,22 @@ public void ApproveAllSuggestedAnalyses(Command cmd) // 2) A parser result - not sure which gets picked if multiple. // #2 May take a while to "percolate" through to become a "guess". var guess = Cache.ServiceLocator.ObjectRepository.GetObject(hvo); + IWfiAnalysis workingAnalysis; if (guess != null && guess is IAnalysis) - occ.Segment.AnalysesRS[occ.Index] = (IAnalysis) guess; + { + occ.Segment.AnalysesRS[occ.Index] = (IAnalysis)guess; + workingAnalysis = guess as IWfiAnalysis; + } else { - occ.Segment.AnalysesRS[occ.Index] = occAn.Wordform.AnalysesOC.FirstOrDefault(); + workingAnalysis = occAn.Wordform.AnalysesOC.FirstOrDefault(); + occ.Segment.AnalysesRS[occ.Index] = workingAnalysis; + } + + if (workingAnalysis != null) + { + // Make sure this analysis is marked as user-approved. + Cache.LangProject.DefaultUserAgent.SetEvaluation(workingAnalysis, Opinions.approves); } } /* else if (occAn.HasWordform && occAn.Wordform.ParserCount > 0) From cdf578134a7b2a0a7b61fa3c7aa3b30960a2dc4d Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 31 Jan 2024 16:23:50 -0500 Subject: [PATCH 037/415] =?UTF-8?q?LT-21443:=20=E2=80=98Approve=20All?= =?UTF-8?q?=E2=80=99=20=E2=80=93=20Fix=20Approve=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In addition to fixing the problems in LT-21443 this change also uses the same approval code for the following approval workflows: 1. Approve All 2. Approve Throughout this Text 3. Approve and Move Next 4. Select a Morphemes then press Enter 5. Select a Morphemes then Left Click on a Different Word 6. Select a Morphemes then Select somewhere else in the GUI (a different Tab, Texts, Texts & Words, Bottom Left List). Change-Id: Id5be16e36d8986a30cda8f4566b6cde38cf76dc0 --- .../FocusBoxController.ApproveAndMove.cs | 359 +++++------------- .../InterlinDocForAnalysisTests.cs | 51 ++- .../Interlinear/InterlinDocForAnalysis.cs | 95 ++--- Src/LexText/Interlinear/SandboxBase.cs | 2 +- 4 files changed, 160 insertions(+), 347 deletions(-) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index 95c62f3aea..23b4054e1b 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -25,7 +25,7 @@ public partial class FocusBoxController internal void ApproveAndStayPut(ICommandUndoRedoText undoRedoText) { // don't navigate, just save. - UpdateRealFromSandbox(undoRedoText, true, SelectedOccurrence); + UpdateRealFromSandbox(undoRedoText, true); } /// @@ -35,78 +35,21 @@ internal void ApproveAndStayPut(ICommandUndoRedoText undoRedoText) /// or clicking the "Approve and Move Next" green check in an analysis. /// /// - internal virtual void ApproveAndMoveNext(ICommandUndoRedoText undoRedoText) + internal virtual void ApproveAndMoveNext(ICommandUndoRedoText cmd) { - ApproveAndMoveNextRecursive(undoRedoText); - } + if (!PreCheckApprove()) + return; - /// - /// Approves an analysis and moves the selection to the next wordform or the - /// next Interlinear line. An Interlinear line is one of the configurable - /// "lines" in the Tools->Configure->Interlinear Lines dialog, not a segement. - /// The list of lines is collected in choices[] below. - /// WordLevel is true for word or analysis lines. The non-word lines are translation and note lines. - /// Normally, this is invoked as a result of pressing the key in an analysis. - /// - /// - /// true if IP moved on, false otherwise - internal virtual bool ApproveAndMoveNextRecursive(ICommandUndoRedoText undoRedoText) - { - if (!SelectedOccurrence.IsValid) - { - // Can happen (at least) when the text we're analyzing got deleted in another window - SelectedOccurrence = null; - InterlinDoc.TryHideFocusBoxAndUninstall(); - return false; - } - var navigator = new SegmentServices.StTextAnnotationNavigator(SelectedOccurrence); - var nextWordform = navigator.GetNextWordformOrDefault(SelectedOccurrence); - if (nextWordform == null || nextWordform.Segment != SelectedOccurrence.Segment || - nextWordform == SelectedOccurrence) - { - // We're at the end of a segment...try to go to an annotation of SelectedOccurrence.Segment - // or possibly (See LT-12229:If the nextWordform is the same as SelectedOccurrence) - // at the end of the text. - UpdateRealFromSandbox(undoRedoText, true, null); // save work done in sandbox - // try to select the first configured annotation (not a null note) in this segment - if (InterlinDoc.SelectFirstTranslationOrNote()) - { // IP should now be on an annotation line. - return true; - } - } - if (nextWordform != null) - { - bool dealtWith = false; - if (nextWordform.Segment != SelectedOccurrence.Segment) - { // Is there another segment before the next wordform? - // It would have no analyses or just punctuation. - // It could have "real" annotations. - AnalysisOccurrence realAnalysis; - ISegment nextSeg = InterlinDoc.GetNextSegment - (SelectedOccurrence.Segment.Owner.IndexInOwner, - SelectedOccurrence.Segment.IndexInOwner, false, out realAnalysis); // downward move - if (nextSeg != null && nextSeg != nextWordform.Segment) - { // This is a segment before the one contaning the next wordform. - if (nextSeg.AnalysesRS.Where(an => an.HasWordform).Count() > 0) - { // Set it as the current segment and recurse - SelectedOccurrence = new AnalysisOccurrence(nextSeg, 0); // set to first analysis - dealtWith = ApproveAndMoveNextRecursive(undoRedoText); - } - else - { // only has annotations: focus on it and set the IP there. - InterlinDoc.SelectFirstTranslationOrNote(nextSeg); - return true; // IP should now be on an annotation line. - } - } - } - if (!dealtWith) - { // If not dealt with continue on to the next wordform. - UpdateRealFromSandbox(undoRedoText, true, nextWordform); - // do the move. - InterlinDoc.SelectOccurrence(nextWordform); - } - } - return true; + UndoableUnitOfWorkHelper.Do(cmd.UndoText, cmd.RedoText, Cache.ActionHandlerAccessor, + () => + { + ApproveAnalysis(SelectedOccurrence, false, true); + }); + + // This should not make any data changes, since we're telling it not to save and anyway + // we already saved the current annotation. And it can't correctly place the focus box + // until the change we just did are completed and PropChanged sent. So keep this outside the UOW. + OnNextBundle(cmd, false, false, false, true); } /// @@ -115,9 +58,7 @@ internal virtual bool ApproveAndMoveNextRecursive(ICommandUndoRedoText undoRedoT /// Approving the state of the FocusBox can be associated with /// different user actions (ie. UOW) /// - /// - internal void UpdateRealFromSandbox(ICommandUndoRedoText undoRedoText, bool fSaveGuess, - AnalysisOccurrence nextWordform) + internal void UpdateRealFromSandbox(ICommandUndoRedoText undoRedoText, bool fSaveGuess) { if (!ShouldCreateAnalysisFromSandbox(fSaveGuess)) return; @@ -136,7 +77,7 @@ internal void UpdateRealFromSandbox(ICommandUndoRedoText undoRedoText, bool fSav // But we don't want it to happen as an automatic side effect of the PropChanged. InterlinDoc.SuspendResettingAnalysisCache = true; UndoableUnitOfWorkHelper.Do(undoText, redoText, - Cache.ActionHandlerAccessor, () => ApproveAnalysisAndMove(fSaveGuess, nextWordform)); + Cache.ActionHandlerAccessor, () => ApproveAnalysis(SelectedOccurrence, false, fSaveGuess)); } finally { @@ -158,31 +99,9 @@ protected virtual bool ShouldCreateAnalysisFromSandbox(bool fSaveGuess) return true; } - - protected virtual void ApproveAnalysisAndMove(bool fSaveGuess, AnalysisOccurrence nextWordform) - { - using (new UndoRedoApproveAndMoveHelper(this, SelectedOccurrence, nextWordform)) - ApproveAnalysis(fSaveGuess); - } - - /// - /// - /// - /// - protected virtual void ApproveAnalysis(bool fSaveGuess) + private void FinishSettingAnalysis(AnalysisTree newAnalysisTree, IAnalysis oldAnalysis) { - IWfiAnalysis obsoleteAna; - AnalysisTree newAnalysisTree = InterlinWordControl.GetRealAnalysis(fSaveGuess, out obsoleteAna); - // if we've made it this far, might as well try to go the whole way through the UOW. - SaveAnalysisForAnnotation(SelectedOccurrence, newAnalysisTree); - FinishSettingAnalysis(newAnalysisTree, InitialAnalysis); - if (obsoleteAna != null) - obsoleteAna.Delete(); - } - - private void FinishSettingAnalysis(AnalysisTree newAnalysisTree, AnalysisTree oldAnalysisTree) - { - if (newAnalysisTree.Analysis == oldAnalysisTree.Analysis) + if (newAnalysisTree.Analysis == oldAnalysis) return; List msaHvoList = new List(); // Collecting for the new analysis is probably overkill, since the MissingEntries combo will only have MSAs @@ -209,150 +128,12 @@ private void SaveAnalysisForAnnotation(AnalysisOccurrence occurrence, AnalysisTr // analysis of the word. occurrence.Analysis = newAnalysisTree.Analysis; - // In case the wordform we point at has a form that doesn't match, we may need to set up an overidden form for the annotation. - IWfiWordform targetWordform = newAnalysisTree.Wordform; - if (targetWordform != null) - { - TryCacheRealWordForm(occurrence); - } - // It's possible if the new analysis is a different case form that the old wordform is now // unattested and should be removed. if (wfToTryDeleting != null && wfToTryDeleting != occurrence.Analysis.Wordform) wfToTryDeleting.DeleteIfSpurious(); } - private static bool BaselineFormDiffersFromAnalysisWord(AnalysisOccurrence occurrence, out ITsString baselineForm) - { - baselineForm = occurrence.BaselineText; // Review JohnT: does this work if the text might have changed?? - var wsBaselineForm = TsStringUtils.GetWsAtOffset(baselineForm, 0); - // We've updated the annotation to have InstanceOf set to the NEW analysis, so what we now derive from - // that is the NEW wordform. - var wfNew = occurrence.Analysis as IWfiWordform; - if (wfNew == null) - return false; // punctuation variations not significant. - var tssWfNew = wfNew.Form.get_String(wsBaselineForm); - return !baselineForm.Equals(tssWfNew); - } - - private void TryCacheRealWordForm(AnalysisOccurrence occurrence) - { - ITsString tssBaselineCbaForm; - if (BaselineFormDiffersFromAnalysisWord(occurrence, out tssBaselineCbaForm)) - { - //m_cache.VwCacheDaAccessor.CacheStringProp(hvoAnnotation, - // InterlinVc.TwficRealFormTag(m_cache), - // tssBaselineCbaForm); - } - } - - internal class UndoRedoApproveAndMoveHelper : DisposableBase - { - internal UndoRedoApproveAndMoveHelper(FocusBoxController focusBox, - AnalysisOccurrence occBeforeApproveAndMove, AnalysisOccurrence occAfterApproveAndMove) - { - Cache = focusBox.Cache; - FocusBox = focusBox; - OccurrenceBeforeApproveAndMove = occBeforeApproveAndMove; - OccurrenceAfterApproveAndMove = occAfterApproveAndMove; - - // add the undo action - AddUndoRedoAction(OccurrenceBeforeApproveAndMove, null); - } - - LcmCache Cache { get; set; } - FocusBoxController FocusBox { get; set; } - AnalysisOccurrence OccurrenceBeforeApproveAndMove { get; set; } - AnalysisOccurrence OccurrenceAfterApproveAndMove { get; set; } - - private UndoRedoApproveAnalysis AddUndoRedoAction(AnalysisOccurrence currentAnnotation, AnalysisOccurrence newAnnotation) - { - if (Cache.ActionHandlerAccessor != null && currentAnnotation != newAnnotation) - { - var undoRedoAction = new UndoRedoApproveAnalysis(FocusBox.InterlinDoc, - currentAnnotation, newAnnotation); - Cache.ActionHandlerAccessor.AddAction(undoRedoAction); - return undoRedoAction; - } - return null; - } - - protected override void DisposeManagedResources() - { - // add the redo action - if (OccurrenceBeforeApproveAndMove != OccurrenceAfterApproveAndMove) - AddUndoRedoAction(null, OccurrenceAfterApproveAndMove); - } - - protected override void DisposeUnmanagedResources() - { - FocusBox = null; - OccurrenceBeforeApproveAndMove = null; - OccurrenceAfterApproveAndMove = null; - } - - protected override void Dispose(bool disposing) - { - Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + " ******"); - base.Dispose(disposing); - } - } - - /// - /// This class allows smarter UndoRedo for ApproveAnalysis, so that the FocusBox can move appropriately. - /// - internal class UndoRedoApproveAnalysis : UndoActionBase - { - readonly InterlinDocForAnalysis m_interlinDoc; - readonly AnalysisOccurrence m_oldOccurrence; - AnalysisOccurrence m_newOccurrence; - - internal UndoRedoApproveAnalysis(InterlinDocForAnalysis interlinDoc, AnalysisOccurrence oldAnnotation, - AnalysisOccurrence newAnnotation) - { - m_interlinDoc = interlinDoc; - m_oldOccurrence = oldAnnotation; - m_newOccurrence = newAnnotation; - } - - #region Overrides of UndoActionBase - - private bool IsUndoable() - { - return m_oldOccurrence != null && m_oldOccurrence.IsValid && m_interlinDoc.IsFocusBoxInstalled; - } - - public override bool Redo() - { - if (m_newOccurrence != null && m_newOccurrence.IsValid) - { - m_interlinDoc.SelectOccurrence(m_newOccurrence); - } - else - { - m_interlinDoc.TryHideFocusBoxAndUninstall(); - } - - return true; - } - - public override bool Undo() - { - if (IsUndoable()) - { - m_interlinDoc.SelectOccurrence(m_oldOccurrence); - } - else - { - m_interlinDoc.TryHideFocusBoxAndUninstall(); - } - - return true; - } - - #endregion - } - /// /// We can navigate from one bundle to another if the focus box controller is /// actually visible. (Earlier versions of this method also checked it was in the right tool, but @@ -471,6 +252,30 @@ private static bool CheckPropSetForAllMorphs(IWfiAnalysis wa, int flid) return wa.MorphBundlesOS.All(bundle => wa.Cache.DomainDataByFlid.get_ObjectProp(bundle.Hvo, flid) != 0); } + /// + /// Common pre-checks used for some of the Approve workflows. + /// + /// true: passed all pre-checks. + public bool PreCheckApprove() + { + if (SelectedOccurrence == null) + return false; + + if (!SelectedOccurrence.IsValid) + { + // Can happen (at least) when the text we're analyzing got deleted in another window + SelectedOccurrence = null; + InterlinDoc.TryHideFocusBoxAndUninstall(); + return false; + } + + var stText = SelectedOccurrence.Paragraph.Owner as IStText; + if (stText == null || stText.ParagraphsOS.Count == 0) + return false; // paranoia, we should be in one of its paragraphs. + + return true; + } + /// /// Using the current focus box content, approve it and apply it to all unanalyzed matching /// wordforms in the text. See LT-8833. @@ -478,14 +283,12 @@ private static bool CheckPropSetForAllMorphs(IWfiAnalysis wa, int flid) /// public void ApproveGuessOrChangesForWholeTextAndMoveNext(Command cmd) { + if (!PreCheckApprove()) + return; + // Go through the entire text looking for matching analyses that can be set to the new // value. - if (SelectedOccurrence == null) - return; - var oldWf = SelectedOccurrence.Analysis.Wordform; - var stText = SelectedOccurrence.Paragraph.Owner as IStText; - if (stText == null || stText.ParagraphsOS.Count == 0) - return; // paranoia, we should be in one of its paragraphs. + // We don't need to discard existing guesses, even though we will modify Segment.Analyses, // since guesses for other wordforms will not be affected, and there will be no remaining // guesses for the word we're confirming everywhere. (This needs to be outside the block @@ -496,29 +299,9 @@ public void ApproveGuessOrChangesForWholeTextAndMoveNext(Command cmd) // Needs to include GetRealAnalysis, since it might create a new one. UndoableUnitOfWorkHelper.Do(cmd.UndoText, cmd.RedoText, Cache.ActionHandlerAccessor, () => - { - IWfiAnalysis obsoleteAna; - AnalysisTree newAnalysisTree = InterlinWordControl.GetRealAnalysis(true, out obsoleteAna); - var wf = newAnalysisTree.Wordform; - if (newAnalysisTree.Analysis == wf) - { - // nothing significant to confirm, so move on - // (return means get out of this lambda expression, not out of the method). - return; - } - SaveAnalysisForAnnotation(SelectedOccurrence, newAnalysisTree); - if (wf != null) - { - ApplyAnalysisToInstancesOfWordform(newAnalysisTree.Analysis, oldWf, wf); - } - // don't try to clean up the old analysis until we've finished walking through - // the text and applied all our changes, otherwise we could delete a wordform - // that is referenced by dummy annotations in the text, and thus cause the display - // to treat them like pronunciations, and just show an unanalyzable text (LT-9953) - FinishSettingAnalysis(newAnalysisTree, InitialAnalysis); - if (obsoleteAna != null) - obsoleteAna.Delete(); - }); + { + ApproveAnalysis(SelectedOccurrence, true, true); + }); }); // This should not make any data changes, since we're telling it not to save and anyway // we already saved the current annotation. And it can't correctly place the focus box @@ -526,10 +309,50 @@ public void ApproveGuessOrChangesForWholeTextAndMoveNext(Command cmd) OnNextBundle(cmd, false, false, false, true); } + /// + /// Common code intended to be used for all analysis approval workflows. + /// + /// The occurrence to approve. + /// if true, approve all occurrences; if false, only approve occ + /// if true, saves guesses; if false, skips guesses but still saves edits. + public virtual void ApproveAnalysis(AnalysisOccurrence occ, bool allOccurrences, bool fSaveGuess) + { + IAnalysis oldAnalysis = occ.Analysis; + IWfiWordform oldWf = occ.Analysis.Wordform; + + IWfiAnalysis obsoleteAna; + AnalysisTree newAnalysisTree = InterlinWordControl.GetRealAnalysis(fSaveGuess, out obsoleteAna); + var wf = newAnalysisTree.Wordform; + if (newAnalysisTree.Analysis == wf) + { + // nothing significant to confirm, so move on + return; + } + SaveAnalysisForAnnotation(occ, newAnalysisTree); + if (wf != null) + { + if (allOccurrences) + { + ApplyAnalysisToInstancesOfWordform(occ, newAnalysisTree.Analysis, oldWf, wf); + } + else + { + occ.Segment.AnalysesRS[occ.Index] = newAnalysisTree.Analysis; + } + } + // don't try to clean up the old analysis until we've finished walking through + // the text and applied all our changes, otherwise we could delete a wordform + // that is referenced by dummy annotations in the text, and thus cause the display + // to treat them like pronunciations, and just show an unanalyzable text (LT-9953) + FinishSettingAnalysis(newAnalysisTree, oldAnalysis); + if (obsoleteAna != null) + obsoleteAna.Delete(); + } + // Caller must create UOW - private void ApplyAnalysisToInstancesOfWordform(IAnalysis newAnalysis, IWfiWordform oldWordform, IWfiWordform newWordform) + private void ApplyAnalysisToInstancesOfWordform(AnalysisOccurrence occurrence, IAnalysis newAnalysis, IWfiWordform oldWordform, IWfiWordform newWordform) { - var navigator = new SegmentServices.StTextAnnotationNavigator(SelectedOccurrence); + var navigator = new SegmentServices.StTextAnnotationNavigator(occurrence); foreach (var occ in navigator.GetAnalysisOccurrencesAdvancingInStText().ToList()) { // We certainly want to update any occurrence that exactly matches the wordform of the analysis we are confirming. diff --git a/Src/LexText/Interlinear/ITextDllTests/InterlinDocForAnalysisTests.cs b/Src/LexText/Interlinear/ITextDllTests/InterlinDocForAnalysisTests.cs index 38476f999c..3ee0bd5280 100644 --- a/Src/LexText/Interlinear/ITextDllTests/InterlinDocForAnalysisTests.cs +++ b/Src/LexText/Interlinear/ITextDllTests/InterlinDocForAnalysisTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2015 SIL International +// Copyright (c) 2015 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -148,6 +148,13 @@ public void ApproveAndStayPut_NewWordGloss() [Test] public void ApproveAndMoveNext_NoChange() { + // Override the InterlinVc for this test, but not other tests. + var origVc = m_interlinDoc.InterlinVc; + m_interlinDoc.InterlinVc = new InterlinDocForAnalysisVc(Cache); + + ISegment seg = m_para0_0.SegmentsOS[0]; + SetUpMocksForTest(seg); + var occurrences = SegmentServices.GetAnalysisOccurrences(m_para0_0).ToList(); m_interlinDoc.SelectOccurrence(occurrences[0]); var initialAnalysisTree = m_focusBox.InitialAnalysis; @@ -161,6 +168,9 @@ public void ApproveAndMoveNext_NoChange() // nothing to undo. Assert.AreEqual(0, Cache.ActionHandlerAccessor.UndoableSequenceCount); + + // Restore the InterlinVc for other tests. + m_interlinDoc.InterlinVc = origVc; } /// @@ -204,7 +214,7 @@ public void ApproveAndMoveNext_NewWordGloss() public void OnAddWordGlossesToFreeTrans_Simple() { ISegment seg = m_para0_0.SegmentsOS[0]; - SetUpMocksForOnAddWordGlossesToFreeTransTest(seg); + SetUpMocksForTest(seg); SetUpGlosses(seg, "hope", "this", "works"); m_interlinDoc.OnAddWordGlossesToFreeTrans(null); @@ -232,7 +242,7 @@ public void OnAddWordGlossesToFreeTrans_ORCs() m_para0_0.Contents = strBldr.GetString(); }); - SetUpMocksForOnAddWordGlossesToFreeTransTest(seg); + SetUpMocksForTest(seg); SetUpGlosses(seg, "hope", null, "this", "works"); m_interlinDoc.OnAddWordGlossesToFreeTrans(null); @@ -247,7 +257,7 @@ public void OnAddWordGlossesToFreeTrans_ORCs() #endregion #region Helper methods - private void SetUpMocksForOnAddWordGlossesToFreeTransTest(ISegment seg) + private void SetUpMocksForTest(ISegment seg) { IVwRootBox rootb = MockRepository.GenerateMock(); m_interlinDoc.MockedRootBox = rootb; @@ -298,6 +308,23 @@ internal MockInterlinDocForAnalyis(IStText testText) m_testText = testText; Vc = new InterlinVc(Cache); Vc.RootSite = this; + m_mediator = new Mediator(); + m_propertyTable = new PropertyTable(m_mediator); + + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (m_mediator != null) + m_mediator.Dispose(); + if (m_propertyTable != null) + m_propertyTable.Dispose(); + } + m_mediator = null; + m_propertyTable = null; + base.Dispose(disposing); } protected override FocusBoxController CreateFocusBoxInternal() @@ -311,6 +338,16 @@ public override void SelectOccurrence(AnalysisOccurrence target) FocusBox.SelectOccurrence(target); } + internal InterlinVc InterlinVc + { + get => Vc; + set + { + Vc = value; + Vc.RootSite = this; + } + } + internal override void UpdateGuesses(HashSet wordforms) { // for now, don't update guesses in these tests. @@ -393,11 +430,11 @@ protected override bool ShouldCreateAnalysisFromSandbox(bool fSaveGuess) return base.ShouldCreateAnalysisFromSandbox(fSaveGuess); } - protected override void ApproveAnalysis(bool fSaveGuess) + public override void ApproveAnalysis(AnalysisOccurrence occ, bool allOccurrences, bool fSaveGuess) { if (DoDuringUnitOfWork != null) NewAnalysisTree.Analysis = DoDuringUnitOfWork().Analysis; - base.ApproveAnalysis(fSaveGuess); + base.ApproveAnalysis(occ, allOccurrences, fSaveGuess); } internal AnalysisTree NewAnalysisTree @@ -489,7 +526,7 @@ AnalysisTree IAnalysisControlInternal.GetRealAnalysis(bool fSaveGuess, out IWfiA public int GetLineOfCurrentSelection() { - throw new NotImplementedException(); + return -1; } public bool SelectOnOrBeyondLine(int startLine, int increment) diff --git a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs index aafa827de4..0358115149 100644 --- a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs +++ b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs @@ -289,7 +289,7 @@ public virtual void TriggerAnalysisSelected(AnalysisOccurrence target, bool fSav return; } if (IsFocusBoxInstalled) - FocusBox.UpdateRealFromSandbox(null, fSaveGuess, target); + FocusBox.UpdateRealFromSandbox(null, fSaveGuess); TryHideFocusBoxAndUninstall(); RecordGuessIfNotKnown(target); InstallFocusBox(); @@ -2073,7 +2073,7 @@ protected virtual bool HandleClickSelection(IVwSelection vwselNew, bool fBundleO if (!fBundleOnly) { if (IsFocusBoxInstalled) - FocusBox.UpdateRealFromSandbox(null, fSaveGuess, null); + FocusBox.UpdateRealFromSandbox(null, fSaveGuess); TryHideFocusBoxAndUninstall(); } @@ -2117,7 +2117,7 @@ protected virtual bool HandleClickSelection(IVwSelection vwselNew, bool fBundleO if (!fBundleOnly) { if (IsFocusBoxInstalled) - FocusBox.UpdateRealFromSandbox(null, fSaveGuess, null); + FocusBox.UpdateRealFromSandbox(null, fSaveGuess); TryHideFocusBoxAndUninstall(); } @@ -2251,7 +2251,7 @@ internal IAnalysis GetGuessForWordform(IWfiWordform wf, int ws) internal bool PrepareToGoAway() { if (IsFocusBoxInstalled) - FocusBox.UpdateRealFromSandbox(null, false, null); + FocusBox.UpdateRealFromSandbox(null, false); return true; } @@ -2272,43 +2272,20 @@ public void ApproveAllSuggestedAnalyses(Command cmd) var helper = SelectionHelper.Create(RootBox.Site); // only helps restore translation and note line selections AnalysisOccurrence focusedWf = SelectedOccurrence; // need to restore focus box if selected - // find the very first analysis - ISegment firstRealSeg = null; - IAnalysis firstRealOcc = null; - int occInd = 0; - foreach (IStPara p in RootStText.ParagraphsOS) + if (!FocusBox.PreCheckApprove()) + return; + + var sandbox = FocusBox.InterlinWordControl as Sandbox; + if (sandbox == null) { - var para = (IStTxtPara) p; - foreach (ISegment seg in para.SegmentsOS) - { - firstRealSeg = seg; - occInd = 0; - foreach(IAnalysis an in seg.AnalysesRS) - { - if (an.HasWordform && an.IsValidObject) - { - firstRealOcc = an; - break; - } - occInd++; - } - if (firstRealOcc != null) break; - } - if (firstRealOcc != null) break; - } - // Set it as the current segment and recurse - if (firstRealOcc == null) - return; // punctuation only or nothing to analyze - AnalysisOccurrence ao = null; - if (focusedWf != null && focusedWf.Analysis == firstRealOcc) - ao = new AnalysisOccurrence(focusedWf.Segment, focusedWf.Index); - else - ao = new AnalysisOccurrence(firstRealSeg, occInd); - TriggerAnalysisSelected(ao, true, true, false); - var navigator = new SegmentServices.StTextAnnotationNavigator(ao); + throw new Exception("Not expecting sandbox to ever be null."); + } + + var navigator = new SegmentServices.StTextAnnotationNavigator(SelectedOccurrence); // This needs to be outside the block for the UOW, since what we are suppressing // happens at the completion of the UOW. + FocusBox.Hide(); SuppressResettingGuesses( () => { @@ -2316,8 +2293,6 @@ public void ApproveAllSuggestedAnalyses(Command cmd) UndoableUnitOfWorkHelper.Do(cmd.UndoText, cmd.RedoText, Cache.ActionHandlerAccessor, () => { - var nav = new SegmentServices.StTextAnnotationNavigator(SelectedOccurrence); - AnalysisOccurrence lastOccurrence; var analyses = navigator.GetAnalysisOccurrencesAdvancingInStText().ToList(); foreach (var occ in analyses) { // This could be punctuation or any kind of analysis. @@ -2326,42 +2301,20 @@ public void ApproveAllSuggestedAnalyses(Command cmd) { // this is an analysis or a wordform int hvo = Vc.GetGuess(occAn); if (occAn.Hvo != hvo) - { // this is a guess, so approve it - // 1) A second occurence of a word that has had a lexicon entry or sense created for it. - // 2) A parser result - not sure which gets picked if multiple. - // #2 May take a while to "percolate" through to become a "guess". - var guess = Cache.ServiceLocator.ObjectRepository.GetObject(hvo); - IWfiAnalysis workingAnalysis; - if (guess != null && guess is IAnalysis) - { - occ.Segment.AnalysesRS[occ.Index] = (IAnalysis)guess; - workingAnalysis = guess as IWfiAnalysis; - } - else - { - workingAnalysis = occAn.Wordform.AnalysesOC.FirstOrDefault(); - occ.Segment.AnalysesRS[occ.Index] = workingAnalysis; - } - - if (workingAnalysis != null) - { - // Make sure this analysis is marked as user-approved. - Cache.LangProject.DefaultUserAgent.SetEvaluation(workingAnalysis, Opinions.approves); - } + { + // Move the sandbox to the next AnalysisOccurrence, then do the approval (using the sandbox data). + sandbox.SwitchWord(occ); + FocusBox.ApproveAnalysis(occ, true, true); } - /* else if (occAn.HasWordform && occAn.Wordform.ParserCount > 0) - { // this doesn't seem to be needed (and may not be correct) - always caught above - bool isHumanNoOpinion = occAn.Wordform.HumanNoOpinionParses.Cast().Any(wf => wf.Hvo == occAn.Hvo); - if (isHumanNoOpinion) - { - occ.Segment.AnalysesRS[occ.Index] = occAn.Wordform.AnalysesOC.FirstOrDefault(); - } - } */ } } + + // Restore the sandbox. + sandbox.SwitchWord(focusedWf); }); - } - ); + }); + FocusBox.Show(); + // MoveFocusBoxIntoPlace(); if (focusedWf != null) SelectOccurrence(focusedWf); diff --git a/Src/LexText/Interlinear/SandboxBase.cs b/Src/LexText/Interlinear/SandboxBase.cs index 6b68ac4605..32daddbdb6 100644 --- a/Src/LexText/Interlinear/SandboxBase.cs +++ b/Src/LexText/Interlinear/SandboxBase.cs @@ -4527,7 +4527,7 @@ public virtual bool OnJumpToTool(object commandObject) // not what we started with. We would save anyway as we switched views, so do it now. var parent = Controller; if (parent != null) - parent.UpdateRealFromSandbox(null, false, null); + parent.UpdateRealFromSandbox(null, false); // This leaves the parent in a bad state, but maybe it would be good if all this is // happening in some other parent, such as the words analysis view? //m_hvoAnalysisGuess = GetRealAnalysis(false); From 551c6bacd218ee9c5c2276a3a1485e61c2a32491 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 5 Feb 2024 12:22:36 -0800 Subject: [PATCH 038/415] Fix regression LT-21721 - Enter should move to trans or note * Using the enter key on the last occurence of a segment should move focust to the first translation or note line Change-Id: Id2623ac4b9e5d23f50064f498676aa304d1b44c1 --- .../Interlinear/FocusBoxController.ApproveAndMove.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index 23b4054e1b..851c7fe146 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -160,6 +160,15 @@ public void OnNextBundle(ICommandUndoRedoText undoRedoText, bool fSaveGuess, boo if (InterlinWordControl!= null) currentLineIndex = InterlinWordControl.GetLineOfCurrentSelection(); var nextOccurrence = GetNextOccurrenceToAnalyze(fForward, skipFullyAnalyzedWords); + // If we are at the end of a segment we should move to the first Translation or note line (if any) + if(nextOccurrence.Segment != SelectedOccurrence.Segment || nextOccurrence == SelectedOccurrence) + { + if (InterlinDoc.SelectFirstTranslationOrNote()) + { + // We moved to a translation or note line, exit + return; + } + } InterlinDoc.TriggerAnalysisSelected(nextOccurrence, fSaveGuess, fMakeDefaultSelection); if (!fMakeDefaultSelection && currentLineIndex >= 0 && InterlinWordControl != null) InterlinWordControl.SelectOnOrBeyondLine(currentLineIndex, 1); From 48837ac946517c3689b75643039374452cf6c238 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 1 Feb 2024 15:14:58 -0500 Subject: [PATCH 039/415] LT-21424: Fix Approve undo issue Fixed the undo problem described in Scenario 4 in the defect. The undo problem only existed after selecting a Morphemes and then clicking on another word to do the approval. (The other approval workflows did not have this issue.) The problem was resolved by executing the code in a way that is similar to what the other approval workflows do. Change-Id: Ia62e5a8be4c503ce4a4b972b9ec32e335b96b24b --- .../FocusBoxController.ApproveAndMove.cs | 58 ++++++++++++++++--- .../Interlinear/InterlinDocForAnalysis.cs | 5 +- Src/LexText/Interlinear/SandboxBase.cs | 2 +- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index 851c7fe146..f2bd5ef76b 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -34,8 +34,7 @@ internal void ApproveAndStayPut(ICommandUndoRedoText undoRedoText) /// Normally, this is invoked as a result of pressing the key /// or clicking the "Approve and Move Next" green check in an analysis. /// - /// - internal virtual void ApproveAndMoveNext(ICommandUndoRedoText cmd) + internal void ApproveAndMoveNext(ICommandUndoRedoText cmd) { if (!PreCheckApprove()) return; @@ -52,6 +51,36 @@ internal virtual void ApproveAndMoveNext(ICommandUndoRedoText cmd) OnNextBundle(cmd, false, false, false, true); } + /// + /// Approves an analysis (if there are edits or if fSaveGuess is true and there is a guess) and + /// moves the selection to target. + /// + /// The occurrence to move to. + /// If the FocusBox parent is not set, then use this value to set it. + /// if true, saves guesses; if false, skips guesses but still saves edits. + /// true to make the default selection within the new sandbox. + internal void ApproveAndMoveTarget(AnalysisOccurrence target, InterlinDocForAnalysis parent, bool fSaveGuess, bool fMakeDefaultSelection) + { + if (!PreCheckApprove()) + return; + + if (Parent == null) + { + Parent = parent; + } + + UndoableUnitOfWorkHelper.Do(ITextStrings.ksUndoApproveAnalysis, ITextStrings.ksRedoApproveAnalysis, Cache.ActionHandlerAccessor, + () => + { + ApproveAnalysis(SelectedOccurrence, false, fSaveGuess); + }); + + // This should not make any data changes, since we're telling it not to save and anyway + // we already saved the current annotation. And it can't correctly place the focus box + // until the change we just did are completed and PropChanged sent. So keep this outside the UOW. + TargetBundle(target, false, fMakeDefaultSelection); + } + /// /// /// @@ -149,16 +178,13 @@ protected bool CanNavigateBundles } /// - /// Move to the next bundle in the direction indicated by fForward. If fSaveGuess is true, save guesses in the current position, - /// using Undo text from the command. If skipFullyAnalyzedWords is true, move to the next item needing analysis, otherwise, the immediate next. + /// Move to the next bundle in the direction indicated by fForward. If fSaveGuess is true, save guesses in the current position. + /// If skipFullyAnalyzedWords is true, move to the next item needing analysis, otherwise, the immediate next. /// If fMakeDefaultSelection is true, make the default selection within the moved focus box. /// - public void OnNextBundle(ICommandUndoRedoText undoRedoText, bool fSaveGuess, bool skipFullyAnalyzedWords, + public void OnNextBundle(ICommandUndoRedoText _, bool fSaveGuess, bool skipFullyAnalyzedWords, bool fMakeDefaultSelection, bool fForward) { - int currentLineIndex = -1; - if (InterlinWordControl!= null) - currentLineIndex = InterlinWordControl.GetLineOfCurrentSelection(); var nextOccurrence = GetNextOccurrenceToAnalyze(fForward, skipFullyAnalyzedWords); // If we are at the end of a segment we should move to the first Translation or note line (if any) if(nextOccurrence.Segment != SelectedOccurrence.Segment || nextOccurrence == SelectedOccurrence) @@ -169,7 +195,21 @@ public void OnNextBundle(ICommandUndoRedoText undoRedoText, bool fSaveGuess, boo return; } } - InterlinDoc.TriggerAnalysisSelected(nextOccurrence, fSaveGuess, fMakeDefaultSelection); + TargetBundle(nextOccurrence, fSaveGuess, fMakeDefaultSelection); + } + + /// + /// Move to the target bundle. + /// + /// The occurrence to move to. + /// if true, saves guesses in the current position; if false, skips guesses but still saves edits. + /// true to make the default selection within the moved focus box. + public void TargetBundle(AnalysisOccurrence target, bool fSaveGuess, bool fMakeDefaultSelection) + { + int currentLineIndex = -1; + if (InterlinWordControl != null) + currentLineIndex = InterlinWordControl.GetLineOfCurrentSelection(); + InterlinDoc.TriggerAnalysisSelected(target, fSaveGuess, fMakeDefaultSelection); if (!fMakeDefaultSelection && currentLineIndex >= 0 && InterlinWordControl != null) InterlinWordControl.SelectOnOrBeyondLine(currentLineIndex, 1); } diff --git a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs index 0358115149..dd66ac1729 100644 --- a/Src/LexText/Interlinear/InterlinDocForAnalysis.cs +++ b/Src/LexText/Interlinear/InterlinDocForAnalysis.cs @@ -2042,7 +2042,7 @@ public override void OriginalWndProc(ref Message msg) /// /// if true, saves guesses; if false, skips guesses but still saves edits. /// - protected virtual bool HandleClickSelection(IVwSelection vwselNew, bool fBundleOnly, bool fSaveGuess) + protected bool HandleClickSelection(IVwSelection vwselNew, bool fBundleOnly, bool fSaveGuess) { if (vwselNew == null) return false; // couldn't select a bundle! @@ -2138,7 +2138,8 @@ protected virtual bool HandleClickSelection(IVwSelection vwselNew, bool fBundleO TryHideFocusBoxAndUninstall(); return false; } - TriggerAnnotationSelected(new AnalysisOccurrence(seg, ianalysis), fSaveGuess); + + FocusBox.ApproveAndMoveTarget(new AnalysisOccurrence(seg, ianalysis), this, fSaveGuess, true); return true; } diff --git a/Src/LexText/Interlinear/SandboxBase.cs b/Src/LexText/Interlinear/SandboxBase.cs index 32daddbdb6..e866a8e9cc 100644 --- a/Src/LexText/Interlinear/SandboxBase.cs +++ b/Src/LexText/Interlinear/SandboxBase.cs @@ -3789,7 +3789,7 @@ internal void ClearAllGlosses() /// public bool ShouldSave(bool fSaveGuess) { - return m_caches.DataAccess.IsDirty() || fSaveGuess && UsingGuess; + return m_caches.DataAccess.IsDirty() || (fSaveGuess && UsingGuess); } /// From a1e13f73bb917c5511df56b6c5d3586f63da6403 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Tue, 6 Feb 2024 13:37:59 -0500 Subject: [PATCH 040/415] =?UTF-8?q?LT-21424:=20Code=20Cleanup=20=E2=80=93?= =?UTF-8?q?=20remove=20unused=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unused parameter on OnNextBundle() Change-Id: Ifd665d40738cd94b2a55e231332bcec51181bdbf --- .../FocusBoxController.ApproveAndMove.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index f2bd5ef76b..1b882d5669 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -48,7 +48,7 @@ internal void ApproveAndMoveNext(ICommandUndoRedoText cmd) // This should not make any data changes, since we're telling it not to save and anyway // we already saved the current annotation. And it can't correctly place the focus box // until the change we just did are completed and PropChanged sent. So keep this outside the UOW. - OnNextBundle(cmd, false, false, false, true); + OnNextBundle(false, false, false, true); } /// @@ -182,8 +182,7 @@ protected bool CanNavigateBundles /// If skipFullyAnalyzedWords is true, move to the next item needing analysis, otherwise, the immediate next. /// If fMakeDefaultSelection is true, make the default selection within the moved focus box. /// - public void OnNextBundle(ICommandUndoRedoText _, bool fSaveGuess, bool skipFullyAnalyzedWords, - bool fMakeDefaultSelection, bool fForward) + public void OnNextBundle(bool fSaveGuess, bool skipFullyAnalyzedWords, bool fMakeDefaultSelection, bool fForward) { var nextOccurrence = GetNextOccurrenceToAnalyze(fForward, skipFullyAnalyzedWords); // If we are at the end of a segment we should move to the first Translation or note line (if any) @@ -355,7 +354,7 @@ public void ApproveGuessOrChangesForWholeTextAndMoveNext(Command cmd) // This should not make any data changes, since we're telling it not to save and anyway // we already saved the current annotation. And it can't correctly place the focus box // until the change we just did are completed and PropChanged sent. So keep this outside the UOW. - OnNextBundle(cmd, false, false, false, true); + OnNextBundle(false, false, false, true); } /// @@ -453,7 +452,7 @@ public bool OnDisplayApproveAndMoveNextSameLine(object commandObject, ref UIItem public bool OnApproveAndMoveNextSameLine(object cmd) { - OnNextBundle(cmd as Command, true, false, false, true); + OnNextBundle(true, true, true, true); return true; } @@ -484,7 +483,7 @@ public bool OnDisplayBrowseMoveNextSameLine(object commandObject, ref UIItemDisp public bool OnBrowseMoveNextSameLine(object cmd) { - OnNextBundle(cmd as Command, false, false, false, true); + OnNextBundle(false, false, false, true); return true; } @@ -497,7 +496,7 @@ public bool OnDisplayBrowseMoveNext(object commandObject, ref UIItemDisplayPrope public bool OnBrowseMoveNext(object cmd) { - OnNextBundle(cmd as Command, false, false, true, true); + OnNextBundle(false, false, true, true); return true; } @@ -566,7 +565,7 @@ public bool OnMoveFocusBoxRight(object cmd) public void OnMoveFocusBoxRight(ICommandUndoRedoText undoRedoText, bool fSaveGuess) { // Move in the literal direction (LT-3706) - OnNextBundle(undoRedoText, fSaveGuess, false, true, !m_fRightToLeft); + OnNextBundle(fSaveGuess, false, true, !m_fRightToLeft); } /// @@ -598,7 +597,7 @@ public bool OnMoveFocusBoxRightNc(object cmd) /// public bool OnMoveFocusBoxLeft(object cmd) { - OnNextBundle(cmd as ICommandUndoRedoText, true, false, true, m_fRightToLeft); + OnNextBundle(true, false, true, m_fRightToLeft); return true; } @@ -621,7 +620,7 @@ public virtual bool OnDisplayMoveFocusBoxLeftNc(object commandObject, ref UIItem /// public bool OnMoveFocusBoxLeftNc(object cmd) { - OnNextBundle(cmd as ICommandUndoRedoText, false, false, true, m_fRightToLeft); + OnNextBundle(false, false, true, m_fRightToLeft); return true; } @@ -660,7 +659,7 @@ public virtual bool OnDisplayNextIncompleteBundleNc(object commandObject, ref UI /// public bool OnNextIncompleteBundle(object cmd) { - OnNextBundle(cmd as ICommandUndoRedoText, true, true, true, true); + OnNextBundle(true, true, true, true); return true; } @@ -671,7 +670,7 @@ public bool OnNextIncompleteBundle(object cmd) /// public bool OnNextIncompleteBundleNc(object cmd) { - OnNextBundle(cmd as ICommandUndoRedoText, false, true, true, true); + OnNextBundle(false, true, true, true); return true; } From 7c26c48aa8dcef55bd8c2eb8e382c69bd80dfb90 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 8 Feb 2024 15:42:46 -0500 Subject: [PATCH 041/415] LT-21634: Add senses missing from Pathway The Pathway code cares about what the class name starts with so instead of pre-pending the parent node to the name, we now append the parent node to the name. Also cleaned up the code that generates unique names to avoid getting names like this: ".headword-referencedentries-1-2-3-4-5" Instead we now get: ".headword-referencedentries-5" Change-Id: I84a68cfcbd2dfc87e6094881b74949202350b9f1 --- Src/xWorks/CssGenerator.cs | 9 ++++++--- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 14 +++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index de049a4492..b36814af35 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -120,12 +120,15 @@ public static string GetBestUniqueNameForNode(Dictionary ConfigurableDictionaryNode node) { Guard.AgainstNull(node.Parent, "There should not be duplicate class names at the top of tree."); - // first try pre-pending the parent node classname - var className = $".{GetClassAttributeForConfig(node.Parent)}-{GetClassAttributeForConfig(node)}"; + // First try appending the parent node classname. Pathway has code that cares about what + // the className starts with, so keep the 'node' name first. + var className = $".{GetClassAttributeForConfig(node)}-{GetClassAttributeForConfig(node.Parent)}"; + + string classNameBase = className; int counter = 0; while (styles.ContainsKey(className)) { - className = $"{className}-{++counter}"; + className = $"{classNameBase}-{++counter}"; } return className; } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 6af2b10723..aaec341336 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -185,7 +185,7 @@ public void GenerateCssForConfiguration_SharedConfigurationGeneratesValidCss() cssGenerator.AddStyles(subEntryHeadwordNode); var cssResult = cssGenerator.GetStylesString(); // verify that the css result contains a line similar to: .sharedsubentries .sharedsubentry .headword span{ - VerifyRegex(cssResult, @"^\s*\.sharedsubentries-mainheadword>\s*span\s*{.*", + VerifyRegex(cssResult, @"^\s*\.mainheadword-sharedsubentries>\s*span\s*{.*", "Css for child node(headword) did not generate a match"); } @@ -338,9 +338,9 @@ public void GenerateCssForConfiguration_BeforeAfterGroupingSpanWorks() Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, "css after rule for the grouping node was not generated"); // Check result for before and after rules equivalent to .headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-mh>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.mh-grouping_hwg>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, "css before rule with Z content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-mh>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.mh-grouping_hwg>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, "css after rule with A content not found on headword"); } @@ -438,10 +438,10 @@ public void GenerateCssForConfiguration_BeforeAfterConfigGeneratesBeforeAfterCss cssGenerator.AddStyles(mainEntryHeadword); cssGenerator.AddStyles(headwordNode); var cssResult = cssGenerator.GetStylesString(); - // Check result for before and after rules equivalent to .subentries-headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - VerifyRegex(cssResult, @"\.subentries-headword>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", + // Check result for before and after rules equivalent to .headword-subentries span:first-child{content:'Z';} and .headword span:last-child{content:'A'} + VerifyRegex(cssResult, @"\.headword-subentries>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", "css before rule with Z content not found on headword"); - VerifyRegex(cssResult, @"\.subentries-headword>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", + VerifyRegex(cssResult, @"\.headword-subentries>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", "css after rule with A content not found on headword"); } @@ -1936,7 +1936,7 @@ public void GenerateCssForConfiguration_SenseComplexFormsNotSubEntriesHeadWord() cssGenerator.AddStyles(headwordMain); cssGenerator.AddStyles(form); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"^\s*\.otherreferencedcomplexforms-headword", "Headword node not generated for non subentry headword"); + VerifyRegex(cssResult, @"^\s*\.headword-otherreferencedcomplexforms", "Headword node not generated for non subentry headword"); } [Test] From bb111a20c0d060c3a0810779cdef086f29f8602c Mon Sep 17 00:00:00 2001 From: mark-sil Date: Tue, 20 Feb 2024 16:24:25 -0500 Subject: [PATCH 042/415] =?UTF-8?q?WIP:=20LT-21634:=20Fix=20=E2=80=98After?= =?UTF-8?q?=E2=80=99=20text=20being=20applied=20twice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The text in the dictionary configuration field: Senses->Grammatical Info.->After was being applied twice to the output that was displayed. Change-Id: I0fc97a3e28af73ea0bd8cd93ae7ede639b2f8544 --- Src/xWorks/CssGenerator.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index b36814af35..7ec6b930af 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -968,6 +968,7 @@ private static string SelectClassName(ConfigurableDictionaryNode configNode, str case ConfiguredLcmGenerator.PropertyType.CollectionType: { // for collections we generate a css selector to match each item e.g '.senses .sense' + // TODO - Should we do the same here? The old code was calling GetClassAttributeForConfig(). return string.Format("{0} .{1}", adjustedClassName, GetClassAttributeForCollectionItem(configNode)); } case ConfiguredLcmGenerator.PropertyType.CmPictureType: @@ -985,11 +986,26 @@ private static string SelectClassName(ConfigurableDictionaryNode configNode, str { spanStyle = "> span"; } + // TODO - Should we do the same here? The old code was calling GetClassAttributeForConfig(). return adjustedClassName + spanStyle; } goto default; } default: + // TODO - Do we just want CSSClassNameOverride or do we want the additional + // name generation that is done in GetClassAttributeForConfig(). + + // Only append the CSSClassNameOverride if it is not already part of the adjustedClassName. + if (configNode.CSSClassNameOverride != null) + { + string classAttName = CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFC) + .Normalize(configNode.CSSClassNameOverride.ToLower()); + + if (!adjustedClassName.ToLower().Contains(classAttName)) + { + return string.Format("{0} > .{1}", adjustedClassName, classAttName); + } + } return adjustedClassName; } } From 6a61f16774d50d3f5ec30e8eb1f49d843ddcbf19 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 19 Feb 2024 16:16:38 -0800 Subject: [PATCH 043/415] Improve buildLocalLibraries script * Ensure Download and Output directores exist * Clean old packages before calling dotnet pack Change-Id: I5626e729d94bf77308c97ad47fcdf9d87a0e1787 --- Build/buildLocalLibraries.sh | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Build/buildLocalLibraries.sh b/Build/buildLocalLibraries.sh index 925db030e3..be836b7384 100755 --- a/Build/buildLocalLibraries.sh +++ b/Build/buildLocalLibraries.sh @@ -39,8 +39,11 @@ function delete_and_pack_liblcm { echo "Deleting files starting with 'SIL.LCModel' in $packages_dir" find "$packages_dir" -name 'SIL.LCModel*' -exec rm -f -r {} \; + echo "Removing liblcm output packages so that dotnet pack will run and output the version" + (cd "$liblcm_dir/artifacts" && rm *nupkg) + echo "Running 'dotnet pack' in the liblcm directory: $liblcm_dir" - pack_output=$(cd "$liblcm_dir" && dotnet pack) + pack_output=$(cd "$liblcm_dir" && dotnet pack -c Debug) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -80,8 +83,11 @@ function delete_and_pack_chorus { find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; + echo "Removing chorus output packages so that dotnet pack will run and output the version" + (cd "$chorus_dir/output" && rm *nupkg) + echo "Running 'dotnet pack' in the chorus directory: $chorus_dir" - pack_output=$(cd "$chorus_dir" && dotnet pack) + pack_output=$(cd "$chorus_dir" && dotnet pack -c Debug) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -120,8 +126,11 @@ function delete_and_pack_libpalaso { find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; done + echo "Removing palaso output packages so that dotnet pack will run and output the version" + (cd "$libpalaso_dir/output" && rm *nupkg) + echo "Running 'dotnet pack' in the libpalaso directory: $libpalaso_dir" - pack_output=$(cd "$libpalaso_dir" && dotnet pack) + pack_output=$(cd "$libpalaso_dir" && dotnet pack -c Debug) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -234,6 +243,9 @@ if [ -z "$libpalaso_dir" ] && [ -z "$liblcm_dir" ] && [ -z "$chorus_dir" ]; then display_usage fi +mkdir ../Output/Debug +mkdir ../Downloads + # Display the provided directory paths echo "libpalaso directory: $libpalaso_dir" echo "liblcm directory: $liblcm_dir" From 3df052d794b636389d93bd8e2d2ee0514d0a72bd Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 20 Feb 2024 14:54:48 -0800 Subject: [PATCH 044/415] =?UTF-8?q?Revert=20"WIP:=20LT-21634:=20Fix=20?= =?UTF-8?q?=E2=80=98After=E2=80=99=20text=20being=20applied..."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bb111a20c0d060c3a0810779cdef086f29f8602c. Change-Id: Ie541029ddbf71bbb70d2bbc91849be226d6e1579 --- Src/xWorks/CssGenerator.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 7ec6b930af..b36814af35 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -968,7 +968,6 @@ private static string SelectClassName(ConfigurableDictionaryNode configNode, str case ConfiguredLcmGenerator.PropertyType.CollectionType: { // for collections we generate a css selector to match each item e.g '.senses .sense' - // TODO - Should we do the same here? The old code was calling GetClassAttributeForConfig(). return string.Format("{0} .{1}", adjustedClassName, GetClassAttributeForCollectionItem(configNode)); } case ConfiguredLcmGenerator.PropertyType.CmPictureType: @@ -986,26 +985,11 @@ private static string SelectClassName(ConfigurableDictionaryNode configNode, str { spanStyle = "> span"; } - // TODO - Should we do the same here? The old code was calling GetClassAttributeForConfig(). return adjustedClassName + spanStyle; } goto default; } default: - // TODO - Do we just want CSSClassNameOverride or do we want the additional - // name generation that is done in GetClassAttributeForConfig(). - - // Only append the CSSClassNameOverride if it is not already part of the adjustedClassName. - if (configNode.CSSClassNameOverride != null) - { - string classAttName = CustomIcu.GetIcuNormalizer(FwNormalizationMode.knmNFC) - .Normalize(configNode.CSSClassNameOverride.ToLower()); - - if (!adjustedClassName.ToLower().Contains(classAttName)) - { - return string.Format("{0} > .{1}", adjustedClassName, classAttName); - } - } return adjustedClassName; } } From 57f53639bd3b28436fda17898a154accdd1c4461 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 21 Feb 2024 14:58:52 -0500 Subject: [PATCH 045/415] =?UTF-8?q?LT-21634:=20Fix=20=E2=80=98After?= =?UTF-8?q?=E2=80=99=20text=20being=20applied=20twice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The text in the dictionary configuration field: Senses->Grammatical Info.->After was being applied twice to the output that was displayed. The sharedgrammaticalinfo class is no longer needed in this Case , and was resulting in the duplicate ‘After’ text, so removed it. Change-Id: I4bc90e33cd3a9e49959516a1a396feafce273be8 --- Src/xWorks/CssGenerator.cs | 9 ---- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 56 +++++++++++++++++++-- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index b36814af35..cc2e49caa5 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -494,15 +494,6 @@ private static List GenerateCssForSenses(ConfigurableDictionaryNode c if (!IsEmptyRule(senseContentRule)) styleRules.Add(senseContentRule); } - - if (senseOptions.ShowSharedGrammarInfoFirst) - { - foreach (var gramInfoNode in configNode.Children.Where(node => node.FieldDescription == "MorphoSyntaxAnalysisRA" && node.IsEnabled)) - { - styleRules.AddRange(GenerateCssFromConfigurationNode(gramInfoNode, collectionSelector + "> .sharedgrammaticalinfo", propertyTable)); - } - } - return styleRules; } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index aaec341336..66c9d3cc4a 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -2009,10 +2009,58 @@ public void GenerateCssForConfiguration_SenseShowGramInfoFirstWorks() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(gramInfo); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra", "Style for non-shared grammatical info not generated"); - VerifyRegex(cssResult, - @"^\s*\.senses\s*>\s*\.sharedgrammaticalinfo\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", - "Style for sharedgrammaticalinfo not placed correctly"); + VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra", "Style for morphosyntaxanalysisra not generated"); + VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", + "Style for morphosyntaxanalysisra not placed correctly"); + } + + [Test] + public void GenerateCssForConfiguration_GramInfoAfterText() + { + GenerateStyle("Dictionary-Contrasting"); + var pos = new ConfigurableDictionaryNode { FieldDescription = "MLPartOfSpeech" }; + var inflectionClass = new ConfigurableDictionaryNode { FieldDescription = "MLInflectionClass" }; + var afterText = "ExactlyOnce"; + var gramInfo = new ConfigurableDictionaryNode + { + FieldDescription = "MorphoSyntaxAnalysisRA", + Label = "Gram. Info.", + Children = new List { pos, inflectionClass }, + Style = "Dictionary-Contrasting", + After = afterText + }; + var gloss = new ConfigurableDictionaryNode { FieldDescription = "Gloss", Style = "FooStyle" }; + var senses = new ConfigurableDictionaryNode + { + FieldDescription = "SensesOS", + CSSClassNameOverride = "Senses", + DictionaryNodeOptions = new DictionaryNodeSenseOptions { ShowSharedGrammarInfoFirst = true }, + Children = new List { gramInfo, gloss } + }; + var entry = new ConfigurableDictionaryNode + { + FieldDescription = "LexEntry", + CSSClassNameOverride = "lexentry", + Children = new List { senses } + }; + + var model = new DictionaryConfigurationModel(); + model.Parts = new List { entry }; + PopulateFieldsForTesting(entry); + var cssGenerator = new CssGenerator(); + cssGenerator.Init(m_propertyTable); + cssGenerator.AddGlobalStyles(model, m_propertyTable); + + //SUT + cssGenerator.AddStyles(senses); + cssGenerator.AddStyles(gramInfo); + var cssResult = cssGenerator.GetStylesString(); + + // Check that the after text is included once, not more or less. + var firstIndex = cssResult.IndexOf(afterText); + var lastIndex = cssResult.LastIndexOf(afterText); + Assert.IsTrue(firstIndex != -1 && firstIndex == lastIndex, + string.Format("After text \'{0}\' was not included exactly one time.", afterText)); } [Test] From ebbf5a64f3d53d28c89fa4cc8549b0beb7687d76 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Wed, 21 Feb 2024 15:55:31 -0500 Subject: [PATCH 046/415] =?UTF-8?q?LT-21634:=20Fix=20=E2=80=98After?= =?UTF-8?q?=E2=80=99=20text=20not=20displaying=20for=20Pictures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pictures Before, Between, and After content should not be in .captionContent. Removed the code that added this to the collectionSelector. Was: .captionContent .pictures> div:first-child:before{ content:'PBefore'; } Now is: .pictures> div:first-child:before{ content:'PBefore'; } Change-Id: I5d80a5b60d938a1abb6459f6e183fc14ea2bee1f --- Src/xWorks/CssGenerator.cs | 1 - Src/xWorks/xWorksTests/CssGeneratorTests.cs | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index cc2e49caa5..78d34c524f 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -840,7 +840,6 @@ private static List GenerateSelectorsFromNode(ConfigurableDictionaryN } case DictionaryNodePictureOptions _: { - collectionSelector = pictCaptionContent + "." + GetClassAttributeForConfig(configNode); betweenSelector = string.Format("{0}> {1}+{1}:before", collectionSelector, " div"); break; } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 66c9d3cc4a..9bec87275c 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -2871,6 +2871,24 @@ public void GenerateCssForConfiguration_PictureBeforeBetweenAfterIsAreGenerated( var pictureBetween = @".*\.pictures>\s*div\s*\+\s*div:before\{\s*content:', ';"; VerifyRegex(cssResult, pictureBetween, "expected Picture between rule is generated"); + + // Verify that the before/after/between picture content is not nested in 'captionContent'. + RegexOptions options = RegexOptions.Singleline | RegexOptions.Multiline; + var captionContentPictureBefore = @".captionContent .pictures> div:first-child:before\{\s*content:'\[';"; + string message = "did not expect Picture before rule to be nested in captionContent."; + Assert.IsFalse(Regex.Match(cssResult, captionContentPictureBefore, options).Success, + string.Format("{3}Expected{0}{1}{0}but got{0}{2}", Environment.NewLine, pictureBefore, cssResult, message + Environment.NewLine)); + + var captionContentPictureAfter = @".captionContent .pictures> div:last-child:after\{\s*content:'\]';"; + message = "did not expect Picture after rule to be nested in captionContent."; + Assert.IsFalse(Regex.Match(cssResult, captionContentPictureAfter, options).Success, + string.Format("{3}Expected{0}{1}{0}but got{0}{2}", Environment.NewLine, pictureAfter, cssResult, message + Environment.NewLine)); + + var captionContentPictureBetween = @".captionContent .*\.pictures>\s*div\s*\+\s*div:before\{\s*content:', ';"; + VerifyRegex(cssResult, pictureBetween, "expected Picture between rule is generated"); + message = "did not expect Picture between rule to be nested in captionContent."; + Assert.IsFalse(Regex.Match(cssResult, captionContentPictureBetween, options).Success, + string.Format("{3}Expected{0}{1}{0}but got{0}{2}", Environment.NewLine, pictureBetween, cssResult, message + Environment.NewLine)); } From 5eec01d790d1abb5ef544f9b88ce715cc32a0900 Mon Sep 17 00:00:00 2001 From: mark-sil Date: Thu, 29 Feb 2024 14:17:49 -0500 Subject: [PATCH 047/415] Use lcm 11.0.0-beta0087 Change-Id: Iaa780ee8aaf43ce90465ffc98fcfdd5848630ee1 --- Build/mkall.targets | 2 +- Build/nuget-common/packages.config | 18 +++++++++--------- .../ITextDllTests/InterlinLineChoicesTests.cs | 8 ++++---- .../LexTextControlsTests/LiftMergerTests.cs | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Build/mkall.targets b/Build/mkall.targets index e7c267f83f..5d9d4a5aa8 100644 --- a/Build/mkall.targets +++ b/Build/mkall.targets @@ -285,7 +285,7 @@ 5.2.0-beta0003 13.0.0-beta0076 9.4.0.1-beta - 11.0.0-beta0083 + 11.0.0-beta0087 70.1.123 2.5.13 diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index be5a41057b..74b142cd1c 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -51,15 +51,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/Src/LexText/Interlinear/ITextDllTests/InterlinLineChoicesTests.cs b/Src/LexText/Interlinear/ITextDllTests/InterlinLineChoicesTests.cs index d5687409ad..beba2dffe2 100644 --- a/Src/LexText/Interlinear/ITextDllTests/InterlinLineChoicesTests.cs +++ b/Src/LexText/Interlinear/ITextDllTests/InterlinLineChoicesTests.cs @@ -533,13 +533,13 @@ public void AddCustomSpecsForAnalAndVern() var wsFrn = frWs.Handle; using (var cFirstAnal = new CustomFieldForTest(Cache, - "Candy Apple Red", + "Candy Apple Red Anal", Cache.MetaDataCacheAccessor.GetClassId("Segment"), WritingSystemServices.kwsAnal, CellarPropertyType.String, Guid.Empty)) using (var cFirstVern = new CustomFieldForTest(Cache, - "Candy Apple Red", + "Candy Apple Red Vern", Cache.MetaDataCacheAccessor.GetClassId("Segment"), WritingSystemServices.kwsVern, CellarPropertyType.String, @@ -571,13 +571,13 @@ public void CreateSpecForCustomAlwaysUsesDefaultWS() var wsGer = deWs.Handle; using (var cFirstAnal = new CustomFieldForTest(Cache, - "Candy Apple Red", + "Candy Apple Red Anal", Cache.MetaDataCacheAccessor.GetClassId("Segment"), WritingSystemServices.kwsAnal, CellarPropertyType.String, Guid.Empty)) using (var cFirstVern = new CustomFieldForTest(Cache, - "Candy Apple Red", + "Candy Apple Red Vern", Cache.MetaDataCacheAccessor.GetClassId("Segment"), WritingSystemServices.kwsVern, CellarPropertyType.String, diff --git a/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs b/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs index 486ff161c2..006899e51b 100644 --- a/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs +++ b/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs @@ -2765,7 +2765,7 @@ public void TestLiftImport9AMergingStTextKeepBoth() SetWritingSystems("fr"); CreateNeededStyles(); - var flidCustom = CreateFirstEntryWithConflictingData(); + var flidCustom = CreateFirstEntryWithConflictingData("Long Text1"); var repoEntry = Cache.ServiceLocator.GetInstance(); var repoSense = Cache.ServiceLocator.GetInstance(); @@ -2789,7 +2789,7 @@ public void TestLiftImport9BMergingStTextKeepOld() SetWritingSystems("fr"); CreateNeededStyles(); - var flidCustom = CreateFirstEntryWithConflictingData(); + var flidCustom = CreateFirstEntryWithConflictingData("Long Text2"); var repoEntry = Cache.ServiceLocator.GetInstance(); var repoSense = Cache.ServiceLocator.GetInstance(); @@ -2813,7 +2813,7 @@ public void TestLiftImport9CMergingStTextKeepNew() SetWritingSystems("fr"); CreateNeededStyles(); - var flidCustom = CreateFirstEntryWithConflictingData(); + var flidCustom = CreateFirstEntryWithConflictingData("Long Text3"); var repoEntry = Cache.ServiceLocator.GetInstance(); var repoSense = Cache.ServiceLocator.GetInstance(); @@ -2855,7 +2855,7 @@ public void TestLiftImport9DMergingStTextKeepOnlyNew() SetWritingSystems("fr"); CreateNeededStyles(); - var flidCustom = CreateFirstEntryWithConflictingData(); + var flidCustom = CreateFirstEntryWithConflictingData("Long Text4"); var repoEntry = Cache.ServiceLocator.GetInstance(); var repoSense = Cache.ServiceLocator.GetInstance(); @@ -2990,7 +2990,7 @@ private void VerifyFirstEntryStTextDataImportExact(ILexEntryRepository repoEntry Assert.IsTrue(tss.Equals(para.Contents), "The third paragraph contents should have all its formatting."); } - private int CreateFirstEntryWithConflictingData() + private int CreateFirstEntryWithConflictingData(string customFieldName) { var entry0 = Cache.ServiceLocator.GetInstance().Create( new Guid("494616cc-2f23-4877-a109-1a6c1db0887e"), Cache.LangProject.LexDbOA); @@ -3007,7 +3007,7 @@ private int CreateFirstEntryWithConflictingData() var mdc = Cache.MetaDataCacheAccessor as IFwMetaDataCacheManaged; Assert.That(mdc, Is.Not.Null); - var flidCustom = mdc.AddCustomField("LexEntry", "Long Text", CellarPropertyType.OwningAtomic, StTextTags.kClassId); + var flidCustom = mdc.AddCustomField("LexEntry", customFieldName, CellarPropertyType.OwningAtomic, StTextTags.kClassId); var hvoText = Cache.DomainDataByFlid.MakeNewObject(StTextTags.kClassId, entry0.Hvo, flidCustom, -2); var text = Cache.ServiceLocator.GetInstance().GetObject(hvoText); From a527f42479e52a8da75942b3e497cddc0c4b4c52 Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Fri, 12 Jan 2024 13:55:11 -0500 Subject: [PATCH 048/415] LT-21707: Initial styling for word export Styles that are expected to be working: * Normal (this is created as a default style, so everything inherits from it unless overridden) * Dictionary-LetterHeading * Dictionary-Normal * Dictionary-Headword * Dictionary-POS * Dictionary-SenseNumber Formatting changes made to these styles in a FieldWorks project should be reflected in the word export unless they relate to below items that are not implemented yet. Things that are not implemented: * before/after/between content * display in separate paragraphs * custom bullets & numbering * subscript/superscript * underline * strikethrough Change-Id: I01a82514fcbacf127a3bc48a08a5906ef29c300f --- Src/xWorks/ConfiguredLcmGenerator.cs | 107 ++-- Src/xWorks/CssGenerator.cs | 6 +- Src/xWorks/ILcmContentGenerator.cs | 30 +- Src/xWorks/LcmJsonGenerator.cs | 41 +- Src/xWorks/LcmWordGenerator.cs | 622 ++++++++++++------ Src/xWorks/LcmXhtmlGenerator.cs | 42 +- Src/xWorks/WordStylesGenerator.cs | 911 +++++++++++++++++++++++++++ Src/xWorks/xWorks.csproj | 1 + 8 files changed, 1470 insertions(+), 290 deletions(-) create mode 100644 Src/xWorks/WordStylesGenerator.cs diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 6c4a9a5059..047a475ce6 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -34,7 +34,6 @@ using System.Threading; using System.Web.UI.WebControls; using XCore; -using FileUtils = SIL.LCModel.Utils.FileUtils; using UnitType = ExCSS.UnitType; namespace SIL.FieldWorks.XWorks @@ -333,12 +332,10 @@ internal static IFragment GenerateContentForEntry(ICmObject entry, ConfigurableD return settings.ContentGenerator.CreateFragment(); } - var pieces = configuration.ReferencedOrDirectChildren - .Select(config => - GenerateContentForFieldByReflection(entry, config, publicationDecorator, - settings)) - .Where(content => content!=null && !string.IsNullOrEmpty(content.ToString())).ToList(); + .Select(config => new ConfigFragment(config, GenerateContentForFieldByReflection(entry, config, publicationDecorator, + settings))) + .Where(content => content.Frag!=null && !string.IsNullOrEmpty(content.Frag.ToString())).ToList(); if (pieces.Count == 0) return settings.ContentGenerator.CreateFragment(); var bldr = settings.ContentGenerator.CreateFragment(); @@ -346,7 +343,7 @@ internal static IFragment GenerateContentForEntry(ICmObject entry, ConfigurableD { var clerk = settings.PropertyTable.GetValue("ActiveClerk", null); var entryClassName = settings.StylesGenerator.AddStyles(configuration).Trim('.'); - settings.ContentGenerator.StartEntry(xw, + settings.ContentGenerator.StartEntry(xw, configuration, entryClassName, entry.Guid, index, clerk); settings.ContentGenerator.AddEntryData(xw, pieces); settings.ContentGenerator.EndEntry(xw); @@ -807,7 +804,7 @@ private static IFragment GenerateContentForPossibility(object propertyValue, Con if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.WriteProcessedObject(false, bldr, className); + return settings.ContentGenerator.WriteProcessedObject(false, bldr, config, className); } // bldr is a fragment that is empty of text, since length = 0 @@ -825,7 +822,7 @@ private static IFragment GenerateContentForPictureCaption(object propertyValue, if (!content.IsNullOrEmpty()) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.WriteProcessedObject(true, content, className); + return settings.ContentGenerator.WriteProcessedObject(true, content, config,className); } return settings.ContentGenerator.CreateFragment(); } @@ -893,7 +890,7 @@ private static IFragment GenerateContentForDefOrGloss(ILexSense sense, Configura { var wsOption = config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions; if (wsOption == null) - throw new ArgumentException(@"Configuration nodes for MultiString fields whould have WritingSystemOptions", "config"); + throw new ArgumentException(@"Configuration nodes for MultiString fields would have WritingSystemOptions", "config"); var bldr = settings.ContentGenerator.CreateFragment(); foreach (var option in wsOption.Options) { @@ -911,8 +908,8 @@ private static IFragment GenerateContentForDefOrGloss(ILexSense sense, Configura if (bldr.Length() > 0) { - var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, config,className); } // bldr is a fragment that is empty of text, since length = 0 return bldr; @@ -938,7 +935,7 @@ private static IFragment GenerateContentForCaptionOrHeadword(ICmPicture picture, } } if (bldr.Length() > 0) - return settings.ContentGenerator.WriteProcessedCollection(false, bldr, GetClassNameAttributeForConfig(config)); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, config, GetClassNameAttributeForConfig(config)); // bldr is a fragment that is empty of text, since length = 0 return bldr; } @@ -1445,10 +1442,10 @@ private static IFragment GenerateContentForCollection(object collectionField, Co if (bldr.Length() > 0 || sharedCollectionInfo.Length() > 0) { - var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; + var className = settings.StylesGenerator.AddStyles(config).Trim('.'); return config.DictionaryNodeOptions is DictionaryNodeSenseOptions ? - settings.ContentGenerator.WriteProcessedSenses(false, bldr, className, sharedCollectionInfo) : - settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); + settings.ContentGenerator.WriteProcessedSenses(false, bldr, config, className, sharedCollectionInfo) : + settings.ContentGenerator.WriteProcessedCollection(false, bldr, config, className); } return settings.ContentGenerator.CreateFragment(); } @@ -1583,7 +1580,7 @@ private static void GenerateContentForLexEntryRefsByType(ConfigurableDictionaryN : null; var className = generateLexType ? settings.StylesGenerator.AddStyles(typeNode).Trim('.') : null; var refsByType = settings.ContentGenerator.AddLexReferences(generateLexType, - lexTypeContent, className, innerBldr.ToString(), IsTypeBeforeForm(config)); + lexTypeContent, config, className, innerBldr.ToString(), IsTypeBeforeForm(config)); bldr.Append(refsByType); } } @@ -1704,9 +1701,35 @@ private static IFragment GenerateContentForSenses(ConfigurableDictionaryNode con // So calculating outside the loop for performance. var isThisSenseNumbered = ShouldThisSenseBeNumbered(filteredSenseCollection[0], config, filteredSenseCollection); var bldr = settings.ContentGenerator.CreateFragment(); + + // TODO: Can handle separate sense paragraph styling here; likely will make the most sense to be handled wherever we deal with before/after content for senses. + // TODO: If handled here (or elsewhere w/in LcmGenerator), remove sense paragraph handling from the CssGenerator, otherwise sense paragraphs will be separated by two lines in the xhtml export. + /*// Only need to check once whether DisplayEachSenseInAParagraph is true -- value will be the same for each item in the loop + bool newParagraphPerSense; + if (senseNode?.DisplayEachSenseInAParagraph == true) + newParagraphPerSense = true; + else + newParagraphPerSense = false; + + //If the first sense must be inline, we append the first sense without a preceding line break + int startSense = 0; + if (senseNode?.DisplayFirstSenseInline == true) + { + bldr.Append(GenerateSenseContent(config, publicationDecorator, filteredSenseCollection[startSense], isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); + startSense++; + } + + for (int i = startSense; i < filteredSenseCollection.Count; i++)*/ + foreach (var item in filteredSenseCollection) { info.SenseCounter++; + + // TODO: sense paragraphs + /*// If each sense belongs in a new paragraph, append a line break before the sense content. + if (newParagraphPerSense) + bldr.AppendBreak();*/ + bldr.Append(GenerateSenseContent(config, publicationDecorator, item, isThisSenseNumbered, settings, isSameGrammaticalInfo, info)); } settings.StylesGenerator.AddStyles(config); @@ -1767,7 +1790,7 @@ private static IFragment InsertGramInfoBeforeSenses(ILexSense item, Configurable var content = GenerateContentForFieldByReflection(item, gramInfoNode, publicationDecorator, settings); if (content.IsNullOrEmpty()) return settings.ContentGenerator.CreateFragment(); - return settings.ContentGenerator.GenerateGramInfoBeforeSensesContent(content); + return settings.ContentGenerator.GenerateGramInfoBeforeSensesContent(content, gramInfoNode); } private static bool IsAllGramInfoTheSame(ConfigurableDictionaryNode config, IEnumerable collection, bool isSubsense, @@ -1869,9 +1892,9 @@ private static IFragment GenerateSenseContent(ConfigurableDictionaryNode config, } if (bldr.Length() == 0) return bldr; - var senseContent = bldr.ToString(); + return settings.ContentGenerator.AddSenseData(senseNumberSpan, IsBlockProperty(config), ((ICmObject)item).Owner.Guid, - senseContent, GetCollectionItemClassAttribute(config)); + bldr, GetCollectionItemClassAttribute(config)); } private static IFragment GeneratePictureContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, @@ -1957,7 +1980,7 @@ private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNod if (bldr.Length() == 0) return bldr; var collectionContent = bldr; - return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), collectionContent); + return settings.ContentGenerator.AddCollectionItem(IsBlockProperty(config), GetCollectionItemClassAttribute(config), config, collectionContent); } private static void GenerateContentForLexRefCollection(ConfigurableDictionaryNode config, @@ -2081,7 +2104,7 @@ private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNo { // targets settings.ContentGenerator.AddCollection(xw, IsBlockProperty(child), - CssGenerator.GetClassAttributeForConfig(child), contentBldr.ToString()); + CssGenerator.GetClassAttributeForConfig(child), config, contentBldr.ToString()); } break; case "OwnerType": @@ -2146,7 +2169,7 @@ private static IFragment GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryN var senseNumberWs = string.IsNullOrEmpty(info.HomographConfig.WritingSystem) ? "en" : info.HomographConfig.WritingSystem; if (string.IsNullOrEmpty(formattedSenseNumber)) return settings.ContentGenerator.CreateFragment(); - return settings.ContentGenerator.GenerateSenseNumber(formattedSenseNumber, senseNumberWs); + return settings.ContentGenerator.GenerateSenseNumber(formattedSenseNumber, senseNumberWs, senseConfigNode); } private static string GetSenseNumber(string numberingStyle, ref SenseInfo info) @@ -2224,7 +2247,7 @@ private static IFragment GenerateContentForICmObject(ICmObject propertyValue, Co if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedObject(false, bldr, className); + return settings.ContentGenerator.WriteProcessedObject(false, bldr, config, className); } return bldr; } @@ -2452,7 +2475,7 @@ private static IFragment GenerateContentForValue(object field, object propertyVa if (!content.IsNullOrEmpty()) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedCollection(false, content, className); + return settings.ContentGenerator.WriteProcessedCollection(false, content, config, className); } } return settings.ContentGenerator.CreateFragment(); @@ -2505,7 +2528,7 @@ private static IFragment GenerateContentForValue(object field, object propertyVa } } if (bldr.Length() > 0) - return settings.ContentGenerator.WriteProcessedCollection(true, bldr, GetClassNameAttributeForConfig(config)); + return settings.ContentGenerator.WriteProcessedCollection(true, bldr, config, GetClassNameAttributeForConfig(config)); // bldr is empty of text return bldr; } @@ -2592,7 +2615,7 @@ private static IFragment GenerateContentForStrings(IMultiStringAccessor multiStr if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, config, className); } // bldr is empty of text return bldr; @@ -2637,7 +2660,7 @@ private static IFragment GenerateContentForVirtualStrings(ICmObject owningObject if (bldr.Length() > 0) { var className = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.WriteProcessedCollection(false, bldr, className); + return settings.ContentGenerator.WriteProcessedCollection(false, bldr, config, className); } // bldr is empty of text return bldr; @@ -2654,7 +2677,7 @@ private static IFragment GenerateWsPrefixAndString(ConfigurableDictionaryNode co var content = GenerateContentForString(requestedString, config, settings, guid, wsName); if (String.IsNullOrEmpty(content.ToString())) return settings.ContentGenerator.CreateFragment(); - return settings.ContentGenerator.GenerateWsPrefixWithString(settings, wsOptions.DisplayWritingSystemAbbreviations, wsId, content); + return settings.ContentGenerator.GenerateWsPrefixWithString(config, settings, wsOptions.DisplayWritingSystemAbbreviations, wsId, content); } private static IFragment GenerateContentForString(ITsString fieldValue, ConfigurableDictionaryNode config, @@ -2678,7 +2701,7 @@ private static IFragment GenerateContentForString(ITsString fieldValue, Configur var fileContent = GenerateContentForAudioFile(writingSystem, audioId, srcAttr, string.Empty, settings); var content = GenerateAudioWsContent(writingSystem, linkTarget, fileContent, settings); if (!content.IsNullOrEmpty()) - return settings.ContentGenerator.WriteProcessedObject(false, content, null); + return settings.ContentGenerator.WriteProcessedObject(false, content, config,null); } } else if (config.IsCustomField && IsUSFM(fieldValue.Text)) @@ -2725,7 +2748,7 @@ private static IFragment GenerateContentForString(ITsString fieldValue, Configur externalLink = props.GetStrPropValue((int)FwTextPropType.ktptObjData); } writingSystem = settings.Cache.WritingSystemFactory.GetStrFromWs(fieldValue.get_WritingSystem(i)); - GenerateRunWithPossibleLink(settings, writingSystem, writer, style, text, linkTarget, rightToLeft, externalLink); + GenerateRunWithPossibleLink(settings, writingSystem, writer, style, text, linkTarget, rightToLeft, config, externalLink); } if (fieldValue.RunCount > 1) @@ -2771,7 +2794,7 @@ private static IFragment GenerateAudioWsContent(string wsId, } private static void GenerateRunWithPossibleLink(GeneratorSettings settings, string writingSystem, IFragmentWriter writer, string style, - string text, Guid linkDestination, bool rightToLeft, string externalLink = null) + string text, Guid linkDestination, bool rightToLeft, ConfigurableDictionaryNode config, string externalLink = null) { settings.ContentGenerator.StartRun(writer, writingSystem); var wsRtl = settings.Cache.WritingSystemFactory.get_Engine(writingSystem).RightToLeftScript; @@ -2785,15 +2808,15 @@ private static void GenerateRunWithPossibleLink(GeneratorSettings settings, stri settings.Cache.WritingSystemFactory.GetWsFromStr(writingSystem), settings.PropertyTable); var css = cssStyle.ToString(); if (!String.IsNullOrEmpty(css)) - settings.ContentGenerator.SetRunStyle(writer, css); + settings.ContentGenerator.SetRunStyle(writer, config, css); } if (linkDestination != Guid.Empty) { - settings.ContentGenerator.StartLink(writer, linkDestination); + settings.ContentGenerator.StartLink(writer, config, linkDestination); } if (!string.IsNullOrEmpty(externalLink)) { - settings.ContentGenerator.StartLink(writer, externalLink.TrimStart((char)FwObjDataTypes.kodtExternalPathName)); + settings.ContentGenerator.StartLink(writer, config, externalLink.TrimStart((char)FwObjDataTypes.kodtExternalPathName)); } if (text.Contains(TxtLineSplit)) { @@ -3008,7 +3031,7 @@ private static void GenerateError(string text, IFragmentWriter writer, Generator new ExCSS.Property("color") { Term = new HtmlColor(222, 0, 0) }, new ExCSS.Property("font-size") { Term = new PrimitiveTerm(UnitType.Ems, 1.5f) } }; - settings.ContentGenerator.SetRunStyle(writer, css.ToString()); + settings.ContentGenerator.SetRunStyle(writer, null, css.ToString()); if (text.Contains(TxtLineSplit)) { var txtContents = text.Split(TxtLineSplit); @@ -3120,6 +3143,18 @@ private static bool IsTypeBeforeForm(ConfigurableDictionaryNode config) return typeBefore; } + public class ConfigFragment + { + public ConfigurableDictionaryNode Config { get; } + public IFragment Frag { get; } + + public ConfigFragment(ConfigurableDictionaryNode config, IFragment frag) + { + Config = config; + Frag = frag; + } + } + public class GeneratorSettings { public ILcmContentGenerator ContentGenerator = new LcmXhtmlGenerator(); diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 78d34c524f..1cbd1eadf1 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -1442,9 +1442,9 @@ private static void AddFontInfoCss(BaseStyleInfo projectStyle, StyleDeclaration // empty span (ie span[lang="en"]{}. If not included, font-family will be added to many more spans. if (fontName == null && projectStyle.Name == "Normal") { - var lgWritingSysytem = cache.ServiceLocator.WritingSystemManager.get_EngineOrNull(wsId); - if(lgWritingSysytem != null) - fontName = lgWritingSysytem.DefaultFontName; + var lgWritingSystem = cache.ServiceLocator.WritingSystemManager.get_EngineOrNull(wsId); + if(lgWritingSystem != null) + fontName = lgWritingSystem.DefaultFontName; } if (fontName != null) diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 0a75535915..e9b9301ee3 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -14,15 +14,15 @@ namespace SIL.FieldWorks.XWorks /// public interface ILcmContentGenerator { - IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content); + IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content); IFragment GenerateAudioLinkContent(string classname, string srcAttribute, string caption, string safeAudioId); - IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className); - IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className); - IFragment GenerateGramInfoBeforeSensesContent(IFragment content); + IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className); + IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className); + IFragment GenerateGramInfoBeforeSensesContent(IFragment content, ConfigurableDictionaryNode config); IFragment GenerateGroupingNode(object field, string className, ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, Func childContentGenerator); - IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, string senseContent, string className); - IFragment AddCollectionItem(bool isBlock, string collectionItemClass, IFragment content); + IFragment AddSenseData(IFragment senseNumberSpan, bool isBlockProperty, Guid ownerGuid, IFragment senseContent, string className); + IFragment AddCollectionItem(bool isBlock, string collectionItemClass, ConfigurableDictionaryNode config,IFragment content); IFragment AddProperty(string className, bool isBlockProperty, string content); IFragment CreateFragment(); IFragment CreateFragment(string str); @@ -33,9 +33,9 @@ IFragment GenerateGroupingNode(object field, string className, ConfigurableDicti void EndBiDiWrapper(IFragmentWriter writer); void StartRun(IFragmentWriter writer, string writingSystem); void EndRun(IFragmentWriter writer); - void SetRunStyle(IFragmentWriter writer, string css); - void StartLink(IFragmentWriter writer, Guid destination); - void StartLink(IFragmentWriter writer, string externalDestination); + void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode config, string css); + void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination); + void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, string externalDestination); void EndLink(IFragmentWriter writer); void AddToRunContent(IFragmentWriter writer, string txtContent); void AddLineBreakInRunContent(IFragmentWriter writer); @@ -47,20 +47,20 @@ IFragment GenerateGroupingNode(object field, string className, ConfigurableDicti void EndTableRow(IFragmentWriter writer); void EndTableBody(IFragmentWriter writer); void EndTable(IFragmentWriter writer); - void StartEntry(IFragmentWriter writer, string className, Guid entryGuid, int index, RecordClerk clerk); - void AddEntryData(IFragmentWriter writer, List pieces); + void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config, string className, Guid entryGuid, int index, RecordClerk clerk); + void AddEntryData(IFragmentWriter writer, List pieces); void EndEntry(IFragmentWriter writer); - void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, string content); + void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, ConfigurableDictionaryNode config, string content); void BeginObjectProperty(IFragmentWriter writer, bool isBlockProperty, string getCollectionItemClassAttribute); void EndObject(IFragmentWriter writer); void WriteProcessedContents(IFragmentWriter writer, IFragment contents); IFragment AddImage(string classAttribute, string srcAttribute, string pictureGuid); IFragment AddImageCaption(string captionContent); - IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs); - IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, string className, string referencesContent, bool typeBefore); + IFragment GenerateSenseNumber(string formattedSenseNumber, string senseNumberWs, ConfigurableDictionaryNode config); + IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, ConfigurableDictionaryNode config, string className, string referencesContent, bool typeBefore); void BeginCrossReference(IFragmentWriter writer, bool isBlockProperty, string className); void EndCrossReference(IFragmentWriter writer); - IFragment WriteProcessedSenses(bool isBlock, IFragment senseContent, string className, IFragment sharedCollectionInfo); + IFragment WriteProcessedSenses(bool isBlock, IFragment senseContent, ConfigurableDictionaryNode config, string className, IFragment sharedCollectionInfo); IFragment AddAudioWsContent(string wsId, Guid linkTarget, IFragment fileContent); IFragment GenerateErrorContent(StringBuilder badStrBuilder); IFragment GenerateVideoLinkContent(string className, string mediaId, string srcAttribute, diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 1b16b330b4..a033b42400 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -38,7 +38,7 @@ public LcmJsonGenerator(LcmCache cache) Cache = cache; } - public IFragment GenerateWsPrefixWithString(ConfiguredLcmGenerator.GeneratorSettings settings, + public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content) { return content; @@ -55,7 +55,7 @@ public IFragment GenerateAudioLinkContent(string classname, string srcAttribute, dynamic audioObject = new JObject(); audioObject.id = safeAudioId; audioObject.src = srcAttribute.Replace("\\", "/"); // expecting relative paths only - return WriteProcessedObject(false, new StringFragment(audioObject.ToString()), "value"); + return WriteProcessedObject(false, new StringFragment(audioObject.ToString()), null,"value"); } public IFragment GenerateVideoLinkContent(string className, string mediaId, @@ -66,10 +66,10 @@ public IFragment GenerateVideoLinkContent(string className, string mediaId, dynamic videoObject = new JObject(); videoObject.id = mediaId; videoObject.src = srcAttribute.Replace("\\", "/"); // expecting relative paths only - return WriteProcessedObject(false, new StringFragment(videoObject.ToString()), "value"); + return WriteProcessedObject(false, new StringFragment(videoObject.ToString()), null, "value"); } - public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, string className) + public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className) { if (elementContent.ToString().StartsWith("{")) return WriteProcessedContents(elementContent, className, string.Empty, ","); @@ -78,7 +78,7 @@ public IFragment WriteProcessedObject(bool isBlock, IFragment elementContent, st return WriteProcessedContents(elementContent, className, "{", "},"); } - public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, string className) + public IFragment WriteProcessedCollection(bool isBlock, IFragment elementContent, ConfigurableDictionaryNode config, string className) { ((StringFragment)elementContent).TrimEnd(','); return WriteProcessedContents(elementContent, className, "[", "],"); @@ -103,7 +103,7 @@ private IFragment WriteProcessedContents(IFragment elementContent, string classN return fragment; } - public IFragment GenerateGramInfoBeforeSensesContent(IFragment content) + public IFragment GenerateGramInfoBeforeSensesContent(IFragment content, ConfigurableDictionaryNode config) { // The grammatical info is generated as a json property on 'senses' return content; @@ -117,7 +117,7 @@ public IFragment GenerateGroupingNode(object field, string className, Configurab return new StringFragment(); } - public IFragment AddCollectionItem(bool isBlock, string className, IFragment content) + public IFragment AddCollectionItem(bool isBlock, string className, ConfigurableDictionaryNode config,IFragment content) { var fragment = new StringFragment(); fragment.StrBuilder.Append(content.IsNullOrEmpty() ? string.Empty : $"{{{content}}},"); @@ -181,18 +181,18 @@ public void EndRun(IFragmentWriter writer) m_runBuilder.Value.Clear(); } - public void SetRunStyle(IFragmentWriter writer, string css) + public void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode config, string css) { if(!string.IsNullOrEmpty(css)) ((JsonFragmentWriter)writer).InsertJsonProperty("style", css); } - public void StartLink(IFragmentWriter writer, Guid destination) + public void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination) { ((JsonFragmentWriter)writer).InsertJsonProperty("guid", "g" + destination); } - public void StartLink(IFragmentWriter writer, string externalLink) + public void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, string externalLink) { ((JsonFragmentWriter)writer).InsertJsonProperty("linkUrl", externalLink); } @@ -246,7 +246,7 @@ public void EndTable(IFragmentWriter writer) // TODO: decide on a useful json representation for tables } - public void StartEntry(IFragmentWriter xw, string className, Guid entryGuid, int index, RecordClerk clerk) + public void StartEntry(IFragmentWriter xw, ConfigurableDictionaryNode config, string className, Guid entryGuid, int index, RecordClerk clerk) { var jsonWriter = (JsonFragmentWriter)xw; jsonWriter.StartObject(); @@ -274,9 +274,10 @@ public void StartEntry(IFragmentWriter xw, string className, Guid entryGuid, int jsonWriter.InsertRawJson(","); } - public void AddEntryData(IFragmentWriter xw, List pieces) + public void AddEntryData(IFragmentWriter xw, List pieces) { - pieces.ForEach(((JsonFragmentWriter)xw).InsertRawJson); + foreach (ConfiguredLcmGenerator.ConfigFragment piece in pieces) + ((JsonFragmentWriter)xw).InsertRawJson(piece.Frag); } public void EndEntry(IFragmentWriter xw) @@ -284,7 +285,7 @@ public void EndEntry(IFragmentWriter xw) ((JsonFragmentWriter)xw).EndObject(); } - public void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, string content) + public void AddCollection(IFragmentWriter writer, bool isBlockProperty, string className, ConfigurableDictionaryNode config, string content) { ((JsonFragmentWriter)writer).InsertPropertyName(className); BeginArray(writer); @@ -350,12 +351,12 @@ public IFragment AddImageCaption(string captionContent) return new StringFragment(captionContent); } - public IFragment GenerateSenseNumber(string formattedSenseNumber, string wsId) + public IFragment GenerateSenseNumber(string formattedSenseNumber, string wsId, ConfigurableDictionaryNode config) { return new StringFragment(formattedSenseNumber); } - public IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, string className, + public IFragment AddLexReferences(bool generateLexType, IFragment lexTypeContent, ConfigurableDictionaryNode config, string className, string referencesContent, bool typeBefore) { var bldr = new StringBuilder(); @@ -406,9 +407,9 @@ public void EndCrossReference(IFragmentWriter writer) /// /// Generates data for all senses of an entry. For better processing of json add sharedGramInfo as a separate property object /// - public IFragment WriteProcessedSenses(bool isBlock, IFragment sensesContent, string classAttribute, IFragment sharedGramInfo) + public IFragment WriteProcessedSenses(bool isBlock, IFragment sensesContent, ConfigurableDictionaryNode config, string classAttribute, IFragment sharedGramInfo) { - return new StringFragment($"{sharedGramInfo.ToString()}{WriteProcessedCollection(isBlock, sensesContent, classAttribute)}"); + return new StringFragment($"{sharedGramInfo.ToString()}{WriteProcessedCollection(isBlock, sensesContent, config, classAttribute)}"); } public IFragment AddAudioWsContent(string wsId, Guid linkTarget, IFragment fileContent) @@ -424,7 +425,7 @@ public IFragment GenerateErrorContent(StringBuilder badStrBuilder) } public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlock, Guid ownerGuid, - string senseContent, string className) + IFragment senseContent, string className) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); @@ -440,7 +441,7 @@ public IFragment AddSenseData(IFragment senseNumberSpan, bool isBlock, Guid owne } xw.WritePropertyName("guid"); xw.WriteValue("g" + ownerGuid); - xw.WriteRaw("," + senseContent.TrimEnd(',')); + xw.WriteRaw("," + senseContent.ToString().TrimEnd(',')); xw.WriteEndObject(); xw.WriteRaw(","); xw.Flush(); diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 31b1ec3635..615b7477b4 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -2,6 +2,17 @@ // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using Icu.Collation; +using SIL.Code; +using SIL.FieldWorks.Common.FwUtils; +using SIL.FieldWorks.Common.Widgets; +using SIL.LCModel; +using SIL.LCModel.DomainServices; +using SIL.LCModel.Utils; +using Style = DocumentFormat.OpenXml.Wordprocessing.Style; using System; using System.Collections.Generic; using System.Diagnostics; @@ -9,16 +20,7 @@ using System.Linq; using System.Text; using System.Web.UI.WebControls; -using DocumentFormat.OpenXml; -using Icu.Collation; -using SIL.FieldWorks.Common.Controls; -using SIL.FieldWorks.Common.FwUtils; -using SIL.LCModel; -using SIL.LCModel.Core.Text; -using SIL.LCModel.Utils; using XCore; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; namespace SIL.FieldWorks.XWorks { @@ -29,6 +31,10 @@ namespace SIL.FieldWorks.XWorks public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator { private LcmCache Cache { get; } + private static Styles _styleSheet { get; set; } = new Styles(); + private static Dictionary _styleDictionary = new Dictionary(); + private ReadOnlyPropertyTable _propertyTable; + public LcmWordGenerator(LcmCache cache) { Cache = cache; @@ -45,12 +51,14 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor var cssPath = System.IO.Path.ChangeExtension(filePath, "css"); var clerk = propertyTable.GetValue("ActiveClerk", null); var cache = propertyTable.GetValue("cache", null); - - + var generator = new LcmWordGenerator(cache); var readOnlyPropertyTable = new ReadOnlyPropertyTable(propertyTable); + + generator.Init(readOnlyPropertyTable); var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, true, true, System.IO.Path.GetDirectoryName(filePath), ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropertyTable, configuration), System.IO.Path.GetFileName(cssPath) == "configured.css") - { ContentGenerator = new LcmWordGenerator(cache) }; + { ContentGenerator = generator, StylesGenerator = generator}; + settings.StylesGenerator.AddGlobalStyles(configuration, readOnlyPropertyTable); string lastHeader = null; var entryContents = new Tuple[entryCount]; var entryActions = new List(); @@ -85,18 +93,19 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor var wsString = entryContents.Length > 0 ? ConfiguredLcmGenerator.GetWsForEntryType(entryContents[0].Item1, settings.Cache) : null; var col = FwUtils.GetCollatorForWs(wsString); + var propStyleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); + foreach (var entry in entryContents) { if (!entry.Item2.IsNullOrEmpty()) { IFragment letterHeader = GenerateLetterHeaderIfNeeded(entry.Item1, - ref lastHeader, col, settings, clerk); + ref lastHeader, col, settings, readOnlyPropertyTable, propStyleSheet, clerk); // If needed, append letter header to the word doc if (!letterHeader.IsNullOrEmpty()) fragment.Append(letterHeader); - // TODO: when/how are styles applied to the letter headers? // Append the entry to the word doc fragment.Append(entry.Item2); } @@ -106,7 +115,29 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor if (progress != null) progress.Message = xWorksStrings.ksGeneratingStyleInfo; - // TODO: Generate styles + // Generate styles + StyleDefinitionsPart stylePart = fragment.mainDocPart.StyleDefinitionsPart; + if (stylePart == null) + { + // Initialize word doc's styles xml + stylePart = AddStylesPartToPackage(fragment.DocFrag); + + // Add generated styles into the stylesheet from the dictionary + foreach (var stylesItem in _styleDictionary.Values) + { + foreach (var style in stylesItem.Descendants + - + - + - + - + - + diff --git a/Src/LexText/ParserUI/ParserReportViewModel.cs b/Src/LexText/ParserUI/ParserReportViewModel.cs index 9aac28d4cb..58374c63c2 100644 --- a/Src/LexText/ParserUI/ParserReportViewModel.cs +++ b/Src/LexText/ParserUI/ParserReportViewModel.cs @@ -14,12 +14,25 @@ public class ParserReportViewModel { public ParserReport ParserReport { get; set; } + private FileTimeToDateTimeConverter m_FileTimeToDateTimeConverter = new FileTimeToDateTimeConverter(); + public string Title { get { - string time = ParserReport.IsDiff ? new TimeSpan(ParserReport.Timestamp).ToString() : new DateTime(ParserReport.Timestamp).ToString(); - return (ParserReport.IsDiff ? "Diff " : "") + ParserReport.ProjectName + ", " + ParserReport.SourceText + ", " + time + "," + ParserReport.MachineName; + string time = ParserReport.IsDiff + ? TimeSpan.FromTicks(ParserReport.Timestamp).ToString() + : m_FileTimeToDateTimeConverter.Convert(ParserReport.Timestamp, null, null, null).ToString(); + return (ParserReport.IsDiff ? ParserUIStrings.ksDiffHeader + " " : "") + ParserReport.ProjectName + ", " + ParserReport.SourceText + ", " + time + ", " + ParserReport.MachineName; + } + } + + public IEnumerable ParseReports + { + get + { + // Use ToList so that sorting the reports doesn't change the data model. + return ParserReport.ParseReports.Values.ToList(); } } diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml b/Src/LexText/ParserUI/ParserReportsDialog.xaml index 8a2934b480..9cc22b49ed 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml @@ -6,11 +6,12 @@ xmlns:local="clr-namespace:SIL.FieldWorks.LexText.Controls" d:DataContext="{d:DesignInstance Type=local:ParserReportsViewModel, IsDesignTimeCreatable=True}" mc:Ignorable="d" - Title="Parser Test Reports" WindowStartupLocation="CenterScreen" + Title="{x:Static local:ParserUIStrings.ksParserTestReports}" WindowStartupLocation="CenterScreen" SizeToContent="WidthAndHeight" MinHeight="300" MaxHeight="500" WindowStyle="ThreeDBorderWindow"> + @@ -19,8 +20,8 @@ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SIL.FieldWorks.LexText.Controls.ParserUIStrings", typeof(ParserUIStrings).Assembly); @@ -51,7 +51,7 @@ internal ParserUIStrings() { /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -63,7 +63,7 @@ internal ParserUIStrings() { /// /// Looks up a localized string similar to Changed value for {0} from {1} to {2}. The value must be between {3} and {4}, inclusive.. /// - internal static string ksChangedValueReport { + public static string ksChangedValueReport { get { return ResourceManager.GetString("ksChangedValueReport", resourceCulture); } @@ -72,7 +72,7 @@ internal static string ksChangedValueReport { /// /// Looks up a localized string similar to Changed a Value. /// - internal static string ksChangeValueDialogTitle { + public static string ksChangeValueDialogTitle { get { return ResourceManager.GetString("ksChangeValueDialogTitle", resourceCulture); } @@ -81,25 +81,88 @@ internal static string ksChangeValueDialogTitle { /// /// Looks up a localized string similar to -. /// - internal static string ksDash { + public static string ksDash { get { return ResourceManager.GetString("ksDash", resourceCulture); } } + /// + /// Looks up a localized string similar to Delete this test report from the disk. + /// + public static string ksDeleteToolTip { + get { + return ResourceManager.GetString("ksDeleteToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Parse was not attempted because of errors in the lexical data. /// - internal static string ksDidNotParse { + public static string ksDidNotParse { get { return ResourceManager.GetString("ksDidNotParse", resourceCulture); } } + /// + /// Looks up a localized string similar to diff. + /// + public static string ksDiffButton { + get { + return ResourceManager.GetString("ksDiffButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the difference between this test report and the report selected by the radio button (older report is subtracted from newer report). + /// + public static string ksDiffButtonToolTip { + get { + return ResourceManager.GetString("ksDiffButtonToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Diff With. + /// + public static string ksDiffHeader { + get { + return ResourceManager.GetString("ksDiffHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The other argument to diff. + /// + public static string ksDiffHeaderToolTip { + get { + return ResourceManager.GetString("ksDiffHeaderToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error Message. + /// + public static string ksErrorMessage { + get { + return ResourceManager.GetString("ksErrorMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The error message reported by the parser. + /// + public static string ksErrorMessageToolTip { + get { + return ResourceManager.GetString("ksErrorMessageToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to . /// - internal static string ksIdle_ { + public static string ksIdle_ { get { return ResourceManager.GetString("ksIdle_", resourceCulture); } @@ -108,7 +171,7 @@ internal static string ksIdle_ { /// /// Looks up a localized string similar to Created by importing the words from files: {0}. /// - internal static string ksImportedFromFilesX { + public static string ksImportedFromFilesX { get { return ResourceManager.GetString("ksImportedFromFilesX", resourceCulture); } @@ -117,7 +180,7 @@ internal static string ksImportedFromFilesX { /// /// Looks up a localized string similar to Created by importing the words from file: {0}. /// - internal static string ksImportedFromFileX { + public static string ksImportedFromFileX { get { return ResourceManager.GetString("ksImportedFromFileX", resourceCulture); } @@ -126,16 +189,34 @@ internal static string ksImportedFromFileX { /// /// Looks up a localized string similar to Loading Files for Word Set {0}. /// - internal static string ksLoadingFilesForWordSetX { + public static string ksLoadingFilesForWordSetX { get { return ResourceManager.GetString("ksLoadingFilesForWordSetX", resourceCulture); } } + /// + /// Looks up a localized string similar to Machine Name. + /// + public static string ksMachineName { + get { + return ResourceManager.GetString("ksMachineName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The machine that the text was parsed on. + /// + public static string ksMachineNameToolTip { + get { + return ResourceManager.GetString("ksMachineNameToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to No files. /// - internal static string ksNoFiles { + public static string ksNoFiles { get { return ResourceManager.GetString("ksNoFiles", resourceCulture); } @@ -144,7 +225,7 @@ internal static string ksNoFiles { /// /// Looks up a localized string similar to No files to import! Please choose at least one file.. /// - internal static string ksNoFilesToImport { + public static string ksNoFilesToImport { get { return ResourceManager.GetString("ksNoFilesToImport", resourceCulture); } @@ -153,25 +234,178 @@ internal static string ksNoFilesToImport { /// /// Looks up a localized string similar to No Parser Loaded. /// - internal static string ksNoParserLoaded { + public static string ksNoParserLoaded { get { return ResourceManager.GetString("ksNoParserLoaded", resourceCulture); } } + /// + /// Looks up a localized string similar to Num Analyses. + /// + public static string ksNumAnalyses { + get { + return ResourceManager.GetString("ksNumAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of analyses produced by the parser. + /// + public static string ksNumAnalysesToolTip { + get { + return ResourceManager.GetString("ksNumAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disapproved Analyses. + /// + public static string ksNumDisapprovedAnalyses { + get { + return ResourceManager.GetString("ksNumDisapprovedAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of analyses produced by the parser that were disapproved by the user. + /// + public static string ksNumDisapprovedAnalysesToolTip { + get { + return ResourceManager.GetString("ksNumDisapprovedAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed Analyses. + /// + public static string ksNumMissingAnalyses { + get { + return ResourceManager.GetString("ksNumMissingAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of analyses approved by the user that the parser failed to produce. + /// + public static string ksNumMissingAnalysesToolTip { + get { + return ResourceManager.GetString("ksNumMissingAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown Analyses. + /// + public static string ksNumNoOpinionAnalyses { + get { + return ResourceManager.GetString("ksNumNoOpinionAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of analyses produced by the parser that were neither approved nor disapproved by the user. + /// + public static string ksNumNoOpinionAnalysesToolTip { + get { + return ResourceManager.GetString("ksNumNoOpinionAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error Messages. + /// + public static string ksNumParseErrors { + get { + return ResourceManager.GetString("ksNumParseErrors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of error messages in the words parsed. + /// + public static string ksNumParseErrorsToolTip { + get { + return ResourceManager.GetString("ksNumParseErrorsToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Words Parsed. + /// + public static string ksNumWordsParsed { + get { + return ResourceManager.GetString("ksNumWordsParsed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of distinct words parsed in the text. + /// + public static string ksNumWordsParsedToolTip { + get { + return ResourceManager.GetString("ksNumWordsParsedToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No Parses. + /// + public static string ksNumZeroParses { + get { + return ResourceManager.GetString("ksNumZeroParses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of words that got no parse. + /// + public static string ksNumZeroParsesToolTip { + get { + return ResourceManager.GetString("ksNumZeroParsesToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Parser Parameters. /// - internal static string ksParserParameters { + public static string ksParserParameters { get { return ResourceManager.GetString("ksParserParameters", resourceCulture); } } + /// + /// Looks up a localized string similar to Parser Test Reports. + /// + public static string ksParserTestReports { + get { + return ResourceManager.GetString("ksParserTestReports", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parse Time. + /// + public static string ksParseTime { + get { + return ResourceManager.GetString("ksParseTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The time it took to parse the word. + /// + public static string ksParseTimeToolTip { + get { + return ResourceManager.GetString("ksParseTimeToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Queue: ({0}/{1}/{2}). /// - internal static string ksQueueXYZ { + public static string ksQueueXYZ { get { return ResourceManager.GetString("ksQueueXYZ", resourceCulture); } @@ -180,7 +414,7 @@ internal static string ksQueueXYZ { /// /// Looks up a localized string similar to Redo Clear Selected Word Parser Analyses. /// - internal static string ksRedoClearParserAnalyses { + public static string ksRedoClearParserAnalyses { get { return ResourceManager.GetString("ksRedoClearParserAnalyses", resourceCulture); } @@ -189,25 +423,214 @@ internal static string ksRedoClearParserAnalyses { /// /// Looks up a localized string similar to Redo Editing Parser Parameters. /// - internal static string ksRedoEditingParserParameters { + public static string ksRedoEditingParserParameters { get { return ResourceManager.GetString("ksRedoEditingParserParameters", resourceCulture); } } + /// + /// Looks up a localized string similar to reparse. + /// + public static string ksReparse { + get { + return ResourceManager.GetString("ksReparse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reparse this word using Try A Word. + /// + public static string ksReparseToolTip { + get { + return ResourceManager.GetString("ksReparseToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please select a second report other than this report using the radio button in the column labelled 'Diff With'.. + /// + public static string ksSelectDiffReport { + get { + return ResourceManager.GetString("ksSelectDiffReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to show. + /// + public static string ksShowAnalyses { + get { + return ResourceManager.GetString("ksShowAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show the analyses of this word. + /// + public static string ksShowAnalysesToolTip { + get { + return ResourceManager.GetString("ksShowAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to show. + /// + public static string ksShowReport { + get { + return ResourceManager.GetString("ksShowReport", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show this test report. + /// + public static string ksShowReportToolTip { + get { + return ResourceManager.GetString("ksShowReportToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to , . /// - internal static string ksSlotNameSeparator { + public static string ksSlotNameSeparator { get { return ResourceManager.GetString("ksSlotNameSeparator", resourceCulture); } } + /// + /// Looks up a localized string similar to Text. + /// + public static string ksText { + get { + return ResourceManager.GetString("ksText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The text that was parsed. + /// + public static string ksTextToolTip { + get { + return ResourceManager.GetString("ksTextToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timestamp. + /// + public static string ksTimestamp { + get { + return ResourceManager.GetString("ksTimestamp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When the text was parsed. + /// + public static string ksTimestampToolTip { + get { + return ResourceManager.GetString("ksTimestampToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Num Analyses. + /// + public static string ksTotalAnalyses { + get { + return ResourceManager.GetString("ksTotalAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total number of analyses in the words parsed. + /// + public static string ksTotalAnalysesToolTip { + get { + return ResourceManager.GetString("ksTotalAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disapproved Analyses. + /// + public static string ksTotalDisapprovedAnalyses { + get { + return ResourceManager.GetString("ksTotalDisapprovedAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total number of disapproved analyses in the words parsed. + /// + public static string ksTotalDisapprovedAnalysesToolTip { + get { + return ResourceManager.GetString("ksTotalDisapprovedAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed Analyses. + /// + public static string ksTotalMissingAnalyses { + get { + return ResourceManager.GetString("ksTotalMissingAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total number of approved analyses that the parser failed to produce in the words parsed. + /// + public static string ksTotalMissingAnalysesToolTip { + get { + return ResourceManager.GetString("ksTotalMissingAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unknown Analyses. + /// + public static string ksTotalNoOpinionAnalyses { + get { + return ResourceManager.GetString("ksTotalNoOpinionAnalyses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total number of analyses that were neither approved not disapproved in the words parsed. + /// + public static string ksTotalNoOpinionAnalysesToolTip { + get { + return ResourceManager.GetString("ksTotalNoOpinionAnalysesToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parse Time. + /// + public static string ksTotalParseTime { + get { + return ResourceManager.GetString("ksTotalParseTime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The total time it took to parse the words. + /// + public static string ksTotalParseTimeToolTip { + get { + return ResourceManager.GetString("ksTotalParseTimeToolTip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Undo Clear Selected Word Parser Analyses. /// - internal static string ksUndoClearParserAnalyses { + public static string ksUndoClearParserAnalyses { get { return ResourceManager.GetString("ksUndoClearParserAnalyses", resourceCulture); } @@ -216,7 +639,7 @@ internal static string ksUndoClearParserAnalyses { /// /// Looks up a localized string similar to Undo Editing Parser Parameters. /// - internal static string ksUndoEditingParserParameters { + public static string ksUndoEditingParserParameters { get { return ResourceManager.GetString("ksUndoEditingParserParameters", resourceCulture); } @@ -225,7 +648,7 @@ internal static string ksUndoEditingParserParameters { /// /// Looks up a localized string similar to Unknown. /// - internal static string ksUnknown { + public static string ksUnknown { get { return ResourceManager.GetString("ksUnknown", resourceCulture); } @@ -234,10 +657,28 @@ internal static string ksUnknown { /// /// Looks up a localized string similar to Update. /// - internal static string ksUpdate { + public static string ksUpdate { get { return ResourceManager.GetString("ksUpdate", resourceCulture); } } + + /// + /// Looks up a localized string similar to Word. + /// + public static string ksWord { + get { + return ResourceManager.GetString("ksWord", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The word that was parsed. + /// + public static string ksWordToolTip { + get { + return ResourceManager.GetString("ksWordToolTip", resourceCulture); + } + } } } diff --git a/Src/LexText/ParserUI/ParserUIStrings.resx b/Src/LexText/ParserUI/ParserUIStrings.resx index 1f3706898b..933b652375 100644 --- a/Src/LexText/ParserUI/ParserUIStrings.resx +++ b/Src/LexText/ParserUI/ParserUIStrings.resx @@ -1,4 +1,4 @@ - + diff --git a/Build/nuget-common/packages.config b/Build/nuget-common/packages.config index 7cbd3c92e4..4fd40b7ee3 100644 --- a/Build/nuget-common/packages.config +++ b/Build/nuget-common/packages.config @@ -52,15 +52,15 @@ - - - - - - - - - + + + + + + + + + From ca731933c124d2548570e3b4c669a84ff12d1f2f Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 2 Aug 2024 12:09:09 -0700 Subject: [PATCH 176/415] LT-21586: Export/import Phonology (#120) * Add code to export phonology * Add import interface * Switch to using PhonologyServices. This depends on the latest liblcm * Fix UI for importing phonology * Improve warning message --- .../Language Explorer/Configuration/Main.xml | 8 +- .../Export Templates/Phonology.xml | 4 + Src/xWorks/ExportDialog.cs | 29 ++++++- Src/xWorks/FwXWindow.cs | 78 +++++++++++++++++++ Src/xWorks/xWorksStrings.Designer.cs | 11 ++- Src/xWorks/xWorksStrings.resx | 3 + 6 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 DistFiles/Language Explorer/Export Templates/Phonology.xml diff --git a/DistFiles/Language Explorer/Configuration/Main.xml b/DistFiles/Language Explorer/Configuration/Main.xml index c7f0a26380..1fecd5759c 100644 --- a/DistFiles/Language Explorer/Configuration/Main.xml +++ b/DistFiles/Language Explorer/Configuration/Main.xml @@ -67,8 +67,9 @@ - - + + + @@ -425,7 +426,8 @@ - + + diff --git a/DistFiles/Language Explorer/Export Templates/Phonology.xml b/DistFiles/Language Explorer/Export Templates/Phonology.xml new file mode 100644 index 0000000000..e762f03275 --- /dev/null +++ b/DistFiles/Language Explorer/Export Templates/Phonology.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/Src/xWorks/ExportDialog.cs b/Src/xWorks/ExportDialog.cs index 7f63aedc80..078d49bee5 100644 --- a/Src/xWorks/ExportDialog.cs +++ b/Src/xWorks/ExportDialog.cs @@ -27,6 +27,7 @@ using SIL.FieldWorks.Common.RootSites; using SIL.LCModel; using SIL.LCModel.DomainImpl; +using SIL.LCModel.DomainServices; using SIL.FieldWorks.FdoUi; using SIL.FieldWorks.LexText.Controls; using SIL.FieldWorks.Resources; @@ -38,6 +39,7 @@ using XCore; using PropertyTable = XCore.PropertyTable; using ReflectionHelper = SIL.LCModel.Utils.ReflectionHelper; +using Newtonsoft.Json; namespace SIL.FieldWorks.XWorks { @@ -84,7 +86,8 @@ protected internal enum FxtTypes kftClassifiedDict, kftSemanticDomains, kftWebonary, - kftWordOpenXml + kftWordOpenXml, + kftPhonology } // ReSharper restore InconsistentNaming protected internal struct FxtType @@ -840,6 +843,13 @@ protected void DoExport(string outPath, bool fLiftOutput) progressDlg.Restartable = true; progressDlg.RunTask(true, ExportGrammarSketch, outPath, ft.m_sDataType, ft.m_sXsltFiles); break; + case FxtTypes.kftPhonology: + progressDlg.Minimum = 0; + progressDlg.Maximum = 1000; + progressDlg.AllowCancel = true; + progressDlg.Restartable = true; + progressDlg.RunTask(true, ExportPhonology, outPath, ft.m_sDataType, ft.m_sXsltFiles); + break; case FxtTypes.kftWordOpenXml: progressDlg.Minimum = 0; progressDlg.Maximum = 1000; @@ -916,8 +926,8 @@ private object ExportConfiguredXhtml(IThreadedProgress progress, object[] args) private object ExportGrammarSketch(IThreadedProgress progress, object[] args) { var outPath = (string)args[0]; - var sDataType = (string) args[1]; - var sXslts = (string) args[2]; + var sDataType = (string)args[1]; + var sXslts = (string)args[2]; m_progressDlg = progress; var parameter = new Tuple(sDataType, outPath, sXslts); m_mediator.SendMessage("SaveAsWebpage", parameter); @@ -925,6 +935,16 @@ private object ExportGrammarSketch(IThreadedProgress progress, object[] args) return null; } + private object ExportPhonology(IThreadedProgress progress, object[] args) + { + var outPath = (string)args[0]; + m_progressDlg = progress; + var phonologyServices = new PhonologyServices(m_cache); + phonologyServices.ExportPhonologyAsXml(outPath); + m_progressDlg.Step(1000); + return null; + } + /// ------------------------------------------------------------------------------------ /// /// Exports as a LIFT file (possibly with one or more range files. @@ -1297,6 +1317,9 @@ protected virtual void ConfigureItem(XmlDocument document, ListViewItem item, Xm case "semanticDomains": ft.m_ft = FxtTypes.kftSemanticDomains; break; + case "phonology": + ft.m_ft = FxtTypes.kftPhonology; + break; default: Debug.Fail("Invalid type attribute value for the template element"); ft.m_ft = FxtTypes.kftFxt; diff --git a/Src/xWorks/FwXWindow.cs b/Src/xWorks/FwXWindow.cs index 6baa1892d4..04141608a8 100644 --- a/Src/xWorks/FwXWindow.cs +++ b/Src/xWorks/FwXWindow.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Threading; using System.Windows.Forms; using System.Xml; using Microsoft.Win32; @@ -39,6 +40,8 @@ using SIL.Reporting; using SIL.Utils; using XCore; +using SIL.LCModel.Application.ApplicationServices; +using NAudio.Utils; namespace SIL.FieldWorks.XWorks { @@ -1921,6 +1924,81 @@ public override IxCoreColleague[] GetMessageTargets() return new IxCoreColleague[]{this}; } + public bool OnImportPhonology(object commandObject) + { + string filename = null; + // ActiveForm can go null (see FWNX-731), so cache its value, and check whether + // we need to use 'this' instead (which might be a better idea anyway). + var form = ActiveForm; + if (form == null) + form = this; + Command command = (Command)commandObject; + string caption = command.ToolTip; + using (var dlg = new OpenFileDialogAdapter()) + { + dlg.CheckFileExists = true; + dlg.RestoreDirectory = true; + dlg.Title = ResourceHelper.GetResourceString("kstidOpenTranslatedLists"); + dlg.ValidateNames = true; + dlg.Multiselect = false; + dlg.Filter = ResourceHelper.FileFilter(FileFilterType.FieldWorksTranslatedLists); + if (dlg.ShowDialog(form) != DialogResult.OK) + return true; + filename = dlg.FileName; + } + DialogResult result = MessageBox.Show(xWorksStrings.DeletePhonology, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (result != DialogResult.Yes) + return true; + DeletePhonology(); + using (new WaitCursor(form, true)) + { + using (var dlg = new ProgressDialogWithTask(this)) + { + dlg.AllowCancel = true; + dlg.Maximum = 200; + dlg.Message = filename; + dlg.RunTask(true, ImportPhonology, filename, caption); + } + } + return true; + } + + private object ImportPhonology(IThreadedProgress dlg, object[] parameters) + { + try + { + var phonologyServices = new PhonologyServices(Cache); + phonologyServices.ImportPhonologyFromXml((string)parameters[0]); + dlg.Step(200); + // Let the dialog update. + Thread.Sleep(100); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.Message); + MessageBox.Show(ex.Message, (string)parameters[1]); + } + return false; + } + + + private void DeletePhonology() + { + NonUndoableUnitOfWorkHelper.Do(Cache.ServiceLocator.GetInstance(), () => + { + IPhPhonData phonData = Cache.LangProject.PhonologicalDataOA; + // Delete what is covered by ImportPhonology. + phonData.ContextsOS.Clear(); + phonData.EnvironmentsOS.Clear(); + phonData.FeatConstraintsOS.Clear(); + phonData.NaturalClassesOS.Clear(); + phonData.PhonemeSetsOS.Clear(); + phonData.PhonRulesOS.Clear(); + Cache.LanguageProject.PhFeatureSystemOA.TypesOC.Clear(); + Cache.LanguageProject.PhFeatureSystemOA.FeaturesOC.Clear(); + }); + } + /// /// Display this import command everywhere. /// diff --git a/Src/xWorks/xWorksStrings.Designer.cs b/Src/xWorks/xWorksStrings.Designer.cs index 7f5c7624d3..12b4a5ec1c 100644 --- a/Src/xWorks/xWorksStrings.Designer.cs +++ b/Src/xWorks/xWorksStrings.Designer.cs @@ -258,6 +258,15 @@ internal static string DeletedObjectDetected { } } + /// + /// Looks up a localized string similar to This will permanently delete the existing phonology and all references to it. Do you want to continue?. + /// + internal static string DeletePhonology { + get { + return ResourceManager.GetString("DeletePhonology", resourceCulture); + } + } + /// /// Looks up a localized string similar to Delete this custom view. /// @@ -2981,7 +2990,7 @@ internal static string UploadToWebonaryController_ExportDictionaryContent_Export } /// - /// Looks up a localized string similar to Initial Connection failed retrying.... + /// Looks up a localized string similar to Initial connection failed retrying.... /// internal static string UploadToWebonaryController_RetryAfterFailedConnection { get { diff --git a/Src/xWorks/xWorksStrings.resx b/Src/xWorks/xWorksStrings.resx index 7b4dc3fee7..560763d324 100644 --- a/Src/xWorks/xWorksStrings.resx +++ b/Src/xWorks/xWorksStrings.resx @@ -1262,4 +1262,7 @@ See USFM documentation for help. Initial connection failed retrying... + + This will permanently delete the existing phonology and all references to it. Do you want to continue? + \ No newline at end of file From ec55db296a133f9f62842a8646aa1d1efca80565 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 2 Aug 2024 15:02:21 -0700 Subject: [PATCH 177/415] Make some parser tester UX improvements (#124) * Move buttons from columns to button row at the bottm * Change button text to clearer options --- Src/LexText/ParserCore/ParserReport.cs | 4 +- Src/LexText/ParserUI/ParserListener.cs | 8 +- Src/LexText/ParserUI/ParserReportViewModel.cs | 25 ++- Src/LexText/ParserUI/ParserReportsDialog.xaml | 206 ++++++++---------- .../ParserUI/ParserReportsDialog.xaml.cs | 79 +++++-- .../ParserUI/ParserReportsViewModel.cs | 70 +++++- .../ParserUI/ParserUIStrings.Designer.cs | 28 ++- Src/LexText/ParserUI/ParserUIStrings.resx | 17 +- 8 files changed, 281 insertions(+), 156 deletions(-) diff --git a/Src/LexText/ParserCore/ParserReport.cs b/Src/LexText/ParserCore/ParserReport.cs index 8cb63c7231..22324182e9 100644 --- a/Src/LexText/ParserCore/ParserReport.cs +++ b/Src/LexText/ParserCore/ParserReport.cs @@ -84,13 +84,13 @@ public class ParserReport: IEquatable /// Whether this report is selected /// [JsonIgnore] - public Boolean IsSelected { get; set; } + public bool IsSelected { get; set; } /// /// Is this report the result of DiffParserReports? /// [JsonIgnore] - public Boolean IsDiff { get; set; } + public bool IsDiff { get; set; } /// /// The filename that the report came from. diff --git a/Src/LexText/ParserUI/ParserListener.cs b/Src/LexText/ParserUI/ParserListener.cs index 03629a2175..c899351c26 100644 --- a/Src/LexText/ParserUI/ParserListener.cs +++ b/Src/LexText/ParserUI/ParserListener.cs @@ -64,7 +64,7 @@ public class ParserListener : IxCoreColleague, IDisposable, IVwNotifyChange private Dictionary m_checkParserResults = null; private int m_checkParserResultsCount = 0; private string m_sourceText = null; - private ObservableCollection m_parserReports = null; + private ObservableCollection m_parserReports = null; private ParserReportsDialog m_parserReportsDialog = null; public void Init(Mediator mediator, PropertyTable propertyTable, XmlNode configurationParameters) @@ -742,12 +742,12 @@ private void ReadParserReports() { if (m_parserReports == null) { - m_parserReports = new ObservableCollection(); + m_parserReports = new ObservableCollection(); var reportDir = ParserReport.GetProjectReportsDirectory(m_cache); foreach (string filename in Directory.EnumerateFiles(reportDir, "*.json")) { var parserReport = ParserReport.ReadJsonFile(filename); - m_parserReports.Add(parserReport); + m_parserReports.Add(new ParserReportViewModel { ParserReport = parserReport}); } } } @@ -761,7 +761,7 @@ private void AddParserReport(ParserReport parserReport) // m_parserReportsDialog's window updates when m_parserReports changes // because m_parserReports is an ObservableCollection. // Add at front so that newest reports appear first. - m_parserReports.Insert(0, parserReport); + m_parserReports.Insert(0, new ParserReportViewModel { ParserReport = parserReport }); } /// diff --git a/Src/LexText/ParserUI/ParserReportViewModel.cs b/Src/LexText/ParserUI/ParserReportViewModel.cs index 58374c63c2..fd1e94e873 100644 --- a/Src/LexText/ParserUI/ParserReportViewModel.cs +++ b/Src/LexText/ParserUI/ParserReportViewModel.cs @@ -1,16 +1,13 @@ using SIL.FieldWorks.WordWorks.Parser; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows; namespace SIL.FieldWorks.LexText.Controls { - public class ParserReportViewModel + public class ParserReportViewModel : INotifyPropertyChanged { public ParserReport ParserReport { get; set; } @@ -36,6 +33,21 @@ public IEnumerable ParseReports } } + public DateTime Timestamp => DateTime.FromFileTime(ParserReport.Timestamp); + + public bool IsSelected + { + get => ParserReport.IsSelected; + set + { + if (ParserReport.IsSelected != value) + { + ParserReport.IsSelected = value; + OnPropertyChanged(nameof(IsSelected)); + } + } + } + public ParserReportViewModel() { ParserReport = new ParserReport(); @@ -48,5 +60,10 @@ public ParserReportViewModel() ParserReport.AddParseReport("error", new ParseReport(null, new ParseResult("error"))); } } + public event PropertyChangedEventHandler PropertyChanged; + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } } } diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml b/Src/LexText/ParserUI/ParserReportsDialog.xaml index 9cc22b49ed..31a03ec9c3 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml @@ -7,124 +7,104 @@ d:DataContext="{d:DesignInstance Type=local:ParserReportsViewModel, IsDesignTimeCreatable=True}" mc:Ignorable="d" Title="{x:Static local:ParserUIStrings.ksParserTestReports}" WindowStartupLocation="CenterScreen" - SizeToContent="WidthAndHeight" MinHeight="300" MaxHeight="500" + SizeToContent="Width" MinHeight="300" WindowStyle="ThreeDBorderWindow"> + + - - - - - - public partial class ParserReportsDialog : Window { - public ObservableCollection ParserReports { get; } + public ObservableCollection ParserReports { get; } public Mediator Mediator { get; set; } @@ -25,7 +28,7 @@ public ParserReportsDialog() InitializeComponent(); } - public ParserReportsDialog(ObservableCollection parserReports, Mediator mediator, LcmCache cache) + public ParserReportsDialog(ObservableCollection parserReports, Mediator mediator, LcmCache cache) { InitializeComponent(); parserReports.Sort((x, y) => y.Timestamp.CompareTo(x.Timestamp)); @@ -37,45 +40,87 @@ public ParserReportsDialog(ObservableCollection parserReports, Med public void ShowParserReport(object sender, RoutedEventArgs e) { - var button = sender as Button; - var parserReport = button.CommandParameter as ParserReport; - ParserListener.ShowParserReport(parserReport, Mediator, Cache); + foreach (var report in ParserReports) + { + if (report.IsSelected) + { + ParserListener.ShowParserReport(report.ParserReport, Mediator, Cache); + break; + } + } } public void DeleteParserReport(object sender, RoutedEventArgs e) { - var button = sender as Button; - var parserReport = button.CommandParameter as ParserReport; - parserReport.DeleteJsonFile(); - ParserReports.Remove(parserReport); + foreach (var report in ParserReports.ToArray()) // ToArray to avoid modifying the collection while iterating + { + if (report.IsSelected) + { + report.ParserReport.DeleteJsonFile(); + ParserReports.Remove(report); + } + } } public void DiffParserReports(object sender, RoutedEventArgs e) { var button = sender as Button; - var parserReport = button.CommandParameter as ParserReport; - ParserReport parserReport2 = null; + ParserReportViewModel parserReport = null; + ParserReportViewModel parserReport2 = null; foreach (var report in ParserReports) { - if (report.IsSelected && report != parserReport) + if (report.ParserReport.IsSelected) { - parserReport2 = report; + if (parserReport == null) + { + parserReport = report; + } + else if(parserReport2 == null) + { + parserReport2 = report; + } + else + { + // other logic should prevent this case, but if we break that logic just throw an exception. + throw new System.Exception("Only two reports can be selected for diffing."); + } } } if (parserReport2 == null) { - MessageBox.Show(ParserUIStrings.ksSelectDiffReport); - return; + throw new System.Exception("Two reports must be selected for diffing."); } if (parserReport.Timestamp < parserReport2.Timestamp) { // swap the two variables. - ParserReport temp = parserReport; + ParserReportViewModel temp = parserReport; parserReport = parserReport2; parserReport2 = temp; } - var diff = parserReport.DiffParserReports(parserReport2); + var diff = parserReport.ParserReport.DiffParserReports(parserReport2.ParserReport); ParserListener.ShowParserReport(diff, Mediator, Cache); } + private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (sender is DataGrid dataGrid) + { + if(dataGrid.SelectedItem is ParserReportViewModel selectedItem) + ParserListener.ShowParserReport(selectedItem.ParserReport, Mediator, Cache); + else + Debug.Fail("Type of Contents of DataGrid changed, adjust double click code."); + } + } + private void CheckBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (sender is CheckBox checkbox) + { + checkbox.Focus(); // Focus the checkbox to handle the click event + e.Handled = true; // Prevent the row from being selected + var newValue = !checkbox.IsChecked ?? true; // Toggle the checkbox value + checkbox.IsChecked = newValue; // Set the new value + var bindingExpression = checkbox.GetBindingExpression(ToggleButton.IsCheckedProperty); + bindingExpression?.UpdateSource(); // Update the binding source + } + } } } diff --git a/Src/LexText/ParserUI/ParserReportsViewModel.cs b/Src/LexText/ParserUI/ParserReportsViewModel.cs index 68742b1923..eeaad00d40 100644 --- a/Src/LexText/ParserUI/ParserReportsViewModel.cs +++ b/Src/LexText/ParserUI/ParserReportsViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Linq; using System.Windows; using SIL.FieldWorks.WordWorks.Parser; @@ -8,17 +9,54 @@ namespace SIL.FieldWorks.LexText.Controls { public class ParserReportsViewModel : INotifyPropertyChanged { - public ObservableCollection ParserReports { get; set; } + private ObservableCollection _parserReports; + public ObservableCollection ParserReports + { + get => _parserReports; + set + { + if (_parserReports != value) + { + // Unsubscribe from PropertyChanged events of old collection items + if (_parserReports != null) + { + foreach (var report in _parserReports) + { + report.PropertyChanged -= OnReportPropertyChanged; + } + } + + _parserReports = value; + + // Subscribe to PropertyChanged events of new collection items + if (_parserReports != null) + { + foreach (var report in _parserReports) + { + report.PropertyChanged += OnReportPropertyChanged; + } + } + + OnPropertyChanged(nameof(ParserReports)); + UpdateButtonStates(); // Update button states when the collection changes + } + } + } + public bool CanShowReport => ParserReports.Count(report => report.IsSelected) == 1; + public bool CanDiffReports => ParserReports.Count(report => report.IsSelected) == 2; + public bool CanDeleteReports => ParserReports.Any(report => report.IsSelected); + public string DiffButtonContent => string.Format(ParserUIStrings.ksDelete, + ParserReports.Count(report => report.IsSelected)); public ParserReportsViewModel() { - ParserReports = new ObservableCollection(); + ParserReports = new ObservableCollection(); // Check if we're in design mode if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { // Populate with design-time data - ParserReports.Add(new ParserReport + ParserReports.Add(new ParserReportViewModel { ParserReport = new ParserReport { ProjectName = "Example Project 1", MachineName = "DevMachine1", @@ -27,8 +65,8 @@ public ParserReportsViewModel() NumWords = 1000, NumParseErrors = 5, NumZeroParses = 3 - }); - ParserReports.Add(new ParserReport + }}); + ParserReports.Add(new ParserReportViewModel { ParserReport = new ParserReport { ProjectName = "Example Project 2", MachineName = "DevMachine2", @@ -37,15 +75,35 @@ public ParserReportsViewModel() NumWords = 1500, NumParseErrors = 2, NumZeroParses = 1 - }); + }}); } } public event PropertyChangedEventHandler PropertyChanged; + + private void OnReportPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(ParserReportViewModel.IsSelected)) + { + // Notify changes to button state properties + OnPropertyChanged(nameof(CanShowReport)); + OnPropertyChanged(nameof(CanDiffReports)); + OnPropertyChanged(nameof(CanDeleteReports)); + OnPropertyChanged(nameof(DiffButtonContent)); + } + } protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + + // Call this method whenever the IsSelected property of any ParserReport changes + public void UpdateButtonStates() + { + OnPropertyChanged(nameof(CanShowReport)); + OnPropertyChanged(nameof(CanDiffReports)); + OnPropertyChanged(nameof(CanDeleteReports)); + } } } diff --git a/Src/LexText/ParserUI/ParserUIStrings.Designer.cs b/Src/LexText/ParserUI/ParserUIStrings.Designer.cs index 4c611d34d1..fe85bcee0f 100644 --- a/Src/LexText/ParserUI/ParserUIStrings.Designer.cs +++ b/Src/LexText/ParserUI/ParserUIStrings.Designer.cs @@ -87,6 +87,15 @@ public static string ksDash { } } + /// + /// Looks up a localized string similar to Delete {0} Reports. + /// + public static string ksDelete { + get { + return ResourceManager.GetString("ksDelete", resourceCulture); + } + } + /// /// Looks up a localized string similar to Delete this test report from the disk. /// @@ -106,7 +115,7 @@ public static string ksDidNotParse { } /// - /// Looks up a localized string similar to diff. + /// Looks up a localized string similar to _Compare. /// public static string ksDiffButton { get { @@ -115,7 +124,7 @@ public static string ksDiffButton { } /// - /// Looks up a localized string similar to Show the difference between this test report and the report selected by the radio button (older report is subtracted from newer report). + /// Looks up a localized string similar to Show the difference between two selected reports (older report is subtracted from newer report) [Alt-C]. /// public static string ksDiffButtonToolTip { get { @@ -430,7 +439,7 @@ public static string ksRedoEditingParserParameters { } /// - /// Looks up a localized string similar to reparse. + /// Looks up a localized string similar to Try A Word.... /// public static string ksReparse { get { @@ -447,6 +456,15 @@ public static string ksReparseToolTip { } } + /// + /// Looks up a localized string similar to Select. + /// + public static string ksSelect { + get { + return ResourceManager.GetString("ksSelect", resourceCulture); + } + } + /// /// Looks up a localized string similar to Please select a second report other than this report using the radio button in the column labelled 'Diff With'.. /// @@ -457,7 +475,7 @@ public static string ksSelectDiffReport { } /// - /// Looks up a localized string similar to show. + /// Looks up a localized string similar to Show. /// public static string ksShowAnalyses { get { @@ -475,7 +493,7 @@ public static string ksShowAnalysesToolTip { } /// - /// Looks up a localized string similar to show. + /// Looks up a localized string similar to Show Report. /// public static string ksShowReport { get { diff --git a/Src/LexText/ParserUI/ParserUIStrings.resx b/Src/LexText/ParserUI/ParserUIStrings.resx index 933b652375..37a83af411 100644 --- a/Src/LexText/ParserUI/ParserUIStrings.resx +++ b/Src/LexText/ParserUI/ParserUIStrings.resx @@ -186,10 +186,10 @@ Delete this test report from the disk - diff + _Compare - Show the difference between this test report and the report selected by the radio button (older report is subtracted from newer report) + Show the difference between two selected reports (older report is subtracted from newer report) [Alt-C] Diff With @@ -261,19 +261,19 @@ The time it took to parse the word - reparse + Try A Word... Reparse this word using Try A Word - show + Show Show the analyses of this word - show + Show Report Show this test report @@ -329,4 +329,11 @@ Please select a second report other than this report using the radio button in the column labelled 'Diff With'. + + Delete {0} Reports + Deleting parser reports {0} is the number selected + + + Select + \ No newline at end of file From 7b42dadfd0627769e9181a29b954d61a57b8916b Mon Sep 17 00:00:00 2001 From: Ariel Rorabaugh Date: Fri, 2 Aug 2024 17:13:16 -0400 Subject: [PATCH 178/415] LT-21807: WordExport - Fix pictures in subentries Images that are in subentries are associated with a Subentries node instead of a Pictures node. When constructing paragraphs for the runs in AddEntryData, we need to add a check for image descendents. This will catch images that aren't in a Pictures node. Change-Id: Ib4b544add3057b948078ed0ee418a4ab87fbc9c4 --- Src/xWorks/LcmWordGenerator.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 8948252eac..e7479be6e2 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -1299,7 +1299,13 @@ public void AddEntryData(IFragmentWriter writer, List().Any(); + // Image captions have a Pictures node as their parent. + // For a main entry, an image will have the "Pictures" ConfigurableDictionaryNode associated with it. + // For subentries, however, the image is a descendant of a "Subentries" ConfigurableDictionaryNode. + // Thus, to know if we're dealing with an image and/or caption, + // we check if the node or its parent is a picture Node, or if the run contains a descendant that is a picture. + if (config.Label == "Pictures" || config.Parent?.Label == "Pictures" || containsDrawing) { // Runs containing pictures or captions need to be in separate paragraphs // from whatever precedes and follows them because they will be added into textframes, @@ -1321,7 +1327,7 @@ public void AddEntryData(IFragmentWriter writer, List().Any()) + if (containsDrawing) { if (pieceHasImage) { From ccd1781c6814aed0b31159244717fb370295db0c Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 5 Aug 2024 09:51:23 -0700 Subject: [PATCH 179/415] Fix LT-21824 by changing error message (#125) --- Src/xWorks/xWorksStrings.Designer.cs | 2 +- Src/xWorks/xWorksStrings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/xWorksStrings.Designer.cs b/Src/xWorks/xWorksStrings.Designer.cs index 12b4a5ec1c..04b988f3e0 100644 --- a/Src/xWorks/xWorksStrings.Designer.cs +++ b/Src/xWorks/xWorksStrings.Designer.cs @@ -2664,7 +2664,7 @@ internal static string PunctInFieldNameError { } /// - /// Looks up a localized string similar to The custom field name "{0}" includes punctuation characters. This may cause export to fail or make the exported file difficult for other programs to use. We recommend that you rename your custom field and then do the export again. + /// Looks up a localized string similar to The custom field name "{0}" includes punctuation characters. This may cause export to fail or make the exported file difficult for other programs to use. Please contact flex_errors@sil.org for help modifying your project. ///Do you want to continue with the export?. /// internal static string PunctInFieldNameWarning { diff --git a/Src/xWorks/xWorksStrings.resx b/Src/xWorks/xWorksStrings.resx index 560763d324..f3d8440873 100644 --- a/Src/xWorks/xWorksStrings.resx +++ b/Src/xWorks/xWorksStrings.resx @@ -822,7 +822,7 @@ Are you sure you want to delete this list? Please remove the punctuation from the custom field name ("{0}") to allow export and some other functions to work. - The custom field name "{0}" includes punctuation characters. This may cause export to fail or make the exported file difficult for other programs to use. We recommend that you rename your custom field and then do the export again. + The custom field name "{0}" includes punctuation characters. This may cause export to fail or make the exported file difficult for other programs to use. Please contact flex_errors@sil.org for help modifying your project. Do you want to continue with the export? From 8e0fe208c956f45ed3a1a041270c968fbccb764b Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 5 Aug 2024 17:11:41 -0700 Subject: [PATCH 180/415] Fix LT-21854: Delete reports button not working properly (#126) * Fix LT-21854: Delete reports button not working properly * Improve how new items are added to ParserReports --- Src/LexText/ParserUI/ParserListener.cs | 6 ++-- .../ParserUI/ParserReportsDialog.xaml.cs | 1 + .../ParserUI/ParserReportsViewModel.cs | 32 +++++++++---------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Src/LexText/ParserUI/ParserListener.cs b/Src/LexText/ParserUI/ParserListener.cs index c899351c26..2a1567da1b 100644 --- a/Src/LexText/ParserUI/ParserListener.cs +++ b/Src/LexText/ParserUI/ParserListener.cs @@ -758,10 +758,10 @@ private void ReadParserReports() private void AddParserReport(ParserReport parserReport) { ReadParserReports(); - // m_parserReportsDialog's window updates when m_parserReports changes - // because m_parserReports is an ObservableCollection. - // Add at front so that newest reports appear first. m_parserReports.Insert(0, new ParserReportViewModel { ParserReport = parserReport }); + if (m_parserReportsDialog != null) + // Reset ParserReports so that the window gets notified when the new report is selected. + ((ParserReportsViewModel)m_parserReportsDialog.DataContext).ParserReports = m_parserReports; } /// diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs b/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs index 09c24aa762..1ec9ecd8b6 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs @@ -57,6 +57,7 @@ public void DeleteParserReport(object sender, RoutedEventArgs e) if (report.IsSelected) { report.ParserReport.DeleteJsonFile(); + report.IsSelected = false; ParserReports.Remove(report); } } diff --git a/Src/LexText/ParserUI/ParserReportsViewModel.cs b/Src/LexText/ParserUI/ParserReportsViewModel.cs index eeaad00d40..609dbfb768 100644 --- a/Src/LexText/ParserUI/ParserReportsViewModel.cs +++ b/Src/LexText/ParserUI/ParserReportsViewModel.cs @@ -15,31 +15,29 @@ public ObservableCollection ParserReports get => _parserReports; set { - if (_parserReports != value) + // Do this even if value == _parserReports because it may have new items. + // Unsubscribe from PropertyChanged events of old collection items + if (_parserReports != null) { - // Unsubscribe from PropertyChanged events of old collection items - if (_parserReports != null) + foreach (var report in _parserReports) { - foreach (var report in _parserReports) - { - report.PropertyChanged -= OnReportPropertyChanged; - } + report.PropertyChanged -= OnReportPropertyChanged; } + } - _parserReports = value; + _parserReports = value; - // Subscribe to PropertyChanged events of new collection items - if (_parserReports != null) + // Subscribe to PropertyChanged events of new collection items + if (_parserReports != null) + { + foreach (var report in _parserReports) { - foreach (var report in _parserReports) - { - report.PropertyChanged += OnReportPropertyChanged; - } + report.PropertyChanged += OnReportPropertyChanged; } - - OnPropertyChanged(nameof(ParserReports)); - UpdateButtonStates(); // Update button states when the collection changes } + + OnPropertyChanged(nameof(ParserReports)); + UpdateButtonStates(); // Update button states when the collection changes } } public bool CanShowReport => ParserReports.Count(report => report.IsSelected) == 1; From b6fb5b2b1d300ba58dc52ee41fa8397fa89a23f1 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 5 Aug 2024 18:02:24 -0700 Subject: [PATCH 181/415] Fix 21855: Scrolling function in Parse Test Reports (#128) --- Src/LexText/ParserUI/ParserReportsDialog.xaml | 2 +- Src/LexText/ParserUI/ParserReportsDialog.xaml.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml b/Src/LexText/ParserUI/ParserReportsDialog.xaml index 31a03ec9c3..6caef14ed4 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml @@ -24,7 +24,7 @@ - + parserRep DataContext = new ParserReportsViewModel { ParserReports = parserReports }; } + private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + ScrollViewer scrollViewer = sender as ScrollViewer; + if (scrollViewer != null) + if (e.Delta > 0) + scrollViewer.LineUp(); + else + scrollViewer.LineDown(); + e.Handled = true; + } + public void ShowParserReport(object sender, RoutedEventArgs e) { foreach (var report in ParserReports) From c636e34b64ff24f8c4a562f421fc649e73bad0ca Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:19:58 -0400 Subject: [PATCH 182/415] Word Export: Fix indenting problem (#129) We need to use the config.DisplayLabel instead of the config.Style for the style reference. Some cases were previously working because these values were the same. The change to the AddStyle() call was to fix a problem with the way paragraph styles were organized in the collection. Change-Id: I2db26f07716407afd7728bc10b6f7eeddb1954e1 --- Src/xWorks/LcmWordGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index e7479be6e2..004f535b02 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -865,7 +865,7 @@ config.DictionaryNodeOptions is DictionaryNodeGroupingOptions && if (!string.IsNullOrEmpty(config.Style)) { WP.ParagraphProperties paragraphProps = - new WP.ParagraphProperties(new ParagraphStyleId() { Val = config.Style }); + new WP.ParagraphProperties(new ParagraphStyleId() { Val = config.DisplayLabel }); groupPara.PrependChild(paragraphProps); } groupData.DocBody.AppendChild(groupPara); @@ -1678,7 +1678,7 @@ public string AddStyles(ConfigurableDictionaryNode node) if (style.Type == StyleValues.Paragraph) { string oldName = style.StyleId; - string newName = s_styleCollection.AddStyle(style, style.StyleId, style.StyleId); + string newName = s_styleCollection.AddStyle(style, node.Style, style.StyleId); Debug.Assert(oldName == newName, "Not expecting the name for a paragraph style to ever change!"); } else From 6d91975dc961e7dab423e2c14f4c72d7df38629a Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Wed, 7 Aug 2024 15:23:48 -0700 Subject: [PATCH 183/415] Fix LT-21858: Parser test reports pane highlights inaccurate report (#130) --- Src/LexText/ParserUI/ParserReportsDialog.xaml | 3 ++- Src/LexText/ParserUI/ParserReportsDialog.xaml.cs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml b/Src/LexText/ParserUI/ParserReportsDialog.xaml index 6caef14ed4..bd57e6f295 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml @@ -25,7 +25,8 @@ - + diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs b/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs index ce7d838ca4..d3bca91982 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml.cs @@ -1,6 +1,7 @@ using SIL.Extensions; using SIL.FieldWorks.WordWorks.Parser; using SIL.LCModel; +using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; @@ -8,6 +9,7 @@ using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; +using System.Windows.Threading; using XCore; namespace SIL.FieldWorks.LexText.Controls @@ -118,8 +120,16 @@ private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) { if(dataGrid.SelectedItem is ParserReportViewModel selectedItem) ParserListener.ShowParserReport(selectedItem.ParserReport, Mediator, Cache); - else - Debug.Fail("Type of Contents of DataGrid changed, adjust double click code."); + } + else + Debug.Fail("Type of Contents of DataGrid changed, adjust double click code."); + } + private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is DataGrid dataGrid) + { + // Turn off selection in favor of the check box. + Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => dataGrid.UnselectAll())); } } private void CheckBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) From f939a7c64447dc6007173b7362e953653427f2ae Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Thu, 8 Aug 2024 15:19:02 -0400 Subject: [PATCH 184/415] Fix 2-column display by correcting image anchor (#133) The horizontal anchor type for the textframe should be set to text rather than page. A text type anchor promts the textframe to move with the text it is anchored to, while a page type anchor would keep the textframe fixed in a particular location on the page. If the horizontal anchor type is set to page, images can appear in the wrong column when the document layout is set to two columns. Change-Id: I28c1644d0ce7bdf33d80d908d3615f12b8a059ab --- Src/xWorks/WordStylesGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index e85fa1e767..66b8279ac2 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -465,7 +465,7 @@ private static Styles GenerateParagraphStyleFromPictureOptions(ConfigurableDicti // A paragraph is turned into a textframe simply by adding a frameproperties object inside the paragraph properties. // We leave a 4-pt border around the textframe--80 twentieths of a point. var textFrameBorder = "80"; - var textFrameProps = new FrameProperties() { Width = textFrameWidth.ToString(), HeightType = HeightRuleValues.Auto, HorizontalSpace = textFrameBorder, VerticalSpace = textFrameBorder, Wrap = TextWrappingValues.NotBeside, VerticalPosition = VerticalAnchorValues.Text, HorizontalPosition = HorizontalAnchorValues.Margin, XAlign = HorizontalAlignmentValues.Right }; + var textFrameProps = new FrameProperties() { Width = textFrameWidth.ToString(), HeightType = HeightRuleValues.Auto, HorizontalSpace = textFrameBorder, VerticalSpace = textFrameBorder, Wrap = TextWrappingValues.NotBeside, VerticalPosition = VerticalAnchorValues.Text, HorizontalPosition = HorizontalAnchorValues.Text, XAlign = HorizontalAlignmentValues.Right }; var parProps = new ParagraphProperties(); frameStyle.StyleId = PictureAndCaptionTextframeStyle; frameStyle.StyleName = new StyleName(){Val = PictureAndCaptionTextframeStyle}; From d8301c774136b34a2078cd750f65d212c657c32a Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 9 Aug 2024 09:38:26 -0700 Subject: [PATCH 185/415] Fix LT-21852: Import Phonology command mistakenly suggests SFM file (#134) --- DistFiles/Language Explorer/Configuration/Main.xml | 2 +- Src/xWorks/FwXWindow.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/Main.xml b/DistFiles/Language Explorer/Configuration/Main.xml index 1fecd5759c..f356e34e46 100644 --- a/DistFiles/Language Explorer/Configuration/Main.xml +++ b/DistFiles/Language Explorer/Configuration/Main.xml @@ -68,7 +68,7 @@ - + diff --git a/Src/xWorks/FwXWindow.cs b/Src/xWorks/FwXWindow.cs index 04141608a8..8859e5610c 100644 --- a/Src/xWorks/FwXWindow.cs +++ b/Src/xWorks/FwXWindow.cs @@ -1938,10 +1938,10 @@ public bool OnImportPhonology(object commandObject) { dlg.CheckFileExists = true; dlg.RestoreDirectory = true; - dlg.Title = ResourceHelper.GetResourceString("kstidOpenTranslatedLists"); + dlg.Title = ResourceHelper.GetResourceString("kstidXML"); dlg.ValidateNames = true; dlg.Multiselect = false; - dlg.Filter = ResourceHelper.FileFilter(FileFilterType.FieldWorksTranslatedLists); + dlg.Filter = ResourceHelper.FileFilter(FileFilterType.XML); if (dlg.ShowDialog(form) != DialogResult.OK) return true; filename = dlg.FileName; From 9f9fdae228b6476100274e3d5f98279b311d0869 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 9 Aug 2024 11:14:51 -0700 Subject: [PATCH 186/415] LT-21859: Fix minor problems in parser test reports (#131) * Fix problems 1-4 of LT-21859 * Switch to On Genre; Fix Beth's height problem * Fix compiler error --- .../Configuration/Words/areaConfiguration.xml | 4 +- Src/LexText/ParserCore/ParserReport.cs | 12 +++- Src/LexText/ParserUI/ParserListener.cs | 54 +++++++++++++---- Src/LexText/ParserUI/ParserReportDialog.xaml | 2 +- .../ParserUI/ParserReportDialog.xaml.cs | 9 ++- Src/LexText/ParserUI/ParserReportViewModel.cs | 6 +- Src/LexText/ParserUI/ParserReportsDialog.xaml | 2 +- Src/LexText/ParserUI/ParserUI.csproj | 10 ++++ .../ParserUI/ParserUIStrings.Designer.cs | 60 +++++++------------ Src/LexText/ParserUI/ParserUIStrings.resx | 29 ++++----- 10 files changed, 109 insertions(+), 79 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml index 59dd6f034c..3a39e8b1d1 100644 --- a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml @@ -18,7 +18,7 @@ - + @@ -277,8 +277,8 @@ - + diff --git a/Src/LexText/ParserCore/ParserReport.cs b/Src/LexText/ParserCore/ParserReport.cs index 22324182e9..ec6db14851 100644 --- a/Src/LexText/ParserCore/ParserReport.cs +++ b/Src/LexText/ParserCore/ParserReport.cs @@ -25,7 +25,7 @@ public class ParserReport: IEquatable public string MachineName { get; set; } /// - /// Either "Testbed Texts", "All Texts", or the name of the text parsed + /// Either the name of the text parsed, the name of the genre parsed, or "All Texts" /// public string SourceText { get; set; } @@ -35,6 +35,11 @@ public class ParserReport: IEquatable /// public long Timestamp { get; set; } + /// + /// Timestamp of report that this report was diffed with (only relevant for IsDiff). + /// + public long DiffTimestamp { get; set; } + /// /// Number of words parsed /// @@ -227,7 +232,8 @@ public ParserReport DiffParserReports(ParserReport other) diff.ProjectName = DiffNames(ProjectName, other.ProjectName); diff.SourceText = DiffNames(SourceText, other.SourceText); diff.MachineName = DiffNames(MachineName, other.MachineName); - diff.Timestamp = Timestamp - other.Timestamp; + diff.Timestamp = Timestamp; + diff.DiffTimestamp = other.Timestamp; diff.NumWords = NumWords - other.NumWords; diff.NumParseErrors = NumParseErrors - other.NumParseErrors; diff.NumZeroParses = NumZeroParses - other.NumZeroParses; @@ -244,7 +250,7 @@ string DiffNames(string name, string otherName) { if (name == otherName) return name; - return name + " - " + otherName; + return otherName + " => " + name; } /// diff --git a/Src/LexText/ParserUI/ParserListener.cs b/Src/LexText/ParserUI/ParserListener.cs index 2a1567da1b..fa69cd0b02 100644 --- a/Src/LexText/ParserUI/ParserListener.cs +++ b/Src/LexText/ParserUI/ParserListener.cs @@ -32,6 +32,9 @@ using XCore; using SIL.ObjectModel; using SIL.LCModel.Core.Text; +using System.Runtime.Remoting.Contexts; +using SIL.FieldWorks.Common.Framework.DetailControls; +using SIL.FieldWorks.Common.Controls; namespace SIL.FieldWorks.LexText.Controls { @@ -518,26 +521,56 @@ public bool OnCheckParserOnCurrentText(object argument) return true; //we handled this. } - public bool OnCheckParserOnTestbed(object argument) + public bool OnCheckParserOnGenre(object argument) { CheckDisposed(); if (ConnectToParser()) { - // Get all of the wordforms in the Testbed texts. - IEnumerable wordforms = new HashSet(); - foreach (var text in m_cache.LanguageProject.InterlinearTexts) - foreach (var genre in text.GenreCategories) - if (genre.ShortName == "Testbed") - wordforms = wordforms.Union(text.UniqueWordforms()); + // Get the selected genre from the user. + string displayWs = "analysis vernacular"; + var labels = ObjectLabel.CreateObjectLabels(m_cache, m_cache.LanguageProject.GenreListOA.PossibilitiesOS, "", displayWs); + var chooser = new SimpleListChooser(null, labels, ParserUIStrings.ksGenre, m_propertyTable.GetValue("HelpTopicProvider")); + // chooser.SetHelpTopic("FLExHelpFile"); + ExpandTreeViewNodes(chooser.TreeView.Nodes); + chooser.ShowDialog(); + ICmPossibility selectedGenre = (ICmPossibility)chooser.SelectedObject; + if (chooser.ChosenOne == null || selectedGenre == null) + return false; + // Get all of the wordforms in the genre's texts. + IEnumerable wordforms = new HashSet(); + foreach (var text in m_cache.LanguageProject.InterlinearTexts.Where(t => t.GenreCategories.Any(genre => ContainsGenre(selectedGenre, genre)))) + { + wordforms = wordforms.Union(text.UniqueWordforms()); + } // Check all of the wordforms. - UpdateWordforms(wordforms, ParserPriority.Medium, checkParser: true, "Testbed Texts"); + var genreName = String.Format(ParserUIStrings.ksXGenre, selectedGenre.Name.AnalysisDefaultWritingSystem.Text); + UpdateWordforms(wordforms, ParserPriority.Medium, checkParser: true, genreName); } return true; //we handled this. } + private void ExpandTreeViewNodes(TreeNodeCollection nodes) + { + foreach (TreeNode node in nodes) + { + node.Expand(); + ExpandTreeViewNodes(node.Nodes); + } + } + + private bool ContainsGenre(ICmPossibility genre1, ICmPossibility genre2) + { + while (genre2 != null) + { + if (genre1 == genre2) return true; + genre2 = genre2.Owner as ICmPossibility; + } + return false; + } + public bool OnCheckParserOnAll(object argument) { CheckDisposed(); @@ -575,6 +608,7 @@ private void UpdateWordforms(IEnumerable wordforms, ParserPriority InitCheckParserResults(wordforms, sourceText); if (wordforms.Count() == 0) { + ReadParserReports(); // Write an empty parser report. var parserReport = WriteParserReport(); AddParserReport(parserReport); @@ -624,8 +658,9 @@ private void WordformUpdatedEventHandler(object sender, WordformUpdatedEventArgs if (m_checkParserResults[key] == null) return; } + // Read parser reports before writing and adding a parser report to avoid duplicates. + ReadParserReports(); // Convert parse results into ParserReport. - var parserReport = WriteParserReport(); AddParserReport(parserReport); ShowParserReport(parserReport, m_mediator, m_cache); @@ -757,7 +792,6 @@ private void ReadParserReports() /// private void AddParserReport(ParserReport parserReport) { - ReadParserReports(); m_parserReports.Insert(0, new ParserReportViewModel { ParserReport = parserReport }); if (m_parserReportsDialog != null) // Reset ParserReports so that the window gets notified when the new report is selected. diff --git a/Src/LexText/ParserUI/ParserReportDialog.xaml b/Src/LexText/ParserUI/ParserReportDialog.xaml index 8328ef6bd9..be56f75bba 100644 --- a/Src/LexText/ParserUI/ParserReportDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportDialog.xaml @@ -7,7 +7,7 @@ d:DataContext="{d:DesignInstance Type=local:ParserReportViewModel, IsDesignTimeCreatable=True}" mc:Ignorable="d" Title="{Binding Title}" WindowStartupLocation="CenterScreen" - SizeToContent="WidthAndHeight" MinHeight="300" MaxHeight="1000" + SizeToContent="Width" Height="500" WindowStyle="ThreeDBorderWindow"> diff --git a/Src/LexText/ParserUI/ParserReportDialog.xaml.cs b/Src/LexText/ParserUI/ParserReportDialog.xaml.cs index 1883419466..1334d2d326 100644 --- a/Src/LexText/ParserUI/ParserReportDialog.xaml.cs +++ b/Src/LexText/ParserUI/ParserReportDialog.xaml.cs @@ -30,14 +30,14 @@ public void ReparseWord(object sender, RoutedEventArgs e) { var button = sender as Button; var parseReport = button.CommandParameter as ParseReport; - Mediator.SendMessage("TryThisWord", parseReport.Word); + Mediator.SendMessage("TryThisWord", RemoveArrow(parseReport.Word)); } public void ShowWordAnalyses(object sender, RoutedEventArgs e) { var button = sender as Button; var parseReport = button.CommandParameter as ParseReport; - var tsString = TsStringUtils.MakeString(parseReport.Word, Cache.DefaultVernWs); + var tsString = TsStringUtils.MakeString(RemoveArrow(parseReport.Word), Cache.DefaultVernWs); IWfiWordform wordform; if (Cache.ServiceLocator.GetInstance().TryGetObject(tsString, out wordform)) { @@ -49,5 +49,10 @@ public void ShowWordAnalyses(object sender, RoutedEventArgs e) MessageBox.Show("Unknown word " + parseReport.Word); } } + + private string RemoveArrow(string word) + { + return word.Replace(" => ", string.Empty); + } } } diff --git a/Src/LexText/ParserUI/ParserReportViewModel.cs b/Src/LexText/ParserUI/ParserReportViewModel.cs index fd1e94e873..d9aaf09b5a 100644 --- a/Src/LexText/ParserUI/ParserReportViewModel.cs +++ b/Src/LexText/ParserUI/ParserReportViewModel.cs @@ -17,9 +17,9 @@ public string Title { get { - string time = ParserReport.IsDiff - ? TimeSpan.FromTicks(ParserReport.Timestamp).ToString() - : m_FileTimeToDateTimeConverter.Convert(ParserReport.Timestamp, null, null, null).ToString(); + string time = m_FileTimeToDateTimeConverter.Convert(ParserReport.Timestamp, null, null, null).ToString(); + if (ParserReport.IsDiff) + time = m_FileTimeToDateTimeConverter.Convert(ParserReport.DiffTimestamp, null, null, null).ToString() + " => " + time; return (ParserReport.IsDiff ? ParserUIStrings.ksDiffHeader + " " : "") + ParserReport.ProjectName + ", " + ParserReport.SourceText + ", " + time + ", " + ParserReport.MachineName; } } diff --git a/Src/LexText/ParserUI/ParserReportsDialog.xaml b/Src/LexText/ParserUI/ParserReportsDialog.xaml index bd57e6f295..9941d38385 100644 --- a/Src/LexText/ParserUI/ParserReportsDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportsDialog.xaml @@ -7,7 +7,7 @@ d:DataContext="{d:DesignInstance Type=local:ParserReportsViewModel, IsDesignTimeCreatable=True}" mc:Ignorable="d" Title="{x:Static local:ParserUIStrings.ksParserTestReports}" WindowStartupLocation="CenterScreen" - SizeToContent="Width" MinHeight="300" + SizeToContent="Width" Height="300" WindowStyle="ThreeDBorderWindow"> diff --git a/Src/LexText/ParserUI/ParserUI.csproj b/Src/LexText/ParserUI/ParserUI.csproj index dc7c17b96a..d77a354945 100644 --- a/Src/LexText/ParserUI/ParserUI.csproj +++ b/Src/LexText/ParserUI/ParserUI.csproj @@ -362,6 +362,16 @@ MSBuild:Compile + + + {C65D2B3D-543D-4F63-B35D-5859F5ECDE1E} + DetailControls + + + {BC490547-D278-4442-BD34-3580DBEFC405} + XMLViews + + diff --git a/Src/LexText/ParserUI/ParserUIStrings.Designer.cs b/Src/LexText/ParserUI/ParserUIStrings.Designer.cs index fe85bcee0f..388890d55a 100644 --- a/Src/LexText/ParserUI/ParserUIStrings.Designer.cs +++ b/Src/LexText/ParserUI/ParserUIStrings.Designer.cs @@ -97,7 +97,7 @@ public static string ksDelete { } /// - /// Looks up a localized string similar to Delete this test report from the disk. + /// Looks up a localized string similar to Delete the selected test reports from the disk. /// public static string ksDeleteToolTip { get { @@ -133,7 +133,7 @@ public static string ksDiffButtonToolTip { } /// - /// Looks up a localized string similar to Diff With. + /// Looks up a localized string similar to Compare. /// public static string ksDiffHeader { get { @@ -141,15 +141,6 @@ public static string ksDiffHeader { } } - /// - /// Looks up a localized string similar to The other argument to diff. - /// - public static string ksDiffHeaderToolTip { - get { - return ResourceManager.GetString("ksDiffHeaderToolTip", resourceCulture); - } - } - /// /// Looks up a localized string similar to Error Message. /// @@ -168,6 +159,15 @@ public static string ksErrorMessageToolTip { } } + /// + /// Looks up a localized string similar to Genre. + /// + public static string ksGenre { + get { + return ResourceManager.GetString("ksGenre", resourceCulture); + } + } + /// /// Looks up a localized string similar to . /// @@ -448,7 +448,7 @@ public static string ksReparse { } /// - /// Looks up a localized string similar to Reparse this word using Try A Word. + /// Looks up a localized string similar to Parse this word using Try A Word. /// public static string ksReparseToolTip { get { @@ -465,15 +465,6 @@ public static string ksSelect { } } - /// - /// Looks up a localized string similar to Please select a second report other than this report using the radio button in the column labelled 'Diff With'.. - /// - public static string ksSelectDiffReport { - get { - return ResourceManager.GetString("ksSelectDiffReport", resourceCulture); - } - } - /// /// Looks up a localized string similar to Show. /// @@ -663,24 +654,6 @@ public static string ksUndoEditingParserParameters { } } - /// - /// Looks up a localized string similar to Unknown. - /// - public static string ksUnknown { - get { - return ResourceManager.GetString("ksUnknown", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Update. - /// - public static string ksUpdate { - get { - return ResourceManager.GetString("ksUpdate", resourceCulture); - } - } - /// /// Looks up a localized string similar to Word. /// @@ -698,5 +671,14 @@ public static string ksWordToolTip { return ResourceManager.GetString("ksWordToolTip", resourceCulture); } } + + /// + /// Looks up a localized string similar to {0} genre. + /// + public static string ksXGenre { + get { + return ResourceManager.GetString("ksXGenre", resourceCulture); + } + } } } diff --git a/Src/LexText/ParserUI/ParserUIStrings.resx b/Src/LexText/ParserUI/ParserUIStrings.resx index 37a83af411..52a54f8155 100644 --- a/Src/LexText/ParserUI/ParserUIStrings.resx +++ b/Src/LexText/ParserUI/ParserUIStrings.resx @@ -156,13 +156,6 @@ Queue: ({0}/{1}/{2}) {0}, {1}, and {2} are numbers (or value of ksDash) - - Unknown - - - Update - related to ParserCoreStrings.ksUpdateX - Undo Editing Parser Parameters @@ -182,9 +175,6 @@ Parse was not attempted because of errors in the lexical data - - Delete this test report from the disk - _Compare @@ -192,10 +182,7 @@ Show the difference between two selected reports (older report is subtracted from newer report) [Alt-C] - Diff With - - - The other argument to diff + Compare Error Message @@ -264,7 +251,7 @@ Try A Word... - Reparse this word using Try A Word + Parse this word using Try A Word Show @@ -326,9 +313,6 @@ The word that was parsed - - Please select a second report other than this report using the radio button in the column labelled 'Diff With'. - Delete {0} Reports Deleting parser reports {0} is the number selected @@ -336,4 +320,13 @@ Select + + Delete the selected test reports from the disk + + + Genre + + + {0} genre + \ No newline at end of file From 22e58c7365d6c1739c11852b4b291dcdea19bc84 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Fri, 9 Aug 2024 12:36:57 -0700 Subject: [PATCH 187/415] Fix LT-21701: Add basic find dialog for Dictionary views (#132) Co-authored-by: Jake Oliver --- Src/FwCoreDlgs/BasicFindDialog.Designer.cs | 12 +- Src/FwCoreDlgs/BasicFindDialog.cs | 31 +++-- Src/FwCoreDlgs/BasicFindDialog.resx | 35 +++++- Src/xWorks/XhtmlDocView.cs | 127 ++++++++++++++++++++- 4 files changed, 191 insertions(+), 14 deletions(-) diff --git a/Src/FwCoreDlgs/BasicFindDialog.Designer.cs b/Src/FwCoreDlgs/BasicFindDialog.Designer.cs index 5cddfb310d..5ef3e44081 100644 --- a/Src/FwCoreDlgs/BasicFindDialog.Designer.cs +++ b/Src/FwCoreDlgs/BasicFindDialog.Designer.cs @@ -40,6 +40,7 @@ private void InitializeComponent() this._searchTextbox = new System.Windows.Forms.TextBox(); this._notificationLabel = new System.Windows.Forms.Label(); this._findNext = new System.Windows.Forms.Button(); + this._findPrev = new System.Windows.Forms.Button(); this.SuspendLayout(); // // _searchTextbox @@ -61,10 +62,18 @@ private void InitializeComponent() this._findNext.UseVisualStyleBackColor = true; this._findNext.Click += new System.EventHandler(this._findNext_Click); // + // _findPrev + // + resources.ApplyResources(this._findPrev, "_findPrev"); + this._findPrev.Name = "_findPrev"; + this._findPrev.UseVisualStyleBackColor = true; + this._findPrev.Click += new System.EventHandler(this._findPrev_Click); + // // BasicFindDialog // resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this._findPrev); this.Controls.Add(this._findNext); this.Controls.Add(this._notificationLabel); this.Controls.Add(this._searchTextbox); @@ -85,5 +94,6 @@ private void InitializeComponent() private TextBox _searchTextbox; private System.Windows.Forms.Label _notificationLabel; private System.Windows.Forms.Button _findNext; - } + private Button _findPrev; + } } \ No newline at end of file diff --git a/Src/FwCoreDlgs/BasicFindDialog.cs b/Src/FwCoreDlgs/BasicFindDialog.cs index c9977fc510..86caf70278 100644 --- a/Src/FwCoreDlgs/BasicFindDialog.cs +++ b/Src/FwCoreDlgs/BasicFindDialog.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2016 SIL International +// Copyright (c) 2016 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -18,6 +18,18 @@ public partial class BasicFindDialog : Form, IBasicFindView /// public event FindNextDelegate FindNext; + /// + public delegate void FindPrevDelegate(object sender, IBasicFindView view); + + /// + public event FindPrevDelegate FindPrev; + + /// + public delegate void SearchTextChangeDelegate(object sender, IBasicFindView view); + + /// + public event SearchTextChangeDelegate SearchTextChanged; + /// /// Basic constructor (for the designer) /// @@ -28,8 +40,7 @@ public BasicFindDialog() private void _findNext_Click(object sender, EventArgs e) { - if(FindNext != null) - FindNext(this, this); + FindNext?.Invoke(this, this); } /// @@ -48,7 +59,8 @@ public string StatusText private void _searchTextbox_TextChanged(object sender, EventArgs e) { - _findNext.Enabled = !string.IsNullOrEmpty(_searchTextbox.Text); + _findNext.Enabled = _findPrev.Enabled = !string.IsNullOrEmpty(_searchTextbox.Text); + SearchTextChanged?.Invoke(this, this); } /// @@ -64,10 +76,15 @@ private void _searchTextbox_KeyDown(object sender, KeyEventArgs e) e.SuppressKeyPress = true; } } - } - /// - public interface IBasicFindView + private void _findPrev_Click(object sender, EventArgs e) + { + FindPrev?.Invoke(this, this); + } + } + + /// + public interface IBasicFindView { /// /// Text to display to the user in the dialog diff --git a/Src/FwCoreDlgs/BasicFindDialog.resx b/Src/FwCoreDlgs/BasicFindDialog.resx index 1bfc79d9fb..7d948ad93f 100644 --- a/Src/FwCoreDlgs/BasicFindDialog.resx +++ b/Src/FwCoreDlgs/BasicFindDialog.resx @@ -138,7 +138,7 @@ $this - 2 + 3 True @@ -162,7 +162,7 @@ $this - 1 + 2 False @@ -189,6 +189,37 @@ $this + 1 + + + False + + + + NoControl + + + 159, 51 + + + 75, 23 + + + 3 + + + Previous + + + _findPrev + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + 0 diff --git a/Src/xWorks/XhtmlDocView.cs b/Src/xWorks/XhtmlDocView.cs index fc59880891..8266cf3154 100644 --- a/Src/xWorks/XhtmlDocView.cs +++ b/Src/xWorks/XhtmlDocView.cs @@ -1187,13 +1187,132 @@ public bool OnFindAndReplaceText(object argument) { if (m_mainView != null) { - var geckoBrowser = m_mainView.NativeBrowser as GeckoWebBrowser; - if (geckoBrowser != null) + var findDialog = new FindDialog(this); + findDialog.Show(m_mainView); + } + return true; + } + + private class FindDialog : BasicFindDialog + { + private string[] results = null; + private int resultIndex = 0; + private XhtmlDocView docView; + public FindDialog(XhtmlDocView doc) + { + docView = doc; + FindNext += FindNextInBrowser; + FindPrev += FindPrevInBrowser; + SearchTextChanged += (sender, args) => { + var lastId = Guid.Empty.ToString(); + if (results != null && results.Length > 0) + lastId = results[resultIndex]; + StatusText = ""; + results = null; + ClearCurrentFindResult(docView.GeckoBrowser, lastId); + }; + } + + private void FindPrevInBrowser(object sender, IBasicFindView view) + { + var geckoBrowser = docView.m_mainView.NativeBrowser as GeckoWebBrowser; + if (geckoBrowser == null) + return; + string lastId = Guid.Empty.ToString(); + if (!InitResults(view.SearchText)) { - geckoBrowser.Window.Find(string.Empty, false, false, true, false, true, true); + lastId = results[resultIndex]; + if (resultIndex - 1 >= 0) + --resultIndex; + else // wrap around + resultIndex = results.Length - 1; } + ScrollAndHighlightResult(geckoBrowser, view, lastId); + } + private void FindNextInBrowser(object sender, IBasicFindView view) + { + var geckoBrowser = docView.m_mainView.NativeBrowser as GeckoWebBrowser; + if (geckoBrowser == null) + return; + string lastId = Guid.Empty.ToString(); + if(!InitResults(view.SearchText)) + { + lastId = results[resultIndex]; + if (resultIndex + 1 < results.Length) + ++resultIndex; + else // wrap around + resultIndex = 0; + } + ScrollAndHighlightResult(geckoBrowser, view, lastId); + } + + private void ScrollAndHighlightResult(GeckoWebBrowser geckoBrowser, IBasicFindView view, string lastId) + { + if (results != null && results.Length > 0) + { + view.StatusText = $"{resultIndex + 1} of {results.Length} Results"; + ClearCurrentFindResult(geckoBrowser, lastId); + var element = geckoBrowser.Document.GetHtmlElementById(results[resultIndex]); + element.ScrollIntoView(true); + docView.AddClassToHtmlElement(element, CurrentSelectedEntryClass); + } + else + { + view.StatusText = "0 Results"; + } + + } + + private void ClearCurrentFindResult(GeckoWebBrowser geckoBrowser, string lastId) + { + var currentElement = geckoBrowser.Document.GetHtmlElementById(lastId); + if (currentElement != null) + docView.RemoveClassFromHtmlElement(currentElement, CurrentSelectedEntryClass); + } + + private bool InitResults(string searchText) + { + var geckoBrowser = docView.m_mainView.NativeBrowser as GeckoWebBrowser; + if (geckoBrowser == null) + throw new ApplicationException(); + if (results == null || results.Length == 0) + { + string newResults = string.Empty; + geckoBrowser.RemoveMessageEventListener("find"); + geckoBrowser.AddMessageEventListener("find", r => newResults = r); + using(var executor = new AutoJSContext(geckoBrowser.Window)) + { + // Javascript query to execute in the browser + // finds every text element matching the search string and returns the id of the first parent element with an id + var browserJsQuery = + "var ids=[];" + + "var containsText = (containsText === undefined)" + // function for finding text in a node (make sure it isn't redefined) + " ? (el, searchText) => Array.from(el.childNodes).some(c => c.nodeType === Node.TEXT_NODE && c.textContent.includes(searchText))" + + " : containsText;" + + "Array.prototype.forEach.call(document.querySelectorAll('span, div, a'), function(element) { if (containsText(element, '" + + searchText + + "')) {" + + " let id = element.id;" + + " if (!id) {" + + " var parent = element.parentElement;" + + " while (parent && !parent.id) {" + + " parent = parent.parentElement;" + + " }" + + " id = parent ? parent.id : null;" + + " }" + + "if (id && !ids.includes(id)) { ids.push(id); } } });" + + "var idsString = ids.join(';');" + + "var event = new MessageEvent('find', { view: window, bubbles: true, cancelable: false, data: idsString });" + // send the results back to the C# code + "document.dispatchEvent(event);"; + executor.EvaluateScript(browserJsQuery); + } + results = newResults.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + resultIndex = 0; + return true; + } + + return false; } - return true; } /// From e78a35829cdff465817d5ef8e0dd236343a09669 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 9 Aug 2024 12:51:37 -0700 Subject: [PATCH 188/415] Fix LT-21869: Run Tests report columns don't expand (#136) --- Src/LexText/ParserUI/ParserReportDialog.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/LexText/ParserUI/ParserReportDialog.xaml b/Src/LexText/ParserUI/ParserReportDialog.xaml index be56f75bba..8197811047 100644 --- a/Src/LexText/ParserUI/ParserReportDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportDialog.xaml @@ -127,7 +127,7 @@ - + + + /// The style to add it's basedOn style. (It's BasedOn value might get modified.) - /// Can be null, but if it is then the paragraph margin is not generated in context. + /// Can be null, but if it is then the only option for getting a basedOnStyle is from + /// the style, not the parent node. private void AddBasedOnStyle(Style style, ConfigurableDictionaryNode node, ReadOnlyPropertyTable propertyTable) { + Debug.Assert(style.Type == StyleValues.Paragraph); + + // No based on styles for pictures. + if (style.StyleId == WordStylesGenerator.PictureAndCaptionTextframeStyle) + return; + + string basedOnStyleName = null; + string basedOnDisplayName = null; + ConfigurableDictionaryNode parentNode = null; if (style.BasedOn != null && !string.IsNullOrEmpty(style.BasedOn.Val)) + { + basedOnStyleName = style.BasedOn.Val; + } + + // If there is no basedOn style, or the basedOn style is "Normal" then use the + // parent node's style for the basedOn style. + if (string.IsNullOrEmpty(basedOnStyleName) || + basedOnStyleName == WordStylesGenerator.NormalParagraphStyleName) + { + if (node?.Parent != null && !string.IsNullOrEmpty(node.Parent.Style) && + (node.Parent.StyleType == ConfigurableDictionaryNode.StyleTypes.Paragraph)) + { + parentNode = node.Parent; + basedOnStyleName = node.Parent.Style; + basedOnDisplayName = node.Parent.DisplayLabel; + } + } + + if (!string.IsNullOrEmpty(basedOnStyleName)) { // If this is a continuation style then base it on a continuation style. bool continuationStyle = style.StyleId.Value.EndsWith(WordStylesGenerator.EntryStyleContinue); + // Currently this method does not work (and should not be used) for continuation styles. The problem is + // that the basedOn name of the regular style has already been changed to the display name. We would + // need a way to get the FLEX name from the display name. + if (continuationStyle) + { + Debug.Assert(!continuationStyle, "Currently this method does not support continuation styles."); + return; + } lock (s_styleCollection) { // If the basedOn style already exists, then update the reference to the basedOn styles unique name. - if (s_styleCollection.TryGetStyle(style.BasedOn.Val, out Style basedOnStyle)) + if (s_styleCollection.TryGetStyle(basedOnStyleName, out Style basedOnStyle)) { style.BasedOn.Val = basedOnStyle.StyleId; if(continuationStyle && style.BasedOn.Val != WordStylesGenerator.NormalParagraphStyleName) @@ -1719,22 +1782,31 @@ private void AddBasedOnStyle(Style style, ConfigurableDictionaryNode node, ReadO // it's basedOn style, then add this basedOn style to the collection. else { - basedOnStyle = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(style.BasedOn.Val, 0, node, propertyTable, !continuationStyle); + basedOnStyle = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(basedOnStyleName, 0, propertyTable, !continuationStyle); // Check if the style is based on itself. This happens with the 'Normal' style and could possibly happen with others. bool basedOnIsDifferent = basedOnStyle.BasedOn?.Val != null && basedOnStyle.StyleId != basedOnStyle.BasedOn?.Val; + + if (!string.IsNullOrEmpty(basedOnDisplayName)) + { + basedOnStyle.StyleId = basedOnDisplayName; + basedOnStyle.StyleName.Val = basedOnStyle.StyleId; + style.BasedOn.Val = basedOnStyle.StyleId; + } if (continuationStyle) { basedOnStyle.StyleId += WordStylesGenerator.EntryStyleContinue; basedOnStyle.StyleName.Val = basedOnStyle.StyleId; - style.BasedOn.Val += WordStylesGenerator.EntryStyleContinue; - + style.BasedOn.Val = basedOnStyle.StyleId; } if (basedOnIsDifferent) { - AddBasedOnStyle(basedOnStyle, node, propertyTable); + // If the parentNode is not null then the basedOnStyle came from the parentNode. + // If the parentNode is null then the basedOnStyle came from the style.BasedOn.Val and + // we should pass null to AddBasedOnStyle since no node is associated with the basedOnStyle. + AddBasedOnStyle(basedOnStyle, parentNode, propertyTable); } - s_styleCollection.AddStyle(basedOnStyle, basedOnStyle.StyleId, basedOnStyle.StyleId); + s_styleCollection.AddStyle(basedOnStyle, basedOnStyleName, basedOnStyle.StyleId); } } } @@ -1783,33 +1855,23 @@ public string AddStyles(ConfigurableDictionaryNode node) // The css className isn't important for the Word export. var className = $".{CssGenerator.GetClassAttributeForConfig(node)}"; - Styles styleContent = null; - styleContent = WordStylesGenerator.CheckRangeOfStylesForEmpties(WordStylesGenerator.GenerateParagraphStylesFromConfigurationNode(node, _propertyTable)); + Style style = WordStylesGenerator.GenerateParagraphStyleFromConfigurationNode(node, _propertyTable); - if (styleContent == null) - return className; - if (!styleContent.Any()) + if (style == null) return className; - foreach (Style style in styleContent.Descendants - - - - + + + + + + + /// Letter header string. /// Letter header style name to display in Word. - internal static DocFragment GenerateLetterHeaderDocFragment(string str, string styleDisplayName) + /// True if this is the first header being written. + internal static DocFragment GenerateLetterHeaderDocFragment(string str, string styleDisplayName, bool firstHeader) { var docFrag = new DocFragment(); // Only create paragraph, run, and text objects if string is nonempty if (!string.IsNullOrEmpty(str)) { - // Everything other than the Letter Header should be 2 columns. Create a empty - // paragraph with two columns for the last paragraph in the section that uses 2 - // columns. (The section is all the entries after the previous letter header.) - var sectProps2 = new SectionProperties( - new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdEven, Type = HeaderFooterValues.Even }, - new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdOdd, Type = HeaderFooterValues.Default }, - new Columns() { EqualWidth = true, ColumnCount = 2 }, - new SectionType() { Val = SectionMarkValues.Continuous } - ); - docFrag.DocBody.AppendChild(new WP.Paragraph(new WP.ParagraphProperties(sectProps2))); + // Don't add this paragraph before the first letter header. It results in an extra blank line. + if (!firstHeader) + { + // Everything other than the Letter Header should be 2 columns. Create a empty + // paragraph with two columns for the last paragraph in the section that uses 2 + // columns. (The section is all the entries after the previous letter header.) + var sectProps2 = new SectionProperties( + new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdEven, Type = HeaderFooterValues.Even }, + new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdOdd, Type = HeaderFooterValues.Default }, + new Columns() { EqualWidth = true, ColumnCount = 2 }, + new SectionType() { Val = SectionMarkValues.Continuous } + ); + docFrag.DocBody.AppendChild(new WP.Paragraph(new WP.ParagraphProperties(sectProps2))); + } // Create the letter header in a paragraph. WP.ParagraphProperties paragraphProps = new WP.ParagraphProperties(new ParagraphStyleId() { Val = styleDisplayName }); From 8de3f0dd97c416b40c1f5c45aa5521e3b233154d Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 10 Dec 2024 12:07:22 -0800 Subject: [PATCH 287/415] Fix LT-21999: Disable Save Report on comparison reports (#232) Co-authored-by: Jason Naylor --- Src/LexText/ParserCore/ParserReport.cs | 1 + Src/LexText/ParserUI/ParserReportDialog.xaml | 1 + Src/LexText/ParserUI/ParserReportViewModel.cs | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Src/LexText/ParserCore/ParserReport.cs b/Src/LexText/ParserCore/ParserReport.cs index ea04de3231..a933a39e97 100644 --- a/Src/LexText/ParserCore/ParserReport.cs +++ b/Src/LexText/ParserCore/ParserReport.cs @@ -237,6 +237,7 @@ public ParserReport DiffParserReports(ParserReport other) diff.ProjectName = DiffNames(ProjectName, other.ProjectName); diff.SourceText = DiffNames(SourceText, other.SourceText); diff.MachineName = DiffNames(MachineName, other.MachineName); + diff.Comment = DiffNames(Comment, other.Comment); diff.Timestamp = Timestamp; diff.DiffTimestamp = other.Timestamp; diff.NumWords = NumWords - other.NumWords; diff --git a/Src/LexText/ParserUI/ParserReportDialog.xaml b/Src/LexText/ParserUI/ParserReportDialog.xaml index 23fa40cb3f..793ccd6450 100644 --- a/Src/LexText/ParserUI/ParserReportDialog.xaml +++ b/Src/LexText/ParserUI/ParserReportDialog.xaml @@ -188,6 +188,7 @@