diff --git a/Src/LexText/ParserCore/ParserCoreTests/ParseFilerProcessingTests.cs b/Src/LexText/ParserCore/ParserCoreTests/ParseFilerProcessingTests.cs
index 0237fb41d2..937d4ce30c 100644
--- a/Src/LexText/ParserCore/ParserCoreTests/ParseFilerProcessingTests.cs
+++ b/Src/LexText/ParserCore/ParserCoreTests/ParseFilerProcessingTests.cs
@@ -13,6 +13,8 @@
using System;
using System.Linq;
+using System.Reflection;
+using System.Xml.Linq;
using NUnit.Framework;
using SIL.FieldWorks.Common.FwUtils;
using SIL.LCModel.Core.Text;
@@ -99,6 +101,37 @@ protected void ExecuteIdleQueue()
m_idleQueue.Clear();
}
+ ///
+ /// Creates a stem entry with two senses, each with its own MoStemMsa, so tests can
+ /// verify that a specific MSA (not just "the first one") gets resolved.
+ ///
+ private ILexEntry CreateStemEntryWithTwoMsas(string form, out IMoStemMsa firstMsa, out IMoStemMsa secondMsa)
+ {
+ ILexEntry entry = m_entryFactory.Create();
+ IMoStemAllomorph entryForm = m_stemAlloFactory.Create();
+ entry.AlternateFormsOS.Add(entryForm);
+ entryForm.Form.VernacularDefaultWritingSystem = TsStringUtils.MakeString(form, m_vernacularWS.Handle);
+ firstMsa = m_stemMsaFactory.Create();
+ entry.MorphoSyntaxAnalysesOC.Add(firstMsa);
+ ILexSense firstSense = m_senseFactory.Create();
+ entry.SensesOS.Add(firstSense);
+ firstSense.MorphoSyntaxAnalysisRA = firstMsa;
+ secondMsa = m_stemMsaFactory.Create();
+ entry.MorphoSyntaxAnalysesOC.Add(secondMsa);
+ ILexSense secondSense = m_senseFactory.Create();
+ entry.SensesOS.Add(secondSense);
+ secondSense.MorphoSyntaxAnalysisRA = secondMsa;
+ return entry;
+ }
+
+ private static ParseMorph InvokeTryCreateParseMorph(LcmCache cache, XElement morphElem, out bool succeeded)
+ {
+ MethodInfo method = typeof(XAmpleParser).GetMethod("TryCreateParseMorph", BindingFlags.NonPublic | BindingFlags.Static);
+ var args = new object[] { cache, morphElem, null };
+ succeeded = (bool) method.Invoke(null, args);
+ return (ParseMorph) args[2];
+ }
+
#endregion // Non-tests
#region Setup and TearDown
@@ -522,6 +555,118 @@ public void LexEntryInflTypeTwoAnalyses()
}
}
+ /// ------------------------------------------------------------------------------------
+ ///
+ /// LT-22563: XAmple's MSI DbRef for an irregularly inflected variant is
+ /// "lexEntryHvo.refIndex.msaHvo" (see LT-22422). TryCreateParseMorph must resolve the
+ /// specific msaHvo from that DbRef; it must not fall back to just the variant's
+ /// main/first sense, since that can be a different MSA than XAmple actually chose
+ /// when the underlying entry has more than one MSA.
+ ///
+ /// ------------------------------------------------------------------------------------
+ [Test]
+ public void TryCreateParseMorph_IrregularVariantWithMultipleMsas_UsesMsaFromDbRef()
+ {
+ ILexEntry crebV = null;
+ IMoStemAllomorph crebVForm = null;
+ IMoStemMsa firstMsa = null;
+ IMoStemMsa secondMsa = null;
+
+ UndoableUnitOfWorkHelper.Do("Undo stuff", "Redo stuff", m_actionHandler, () =>
+ {
+ // 'seek' has two senses/MSAs; 'creb' is an irregularly inflected variant of it.
+ ILexEntryInflType inflType = m_lexEntryInflTypeFactory.Create();
+ Cache.LangProject.LexDbOA.VariantEntryTypesOA.PossibilitiesOS.Add(inflType);
+
+ ILexEntry seekV = CreateStemEntryWithTwoMsas("seekVTEST", out firstMsa, out secondMsa);
+
+ crebV = m_entryFactory.Create();
+ crebVForm = m_stemAlloFactory.Create();
+ crebV.AlternateFormsOS.Add(crebVForm);
+ crebVForm.Form.VernacularDefaultWritingSystem = TsStringUtils.MakeString("crebVTEST", m_vernacularWS.Handle);
+ ILexEntryRef lexEntryRef = m_lexEntryRefFactory.Create();
+ crebV.EntryRefsOS.Add(lexEntryRef);
+ lexEntryRef.ComponentLexemesRS.Add(seekV);
+ lexEntryRef.VariantEntryTypesRS.Add(inflType);
+ });
+
+ // Mimic what XAmple echoes back for this kind of variant: MoForm DbRef is the
+ // allomorph hvo, and MSI DbRef is "lexEntryHvo.refIndex.msaHvo", pointing
+ // specifically at the *second* MSA of 'seek'.
+ var morphElem = new XElement("Morph",
+ new XElement("MoForm", new XAttribute("DbRef", crebVForm.Hvo)),
+ new XElement("MSI", new XAttribute("DbRef", string.Format("{0}.0.{1}", crebV.Hvo, secondMsa.Hvo))));
+
+ bool succeeded;
+ ParseMorph morph = InvokeTryCreateParseMorph(Cache, morphElem, out succeeded);
+
+ Assert.That(succeeded, Is.True);
+ Assert.That(morph, Is.Not.Null);
+ Assert.That(morph.Msa.Hvo, Is.EqualTo(secondMsa.Hvo),
+ "Should use the MSA hvo encoded in the MSI DbRef, not just the variant's first/main sense");
+ Assert.That(morph.Msa.Hvo, Is.Not.EqualTo(firstMsa.Hvo));
+ }
+
+ /// ------------------------------------------------------------------------------------
+ ///
+ /// LT-22563: hardens the above test by giving the variant *two* EntryRefsOS (pointing
+ /// at two different base entries, each itself with two MSAs) and checking both possible
+ /// refIndex values. This guards both halves of the "lexEntryHvo.refIndex.msaHvo" DbRef:
+ /// picking the right LexEntryRef by index, and then the right MSA within that ref's
+ /// target entry -- not just the case where there is only one ref to pick from.
+ ///
+ /// ------------------------------------------------------------------------------------
+ [TestCase(0)]
+ [TestCase(1)]
+ public void TryCreateParseMorph_IrregularVariantWithMultipleEntryRefsAndMsas_UsesRefIndexAndMsaFromDbRef(int entryRefIndex)
+ {
+ ILexEntry crebV = null;
+ IMoStemAllomorph crebVForm = null;
+ IMoStemMsa believeSecondMsa = null;
+ IMoStemMsa seekSecondMsa = null;
+
+ UndoableUnitOfWorkHelper.Do("Undo stuff", "Redo stuff", m_actionHandler, () =>
+ {
+ ILexEntryInflType inflType = m_lexEntryInflTypeFactory.Create();
+ Cache.LangProject.LexDbOA.VariantEntryTypesOA.PossibilitiesOS.Add(inflType);
+
+ IMoStemMsa believeFirstMsa;
+ ILexEntry believeV = CreateStemEntryWithTwoMsas("believeVTEST2", out believeFirstMsa, out believeSecondMsa);
+ IMoStemMsa seekFirstMsa;
+ ILexEntry seekV = CreateStemEntryWithTwoMsas("seekVTEST2", out seekFirstMsa, out seekSecondMsa);
+
+ crebV = m_entryFactory.Create();
+ crebVForm = m_stemAlloFactory.Create();
+ crebV.AlternateFormsOS.Add(crebVForm);
+ crebVForm.Form.VernacularDefaultWritingSystem = TsStringUtils.MakeString("crebVTEST2", m_vernacularWS.Handle);
+
+ // EntryRefsOS[0] -> believeV, EntryRefsOS[1] -> seekV.
+ ILexEntryRef believeRef = m_lexEntryRefFactory.Create();
+ crebV.EntryRefsOS.Add(believeRef);
+ believeRef.ComponentLexemesRS.Add(believeV);
+ believeRef.VariantEntryTypesRS.Add(inflType);
+
+ ILexEntryRef seekRef = m_lexEntryRefFactory.Create();
+ crebV.EntryRefsOS.Add(seekRef);
+ seekRef.ComponentLexemesRS.Add(seekV);
+ seekRef.VariantEntryTypesRS.Add(inflType);
+ });
+
+ IMoStemMsa expectedMsa = entryRefIndex == 0 ? believeSecondMsa : seekSecondMsa;
+
+ var morphElem = new XElement("Morph",
+ new XElement("MoForm", new XAttribute("DbRef", crebVForm.Hvo)),
+ new XElement("MSI", new XAttribute("DbRef", string.Format("{0}.{1}.{2}", crebV.Hvo, entryRefIndex, expectedMsa.Hvo))));
+
+ bool succeeded;
+ ParseMorph morph = InvokeTryCreateParseMorph(Cache, morphElem, out succeeded);
+
+ Assert.That(succeeded, Is.True);
+ Assert.That(morph, Is.Not.Null);
+ Assert.That(morph.Msa.Hvo, Is.EqualTo(expectedMsa.Hvo),
+ string.Format("Should resolve EntryRefsOS[{0}] and the specific msa hvo encoded in the DbRef", entryRefIndex));
+ }
+
[Test]
public void LexEntryInflTypeAnalysisWithNullForSlotFiller()
{
diff --git a/Src/LexText/ParserCore/ParserXmlWriterExtensions.cs b/Src/LexText/ParserCore/ParserXmlWriterExtensions.cs
index 9172e2ed94..9cd0511f5f 100644
--- a/Src/LexText/ParserCore/ParserXmlWriterExtensions.cs
+++ b/Src/LexText/ParserCore/ParserXmlWriterExtensions.cs
@@ -18,7 +18,7 @@ namespace SIL.FieldWorks.WordWorks.Parser
{
internal static class ParserXmlWriterExtensions
{
- private static Tuple ProcessMsaHvo(string msaHvo)
+ public static Tuple ProcessMsaHvo(string msaHvo)
{
string[] msaHvoParts = msaHvo.Split('.');
// the msa hvo has one part or three parts separated by a period.
diff --git a/Src/LexText/ParserCore/XAmpleParser.cs b/Src/LexText/ParserCore/XAmpleParser.cs
index df7d2e0150..57bde6cbe2 100644
--- a/Src/LexText/ParserCore/XAmpleParser.cs
+++ b/Src/LexText/ParserCore/XAmpleParser.cs
@@ -245,9 +245,11 @@ private static bool TryCreateParseMorph(LcmCache cache, XElement morphElem, out
// required slots in affix templates. The parser filer can ignore these.
// 3. ().TryGetObject(int.Parse(formHvo), out objForm))
{
@@ -261,8 +263,9 @@ private static bool TryCreateParseMorph(LcmCache cache, XElement morphElem, out
return true;
}
- // Irregulary inflected forms can have a combination MSA hvo: the LexEntry hvo, a period, and an index to the LexEntryRef
- Tuple msaTuple = ProcessMsaHvo(msaHvo);
+ // Irregulary inflected forms can have a combination MSA hvo: the LexEntry hvo, a period, an index to the LexEntryRef,
+ // another period and the MSA hvo
+ Tuple msaTuple = ParserXmlWriterExtensions.ProcessMsaHvo(msaHvo);
ICmObject objMsa;
if (!cache.ServiceLocator.GetInstance().TryGetObject(msaTuple.Item1, out objMsa))
{
@@ -286,10 +289,19 @@ private static bool TryCreateParseMorph(LcmCache cache, XElement morphElem, out
ILexEntryRef lexEntryRef = msaAsLexEntry.EntryRefsOS[msaTuple.Item2];
if (lexEntryRef != null && lexEntryRef.ComponentLexemesRS.Count() > 0)
{
- // make sure there is at least one component lexeme (LT-22328)
- ILexSense sense = MorphServices.GetMainOrFirstSenseOfVariant(lexEntryRef);
var inflType = lexEntryRef.VariantEntryTypesRS[0] as ILexEntryInflType;
- morph = new ParseMorph(form, sense.MorphoSyntaxAnalysisRA, inflType);
+ IMoStemMsa stemMsa = (IMoStemMsa)cache.ServiceLocator.GetInstance().GetObject(msaTuple.Item3);
+ if (stemMsa != null)
+ {
+ // use the msa itself
+ morph = new ParseMorph(form, stemMsa, inflType);
+ }
+ else
+ {
+ // make sure there is at least one component lexeme (LT-22328)
+ ILexSense sense = MorphServices.GetMainOrFirstSenseOfVariant(lexEntryRef);
+ morph = new ParseMorph(form, sense.MorphoSyntaxAnalysisRA, inflType);
+ }
return true;
}
}
@@ -300,12 +312,6 @@ private static bool TryCreateParseMorph(LcmCache cache, XElement morphElem, out
return true;
}
- private static Tuple ProcessMsaHvo(string msaHvo)
- {
- string[] msaHvoParts = msaHvo.Split('.');
- return Tuple.Create(int.Parse(msaHvoParts[0]), msaHvoParts.Length == 2 ? int.Parse(msaHvoParts[1]) : 0);
- }
-
public XDocument ParseWordXml(string word)
{
CheckDisposed();