diff --git a/src/SIL.LCModel/DomainImpl/OverridesCellar.cs b/src/SIL.LCModel/DomainImpl/OverridesCellar.cs
index 40599fe1..e32fa6d0 100644
--- a/src/SIL.LCModel/DomainImpl/OverridesCellar.cs
+++ b/src/SIL.LCModel/DomainImpl/OverridesCellar.cs
@@ -2247,8 +2247,9 @@ public ITsString GetFeatureValueString(bool fLongForm)
var tisb = TsStringUtils.MakeIncStrBldr();
tisb.SetIntPropValues((int)FwTextPropType.ktptWs, (int)FwTextPropVar.ktpvDefault, Cache.DefaultUserWs);
- var sFeature = GetFeatureString(fLongForm);
- var sValue = GetValueString(fLongForm);
+ // ValueRA == null is an empty value that is filled in by FillInBlanks.
+ var sFeature = GetFeatureString(fLongForm || ValueRA == null);
+ var sValue = ValueRA == null ? "*" : GetValueString(fLongForm);
if ((!fLongForm) &&
(FeatureRA != null) &&
(FeatureRA.DisplayToRightOfValues))
@@ -3871,8 +3872,8 @@ from myitem in FeatureSpecsOC
else
{
if (myFeatureValues.First() is IFsComplexValue complex &&
- spec is IFsComplexValue newComplexValue &&
- complex.ValueOA is IFsFeatStruc fs)
+ spec is IFsComplexValue newComplexValue &&
+ complex.ValueOA is IFsFeatStruc fs)
{
fs.PriorityUnion(newComplexValue.ValueOA as IFsFeatStruc);
}
@@ -3885,6 +3886,87 @@ spec is IFsComplexValue newComplexValue &&
}
}
+
+ ///
+ /// Fill in the blanks.
+ /// Remove unfilled blanks and empty feature structures.
+ ///
+ /// the values to fill with
+ public IFsFeatStruc FillInBlanks(IFsFeatStruc fsValue)
+ {
+ foreach (IFsFeatureSpecification spec in FeatureSpecsOC.ToList())
+ {
+ // Find matching specification.
+ IFsFeatureSpecification spec2 = null;
+ if (fsValue != null)
+ {
+ foreach (var spec3 in fsValue.FeatureSpecsOC)
+ {
+ if (spec3.FeatureRA.Name == spec.FeatureRA.Name)
+ {
+ spec2 = spec3;
+ }
+ }
+ }
+ // Fill in blanks in spec.
+ if (spec is IFsClosedValue closed && closed != null && closed.ValueRA == null)
+ {
+ if (spec2 is IFsClosedValue closed2 && closed2 != null)
+ {
+ // Fill in blank.
+ closed.ValueRA = closed2.ValueRA;
+
+ }
+ if (closed.ValueRA == null)
+ {
+ // Remove unfilled blank.
+ FeatureSpecsOC.Remove(spec);
+ }
+ }
+ if (spec is IFsComplexValue complex && complex != null)
+ {
+ // Recursively fill in blanks.
+ if (complex.ValueOA is IFsFeatStruc fs)
+ {
+ IFsComplexValue complex2 = spec2 as IFsComplexValue;
+ if (fs.FillInBlanks(complex2?.ValueOA as IFsFeatStruc) == null)
+ {
+ // Remove empty feature structure.
+ FeatureSpecsOC.Remove(complex);
+ }
+ }
+ }
+ }
+ // See if removing blanks emptied the feature structure.
+ if (FeatureSpecsOC.Count == 0)
+ {
+ return null;
+ }
+ return this;
+ }
+
+ public bool ContainsBlank()
+ {
+ foreach (IFsFeatureSpecification spec in FeatureSpecsOC.ToList())
+ {
+ if (spec is IFsClosedValue closed && closed != null && closed.ValueRA == null)
+ {
+ return true;
+ }
+ if (spec is IFsComplexValue complex && complex != null)
+ {
+ if (complex.ValueOA is IFsFeatStruc fs)
+ {
+ if (fs.ContainsBlank())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
protected override void RemoveObjectSideEffectsInternal(RemoveObjectEventArgs e)
{
switch (e.Flid)
diff --git a/src/SIL.LCModel/InterfaceAdditions.cs b/src/SIL.LCModel/InterfaceAdditions.cs
index d10bd041..e0de10d8 100644
--- a/src/SIL.LCModel/InterfaceAdditions.cs
+++ b/src/SIL.LCModel/InterfaceAdditions.cs
@@ -2323,6 +2323,19 @@ ITsString LongNameSortedTSS
///
/// the new feature structure
void PriorityUnion(IFsFeatStruc fsNew);
+
+ ///
+ /// Fill in the blanks of this feature structure
+ /// using the given values.
+ /// Eliminates blanks with no values and returns null if there is nothing left.
+ ///
+ /// feature structure with given values
+ IFsFeatStruc FillInBlanks(IFsFeatStruc fsValues);
+
+ ///
+ /// Determine if this feature structure contains a blank.
+ ///
+ public bool ContainsBlank();
}
///
diff --git a/tests/SIL.LCModel.Tests/DomainImpl/CellarTests.cs b/tests/SIL.LCModel.Tests/DomainImpl/CellarTests.cs
index abc0ba07..b232f3c4 100644
--- a/tests/SIL.LCModel.Tests/DomainImpl/CellarTests.cs
+++ b/tests/SIL.LCModel.Tests/DomainImpl/CellarTests.cs
@@ -459,6 +459,22 @@ public void AddClosedFeaturesToFeatureSystemAndThenToAFeatureStructure()
Assert.AreEqual("Gen", cv.FeatureRA.Abbreviation.AnalysisDefaultWritingSystem.Text, "Expect to have Gen feature name");
Assert.AreEqual("Neut", cv.ValueRA.Abbreviation.AnalysisDefaultWritingSystem.Text, "Expect to have Neut feature value");
}
+
+ // Test FillInBlanks.
+ IFsClosedValue closedValue = featStruct.FeatureSpecsOC.First() as IFsClosedValue;
+ closedValue.ValueRA = null;
+ Assert.AreEqual("Gen:*", featStruct.ShortName);
+ Assert.IsTrue(featStruct.ContainsBlank());
+ Assert.IsFalse(featStrucGenNeut.ContainsBlank());
+ featStruct = featStruct.FillInBlanks(featStrucGenNeut);
+ Assert.IsFalse(featStruct.ContainsBlank());
+ Assert.AreEqual("Agr", featStruct.TypeRA.Abbreviation.AnalysisDefaultWritingSystem.Text, "Expect type Agr");
+ Assert.AreEqual(1, featStruct.FeatureSpecsOC.Count, "should have one feature spec");
+ foreach (IFsClosedValue cv in featStruct.FeatureSpecsOC)
+ {
+ Assert.AreEqual("Gen", cv.FeatureRA.Abbreviation.AnalysisDefaultWritingSystem.Text, "Expect to have Gen feature name");
+ Assert.AreEqual("Neut", cv.ValueRA.Abbreviation.AnalysisDefaultWritingSystem.Text, "Expect to have Neut feature value");
+ }
}
/// ------------------------------------------------------------------------------------
@@ -667,6 +683,31 @@ public void AddComplexFeaturesToFeatureSystemAndThenToAFeatureStructure()
// Check for correct LongName
Assert.AreEqual("[asp:aor sbj:[gen:n num:sg pers:1]]", featStruct.LongName, "Incorrect LongName for merged feature struture");
Assert.AreEqual("[asp:aor sbj:[gen:n num:sg pers:1]]", featStruct.LongNameSorted, "Incorrect LongNameSorted for merged feature struture");
+
+ // Test FillInBlanks.
+ pos.DefaultFeaturesOA = null;
+ pos.DefaultFeaturesOA = Cache.ServiceLocator.GetInstance().Create();
+ featStruct = pos.DefaultFeaturesOA;
+ featStruct.AddFeatureFromXml(itemFem, msfs);
+ IFsComplexValue complexValue = featStruct.FeatureSpecsOC.First() as IFsComplexValue;
+ IFsFeatStruc fsValue = complexValue.ValueOA as IFsFeatStruc;
+ IFsClosedValue closedValue = fsValue.FeatureSpecsOC.First() as IFsClosedValue;
+ closedValue.ValueRA = null;
+ Assert.AreEqual("gen:*", featStruct.ShortName);
+ Assert.IsTrue(featStruct.ContainsBlank());
+ Assert.IsFalse(featStruct2.ContainsBlank());
+ featStruct = featStruct.FillInBlanks(featStruct2);
+ Assert.IsFalse(featStruct2.ContainsBlank());
+ Assert.AreEqual("[sbj:[gen:n]]", featStruct.LongName, "Incorrect LongName for merged feature struture");
+
+ // Test removing FillInBlanks.
+ closedValue.ValueRA = null;
+ featStruct2.FeatureSpecsOC.Remove(featStruct2.FeatureSpecsOC.First());
+ Assert.AreEqual("gen:*", featStruct.ShortName);
+ Assert.IsTrue(featStruct.ContainsBlank());
+ Assert.IsFalse(featStruct2.ContainsBlank());
+ featStruct = featStruct.FillInBlanks(featStruct2);
+ Assert.AreEqual(null, featStruct, "FillInBlanks didn't remove unfilled blanks");
}