/// ------------------------------------------------------------------------------------ /// <summary> /// Perform one increment migration step. /// /// In this case, the migration is not related to a model change, /// but is a simple data change that removes the class elements in the xml. /// The end resujlt xml will have the top-level 'rt' element and zero, or more, /// property level elements. /// </summary> /// ------------------------------------------------------------------------------------ public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository) { DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000014); // No. We need to convert all instances, even if they are no longer part of the model. // DM19, for example, removes LgWritingSystem instances and the class form the model, // but it tries to access the data as if it had been properly processed by DM15, // *but* this code would leave out LgWritingSystem instnaces form being processed here. //foreach (var dto in domainObjectDtoRepository.AllInstancesWithSubclasses("CmObject")) foreach (var dto in domainObjectDtoRepository.AllInstances()) { var rtElement = XElement.Parse(dto.Xml); // Removes all current child nodes (class level), // and replaces them with the old property nodes (if any). #if !__MonoCS__ rtElement.ReplaceNodes(rtElement.Elements().Elements()); dto.Xml = rtElement.ToString(); #else // FWNX-165: work around mono bug https://bugzilla.novell.com/show_bug.cgi?id=592435 var copy = new XElement(rtElement); copy.ReplaceNodes(rtElement.Elements().Elements()); dto.Xml = copy.ToString(); #endif domainObjectDtoRepository.Update(dto); } DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Cleans up legacy ChkRendering objects with null SurfaceForms. /// </summary> /// <param name="repoDto"> /// Repository of all CmObject DTOs available for one migration step. /// </param> /// <remarks> /// The method must add/remove/update the DTOs to the repository, /// as it adds/removes objects as part of it work. /// /// Implementors of this interface should ensure the Repository's /// starting model version number is correct for the step. /// Implementors must also increment the Repository's model version number /// at the end of its migration work. /// /// The method also should normally modify the xml string(s) /// of relevant DTOs, since that string will be used by the main /// data migration calling client (ie. BEP). /// </remarks> /// ------------------------------------------------------------------------------------ public void PerformMigration(IDomainObjectDTORepository repoDto) { DataMigrationServices.CheckVersionNumber(repoDto, 7000046); Dictionary <Guid, DomainObjectDTO> mapOfRenderingsToChk = new Dictionary <Guid, DomainObjectDTO>(); HashSet <DomainObjectDTO> renderingsToDelete = new HashSet <DomainObjectDTO>(); foreach (DomainObjectDTO dto in repoDto.AllInstances()) { XElement data = XElement.Parse(dto.Xml); XAttribute classAttr = data.Attribute("class"); if (classAttr.Value == "ChkTerm") { XElement renderings = data.Element("Renderings"); if (renderings != null) { foreach (XElement r in renderings.Elements()) { mapOfRenderingsToChk[new Guid(r.Attribute("guid").Value)] = dto; } } } else if (classAttr.Value == "ChkRendering") { XElement surfaceForm = data.Element("SurfaceForm"); if (surfaceForm == null || !surfaceForm.HasElements) { renderingsToDelete.Add(dto); } } } foreach (DomainObjectDTO rendering in renderingsToDelete) { DomainObjectDTO chkTerm = mapOfRenderingsToChk[new Guid(rendering.Guid)]; XElement termData = XElement.Parse(chkTerm.Xml); XElement renderings = termData.Element("Renderings"); XElement bogusRendering = renderings.Elements().First(e => e.Attribute("guid").Value.Equals(rendering.Guid, StringComparison.OrdinalIgnoreCase)); bogusRendering.Remove(); DataMigrationServices.UpdateDTO(repoDto, chkTerm, termData.ToString()); repoDto.Remove(rendering); } DataMigrationServices.IncrementVersionNumber(repoDto); }
/// <summary> /// Remove all attributes from the "Uni"> element. /// </summary> /// <param name="domainObjectDtoRepository"></param> public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository) { DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000066); foreach (var dto in domainObjectDtoRepository.AllInstances()) { var element = XElement.Parse(dto.Xml); var uniElementsWithAttrs = element.Elements().Elements("Uni").Where(uniElement => uniElement.HasAttributes).ToList(); if (uniElementsWithAttrs.Count == 0) continue; foreach (var uniElementWithAttrs in uniElementsWithAttrs) { uniElementWithAttrs.Attributes().Remove(); dto.Xml = element.ToString(); domainObjectDtoRepository.Update(dto); } } DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository); }
/// <summary> /// Remove all attributes from the "Uni"> element. /// </summary> /// <param name="domainObjectDtoRepository"></param> public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository) { DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000066); foreach (var dto in domainObjectDtoRepository.AllInstances()) { var element = XElement.Parse(dto.Xml); var uniElementsWithAttrs = element.Elements().Elements("Uni").Where(uniElement => uniElement.HasAttributes).ToList(); if (uniElementsWithAttrs.Count == 0) { continue; } foreach (var uniElementWithAttrs in uniElementsWithAttrs) { uniElementWithAttrs.Attributes().Remove(); dto.Xml = element.ToString(); domainObjectDtoRepository.Update(dto); } } DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Cleans up legacy ChkRendering objects with null SurfaceForms. /// </summary> /// <param name="repoDto"> /// Repository of all CmObject DTOs available for one migration step. /// </param> /// <remarks> /// The method must add/remove/update the DTOs to the repository, /// as it adds/removes objects as part of it work. /// /// Implementors of this interface should ensure the Repository's /// starting model version number is correct for the step. /// Implementors must also increment the Repository's model version number /// at the end of its migration work. /// /// The method also should normally modify the xml string(s) /// of relevant DTOs, since that string will be used by the main /// data migration calling client (ie. BEP). /// </remarks> /// ------------------------------------------------------------------------------------ public void PerformMigration(IDomainObjectDTORepository repoDto) { DataMigrationServices.CheckVersionNumber(repoDto, 7000046); Dictionary<Guid, DomainObjectDTO> mapOfRenderingsToChk = new Dictionary<Guid, DomainObjectDTO>(); HashSet<DomainObjectDTO> renderingsToDelete = new HashSet<DomainObjectDTO>(); foreach (DomainObjectDTO dto in repoDto.AllInstances()) { XElement data = XElement.Parse(dto.Xml); XAttribute classAttr = data.Attribute("class"); if (classAttr.Value == "ChkTerm") { XElement renderings = data.Element("Renderings"); if (renderings != null) foreach (XElement r in renderings.Elements()) mapOfRenderingsToChk[new Guid(r.Attribute("guid").Value)] = dto; } else if (classAttr.Value == "ChkRendering") { XElement surfaceForm = data.Element("SurfaceForm"); if (surfaceForm == null || !surfaceForm.HasElements) renderingsToDelete.Add(dto); } } foreach (DomainObjectDTO rendering in renderingsToDelete) { DomainObjectDTO chkTerm = mapOfRenderingsToChk[new Guid(rendering.Guid)]; XElement termData = XElement.Parse(chkTerm.Xml); XElement renderings = termData.Element("Renderings"); XElement bogusRendering = renderings.Elements().First(e => e.Attribute("guid").Value.Equals(rendering.Guid, StringComparison.OrdinalIgnoreCase)); bogusRendering.Remove(); DataMigrationServices.UpdateDTO(repoDto, chkTerm, termData.ToString()); repoDto.Remove(rendering); } DataMigrationServices.IncrementVersionNumber(repoDto); }
/// <summary> /// Perform one increment migration step. /// </summary> /// <param name="domainObjectDtoRepository">Repository of all CmObject DTOs available for one migration step.</param> /// <remarks> /// The method must add/remove/update the DTOs to the repository, /// as it adds/removes objects as part of it work. /// Implementors of this interface should ensure the Repository's /// starting model version number is correct for the step. /// Implementors must also increment the Repository's model version number /// at the end of its migration work. /// The method also should normally modify the xml string(s) /// of relevant DTOs, since that string will be used by the main /// data migration calling client (ie. BEP). /// </remarks> public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository) { DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000018); // collect all writing system info var guidToWsInfo = new Dictionary<string, Tuple<string, DomainObjectDTO, XElement>>(); foreach (DomainObjectDTO wsDto in domainObjectDtoRepository.AllInstancesSansSubclasses("LgWritingSystem").ToArray()) { XElement wsElem = XElement.Parse(wsDto.Xml); XElement icuLocaleElem = wsElem.Element("ICULocale"); var icuLocale = icuLocaleElem.Element("Uni").Value; string langTag = Version19LangTagUtils.ToLangTag(icuLocale); guidToWsInfo[wsDto.Guid.ToLowerInvariant()] = Tuple.Create(langTag, wsDto, wsElem); } // remove all CmSortSpec objects foreach (DomainObjectDTO sortSpecDto in domainObjectDtoRepository.AllInstancesSansSubclasses("CmSortSpec").ToArray()) domainObjectDtoRepository.Remove(sortSpecDto); // remove SortSpecs property from LangProject DomainObjectDTO lpDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First(); XElement lpElem = XElement.Parse(lpDto.Xml); XElement sortSpecsElem = lpElem.Element("SortSpecs"); bool lpModified = false; if (sortSpecsElem != null) { sortSpecsElem.Remove(); lpModified = true; } var referencedWsIds = new HashSet<string>(); // convert all LangProject writing system references to strings if (ConvertRefToString(lpElem.Element("AnalysisWss"), guidToWsInfo, referencedWsIds)) lpModified = true; if (ConvertRefToString(lpElem.Element("VernWss"), guidToWsInfo, referencedWsIds)) lpModified = true; if (ConvertRefToString(lpElem.Element("CurAnalysisWss"), guidToWsInfo, referencedWsIds)) lpModified = true; if (ConvertRefToString(lpElem.Element("CurPronunWss"), guidToWsInfo, referencedWsIds)) lpModified = true; if (ConvertRefToString(lpElem.Element("CurVernWss"), guidToWsInfo, referencedWsIds)) lpModified = true; if (lpModified) DataMigrationServices.UpdateDTO(domainObjectDtoRepository, lpDto, lpElem.ToString()); // convert all ReversalIndex writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "ReversalIndex", guidToWsInfo, referencedWsIds); // convert all WordformLookupList writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "WordformLookupList", guidToWsInfo, referencedWsIds); // convert all CmPossibilityList writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "CmPossibilityList", guidToWsInfo, referencedWsIds); // convert all UserViewField writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "UserViewField", guidToWsInfo, referencedWsIds); // convert all CmBaseAnnotation writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "CmBaseAnnotation", guidToWsInfo, referencedWsIds); // convert all FsOpenFeature writing system references to strings ConvertAllRefsToStrings(domainObjectDtoRepository, "FsOpenFeature", guidToWsInfo, referencedWsIds); // convert all ScrMarkerMapping ICU locales to lang tags ConvertAllIcuLocalesToLangTags(domainObjectDtoRepository, "ScrMarkerMapping", referencedWsIds); // convert all ScrImportSource ICU locales to lang tags ConvertAllIcuLocalesToLangTags(domainObjectDtoRepository, "ScrImportSource", referencedWsIds); // convert all ICU locales to Language Tags and remove legacy magic font names foreach (DomainObjectDTO dto in domainObjectDtoRepository.AllInstances()) UpdateStringsAndProps(domainObjectDtoRepository, dto, referencedWsIds); var localStoreFolder = Path.Combine(domainObjectDtoRepository.ProjectFolder, FdoFileHelper.ksWritingSystemsDir); // If any writing systems that project needs don't already exist as LDML files, // create them, either by copying relevant data from a shipping LDML file, or by // extracting data from the obsolete writing system object's XML. if (!string.IsNullOrEmpty(domainObjectDtoRepository.ProjectFolder)) { foreach (Tuple<string, DomainObjectDTO, XElement> wsInfo in guidToWsInfo.Values) { if (referencedWsIds.Contains(wsInfo.Item1)) { var ws = new Version19WritingSystemDefn(); var langTag = wsInfo.Item1; ws.LangTag = langTag; var ldmlFileName = Path.ChangeExtension(langTag, "ldml"); string localPath = Path.Combine(localStoreFolder, ldmlFileName); if (File.Exists(localPath)) continue; // already have one. string globalPath = Path.Combine(DirectoryFinder.GlobalWritingSystemStoreDirectory, ldmlFileName); if (File.Exists(globalPath)) continue; // already have one. // Need to make one. // Code similar to this was in the old migrator (prior to 7000043). It does not work // because the XML files it is looking for are in the Languages subdirectory of the // FieldWorks 6 data directory, and this is looking in the FW 7 one. No one has complained // so we decided not to try to fix it for the new implementation of the migration. //string langDefPath = Path.Combine(FwDirectoryFinder.GetDataSubDirectory("Languages"), // Path.ChangeExtension(langTag, "xml")); //if (File.Exists(langDefPath)) // FillWritingSystemFromLangDef(XElement.Load(langDefPath), ws); //else FillWritingSystemFromFDO(domainObjectDtoRepository, wsInfo.Item3, ws); ws.Save(localPath); } } } foreach (Tuple<string, DomainObjectDTO, XElement> wsInfo in guidToWsInfo.Values) { // this should also remove all LgCollations as well DataMigrationServices.RemoveIncludingOwnedObjects(domainObjectDtoRepository, wsInfo.Item2, false); } DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository); }
// We update every instance of an AUni, AStr, Run, or WsProp element that has a ws attribute. // Also the value of every top-level WritingSystem element that has a Uni child // Finally several ws-list properties of langProject. // AUni, ASTr, and Run are very common; WsProp and WritingSystem are relatively rare. So there's some // inefficiency in checking for them everywhere. I'm guess it won't add all that much overhead, and // it simplifies the code and testing. public void Migrate() { foreach (DomainObjectDTO dto in m_repoDto.AllInstances()) { var changed = false; XElement data = XElement.Parse(dto.Xml); var elementsToRemove = new List <XElement>(); foreach (XElement elt in data.XPathSelectElements("//*[name()='AUni' or name()='AStr' or name()='Run' or name()='WsProp' or name()='Prop']")) { if ((elt.Name == "AUni" || elt.Name == "AStr") && string.IsNullOrEmpty(elt.Value)) { changed = true; elementsToRemove.Add(elt); // don't remove right away, messes up the iteration. continue; } XAttribute attr = elt.Attribute("ws"); if (attr == null) { continue; // pathological, but let's try to survive } string oldTag = attr.Value; string newTag; if (TryGetNewTag(oldTag, out newTag)) { changed = true; attr.Value = newTag; } } foreach (XElement elt in elementsToRemove) { elt.Remove(); } var wsElt = data.Element("WritingSystem"); if (wsElt != null) { var uniElt = wsElt.Element("Uni"); if (uniElt != null) { string newTag1; if (TryGetNewTag(uniElt.Value, out newTag1)) { changed = true; uniElt.Value = newTag1; } } } XElement residueElt = data.Element("LiftResidue"); if (residueElt != null) { bool changedResidue = false; var uniElt = residueElt.Element("Uni"); if (uniElt != null) { // We may have more than one root element which .Parse can't handle. LT-11856, LT-11698. XElement contentElt = XElement.Parse("<x>" + uniElt.Value + "</x>"); foreach (XElement elt in contentElt.XPathSelectElements("//*[@lang]")) { var attr = elt.Attribute("lang"); if (attr == null) { continue; // pathological, but let's try to survive } var oldTag = attr.Value; string newTag; if (TryGetNewTag(oldTag, out newTag)) { changedResidue = true; attr.Value = newTag; } } if (changedResidue) { changed = true; uniElt.Value = ""; foreach (var node in contentElt.Nodes()) { uniElt.Value += node.ToString(); } } } } if (changed) { DataMigrationServices.UpdateDTO(m_repoDto, dto, data.ToString()); } } DomainObjectDTO langProjDto = m_repoDto.AllInstancesSansSubclasses("LangProject").First(); XElement langProj = XElement.Parse(langProjDto.Xml); bool lpChanged = UpdateAttr(langProj, "AnalysisWss"); lpChanged |= UpdateAttr(langProj, "CurVernWss"); lpChanged |= UpdateAttr(langProj, "CurAnalysisWss"); lpChanged |= UpdateAttr(langProj, "CurPronunWss"); lpChanged |= UpdateAttr(langProj, "VernWss"); if (lpChanged) { DataMigrationServices.UpdateDTO(m_repoDto, langProjDto, langProj.ToString()); } string settingsFolder = Path.Combine(m_repoDto.ProjectFolder, LcmFileHelper.ksConfigurationSettingsDir); if (Directory.Exists(settingsFolder)) { foreach (string layoutFile in Directory.GetFiles(settingsFolder, m_layoutFilePattern)) { XElement layout = XElement.Parse(File.ReadAllText(layoutFile, Encoding.UTF8)); bool changedFile = false; foreach (XElement elt in layout.XPathSelectElements("//*[@ws]")) { changedFile |= FixWSAtttribute(elt.Attribute("ws")); } foreach (XElement elt in layout.XPathSelectElements("//*[@visibleWritingSystems]")) { changedFile |= FixWSAtttribute(elt.Attribute("visibleWritingSystems")); } if (changedFile) { using (var xmlWriter = XmlWriter.Create(layoutFile, new XmlWriterSettings { Encoding = Encoding.UTF8 })) layout.WriteTo(xmlWriter); } } string localSettingsPath = Path.Combine(settingsFolder, "db$local$Settings.xml"); if (File.Exists(localSettingsPath)) { XElement settings = XElement.Parse(File.ReadAllText(localSettingsPath, Encoding.UTF8)); bool changedFile = false; var namesAndPatterns = new Dictionary <string, string>(); // Each item in this dictionary should be a property name that occurs in the <name> attribute of a <Property> element // in the db$local$Settings.xml file, mapping to a regular expression that will pick out the writing system tag // in the corresponding <value> element in the property table. Each regex must have a 'target' group which matches the // writing system. // Looking in a list like this, we want number followed by % followed by ws code,5062001%,5062001%x-kal,5112002%,5112002%x-kal, namesAndPatterns["db$local$InterlinConfig_Edit_Interlinearizer"] = ",[0-9]+%(?'target'[^,]+)"; // Here we expect to find something like ws="x-kal" namesAndPatterns["db$local$LexDb.Entries_sorter"] = "ws=\"(?'target'[^\"]+)\""; // The value of this one simply IS a writing system. namesAndPatterns["db$local$ConcordanceWs"] = "^(?'target'.*)$"; foreach (XElement elt in settings.Elements("Property")) { XElement nameElt = elt.Element("name"); if (nameElt == null) { continue; } string pattern; string propName = nameElt.Value; if (namesAndPatterns.TryGetValue(propName, out pattern)) { ReplaceWSIdInValue(elt, pattern, ref changedFile); } else if (propName.EndsWith("_sorter") || propName.EndsWith("_filter") || propName.EndsWith("_ColumnList")) { ReplaceWSIdInValue(elt, "ws=\"(?'target'[^\"]+)\"", ref changedFile); } } if (changedFile) { using (var xmlWriter = XmlWriter.Create(localSettingsPath, new XmlWriterSettings { Encoding = Encoding.UTF8 })) settings.WriteTo(xmlWriter); } } } }
// We update every instance of an AUni, AStr, Run, or WsProp element that has a ws attribute. // Also the value of every top-level WritingSystem element that has a Uni child // Finally several ws-list properties of langProject. // AUni, ASTr, and Run are very common; WsProp and WritingSystem are relatively rare. So there's some // inefficiency in checking for them everywhere. I'm guess it won't add all that much overhead, and // it simplifies the code and testing. private void UpdateTags(IDomainObjectDTORepository repoDto) { foreach (var dto in repoDto.AllInstances()) { var changed = false; XElement data = XElement.Parse(dto.Xml); var elementsToRemove = new List<XElement>(); foreach (var elt in data.XPathSelectElements("//*[name()='AUni' or name()='AStr' or name()='Run' or name()='WsProp' or name()='Prop']")) { if ((elt.Name == "AUni" || elt.Name == "AStr") && string.IsNullOrEmpty(elt.Value)) { changed = true; elementsToRemove.Add(elt); // don't remove right away, messes up the iteration. continue; } var attr = elt.Attribute("ws"); if (attr == null) continue; // pathological, but let's try to survive var oldTag = attr.Value; string newTag; if (TryGetNewTag(oldTag, out newTag)) { changed = true; attr.Value = newTag; } } foreach (var elt in elementsToRemove) elt.Remove(); var wsElt = data.Element("WritingSystem"); if (wsElt != null) { var uniElt = wsElt.Element("Uni"); if (uniElt != null) { string newTag1; if (TryGetNewTag(uniElt.Value, out newTag1)) { changed = true; uniElt.Value = newTag1; } } } var residueElt = data.Element("LiftResidue"); if (residueElt != null) { bool changedResidue = false; var uniElt = residueElt.Element("Uni"); if (uniElt != null) { // We may have more than one root element which .Parse can't handle. LT-11856, LT-11698. var contentElt = XElement.Parse("<x>" + uniElt.Value + "</x>"); foreach (var elt in contentElt.XPathSelectElements("//*[@lang]")) { var attr = elt.Attribute("lang"); if (attr == null) continue; // pathological, but let's try to survive var oldTag = attr.Value; string newTag; if (TryGetNewTag(oldTag, out newTag)) { changedResidue = true; attr.Value = newTag; } } if (changedResidue) { changed = true; uniElt.Value = ""; foreach (var node in contentElt.Nodes()) uniElt.Value += node.ToString(); } } } if (changed) { DataMigrationServices.UpdateDTO(repoDto, dto, data.ToString()); } } var langProjDto = repoDto.AllInstancesSansSubclasses("LangProject").First(); var langProj = XElement.Parse(langProjDto.Xml); bool lpChanged = UpdateAttr(langProj, "AnalysisWss"); lpChanged |= UpdateAttr(langProj, "CurVernWss"); lpChanged |= UpdateAttr(langProj, "CurAnalysisWss"); lpChanged |= UpdateAttr(langProj, "CurPronunWss"); lpChanged |= UpdateAttr(langProj, "VernWss"); if (lpChanged) DataMigrationServices.UpdateDTO(repoDto, langProjDto, langProj.ToString()); var settingsFolder = Path.Combine(repoDto.ProjectFolder, FdoFileHelper.ksConfigurationSettingsDir); if (Directory.Exists(settingsFolder)) { m_tagMap["$wsname"] = "$wsname"; // should never be changed. foreach (var layoutFile in Directory.GetFiles(settingsFolder, "*_Layouts.xml")) { var layout = XElement.Parse(File.ReadAllText(layoutFile, Encoding.UTF8)); bool changedFile = false; foreach (var elt in layout.XPathSelectElements("//*[@ws]")) { changedFile |= FixWsAtttribute(elt.Attribute("ws")); } foreach (var elt in layout.XPathSelectElements("//*[@visibleWritingSystems]")) { changedFile |= FixWsAtttribute(elt.Attribute("visibleWritingSystems")); } if (changedFile) { using (var xmlWriter = XmlWriter.Create(layoutFile, new XmlWriterSettings() { Encoding = Encoding.UTF8 })) layout.WriteTo(xmlWriter); } } var localSettingsPath = Path.Combine(settingsFolder, "db$local$Settings.xml"); if (File.Exists(localSettingsPath)) { var settings = XElement.Parse(File.ReadAllText(localSettingsPath, Encoding.UTF8)); bool changedFile = false; var namesAndPatterns = new Dictionary<string, string>(); // Each item in this dictionary should be a property name that occurs in the <name> attribute of a <Property> element // in the db$local$Settings.xml file, mapping to a regular expression that will pick out the writing system tag // in the corresponding <value> element in the property table. Each regex must have a 'target' group which matches the // writing system. // Looking in a list like this, we want number followed by % followed by ws code,5062001%,5062001%x-kal,5112002%,5112002%x-kal, namesAndPatterns["db$local$InterlinConfig_Edit_Interlinearizer"] = ",[0-9]+%(?'target'[^,]+)"; // Here we expect to find something like ws="x-kal" namesAndPatterns["db$local$LexDb.Entries_sorter"] = "ws=\"(?'target'[^\"]+)\""; // The value of this one simply IS a writing system. namesAndPatterns["db$local$ConcordanceWs"] = "^(?'target'.*)$"; foreach (var elt in settings.Elements("Property")) { var nameElt = elt.Element("name"); if (nameElt == null) continue; string pattern; var propName = nameElt.Value; if (namesAndPatterns.TryGetValue(propName, out pattern)) ReplaceWsIdInValue(elt, pattern, ref changedFile); else if (propName.EndsWith("_sorter") || propName.EndsWith("_filter") || propName.EndsWith("_ColumnList")) ReplaceWsIdInValue(elt, "ws=\"(?'target'[^\"]+)\"", ref changedFile); } if (changedFile) { using (var xmlWriter = XmlWriter.Create(localSettingsPath, new XmlWriterSettings() { Encoding = Encoding.UTF8 })) settings.WriteTo(xmlWriter); } } } }