/// <summary> /// This recursive method will migrate new nodes from sourceNode into destinationNode node /// </summary> private static void MigrateNewChildNodesAndOptionsInto(ConfigurableDictionaryNode destinationNode, ConfigurableDictionaryNode sourceNode) { // REVIEW (Hasso) 2017.03: If this is a NoteInParaStyles node: Rather than overwriting the user's Options, copy their Options into a new WS&ParaOptions if ((destinationNode.DictionaryNodeOptions == null || DictionaryConfigurationModel.NoteInParaStyles.Contains(sourceNode.FieldDescription)) && sourceNode.DictionaryNodeOptions != null) { destinationNode.DictionaryNodeOptions = sourceNode.DictionaryNodeOptions; } EnsureCssOverrideAndStylesAreUpdated(destinationNode, sourceNode); // LT-18286: don't merge direct children into sharing parents. if (destinationNode.ReferencedNode != null || destinationNode.Children == null || sourceNode.Children == null) { return; } // First recurse into each matching child node foreach (var newChild in sourceNode.Children) { var matchFromDestination = FindMatchingChildNode(newChild.Label, destinationNode.Children); if (matchFromDestination != null) { MigrateNewChildNodesAndOptionsInto(matchFromDestination, newChild); } else { var indexOfNewChild = sourceNode.Children.FindIndex(n => n.Label == newChild.Label); InsertNewNodeIntoOldConfig(destinationNode, newChild.DeepCloneUnderParent(destinationNode, true), sourceNode, indexOfNewChild); } } }
private static void SetReferenceItem(ConfigurableDictionaryNode configNode) { switch (configNode.FieldDescription) { case "Subentries": configNode.ReferenceItem = "MainEntrySubentries"; if (configNode.DictionaryNodeOptions == null) { configNode.DictionaryNodeOptions = new DictionaryNodeListAndParaOptions { ListId = DictionaryNodeListOptions.ListIds.Complex }; } break; case "SensesOS": if (configNode.Parent.FieldDescription == "SensesOS") // update only subsenses { configNode.ReferenceItem = "MainEntrySubsenses"; } break; case "SubentriesOS": // uniquely identifies Reversal Index Subentries configNode.ReferenceItem = "AllReversalSubentries"; break; } }
/// <summary> /// This recursive method will create group nodes in the migrated config and move all children in the node /// which belong in the group into it /// </summary> private static void MoveNodesIntoNewGroups(ConfigurableDictionaryNode oldConfigNode, ConfigurableDictionaryNode defaultNode) { if (oldConfigNode.Children == null || defaultNode.Children == null) { return; } // First recurse into each matching child node foreach (var oldChild in oldConfigNode.Children) { var matchFromDefault = FindMatchingChildNode(oldChild.Label, defaultNode.Children); if (matchFromDefault != null) { MoveNodesIntoNewGroups(oldChild, matchFromDefault); } } // Next find any groups at this level and move the matching children defaultChildren into the group foreach (var group in defaultNode.Children.Where(n => n.DictionaryNodeOptions is DictionaryNodeGroupingOptions)) { // DeepClone skips children for grouping nodes. We want this, because we are going to move children in from the old configNode. var groupNode = group.DeepCloneUnderParent(oldConfigNode); groupNode.Children = new List <ConfigurableDictionaryNode>(); for (var i = oldConfigNode.Children.Count - 1; i >= 0; --i) { var oldChild = oldConfigNode.Children[i]; if (group.Children.Any(groupChild => groupChild.Label == oldChild.Label)) { groupNode.Children.Insert(0, oldChild); oldChild.Parent = groupNode; oldConfigNode.Children.RemoveAt(i); } } InsertNewNodeIntoOldConfig(oldConfigNode, groupNode, defaultNode, defaultNode.Children.IndexOf(group)); } }
public void BuildPathStringFromNode() { var subsenses = new ConfigurableDictionaryNode { Label = "Subsenses", FieldDescription = "SensesOS", ReferenceItem = "SharedSenses" }; var sharedSenses = new ConfigurableDictionaryNode { Label = "SharedSenses", FieldDescription = "SensesOS", Children = new List <ConfigurableDictionaryNode> { subsenses } }; var senses = new ConfigurableDictionaryNode { Label = "Senses", FieldDescription = "SensesOS", ReferenceItem = "SharedSenses" }; var mainEntry = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", Children = new List <ConfigurableDictionaryNode> { senses } }; var model = DictionaryConfigurationModelTests.CreateSimpleSharingModel(mainEntry, sharedSenses); CssGeneratorTests.PopulateFieldsForTesting(model); // PopulateFieldsForTesting populates each node's Label with its FieldDescription Assert.AreEqual("LexEntry > Senses > SharedSenses > Subsenses", DictionaryConfigurationMigrator.BuildPathStringFromNode(subsenses)); Assert.AreEqual("LexEntry > Senses > Subsenses", DictionaryConfigurationMigrator.BuildPathStringFromNode(subsenses, false)); Assert.AreEqual("LexEntry", DictionaryConfigurationMigrator.BuildPathStringFromNode(mainEntry)); }
/// <summary> /// Makes sure EntryType node contains ReverseAbbr and ReverseName nodes /// </summary> private static void SetEntryTypeChildrenBackward(ConfigurableDictionaryNode entryTypeNode) { var abbrNode = entryTypeNode.Children.Find(node => node.FieldDescription == Abbr); SwapOutNodeLabelAndField(abbrNode, ReversePrefix + Abbr, RevAbbr); var nameNode = entryTypeNode.Children.Find(node => node.FieldDescription == Name); SwapOutNodeLabelAndField(nameNode, ReversePrefix + Name, RevName); }
/// <summary> /// Makes sure EntryType node contains Abbreviation and Name nodes /// </summary> private static void SetEntryTypeChildrenForward(ConfigurableDictionaryNode entryTypeNode) { var abbrNode = entryTypeNode.Children.Find(node => node.FieldDescription == RevAbbr); SwapOutNodeLabelAndField(abbrNode, Abbr, Abbr); var nameNode = entryTypeNode.Children.Find(node => node.FieldDescription == RevName); SwapOutNodeLabelAndField(nameNode, Name, Name); }
private static void AddSubsubEntriesOptionsIfNeeded(ConfigurableDictionaryNode mainEntrySubEntries) { if (mainEntrySubEntries.DictionaryNodeOptions == null) { mainEntrySubEntries.DictionaryNodeOptions = new DictionaryNodeListAndParaOptions { ListId = DictionaryNodeListOptions.ListIds.Complex }; } }
/// <summary>LT-18288: Change Headword to HeadwordRef for All "Referenced Sense Headword" to allow users to select WS</summary> private static void ChangeReferenceSenseHeadwordFieldName(ConfigurableDictionaryNode oldConfigPart) { DCM.PerformActionOnNodes(oldConfigPart.Children, node => { if (node.Label == "Referenced Sense Headword") { node.FieldDescription = "HeadWordRef"; } }); }
/// <summary> /// If node is a Pictures node, update its child nodes by removing Sense Number and adding Headword and Gloss. /// Part of LT-12572. /// </summary> private static void UpdatePicturesChildren(ConfigurableDictionaryNode node) { if (node == null) { return; } if (node.Label != "Pictures") { return; } node.Children.RemoveAll(child => child.Label == "Sense Number"); var analysisWsOptions = new DictionaryNodeWritingSystemOptions { WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Analysis, DisplayWritingSystemAbbreviations = false, Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> { new DictionaryNodeListOptions.DictionaryNodeOption { Id = "analysis", IsEnabled = true } } }; var vernacularWsOptions = new DictionaryNodeWritingSystemOptions { WsType = DictionaryNodeWritingSystemOptions.WritingSystemType.Vernacular, DisplayWritingSystemAbbreviations = false, Options = new List <DictionaryNodeListOptions.DictionaryNodeOption> { new DictionaryNodeListOptions.DictionaryNodeOption { Id = "vernacular", IsEnabled = true } } }; var headwordNode = new ConfigurableDictionaryNode { After = " ", Between = " ", Label = "Headword", FieldDescription = "Owner", SubField = "OwnerOutlineName", CSSClassNameOverride = "headword", Style = "Dictionary-Headword", IsEnabled = true, DictionaryNodeOptions = vernacularWsOptions }; var glossNode = new ConfigurableDictionaryNode { After = " ", Between = " ", Label = "Gloss", FieldDescription = "Owner", SubField = "Gloss", IsEnabled = true, DictionaryNodeOptions = analysisWsOptions }; node.Children.Add(headwordNode); node.Children.Add(glossNode); }
/// <summary>LT-18286: One Sharing Parent in Hybrid erroneously got direct children (from a migration step). Remove them.</summary> private static void RemoveHiddenChildren(ConfigurableDictionaryNode parent, ISimpleLogger logger) { DCM.PerformActionOnNodes(parent.Children, p => { if (p.ReferencedNode != null && p.Children != null && p.Children.Any()) { logger.WriteLine(DCM.BuildPathStringFromNode(p) + " contains both Referenced And Direct Children. Removing Direct Children."); p.Children = new List <ConfigurableDictionaryNode>(); } }); }
private static void RemoveReferencedHeadwordSubField(ConfigurableDictionaryNode part) { DCM.PerformActionOnNodes(part.Children, node => { // AllReversalSubentries under Referenced Headword field is ReversalName if (node.FieldDescription == "ReversalName" && node.SubField == "MLHeadWord") { node.SubField = null; } }); }
private static void RemoveMostOfGramInfoUnderRefdComplexForms(ConfigurableDictionaryNode part) { DCM.PerformActionOnNodes(part.Children, node => { // GramInfo under (Other) Ref'd Complex Forms is MorphoSystaxAnalyses // GramInfo under Senses is MorphoSyntaxAnalysisRA and should not lose any children if (node.FieldDescription == "MorphoSyntaxAnalyses") { node.Children.RemoveAll(child => child.FieldDescription != "MLPartOfSpeech"); } }); }
/// <summary>LT-18920: Change Referringsenses to Senses for all the reversal index configurations.</summary> private static void ChangeReferringsensesToSenses(ConfigurableDictionaryNode part) { if (part.FieldDescription == "ReversalIndexEntry" || part.FieldDescription == "SubentriesOS") { DCM.PerformActionOnNodes(part.Children, node => { if (node.FieldDescription == "ReferringSenses") { node.FieldDescription = "SensesRS"; } }); } }
private static void MigratePartFromOldVersionToCurrent(ISimpleLogger logger, DictionaryConfigurationModel oldConfig, ConfigurableDictionaryNode oldConfigPart, ConfigurableDictionaryNode currentDefaultConfigPart) { var oldVersion = oldConfig.Version; if (oldVersion < FirstAlphaMigrator.VersionAlpha3) { throw new ApplicationException("Beta migration starts at VersionAlpha3 (8)"); } switch (oldVersion) { case FirstAlphaMigrator.VersionAlpha3: MoveNodesIntoNewGroups(oldConfigPart, currentDefaultConfigPart); MigrateNewChildNodesAndOptionsInto(oldConfigPart, currentDefaultConfigPart); goto case 9; case 9: UpgradeEtymologyCluster(oldConfigPart, oldConfig); goto case 10; case 10: case 11: case 12: case 13: RemoveMostOfGramInfoUnderRefdComplexForms(oldConfigPart); goto case VersionBeta5; case VersionBeta5: case 15: MigrateNewChildNodesAndOptionsInto(oldConfigPart, currentDefaultConfigPart); goto case 16; case 16: RemoveHiddenChildren(oldConfigPart, logger); goto case VersionRC2; case VersionRC2: ChangeReferenceSenseHeadwordFieldName(oldConfigPart); goto case 18; case 18: RemoveReferencedHeadwordSubField(oldConfigPart); break; default: logger.WriteLine(string.Format( "Unable to migrate {0}: no migration instructions for version {1}", oldConfigPart.Label, oldVersion)); break; } }
/// <summary> /// Swap out node Label and FieldDescription, checks for null node and empty strings /// in case only one of the parameters needs changing. /// </summary> private static void SwapOutNodeLabelAndField(ConfigurableDictionaryNode node, string label, string fieldDescription) { if (node == null) { return; } if (!string.IsNullOrEmpty(label)) { node.Label = label; } if (!string.IsNullOrEmpty(fieldDescription)) { node.FieldDescription = fieldDescription; } }
private static void InsertNewNodeIntoOldConfig(ConfigurableDictionaryNode destinationParentNode, ConfigurableDictionaryNode newChildNode, ConfigurableDictionaryNode sourceParentNode, int indexInSourceParentNode) { if (indexInSourceParentNode == 0) { destinationParentNode.Children.Insert(0, newChildNode); } else { var olderSiblingLabel = sourceParentNode.Children[indexInSourceParentNode - 1].Label; var indexOfOlderSibling = destinationParentNode.Children.FindIndex(n => n.Label == olderSiblingLabel); if (indexOfOlderSibling >= 0) { destinationParentNode.Children.Insert(indexOfOlderSibling + 1, newChildNode); } else { destinationParentNode.Children.Add(newChildNode); } } }
private static void UseConfigReferencedEntriesAsPrimary(ConfigurableDictionaryNode part) { if (part.FieldDescription == "ReversalIndexEntry" || part.FieldDescription == "SubentriesOS") { DCM.PerformActionOnNodes(part.Children, node => { if (node.DisplayLabel == "Primary Entry References" && node.FieldDescription == "EntryRefsWithThisMainSense") { node.FieldDescription = "MainEntryRefs"; node.HideCustomFields = true; node.Before = " ("; node.After = ")"; } if (node.DisplayLabel == "Primary Entry(s)" && node.FieldDescription == "PrimarySensesOrEntries") { node.FieldDescription = "ConfigReferencedEntries"; node.CSSClassNameOverride = "referencedentries"; } }); } }
private static void AddSubsubNodeIfNeeded(ConfigurableDictionaryNode subNode) { if (subNode.Children.Any(n => n.FieldDescription == subNode.FieldDescription)) { return; } switch (subNode.FieldDescription) { case "SensesOS": // On the odd chance Subsense has no SenseOptions, construct some. subNode.DictionaryNodeOptions = subNode.DictionaryNodeOptions ?? new DictionaryNodeSenseOptions(); // Add in the new subsubsenses node subNode.Children.Add(new ConfigurableDictionaryNode { Label = "Subsubsenses", IsEnabled = true, Style = "Dictionary-Sense", FieldDescription = "SensesOS", CSSClassNameOverride = "subsenses", ReferenceItem = "MainEntrySubsenses", DictionaryNodeOptions = subNode.DictionaryNodeOptions.DeepClone() }); break; case "SubentriesOS": // SubentriesOS uniquely identifies Reversal Index subentries // Add in the new reversal subsubentries node subNode.Children.Add(new ConfigurableDictionaryNode { Label = "Reversal Subsubentries", IsEnabled = true, Style = "Reversal-Subentry", FieldDescription = "SubentriesOS", CSSClassNameOverride = "subentries", ReferenceItem = "AllReversalSubentries" }); break; } }
private static void EnsureCssOverrideAndStylesAreUpdated(ConfigurableDictionaryNode destinationNode, ConfigurableDictionaryNode sourceNode) { if (sourceNode.StyleType != ConfigurableDictionaryNode.StyleTypes.Default && destinationNode.StyleType == ConfigurableDictionaryNode.StyleTypes.Default) { var nodeStyleType = sourceNode.StyleType; var nodeParaOpts = destinationNode.DictionaryNodeOptions as IParaOption; if (nodeParaOpts != null) { nodeStyleType = nodeParaOpts.DisplayEachInAParagraph ? ConfigurableDictionaryNode.StyleTypes.Paragraph : ConfigurableDictionaryNode.StyleTypes.Character; } destinationNode.StyleType = nodeStyleType; } if (sourceNode.StyleType == destinationNode.StyleType && // in case the user changed, for example, from Paragraph to Character style !string.IsNullOrEmpty(sourceNode.Style) && string.IsNullOrEmpty(destinationNode.Style)) { destinationNode.Style = sourceNode.Style; } if (!string.IsNullOrEmpty(sourceNode.CSSClassNameOverride) && string.IsNullOrEmpty(destinationNode.CSSClassNameOverride)) { destinationNode.CSSClassNameOverride = sourceNode.CSSClassNameOverride; } }
private static bool IsComplexFormsNode(ConfigurableDictionaryNode node) { var options = node.DictionaryNodeOptions as DictionaryNodeListOptions; return(options != null && options.ListId == DictionaryNodeListOptions.ListIds.Complex); }
/// <summary> /// Case FirstAlphaMigrator.VersionAlpha3 above will pull in all the new nodes in the Etymology cluster by Label. /// Gloss is the only pre-existing node that doesn't have a new name, so it won't be replaced. /// It needs to be marked Enabled. The main Etymology node needs several modifications. /// Three old nodes will need deleting: Etymological Form, Comment and Source /// </summary> /// <param name="oldConfigPart"></param> /// <param name="oldConfig"></param> private static void UpgradeEtymologyCluster(ConfigurableDictionaryNode oldConfigPart, DictionaryConfigurationModel oldConfig) { if (oldConfigPart.Children == null || oldConfigPart.Children.Count == 0) { return; // safety net } var etymNodes = new List <ConfigurableDictionaryNode>(); DCM.PerformActionOnNodes(oldConfigPart.Children, node => { if (node.Label == "Etymology") { etymNodes.Add(node); // since we have to do some node deleting, just collect up the relevant nodes } }); foreach (var node in etymNodes) { if (node.IsCustomField) // Unfortunately there are some pathological users who have ancient custom fields named etymology { continue; // Leave them be } // modify main node var etymSequence = "EtymologyOS"; if (oldConfig.IsReversal) { node.SubField = etymSequence; node.FieldDescription = "Entry"; node.IsEnabled = true; } else { node.FieldDescription = etymSequence; node.IsEnabled = !oldConfig.IsHybrid; } node.CSSClassNameOverride = "etymologies"; node.Before = "("; node.Between = " "; node.After = ") "; if (node.Children == null) { continue; } // enable Gloss node var glossNode = node.Children.Find(n => n.Label == "Gloss"); if (glossNode != null) { glossNode.IsEnabled = true; } // enable Source Language Notes var notesList = node.Children.Find(n => n.FieldDescription == "LanguageNotes"); if (notesList != null) // ran into some cases where this node didn't exist in reversal config! { notesList.IsEnabled = true; } // remove old children var nodesToRemove = new[] { "Etymological Form", "Comment", "Source" }; node.Children.RemoveAll(n => nodesToRemove.Contains(n.Label)); } // Etymology changed too much to be matched in the PreHistoricMigration and was marked as custom DCM.PerformActionOnNodes(etymNodes, n => { n.IsCustomField = false; }); }
public void StoredDefaultsUpdatedFromCurrentDefaults() { var subsenses = new ConfigurableDictionaryNode { Label = "Subsenses", FieldDescription = "SensesOS" }; var inBoth = new ConfigurableDictionaryNode { Label = "In Both", FieldDescription = "Both" }; var inOld = new ConfigurableDictionaryNode { Label = "inOld", FieldDescription = "OnlyOld", Children = new List <ConfigurableDictionaryNode> { subsenses } }; var senses = new ConfigurableDictionaryNode { Label = "Senses", FieldDescription = "SensesOS", Children = new List <ConfigurableDictionaryNode> { inOld, inBoth } }; var mainEntry = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", Children = new List <ConfigurableDictionaryNode> { senses } }; var oldModel = new DictionaryConfigurationModel { Parts = new List <ConfigurableDictionaryNode> { mainEntry } }; CssGeneratorTests.PopulateFieldsForTesting(oldModel); // PopulateFieldsForTesting populates each node's Label with its FieldDescription sets all isEnabled to true var newMain = mainEntry.DeepCloneUnderSameParent(); newMain.Children[0].Before = "{"; newMain.Children[0].Between = ","; newMain.Children[0].After = "}"; newMain.Children[0].Style = "Stylish"; newMain.Children[0].IsEnabled = false; newMain.Children[0].Children.RemoveAt(0); // Remove inOld var newModel = new DictionaryConfigurationModel { Parts = new List <ConfigurableDictionaryNode> { newMain } }; // Verify valid starting point Assert.AreNotEqual("{", oldModel.Parts[0].Children[0].Before, "Invalid preconditions"); Assert.AreNotEqual("}", oldModel.Parts[0].Children[0].After, "Invalid preconditions"); Assert.AreNotEqual(",", oldModel.Parts[0].Children[0].Between, "Invalid preconditions"); Assert.AreNotEqual("Stylish", oldModel.Parts[0].Children[0].Style, "Invalid preconditions"); Assert.True(oldModel.Parts[0].Children[0].IsEnabled, "Invalid preconditions"); DictionaryConfigurationMigrator.LoadConfigWithCurrentDefaults(oldModel, newModel); // SUT Assert.AreEqual(2, oldModel.Parts[0].Children[0].Children.Count, "Old non-matching part was not retained"); Assert.AreEqual("{", oldModel.Parts[0].Children[0].Before, "Before not copied from new defaults"); Assert.AreEqual("}", oldModel.Parts[0].Children[0].After, "After not copied from new defaults"); Assert.AreEqual(",", oldModel.Parts[0].Children[0].Between, "Between not copied from new defaults"); Assert.AreEqual("Stylish", oldModel.Parts[0].Children[0].Style, "Style not copied from new defaults"); Assert.False(oldModel.Parts[0].Children[0].IsEnabled, "IsEnabled value not copied from new defaults"); }