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"); }