Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions Src/LexText/ParserCore/ParserCoreTests/ParseFilerProcessingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -99,6 +101,37 @@ protected void ExecuteIdleQueue()
m_idleQueue.Clear();
}

/// <summary>
/// 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.
/// </summary>
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
Expand Down Expand Up @@ -522,6 +555,118 @@ public void LexEntryInflTypeTwoAnalyses()
}
}

/// ------------------------------------------------------------------------------------
/// <summary>
/// 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.
/// </summary>
/// ------------------------------------------------------------------------------------
[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));
}

/// ------------------------------------------------------------------------------------
/// <summary>
/// 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.
/// </summary>
/// ------------------------------------------------------------------------------------
[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()
{
Expand Down
2 changes: 1 addition & 1 deletion Src/LexText/ParserCore/ParserXmlWriterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace SIL.FieldWorks.WordWorks.Parser
{
internal static class ParserXmlWriterExtensions
{
private static Tuple<int, int, int> ProcessMsaHvo(string msaHvo)
public static Tuple<int, int, int> ProcessMsaHvo(string msaHvo)
{
string[] msaHvoParts = msaHvo.Split('.');
// the msa hvo has one part or three parts separated by a period.
Expand Down
34 changes: 20 additions & 14 deletions Src/LexText/ParserCore/XAmpleParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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. <MSI DbRef="y"... and y is an hvo for a LexEntry.
// The LexEntry is a variant form for the first set of LexEntryRefs.
// 4. <MSI DbRef="y"... and y is an hvo for a LexEntry followed by a period and an index digit.
// The LexEntry is a variant form and the (non-zero) index indicates
// which set of LexEntryRefs it is for.
// 4. <MSI DbRef="y.i.z"... and y is an hvo for a LexEntry, i is an index to the
// LexEntryRef, and z is the hvo of the specific MSA (of that LexEntryRef's
// target entry) that was used. The LexEntry is a variant form; i indicates
// which set of LexEntryRefs it is for, and z indicates which of that entry's
// MSAs to use, since it can have more than one (see LT-22422/LT-22563).
ICmObject objForm;
if (!cache.ServiceLocator.GetInstance<ICmObjectRepository>().TryGetObject(int.Parse(formHvo), out objForm))
{
Expand All @@ -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<int, int> 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<int, int, int> msaTuple = ParserXmlWriterExtensions.ProcessMsaHvo(msaHvo);
Comment thread
AndyBlack marked this conversation as resolved.
ICmObject objMsa;
if (!cache.ServiceLocator.GetInstance<ICmObjectRepository>().TryGetObject(msaTuple.Item1, out objMsa))
{
Expand All @@ -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<ICmObjectRepository>().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;
}
}
Expand All @@ -300,12 +312,6 @@ private static bool TryCreateParseMorph(LcmCache cache, XElement morphElem, out
return true;
}

private static Tuple<int, int> 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();
Expand Down
Loading