public void Write_ValidXml_XmlUpdated()
        {
            const string projectSettingsXml =
                @"<ProjectLexiconSettings>
  <WritingSystems>
    <WritingSystem id=""qaa-Qaaa-QM-x-kal-Fake-ZG-var1-var2-var3"">
      <Abbreviation>kal</Abbreviation>
      <LanguageName>Kalaba</LanguageName>
      <ScriptName>Fake</ScriptName>
      <RegionName>Zolrog</RegionName>
    </WritingSystem>
  </WritingSystems>
</ProjectLexiconSettings>";

            var settingsStore = new MemorySettingsStore {
                SettingsElement = XElement.Parse(projectSettingsXml)
            };
            var projectSettingsDataMapper = new ProjectLexiconSettingsWritingSystemDataMapper(settingsStore);
            var ws1 = new WritingSystemDefinition("qaa-Qaaa-QM-x-kal-Fake-ZG-var1-var2-var3");

            ws1.Abbreviation    = "ka";
            ws1.SpellCheckingId = "en_US";
            ws1.LegacyMapping   = "converter";
            ws1.Keyboard        = "Old Keyboard";
            var scd = new SystemCollationDefinition {
                LanguageTag = "snarf"
            };

            ws1.DefaultCollation = scd;
            projectSettingsDataMapper.Write(ws1);

            Assert.That(settingsStore.SettingsElement, Is.XmlEqualTo(
                            @"<ProjectLexiconSettings>
  <WritingSystems>
    <WritingSystem id=""qaa-Qaaa-QM-x-kal-Fake-ZG-var1-var2-var3"">
      <Abbreviation>ka</Abbreviation>
      <SpellCheckingId>en_US</SpellCheckingId>
      <LegacyMapping>converter</LegacyMapping>
      <Keyboard>Old Keyboard</Keyboard>
      <SystemCollation>snarf</SystemCollation>
    </WritingSystem>
  </WritingSystems>
</ProjectLexiconSettings>"));
        }
		public void Migrate_OriginalFileContainsSystemCollationInfo_CollationInfoIsMigrated()
		{
			using (var environment = new TestEnvironment())
			{
				environment.WriteLdmlFile(
					"test.ldml",
					LdmlContentForTests.Version0WithSystemCollationInfo());
				var wsV0 = new WritingSystemDefinitionV0();
				new LdmlAdaptorV0().Read(environment.FilePath("test.ldml"), wsV0);

				var migrator = new LdmlInFolderWritingSystemRepositoryMigrator(environment.LdmlPath, environment.OnMigrateCallback);
				migrator.Migrate();
				var repo = new TestLdmlInFolderWritingSystemRepository(environment.LdmlPath);
				migrator.ResetRemovedProperties(repo);

				WritingSystemDefinition ws = repo.Get("de");
				var scd = new SystemCollationDefinition {LanguageTag = "de"};
				Assert.That(ws.DefaultCollation.ValueEquals(scd), Is.True);
			}
		}
        public override void Migrate(string sourceFilePath, string destinationFilePath)
        {
            string sourceFileName = Path.GetFileName(sourceFilePath);

            var writingSystemDefinitionV1 = new WritingSystemDefinitionV1();

            new LdmlAdaptorV1().Read(sourceFilePath, writingSystemDefinitionV1);

            string abbreviation = writingSystemDefinitionV1.Abbreviation;
            float  defaultFontSize = writingSystemDefinitionV1.DefaultFontSize;
            string keyboard = writingSystemDefinitionV1.Keyboard;
            string spellCheckingId = writingSystemDefinitionV1.SpellCheckingId;
            string defaultFontName = writingSystemDefinitionV1.DefaultFontName;
            string languageName = writingSystemDefinitionV1.LanguageName.IsOneOf("Unknown Language", "Language Not Listed") ? string.Empty : writingSystemDefinitionV1.LanguageName;
            string variant, privateUse;

            IetfLanguageTag.SplitVariantAndPrivateUse(writingSystemDefinitionV1.Variant, out variant, out privateUse);
            var langTagCleaner = new IetfLanguageTagCleaner(writingSystemDefinitionV1.Language, writingSystemDefinitionV1.Script, writingSystemDefinitionV1.Region,
                                                            variant, privateUse);

            langTagCleaner.Clean();
            string                    langTag           = IetfLanguageTag.Canonicalize(langTagCleaner.GetCompleteTag());
            List <string>             knownKeyboards    = writingSystemDefinitionV1.KnownKeyboards.Select(k => string.IsNullOrEmpty(k.Locale) ? k.Layout : string.Format("{0}_{1}", k.Locale, k.Layout)).ToList();
            bool                      isGraphiteEnabled = false;
            string                    legacyMapping     = string.Empty;
            string                    scriptName        = string.Empty;
            string                    regionName        = string.Empty;
            string                    variantName       = string.Empty;
            SystemCollationDefinition scd = null;

            // Create system collation definition if applicable
            if ((writingSystemDefinitionV1.SortUsing == WritingSystemDefinitionV1.SortRulesType.OtherLanguage) && (!string.IsNullOrEmpty(writingSystemDefinitionV1.SortRules)))
            {
                scd = new SystemCollationDefinition {
                    LanguageTag = writingSystemDefinitionV1.SortRules
                }
            }
            ;

            // Migrate fields from legacy fw namespace, and then remove fw namespace
            XElement ldmlElem = XElement.Load(sourceFilePath);
            XElement fwElem   = ldmlElem.Elements("special").FirstOrDefault(e => !string.IsNullOrEmpty((string)e.Attribute(XNamespace.Xmlns + "fw")));

            if (fwElem != null)
            {
                XElement graphiteEnabledElem = fwElem.Element(FW + "graphiteEnabled");
                if (graphiteEnabledElem != null)
                {
                    if (!bool.TryParse((string)graphiteEnabledElem.Attribute("value"), out isGraphiteEnabled))
                    {
                        isGraphiteEnabled = false;
                    }
                }

                // LegacyMapping
                XElement legacyMappingElem = fwElem.Element(FW + "legacyMapping");
                if (legacyMappingElem != null)
                {
                    legacyMapping = (string)legacyMappingElem.Attribute("value");
                }

                // ScriptName
                XElement scriptNameElem = fwElem.Element(FW + "scriptName");
                if (scriptNameElem != null)
                {
                    scriptName = (string)scriptNameElem.Attribute("value");
                }

                // RegionName
                XElement regionNameElem = fwElem.Element(FW + "regionName");
                if (regionNameElem != null)
                {
                    regionName = (string)regionNameElem.Attribute("value");
                }

                // VariantName
                XElement variantNameElem = fwElem.Element(FW + "variantName");
                if (variantNameElem != null)
                {
                    variantName = (string)variantNameElem.Attribute("value");
                }
            }

            // Record the details for use in PostMigrate where we change the file name to match the ieft language tag where we can.
            var migrationInfo = new LdmlMigrationInfo(sourceFileName)
            {
                LanguageTagBeforeMigration = writingSystemDefinitionV1.Bcp47Tag,
                LanguageTagAfterMigration  = langTag,
                RemovedPropertiesSetter    = ws =>
                {
                    if (!string.IsNullOrEmpty(abbreviation))
                    {
                        ws.Abbreviation = abbreviation;
                    }
                    if (defaultFontSize != 0)
                    {
                        ws.DefaultFontSize = defaultFontSize;
                    }
                    if (!string.IsNullOrEmpty(keyboard))
                    {
                        ws.Keyboard = keyboard;
                    }
                    if (!string.IsNullOrEmpty(spellCheckingId))
                    {
                        ws.SpellCheckingId = spellCheckingId;
                    }
                    if (!string.IsNullOrEmpty(defaultFontName))
                    {
                        ws.DefaultFont = ws.Fonts[defaultFontName];
                    }
                    if (!string.IsNullOrEmpty(languageName))
                    {
                        ws.Language = new LanguageSubtag(ws.Language, languageName);
                    }
                    ws.IsGraphiteEnabled = isGraphiteEnabled;
                    if (!string.IsNullOrEmpty(legacyMapping))
                    {
                        ws.LegacyMapping = legacyMapping;
                    }
                    if (!string.IsNullOrEmpty(scriptName) && ws.Script != null && ws.Script.IsPrivateUse)
                    {
                        ws.Script = new ScriptSubtag(ws.Script, scriptName);
                    }
                    if (!string.IsNullOrEmpty(regionName) && ws.Region != null && ws.Region.IsPrivateUse)
                    {
                        ws.Region = new RegionSubtag(ws.Region, regionName);
                    }
                    if (scd != null)
                    {
                        ws.DefaultCollation = scd;
                    }
                    foreach (string keyboardId in knownKeyboards)
                    {
                        IKeyboardDefinition kd;
                        if (!Keyboard.Controller.TryGetKeyboard(keyboardId, out kd))
                        {
                            kd = Keyboard.Controller.CreateKeyboard(keyboardId, KeyboardFormat.Unknown, Enumerable.Empty <string>());
                        }
                        ws.KnownKeyboards.Add(kd);
                    }
                }
            };

            _migrationInfo.Add(migrationInfo);

            // Store things that stay in ldml but are being moved: WindowsLcid, variantName, font, known keyboards, collations, font features, character sets

            // misc properties
            var staging = new Staging
            {
                WindowsLcid     = writingSystemDefinitionV1.WindowsLcid,
                DefaultFontName = writingSystemDefinitionV1.DefaultFontName,
                SortUsing       = writingSystemDefinitionV1.SortUsing,
                SortRules       = writingSystemDefinitionV1.SortRules,
            };

            // Determine if variantName is non-common private use before preserving it
            if (!string.IsNullOrEmpty(variantName))
            {
                int index = IetfLanguageTag.GetIndexOfFirstNonCommonPrivateUseVariant(IetfLanguageTag.GetVariantSubtags(migrationInfo.LanguageTagAfterMigration));
                if (index > -1)
                {
                    staging.VariantName = variantName;
                }
            }

            if (fwElem != null)
            {
                // DefaultFontFeatures
                XElement fontFeatsElem = fwElem.Element(FW + "defaultFontFeatures");
                if (fontFeatsElem != null && !string.IsNullOrEmpty(staging.DefaultFontName))
                {
                    staging.DefaultFontFeatures = (string)fontFeatsElem.Attribute("value");
                }

                //MatchedPairs, PunctuationPatterns, QuotationMarks deprecated

                // Valid Chars
                XElement validCharsElem = fwElem.Element(FW + "validChars");
                if (validCharsElem != null)
                {
                    try
                    {
                        var fwValidCharsElem = XElement.Parse((string)validCharsElem.Attribute("value"));
                        AddCharacterSet(fwValidCharsElem, staging, "WordForming", "main");
                        AddCharacterSet(fwValidCharsElem, staging, "Numeric", "numeric");
                        AddCharacterSet(fwValidCharsElem, staging, "Other", "punctuation");
                    }
                    catch (XmlException)
                    {
                        ParseLegacyWordformingCharOverridesFile(staging);
                    }
                }
            }

            _staging[sourceFileName] = staging;
        }
        public void Read_ValidXml_SetsAllProperties()
        {
            const string projectSettingsXml =
                @"<ProjectLexiconSettings>
  <WritingSystems>
    <WritingSystem id=""qaa-Qaaa-QM-x-kal-Fake-ZG-var1-var2"">
      <Abbreviation>kal</Abbreviation>
      <LanguageName>Kalaba</LanguageName>
      <ScriptName>Fake</ScriptName>
      <RegionName>Zolrog</RegionName>
      <SystemCollation>snarf</SystemCollation>
    </WritingSystem>
    <WritingSystem id=""fr-FR"">
      <SpellCheckingId>fr_FR</SpellCheckingId>
      <LegacyMapping>converter</LegacyMapping>
      <Keyboard>Old Keyboard</Keyboard>
    </WritingSystem>
  </WritingSystems>
</ProjectLexiconSettings>";

            var projectSettingsDataMapper = new ProjectLexiconSettingsWritingSystemDataMapper(new MemorySettingsStore {
                SettingsElement = XElement.Parse(projectSettingsXml)
            });

            var ws1 = new WritingSystemDefinition("qaa-Qaaa-QM-x-kal-Fake-ZG-var1-var2");

            projectSettingsDataMapper.Read(ws1);

            Assert.That(ws1.Abbreviation, Is.EqualTo("kal"));
            Assert.That(ws1.Language.Name, Is.EqualTo("Kalaba"));
            Assert.That(ws1.Script.Name, Is.EqualTo("Fake"));
            Assert.That(ws1.Region.Name, Is.EqualTo("Zolrog"));
            Assert.That(ws1.SpellCheckingId, Is.EqualTo(string.Empty));
            Assert.That(ws1.LegacyMapping, Is.EqualTo(string.Empty));
            Assert.That(ws1.Keyboard, Is.EqualTo(string.Empty));
            var scd = new SystemCollationDefinition {
                LanguageTag = "snarf"
            };

            Assert.That(ws1.DefaultCollation, Is.ValueEqualTo(scd));

            var ws2 = new WritingSystemDefinition("fr-FR");

            projectSettingsDataMapper.Read(ws2);

            Assert.That(ws2.Abbreviation, Is.EqualTo("fr"));
            Assert.That(ws2.Language.Name, Is.EqualTo("French"));
            Assert.That(ws2.Script.Name, Is.EqualTo("Latin"));
            Assert.That(ws2.Region.Name, Is.EqualTo("France"));
            Assert.That(ws2.Variants, Is.Empty);
            Assert.That(ws2.SpellCheckingId, Is.EqualTo("fr_FR"));
            Assert.That(ws2.LegacyMapping, Is.EqualTo("converter"));
            Assert.That(ws2.Keyboard, Is.EqualTo("Old Keyboard"));

            var ws3 = new WritingSystemDefinition("es");

            projectSettingsDataMapper.Read(ws3);

            Assert.That(ws3.Abbreviation, Is.EqualTo("es"));
            Assert.That(ws3.Language.Name, Is.EqualTo("Spanish"));
            Assert.That(ws3.Script.Name, Is.EqualTo("Latin"));
            Assert.That(ws3.Region, Is.Null);
            Assert.That(ws3.Variants, Is.Empty);
            Assert.That(ws3.SpellCheckingId, Is.EqualTo(string.Empty));
            Assert.That(ws3.LegacyMapping, Is.EqualTo(string.Empty));
            Assert.That(ws3.Keyboard, Is.EqualTo(string.Empty));
        }
 public SystemCollationDefinition(SystemCollationDefinition scd)
     : base(scd)
 {
     _languageTag = scd._languageTag;
 }
		public override void Migrate(string sourceFilePath, string destinationFilePath)
		{
			string sourceFileName = Path.GetFileName(sourceFilePath);

			var writingSystemDefinitionV1 = new WritingSystemDefinitionV1();
			new LdmlAdaptorV1().Read(sourceFilePath, writingSystemDefinitionV1);

			string abbreviation = writingSystemDefinitionV1.Abbreviation;
			float defaultFontSize = writingSystemDefinitionV1.DefaultFontSize;
			string keyboard = writingSystemDefinitionV1.Keyboard;
			string spellCheckingId = writingSystemDefinitionV1.SpellCheckingId;
			string defaultFontName = writingSystemDefinitionV1.DefaultFontName;
			string languageName = writingSystemDefinitionV1.LanguageName.IsOneOf("Unknown Language", "Language Not Listed") ? string.Empty : writingSystemDefinitionV1.LanguageName;
			string variant, privateUse;
			IetfLanguageTag.SplitVariantAndPrivateUse(writingSystemDefinitionV1.Variant, out variant, out privateUse);
			var langTagCleaner = new IetfLanguageTagCleaner(writingSystemDefinitionV1.Language, writingSystemDefinitionV1.Script, writingSystemDefinitionV1.Region,
				variant, privateUse);
			langTagCleaner.Clean();
			string langTag = IetfLanguageTag.Canonicalize(langTagCleaner.GetCompleteTag());
			List<string> knownKeyboards = writingSystemDefinitionV1.KnownKeyboards.Select(k => string.IsNullOrEmpty(k.Locale) ? k.Layout : string.Format("{0}_{1}", k.Locale, k.Layout)).ToList();
			bool isGraphiteEnabled = false;
			string legacyMapping = string.Empty;
			string scriptName = string.Empty;
			string regionName = string.Empty;
			string variantName = string.Empty;
			SystemCollationDefinition scd = null;

			// Create system collation definition if applicable
			if ((writingSystemDefinitionV1.SortUsing == WritingSystemDefinitionV1.SortRulesType.OtherLanguage) && (!string.IsNullOrEmpty(writingSystemDefinitionV1.SortRules)))
				scd = new SystemCollationDefinition { LanguageTag = writingSystemDefinitionV1.SortRules };

			// Migrate fields from legacy fw namespace, and then remove fw namespace
			XElement ldmlElem = XElement.Load(sourceFilePath);
			XElement fwElem = ldmlElem.Elements("special").FirstOrDefault(e => !string.IsNullOrEmpty((string) e.Attribute(XNamespace.Xmlns + "fw")));
			if (fwElem != null)
			{
				XElement graphiteEnabledElem = fwElem.Element(FW + "graphiteEnabled");
				if (graphiteEnabledElem != null)
				{
					if (!bool.TryParse((string) graphiteEnabledElem.Attribute("value"), out isGraphiteEnabled))
						isGraphiteEnabled = false;
				}

				// LegacyMapping
				XElement legacyMappingElem = fwElem.Element(FW + "legacyMapping");
				if (legacyMappingElem != null)
					legacyMapping = (string) legacyMappingElem.Attribute("value");

				// ScriptName
				XElement scriptNameElem = fwElem.Element(FW + "scriptName");
				if (scriptNameElem != null)
					scriptName = (string) scriptNameElem.Attribute("value");

				// RegionName
				XElement regionNameElem = fwElem.Element(FW + "regionName");
				if (regionNameElem != null)
					regionName = (string) regionNameElem.Attribute("value");

				// VariantName
				XElement variantNameElem = fwElem.Element(FW + "variantName");
				if (variantNameElem != null)
					variantName = (string) variantNameElem.Attribute("value");
			}

			// Record the details for use in PostMigrate where we change the file name to match the ieft language tag where we can.
			var migrationInfo = new LdmlMigrationInfo(sourceFileName)
				{
					LanguageTagBeforeMigration = writingSystemDefinitionV1.Bcp47Tag,
					LanguageTagAfterMigration = langTag,
					RemovedPropertiesSetter = ws =>
					{
						if (!string.IsNullOrEmpty(abbreviation))
							ws.Abbreviation = abbreviation;
						if (defaultFontSize != 0)
							ws.DefaultFontSize = defaultFontSize;
						if (!string.IsNullOrEmpty(keyboard))
							ws.Keyboard = keyboard;
						if (!string.IsNullOrEmpty(spellCheckingId))
							ws.SpellCheckingId = spellCheckingId;
						if (!string.IsNullOrEmpty(defaultFontName))
							ws.DefaultFont = ws.Fonts[defaultFontName];
						if (!string.IsNullOrEmpty(languageName))
							ws.Language = new LanguageSubtag(ws.Language, languageName);
						ws.IsGraphiteEnabled = isGraphiteEnabled;
						if (!string.IsNullOrEmpty(legacyMapping))
							ws.LegacyMapping = legacyMapping;
						if (!string.IsNullOrEmpty(scriptName) && ws.Script != null && ws.Script.IsPrivateUse)
							ws.Script = new ScriptSubtag(ws.Script, scriptName);
						if (!string.IsNullOrEmpty(regionName) && ws.Region != null && ws.Region.IsPrivateUse)
							ws.Region = new RegionSubtag(ws.Region, regionName);
						if (scd != null)
							ws.DefaultCollation = scd;
						foreach (string keyboardId in knownKeyboards)
						{
							IKeyboardDefinition kd;
							if (!Keyboard.Controller.TryGetKeyboard(keyboardId, out kd))
								kd = Keyboard.Controller.CreateKeyboard(keyboardId, KeyboardFormat.Unknown, Enumerable.Empty<string>());
							ws.KnownKeyboards.Add(kd);
						}
					}
				};

			_migrationInfo.Add(migrationInfo);

			// Store things that stay in ldml but are being moved: WindowsLcid, variantName, font, known keyboards, collations, font features, character sets

			// misc properties
			var staging = new Staging
			{
				WindowsLcid = writingSystemDefinitionV1.WindowsLcid,
				DefaultFontName = writingSystemDefinitionV1.DefaultFontName,
				SortUsing = writingSystemDefinitionV1.SortUsing,
				SortRules = writingSystemDefinitionV1.SortRules,
			};

			// Determine if variantName is non-common private use before preserving it
			if (!string.IsNullOrEmpty(variantName))
			{
				int index = IetfLanguageTag.GetIndexOfFirstNonCommonPrivateUseVariant(IetfLanguageTag.GetVariantSubtags(migrationInfo.LanguageTagAfterMigration));
				if (index > -1)
					staging.VariantName = variantName;
			}

			if (fwElem != null)
			{
				// DefaultFontFeatures
				XElement fontFeatsElem = fwElem.Element(FW + "defaultFontFeatures");
				if (fontFeatsElem != null && !string.IsNullOrEmpty(staging.DefaultFontName))
					staging.DefaultFontFeatures = (string) fontFeatsElem.Attribute("value");

				//MatchedPairs, PunctuationPatterns, QuotationMarks deprecated

				// Valid Chars
				XElement validCharsElem = fwElem.Element(FW + "validChars");
				if (validCharsElem != null)
				{
					try
					{
						var fwValidCharsElem = XElement.Parse((string) validCharsElem.Attribute("value"));
						AddCharacterSet(fwValidCharsElem, staging, "WordForming", "main");
						AddCharacterSet(fwValidCharsElem, staging, "Numeric", "numeric");
						AddCharacterSet(fwValidCharsElem, staging, "Other", "punctuation");
					}
					catch (XmlException)
					{
						ParseLegacyWordformingCharOverridesFile(staging);
					}
				}
			}

			_staging[sourceFileName] = staging;
		}
		public SystemCollationDefinition(SystemCollationDefinition scd)
			: base(scd)
		{
			_languageTag = scd._languageTag;
		}
示例#8
0
        /// <summary>
        /// Creates a TreeNode tree along genres populated by the texts that claim them.
        /// The list of textsWithNoGenre is also populated.
        /// Recursively descend the genre tree and duplicate parts that have corresponding texts.
        /// </summary>
        /// <param name="parent">The parent to attach the genres to. If null, nothing is done.</param>
        /// <param name="genreList">The owning sequence of genres - its a tree.</param>
        /// <param name="allTexts">The flat list of all texts in the project.</param>
        private static void LoadTextsFromGenres(TreeNode parent, ILcmOwningSequence <ICmPossibility> genreList, IEnumerable <LCModel.IText> allTexts)
        {
            if (parent == null)
            {
                return;
            }
            var sortedGenreList = new List <ICmPossibility>(genreList);
            var sorter          = new CmPossibilitySorter();

            sortedGenreList.Sort(sorter);
            foreach (var gen in sortedGenreList)
            {
                // This tree node is added to genreTreeNodes if there are texts or children
                var genItem = new TreeNode(gen.ChooserNameTS.Text);

                // LT-12179: Create a List for collecting selected tree nodes which we will later sort
                // before actually adding them to the tree:
                var sortedNodes    = new List <TreeNode>();
                var foundFirstText = false;

                foreach (var tex in allTexts)
                {                   // This tex may not have a genre or it may claim to be in more than one
                    if (!Enumerable.Contains(tex.GenresRC, gen))
                    {
                        continue;
                    }
                    var texItem = new TreeNode(tex.ChooserNameTS.Text)
                    {
                        Tag  = tex.ContentsOA,
                        Name = "Text"
                    };

                    // LT-12179: Add the new TreeNode to the (not-yet-)sorted list:
                    sortedNodes.Add(texItem);

                    // LT-12179: If this is the first tex we've added, establish the collator's details
                    // according to the writing system at the start of the tex:
                    if (foundFirstText)
                    {
                        continue;
                    }
                    foundFirstText = true;
                }

                // Always use the system collation definition. Texts have different writing systems anyhow.
                var wsCollator = new SystemCollationDefinition();

                // LT-12179:
                if (foundFirstText)
                {
                    // Order the TreeNodes alphabetically:
                    sortedNodes.Sort((x, y) => wsCollator.Collator.Compare(x.Text, y.Text));
                    // Add the TreeNodes to the tree:
                    genItem.Nodes.AddRange(sortedNodes.ToArray());
                }

                if (gen.SubPossibilitiesOS.Count > 0)
                {                   // Descend to the child genres regardless if there were texts assigned to this genre
                    LoadTextsFromGenres(genItem, gen.SubPossibilitiesOS, allTexts);
                }

                //Add the node even if there are no texts that point to this genre.
                genItem.Tag  = gen;                 // ICmPossibility
                genItem.Name = "Genre";
                parent.Nodes.Add(genItem);
            }
        }
示例#9
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Load texts by Genre into the texts tree view.
        /// </summary>
        /// <returns>A control tree of the Texts in the project</returns>
        /// ------------------------------------------------------------------------------------
        private TreeNode LoadTextsByGenreAndWithoutGenre()
        {
            if (m_cache.LanguageProject.GenreListOA == null)
            {
                return(null);
            }
            var genreList = m_cache.LanguageProject.GenreListOA.PossibilitiesOS;

            Debug.Assert(genreList != null);
            var allTexts = m_cache.ServiceLocator.GetInstance <ITextRepository>().AllInstances();

            if (allTexts == null)
            {
                return(null);
            }

            // Title node for all texts, Biblical and otherwise
            var textsNode = new TreeNode("All Texts in Genres and not in Genres")
            {
                Name = "Texts"
            };

            // For each genre, find the texts that claim it
            LoadTextsFromGenres(textsNode, genreList, allTexts);

            var textsWithNoGenre = new List <TreeNode>();            // and get the ones with no genre
            // LT-12179: Create a List for collecting selected tree nodes which we will later sort
            // before actually adding them to the tree:
            var foundFirstText = false;

            foreach (var tex in allTexts)
            {
                if (tex.GenresRC.Any())
                {
                    continue;
                }
                var texItem = new TreeNode(tex.ChooserNameTS.Text)
                {
                    Tag  = tex.ContentsOA,
                    Name = "Text"
                };
                textsWithNoGenre.Add(texItem);

                // LT-12179: If this is the first tex we've added, establish the collator's details
                // according to the writing system at the start of the tex:
                if (foundFirstText)
                {
                    continue;
                }
                foundFirstText = true;
            }

            if (!textsWithNoGenre.Any())
            {
                return(textsNode);
            }
            // Just grab a system collator since text titles can be in various languages. Sorting by any one ws is going to
            // do something wrong part of the time anyhow.
            var wsCollator = new SystemCollationDefinition();

            // LT-12179: Order the TreeNodes alphabetically:
            try
            {
                textsWithNoGenre.Sort((x, y) => wsCollator.Collator.Compare(x.Text, y.Text));
            }
            catch (AccessViolationException)
            {
                // sort out sorting troubles later.
                // icu.net 2.5.4+Branch.master.Sha.aa2e04611b4... can throw an AccessViolationException in
                // RuleBasedCollator.Compare for yet-unknown reasons. See
                // https://github.com/sillsdev/icu-dotnet/issues/130 and LT-20194.
                // This may be resolved in the current version of ICU.
            }
            // Make a TreeNode for the texts with no known genre
            var woGenreTreeNode = new TreeNode("No Genre", textsWithNoGenre.ToArray())
            {
                Name = "TextsWoGenre"
            };

            textsNode.Nodes.Add(woGenreTreeNode);
            return(textsNode);
        }