diff --git a/.gitignore b/.gitignore index cd6da820..8facc20b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ artifacts/ lib/downloads/ .idea/ .vs/ +*.bak src/SIL.LCModel.Core/KernelInterfaces/Kernel.cs src/SIL.LCModel/Infrastructure/Impl/Generated*.cs diff --git a/src/SIL.LCModel/Application/ApplicationServices/XmlImportData.cs b/src/SIL.LCModel/Application/ApplicationServices/XmlImportData.cs index 6b265d47..93af69c4 100644 --- a/src/SIL.LCModel/Application/ApplicationServices/XmlImportData.cs +++ b/src/SIL.LCModel/Application/ApplicationServices/XmlImportData.cs @@ -995,6 +995,18 @@ private void ReadXmlObject(XmlReader xrdr, FieldInfo fi, ICmObject objToUse) cmo = m_cache.LangProject.LexDbOA; Debug.Assert(cmo != null); break; + case "PhBdryMarker": + IPhBdryMarkerRepository repository = m_cache.ServiceLocator.GetInstance(); + IPhBdryMarkerFactory factory = m_cache.ServiceLocator.GetInstance(); + Guid guid = Guid.Parse(xrdr.GetAttribute("Guid")); + IPhBdryMarker marker; + if (repository.TryGetObject(guid, out marker)) + cmo = marker; + else + cmo = factory.Create(guid, (IPhPhonemeSet)fi.Owner); + // Remove the default code added by PhTerminalUnit.SetDefaultValuesAfterInit in OverridesLing_Lex. + (cmo as PhBdryMarker).CodesOS.Clear(); + break; case "PhPhonData": cmo = m_cache.LangProject.PhonologicalDataOA; Debug.Assert(cmo != null); @@ -1003,6 +1015,10 @@ private void ReadXmlObject(XmlReader xrdr, FieldInfo fi, ICmObject objToUse) cmo = m_cache.LangProject.PhFeatureSystemOA; Debug.Assert(cmo != null); break; + case "PhPhonemeSet": + cmo = m_cache.LangProject.PhonologicalDataOA.GetPhonemeSet(); + Debug.Assert(cmo != null); + break; default: int clid = m_mdc.GetClassId(sClass); if (fi != null) @@ -1048,7 +1064,6 @@ private void ReadXmlObject(XmlReader xrdr, FieldInfo fi, ICmObject objToUse) cmo = m_repoCmObject.GetObject(hvo); // Remove the default code added by PhTerminalUnit.SetDefaultValuesAfterInit in OverridesLing_Lex. (cmo as PhPhoneme)?.CodesOS.Clear(); - (cmo as PhBdryMarker)?.CodesOS.Clear(); // Remove default values. (cmo as PhRegularRule)?.RightHandSidesOS.Clear(); } diff --git a/src/SIL.LCModel/DomainImpl/OverridesLing_Lex.cs b/src/SIL.LCModel/DomainImpl/OverridesLing_Lex.cs index 58959921..834c52fd 100644 --- a/src/SIL.LCModel/DomainImpl/OverridesLing_Lex.cs +++ b/src/SIL.LCModel/DomainImpl/OverridesLing_Lex.cs @@ -6858,6 +6858,21 @@ public List AllPhonemes() return phReps; } + /// + /// Get the PhonemeSet. + /// Hides the fact that multiple PhonemeSets used to be allowed. + /// + /// + /// + public IPhPhonemeSet GetPhonemeSet() + { + if (this.PhonemeSetsOS.Count == 0) + throw new Exception("Missing PhonemeSet."); + if (this.PhonemeSetsOS.Count > 1) + throw new Exception("Too many PhonemeSets."); + return this.PhonemeSetsOS[0]; + } + /// /// Return the list of abbreviations for all the natural classes defined, each in the /// default analysis writing system. diff --git a/src/SIL.LCModel/DomainServices/M3ModelExportServices.cs b/src/SIL.LCModel/DomainServices/M3ModelExportServices.cs index 0ce09b8c..67f43d02 100644 --- a/src/SIL.LCModel/DomainServices/M3ModelExportServices.cs +++ b/src/SIL.LCModel/DomainServices/M3ModelExportServices.cs @@ -591,12 +591,16 @@ select ExportItemAsReference(constraint, constraints.IndexOf(constraint), "Featu new XAttribute("Id", rhs.Hvo), new XElement("StrucChange", from structChange in rhs.StrucChangeOS select ExportContext(structChange)), - new XElement("InputPOSes", from pos in rhs.InputPOSesRC - select ExportItemAsReference(pos, "RequiredPOS")), - new XElement("ReqRuleFeats", from rrf in rhs.ReqRuleFeatsRC - select ExportItemAsReference(rrf, "RuleFeat")), - new XElement("ExclRuleFeats", from erf in rhs.ExclRuleFeatsRC - select ExportItemAsReference(erf, "RuleFeat")), + // RuleFeats and POS are not part of the phonology. + phonology ? null + : new XElement("InputPOSes", from pos in rhs.InputPOSesRC + select ExportItemAsReference(pos, "RequiredPOS")), + phonology ? null + : new XElement("ReqRuleFeats", from rrf in rhs.ReqRuleFeatsRC + select ExportItemAsReference(rrf, "RuleFeat")), + phonology ? null + : new XElement("ExclRuleFeats", from erf in rhs.ExclRuleFeatsRC + select ExportItemAsReference(erf, "RuleFeat")), new XElement("LeftContext", ExportContext(rhs.LeftContextOA)), new XElement("RightContext", ExportContext(rhs.RightContextOA))))); break; diff --git a/src/SIL.LCModel/DomainServices/PhonologyServices.cs b/src/SIL.LCModel/DomainServices/PhonologyServices.cs index 6449cafa..a2a7a52b 100644 --- a/src/SIL.LCModel/DomainServices/PhonologyServices.cs +++ b/src/SIL.LCModel/DomainServices/PhonologyServices.cs @@ -11,6 +11,7 @@ using SIL.LCModel.Core.Text; using SIL.LCModel.Infrastructure.Impl; using static Icu.Normalization.Normalizer2; +using SIL.LCModel.Core.KernelInterfaces; namespace SIL.LCModel.DomainServices { @@ -74,42 +75,27 @@ public void ImportPhonologyFromXml(TextReader rdr) // () => AssignVernacularWritingSystemToDefaultPhPhonemes(Cache)); } - private void AssignVernacularWritingSystemToDefaultPhPhonemes(LcmCache cache) + /// + /// Clear PhonologicalData and Phonological Features. + /// Don't clear boundary markers. + /// + public void DeletePhonology() { - // For all PhCodes in the default phoneme set, change the writing system from "en" to icuLocale - if (cache.LanguageProject.PhonologicalDataOA.PhonemeSetsOS.Count == 0) - return; - var phSet = cache.LanguageProject.PhonologicalDataOA.PhonemeSetsOS[0]; - int wsVern = m_wsVernId == null - ? cache.DefaultVernWs - : cache.ServiceLocator.WritingSystemManager.Get(m_wsVernId).Handle; - foreach (var phone in phSet.PhonemesOC) - { - foreach (var code in phone.CodesOS) - { - - if (code.Representation.VernacularDefaultWritingSystem.Length == 0) - code.Representation.VernacularDefaultWritingSystem = - TsStringUtils.MakeString(code.Representation.UserDefaultWritingSystem.Text, wsVern); - } - if (phone.Name.VernacularDefaultWritingSystem.Length == 0) - phone.Name.VernacularDefaultWritingSystem = - TsStringUtils.MakeString(phone.Name.UserDefaultWritingSystem.Text, wsVern); - } - foreach (var mrkr in phSet.BoundaryMarkersOC) + NonUndoableUnitOfWorkHelper.Do(Cache.ServiceLocator.GetInstance(), () => { - foreach (var code in mrkr.CodesOS) - { - if (code.Representation.VernacularDefaultWritingSystem.Length == 0) - code.Representation.VernacularDefaultWritingSystem = - TsStringUtils.MakeString(code.Representation.UserDefaultWritingSystem.Text, wsVern); - } - if (mrkr.Name.VernacularDefaultWritingSystem.Length == 0) - mrkr.Name.VernacularDefaultWritingSystem = - TsStringUtils.MakeString(mrkr.Name.UserDefaultWritingSystem.Text, wsVern); - } + IPhPhonData phonData = Cache.LangProject.PhonologicalDataOA; + // Delete what is covered by ImportPhonology. + phonData.ContextsOS.Clear(); + phonData.EnvironmentsOS.Clear(); + phonData.FeatConstraintsOS.Clear(); + phonData.NaturalClassesOS.Clear(); + phonData.GetPhonemeSet().PhonemesOC.Clear(); + // Don't clear phonData.GetPhonemeSet().BoundaryMarkersOC! + // They have GUIDs known to the code. + phonData.PhonRulesOS.Clear(); + Cache.LanguageProject.PhFeatureSystemOA.TypesOC.Clear(); + Cache.LanguageProject.PhFeatureSystemOA.FeaturesOC.Clear(); + }); } - - } } diff --git a/src/SIL.LCModel/InterfaceAdditions.cs b/src/SIL.LCModel/InterfaceAdditions.cs index e81c3f19..81d40bc6 100644 --- a/src/SIL.LCModel/InterfaceAdditions.cs +++ b/src/SIL.LCModel/InterfaceAdditions.cs @@ -5626,6 +5626,12 @@ public partial interface IPhPhonData /// List AllNaturalClassAbbrs(); + /// + /// Get the PhonemeSet. + /// Hides the fact that the data structure used to allow more than one. + /// + IPhPhonemeSet GetPhonemeSet(); + /// /// Rebuild the list of PhonRuleFeats /// diff --git a/tests/SIL.LCModel.Tests/DomainServices/PhonologyServicesTest.cs b/tests/SIL.LCModel.Tests/DomainServices/PhonologyServicesTest.cs index 83ac1d96..78376e37 100644 --- a/tests/SIL.LCModel.Tests/DomainServices/PhonologyServicesTest.cs +++ b/tests/SIL.LCModel.Tests/DomainServices/PhonologyServicesTest.cs @@ -98,6 +98,15 @@ private void TestProject(string projectsDirectory, string dbFileName) using (var cache = LcmCache.CreateCacheFromExistingData(projectId, "en", m_ui, m_lcmDirectories, new LcmSettings(), new DummyProgressDlg())) { + // Create PhonemeSet if necessary. + NonUndoableUnitOfWorkHelper.Do(m_cache.ActionHandlerAccessor, () => + { + if (m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS.Count == 0) + { + var phonemeset = m_cache.ServiceLocator.GetInstance().Create(); + m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS.Add(phonemeset); + } + }); // Export project as XML. var services = new PhonologyServices(cache); XDocument xdoc = services.ExportPhonologyAsXml(); @@ -109,6 +118,7 @@ private void TestProject(string projectsDirectory, string dbFileName) var vernWs = cache.ServiceLocator.WritingSystemManager.Get(cache.DefaultVernWs); SetDefaultVernacularWritingSystem(m_cache, vernWs); var services2 = new PhonologyServices(m_cache, vernWs.Id); + services2.DeletePhonology(); services2.ImportPhonologyFromXml(rdr); xdoc2 = services2.ExportPhonologyAsXml(); } @@ -125,8 +135,16 @@ private void TestXml(string xml, string vernWs) { m_cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem = m_cache.ServiceLocator.WritingSystemManager.Get(vernWs); + if (m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS.Count == 0) + { + var phonemeset = m_cache.ServiceLocator.GetInstance().Create(); + m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS.Add(phonemeset); + } }); + ILcmOwningSequence phonemeList = m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS; + IPhPhonemeSet phonemeSet = m_cache.LangProject.PhonologicalDataOA.GetPhonemeSet(); var services = new PhonologyServices(m_cache); + services.DeletePhonology(); using (var rdr = new StringReader(xml)) { services.ImportPhonologyFromXml(rdr); @@ -134,6 +152,22 @@ private void TestXml(string xml, string vernWs) var xml2 = xdoc2.ToString(); TestXml(xdoc, xdoc2); } + // Verify that the references to the PhonemeSet didn't change. + Assert.IsTrue(ReferenceEquals(phonemeList, m_cache.LangProject.PhonologicalDataOA.PhonemeSetsOS)); + Assert.IsTrue(ReferenceEquals(phonemeSet, m_cache.LangProject.PhonologicalDataOA.GetPhonemeSet())); + // Verify that the boundary markers exist with the right GUIDs. + bool hasMorphBdry = false; + bool hasWordBdry = false; + foreach (var marker in m_cache.LangProject.PhonologicalDataOA.GetPhonemeSet().BoundaryMarkersOC) + { + if (marker.Guid == LangProjectTags.kguidPhRuleMorphBdry) + hasMorphBdry = true; + if (marker.Guid == LangProjectTags.kguidPhRuleWordBdry) + hasWordBdry = true; + } + Assert.IsTrue(hasMorphBdry); + Assert.IsTrue(hasWordBdry); + Assert.IsTrue(m_cache.LangProject.PhonologicalDataOA.GetPhonemeSet().BoundaryMarkersOC.Count == 2); } private void TestXml(XDocument xdoc, XDocument xdoc2)