private void AddSharedNodesToAlphaConfigurations(DictionaryConfigurationModel model) { if (model.SharedItems.Any()) { m_logger.WriteLine("Not adding shared nodes because some already exist:"); m_logger.IncreaseIndent(); model.SharedItems.ForEach(si => m_logger.WriteLine(DCM.BuildPathStringFromNode(si))); m_logger.DecreaseIndent(); return; } DCM.PerformActionOnNodes(model.Parts, SetReferenceItem); if (model.IsReversal) { var reversalSubEntries = FindMainEntryDescendant(model, "SubentriesOS"); AddSubsubNodeIfNeeded(reversalSubEntries); DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, reversalSubEntries, "allreversalsubentries"); } else // is Configured Dictionary { var mainEntrySubSenseNode = FindMainEntryDescendant(model, "SensesOS", "SensesOS"); AddSubsubNodeIfNeeded(mainEntrySubSenseNode); DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, mainEntrySubSenseNode, "mainentrysubsenses"); var mainEntrySubEntries = FindMainEntryDescendant(model, "Subentries"); AddSubsubEntriesOptionsIfNeeded(mainEntrySubEntries); DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, mainEntrySubEntries, "mainentrysubentries"); } // Remove direct children from nodes with referenced children DCM.PerformActionOnNodes(model.PartsAndSharedItems, n => { if (!string.IsNullOrEmpty(n.ReferenceItem)) { n.Children = null; } }); }
internal DictionaryConfigurationModel LoadBetaDefaultForAlphaConfig(DictionaryConfigurationModel config) { var dictionaryFolder = Path.Combine(FwDirectoryFinder.DefaultConfigurations, DictionaryConfigurationListener.DictionaryConfigurationDirectoryName); var reversalFolder = Path.Combine(FwDirectoryFinder.DefaultConfigurations, DictionaryConfigurationListener.ReversalIndexConfigurationDirectoryName); string configPath; // There is only one default config for reversals if (config.IsReversal) { configPath = Path.Combine(reversalFolder, DictionaryConfigurationModel.AllReversalIndexesFilenameBase + DictionaryConfigurationModel.FileExtension); } else if (config.IsRootBased) { configPath = Path.Combine(dictionaryFolder, DCM.RootFileName + DictionaryConfigurationModel.FileExtension); } else if (config.IsHybrid) // Hybrid configs have subentries { configPath = Path.Combine(dictionaryFolder, DCM.HybridFileName + DictionaryConfigurationModel.FileExtension); } else // Must be Lexeme { configPath = Path.Combine(dictionaryFolder, DCM.LexemeFileName + DictionaryConfigurationModel.FileExtension); } return(new DictionaryConfigurationModel(configPath, Cache)); }
/// <param name="model"></param> /// <param name="ancestors">Fields in the desired node's ancestry, included the desired node's field, but not including Main Entry</param> /// <remarks>Currently ignores nodes' subfields</remarks> private ConfigurableDictionaryNode FindMainEntryDescendant(DictionaryConfigurationModel model, params string[] ancestors) { var failureMessage = "Unable to find 'Main Entry"; var nextNode = model.Parts.FirstOrDefault(); foreach (var ancestor in ancestors) { if (nextNode == null) { break; } failureMessage += DCM.NodePathSeparator + ancestor; nextNode = nextNode.Children.Find(n => n.FieldDescription == ancestor && string.IsNullOrEmpty(n.LabelSuffix)); } if (nextNode != null) { return(nextNode); } // If we couldn't find the node, this is probably a test that didn't have a full model m_logger.WriteLine(string.Format("Unable to find '{0}'", string.Join(DCM.NodePathSeparator, new[] { "Main Entry" }.Concat(ancestors)))); m_logger.WriteLine(failureMessage + "'"); return(new ConfigurableDictionaryNode { Children = new List <ConfigurableDictionaryNode>() }); }
public void MigrateOldConfigurationsIfNeeded_MatchesLabelsWhenUIIsLocalized() { // Localize a Part's label to German (sufficient to cause a mismatched nodes crash if one config's labels are localized) var localizedPartLabels = new Dictionary <string, string>(); localizedPartLabels["Main Entry"] = "Haupteintrag"; var pathsToL10nStrings = (Dictionary <string, Dictionary <string, string> >)ReflectionHelper.GetField(m_mediator.StringTbl, "m_pathsToStrings"); pathsToL10nStrings["group[@id = 'LocalizedAttributes']/"] = localizedPartLabels; var configSettingsDir = FdoFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path)); var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.DictionaryConfigurationDirectoryName, "Lexeme" + DictionaryConfigurationModel.FileExtension); Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated"); Directory.CreateDirectory(configSettingsDir); File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] { @"<layoutType label='Lexeme-based (complex forms as main entries)' layout='publishStem'><configure class='LexEntry' label='Main Entry' layout='publishStemEntry' />", @"<configure class='LexEntry' label='Minor Entry' layout='publishStemMinorEntry' hideConfig='true' /></layoutType>'" }); var migrator = new DictionaryConfigurationMigrator(m_mediator); Assert.DoesNotThrow(() => migrator.MigrateOldConfigurationsIfNeeded(), "ArgumentException indicates localized labels."); // SUT var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache); Assert.AreEqual(2, updatedConfigModel.Parts.Count, "Should have 2 top-level nodes"); Assert.AreEqual("Main Entry", updatedConfigModel.Parts[0].Label); DirectoryUtilities.DeleteDirectoryRobust(configSettingsDir); }
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; } }
internal void MigrateFrom83Alpha(DictionaryConfigurationModel alphaModel) { // original migration neglected to update the version number; -1 (Pre83) is the same as 1 (Alpha1) if (alphaModel.Version == PreHistoricMigrator.VersionPre83 || alphaModel.Version == PreHistoricMigrator.VersionAlpha1) { RemoveNonLoadableData(alphaModel.PartsAndSharedItems); } // now that it's safe to specify them, it would be helpful to have parents in certain steps: DictionaryConfigurationModel.SpecifyParentsAndReferences(alphaModel.Parts, alphaModel.SharedItems); switch (alphaModel.Version) { case -1: case 1: case 2: HandleNodewiseChanges(alphaModel.PartsAndSharedItems, m_logger, 2, alphaModel.IsReversal); goto case 3; case 3: HandleNodewiseChanges(alphaModel.PartsAndSharedItems, m_logger, 3, alphaModel.IsReversal); DCM.SetWritingSystemForReversalModel(alphaModel, Cache); AddSharedNodesToAlphaConfigurations(alphaModel); goto case 4; case 4: HandleNodewiseChanges(alphaModel.PartsAndSharedItems, m_logger, 4, alphaModel.IsReversal); goto case VersionAlpha2; case VersionAlpha2: HandleNodewiseChanges(alphaModel.PartsAndSharedItems, m_logger, VersionAlpha2, alphaModel.IsReversal); goto case 6; case 6: HandleNodewiseChanges(alphaModel.PartsAndSharedItems, m_logger, 6, alphaModel.IsReversal); goto case 7; case 7: var fileName = Path.GetFileNameWithoutExtension(alphaModel.FilePath); if (!alphaModel.IsRootBased) { alphaModel.IsRootBased = fileName == DCM.RootFileName; } break; default: m_logger.WriteLine(string.Format( "Unable to migrate {0}: no migration instructions for version {1}", alphaModel.Label, alphaModel.Version)); break; } alphaModel.Version = VersionAlpha3; }
internal void MigrateFrom83Alpha(ISimpleLogger logger, DictionaryConfigurationModel oldConfig, DictionaryConfigurationModel currentDefaultModel) { // it may be helpful to have parents and current custom fields in the oldConfig (currentDefaultModel already has them): DictionaryConfigurationModel.SpecifyParentsAndReferences(oldConfig.Parts, oldConfig.SharedItems); DictionaryConfigurationController.MergeCustomFieldsIntoDictionaryModel(oldConfig, Cache); ChooseAppropriateComplexForms(oldConfig); // 13->14, but needed before rearranging and adding new nodes ConflateMainEntriesIfNecessary(logger, oldConfig); // 12->13, but needed before rearranging and adding new nodes var currentDefaultList = new List <ConfigurableDictionaryNode>(currentDefaultModel.PartsAndSharedItems); foreach (var part in oldConfig.PartsAndSharedItems) { MigratePartFromOldVersionToCurrent(logger, oldConfig, part, FindMatchingChildNode(part.Label, currentDefaultList)); } oldConfig.Version = DCM.VersionCurrent; logger.WriteLine("Migrated to version " + DCM.VersionCurrent); }
/// <summary> /// Earlier versions of Hybrid mistakenly used VisibleComplexFormBackRefs instead of ComplexFormsNotSubentries. Correct this mistake. /// Referenced Complex Forms should be *Other* Referenced Complex Forms whenever they are siblings of Subentries /// </summary> private static void ChooseAppropriateComplexForms(DictionaryConfigurationModel migratingModel) { if (!migratingModel.IsHybrid) { return; } DCM.PerformActionOnNodes(migratingModel.Parts, parentNode => { if (parentNode.ReferencedOrDirectChildren != null && parentNode.ReferencedOrDirectChildren.Any(node => node.FieldDescription == "Subentries")) { parentNode.ReferencedOrDirectChildren.Where(sib => sib.FieldDescription == "VisibleComplexFormBackRefs").ForEach(sib => { sib.Label = "Other Referenced Complex Forms"; sib.FieldDescription = "ComplexFormsNotSubentries"; }); } }); }
public void MigrateOldConfigurationsIfNeeded_BringsPreHistoricFileToCurrentVersion() { var configSettingsDir = FdoFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path)); var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.DictionaryConfigurationDirectoryName, "Lexeme" + DictionaryConfigurationModel.FileExtension); Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated"); Directory.CreateDirectory(configSettingsDir); File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] { @"<layoutType label='Lexeme-based (complex forms as main entries)' layout='publishStem'><configure class='LexEntry' label='Main Entry' layout='publishStemEntry' />", @"<configure class='LexEntry' label='Minor Entry' layout='publishStemMinorEntry' hideConfig='true' /></layoutType>'" }); var migrator = new DictionaryConfigurationMigrator(m_mediator); migrator.MigrateOldConfigurationsIfNeeded(); // SUT var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache); Assert.AreEqual(DictionaryConfigurationMigrator.VersionCurrent, updatedConfigModel.Version); DirectoryUtilities.DeleteDirectoryRobust(configSettingsDir); }
internal void MigrateFrom83Alpha(ISimpleLogger logger, DictionaryConfigurationModel oldConfig, DictionaryConfigurationModel currentDefaultModel) { // it may be helpful to have parents and current custom fields in the oldConfig (currentDefaultModel already has them): DictionaryConfigurationModel.SpecifyParentsAndReferences(oldConfig.Parts, oldConfig.SharedItems); DictionaryConfigurationController.MergeCustomFieldsIntoDictionaryModel(oldConfig, Cache); ChooseAppropriateComplexForms(oldConfig); // 13->14, but needed before rearranging and adding new nodes ConflateMainEntriesIfNecessary(logger, oldConfig); // 12->13, but needed before rearranging and adding new nodes var currentDefaultList = new List <ConfigurableDictionaryNode>(currentDefaultModel.PartsAndSharedItems); foreach (var part in oldConfig.PartsAndSharedItems) { var defaultPart = FindMatchingChildNode(part.Label, currentDefaultList); if (defaultPart == null) { throw new NullReferenceException(string.Format( "{0} is corrupt. {1} has no corresponding part in the defaults (perhaps it missed a rename migration step).", oldConfig.FilePath, part.Label)); } MigratePartFromOldVersionToCurrent(logger, oldConfig, part, defaultPart); } oldConfig.Version = DCM.VersionCurrent; logger.WriteLine("Migrated to version " + DCM.VersionCurrent); }
private static void ConflateMainEntriesIfNecessary(ISimpleLogger logger, DictionaryConfigurationModel config) { if (config.Version >= VersionBeta5 || config.IsRootBased || config.IsReversal) { return; } var mainEntriesComplexForms = config.Parts.FirstOrDefault(n => IsComplexFormsNode(n) && n.IsEnabled) ?? config.Parts.FirstOrDefault(IsComplexFormsNode); if (mainEntriesComplexForms == null) { logger.WriteLine(string.Format("Unable to conflate main entries for config '{0}' (version {1})", config.Label, config.Version)); return; } MigrateNewChildNodesAndOptionsInto(config.Parts[0], mainEntriesComplexForms); // Remove all complex form nodes *except* Main Entry for (var i = config.Parts.Count - 1; i >= 1; --i) { if (IsComplexFormsNode(config.Parts[i])) { config.Parts.RemoveAt(i); } } }
public void MigrateOldConfigurationsIfNeeded_PreservesOrderOfBibliographies() { var configSettingsDir = FdoFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path)); var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.ReversalIndexConfigurationDirectoryName, "AllReversalIndexes" + DictionaryConfigurationModel.FileExtension); Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated"); Directory.CreateDirectory(configSettingsDir); File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] { @"<layoutType label='All Reversal Indexes' layout='publishReversal'>", @"<configure class='ReversalIndexEntry' label='Reversal Entry' layout='publishReversalEntry' /></layoutType>'" }); var migrator = new DictionaryConfigurationMigrator(m_mediator); migrator.MigrateOldConfigurationsIfNeeded(); // SUT var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache); var refdSenseChildren = updatedConfigModel.Parts[0].Children.Find(n => n.Label == "Referenced Senses").Children; var bibCount = 0; for (var i = 0; i < refdSenseChildren.Count; i++) { var bibNode = refdSenseChildren[i]; if (!bibNode.Label.StartsWith("Bibliography")) { continue; } StringAssert.StartsWith("Bibliography (", bibNode.Label, "Should specify (entry|sense), lest we never know"); Assert.False(bibNode.IsCustomField, bibNode.Label + " should not be custom."); // Rough test to ensure Bibliography nodes aren't bumped to the end of the list. In the defaults, the later Bibliography // node is a little more than five nodes from the end Assert.LessOrEqual(i, refdSenseChildren.Count - 5, "Bibliography nodes should not have been bumped to the end of the list"); ++bibCount; } Assert.AreEqual(2, bibCount, "Should be exactly two Bibliography nodes (sense and entry)"); DirectoryUtilities.DeleteDirectoryRobust(configSettingsDir); }
/// <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"); }