// ExportMorphTypes rules go above this line.

		private static XElement ExportPhonologicalData(IPhPhonData phonologicalData, Icu.UNormalizationMode mode)
		{
			return new XElement("PhPhonData",
								new XAttribute("Id", phonologicalData.Hvo),
								new XElement("Environments",
									from goodEnvironment in phonologicalData.Services.GetInstance<IPhEnvironmentRepository>().AllValidInstances()
									select new XElement("PhEnvironment",
										new XAttribute("Id", goodEnvironment.Hvo),
										new XAttribute("StringRepresentation",
											Normalize(goodEnvironment.StringRepresentation, mode)),
										CreateAttribute("LeftContext", goodEnvironment.LeftContextRA),
										CreateAttribute("RightContext", goodEnvironment.RightContextRA),
										ExportBestAnalysis(goodEnvironment.Name, "Name", mode),
										ExportBestAnalysis(goodEnvironment.Description, "Description", mode))),
								new XElement("NaturalClasses", from naturalClass in phonologicalData.NaturalClassesOS
																select ExportNaturalClass(naturalClass, mode)),
								new XElement("Contexts", from context in phonologicalData.ContextsOS
														  select ExportContext(context)),
								new XElement("PhonemeSets", from phonemeSet in phonologicalData.PhonemeSetsOS
															 select ExportPhonemeSet(phonemeSet, mode)),
								new XElement("FeatureConstraints", from featureConstraint in phonologicalData.FeatConstraintsOS
																 select ExportFeatureConstraint(featureConstraint)),
								new XElement("PhonRules", from phonRule in phonologicalData.PhonRulesOS
											where !phonRule.Disabled
											select ExportPhonRule(phonRule, mode)),
								ExportPhonRuleFeats(phonologicalData, mode),
							   new XElement("PhIters"),
							   new XElement("PhIters"),
							   new XElement("PhIters"),
							   new XElement("PhIters"),
							   new XElement("PhIters"),
							   new XElement("PhIters"));
		}
		private static XElement ExportPhonRuleFeats(IPhPhonData phonData, Icu.UNormalizationMode mode)
		{
			return new XElement("PhonRuleFeats",
				from phonRuleFeat in phonData.PhonRuleFeatsOA.ReallyReallyAllPossibilities
				select ExportPhonRuleFeat(phonRuleFeat as IPhPhonRuleFeat, mode));
		}
        public void ParserDataChanges()
        {
            XmlNode node;

#if !ShowDumpResult
            m_fxtResult.Save(Path.Combine(System.IO.Path.GetTempPath(), "TestFxtUpdateBefore.xml"));
#endif
            // -------------
            // Make data changes
            // -------------
            // Make a change to stem allomorph
            ILangProject lp           = Cache.LangProject;
            ILexDb       lexdb        = lp.LexDbOA;
            int[]        aiLexEntries = lexdb.EntriesOC.HvoArray;
            int          hvoLexEntry  = aiLexEntries[0];
            ILexEntry    lexEntry     = CmObject.CreateFromDBObject(Cache, hvoLexEntry) as ILexEntry;
            Assert.IsNotNull(lexEntry);
            IMoStemAllomorph stemAllomorph = lexEntry.LexemeFormOA as IMoStemAllomorph;
            Assert.IsNotNull(stemAllomorph);
            stemAllomorph.Form.SetAlternative("bili-changed", Cache.DefaultVernWs);
            int hvoStemAllomorph = stemAllomorph.Hvo;
            stemAllomorph.IsAbstract = true;

            // Delete an affix allomorph
            hvoLexEntry = aiLexEntries[3];
            lexEntry    = CmObject.CreateFromDBObject(Cache, hvoLexEntry) as ILexEntry;
            Assert.IsNotNull(lexEntry);
            IMoAffixAllomorph affixAllomorph = lexEntry.AlternateFormsOS[1] as IMoAffixAllomorph;
            Assert.IsNotNull(affixAllomorph);
            int hvoAffixAllomorph = affixAllomorph.Hvo;
            lexEntry.AlternateFormsOS.RemoveAt(1);
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, hvoLexEntry, (int)LexEntry.LexEntryTags.kflidAlternateForms, 1, 0, 1);

            // Add a new affix allomorph
            IMoAffixAllomorph newAffixAllomorph = new MoAffixAllomorph();
            lexEntry.AlternateFormsOS.Append(newAffixAllomorph);
            newAffixAllomorph.Form.SetAlternative("him-new", Cache.DefaultVernWs);
            int hvoNewAffixAllomorph = newAffixAllomorph.Hvo;
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, hvoLexEntry, (int)LexEntry.LexEntryTags.kflidAlternateForms, lexEntry.AlternateFormsOS.Count - 1, 1, 0);

            // add a compound rule
            IMoMorphData    morphData   = lp.MorphologicalDataOA;
            IMoEndoCompound compRuleNew = new MoEndoCompound();
            morphData.CompoundRulesOS.Append(compRuleNew);
            string sCompRuleName = "new compound rule";
            compRuleNew.Name.AnalysisDefaultWritingSystem = sCompRuleName;
            compRuleNew.HeadLast = true;
            int hvoPOS = lp.PartsOfSpeechOA.PossibilitiesOS.FirstItem.Hvo;
            compRuleNew.LeftMsaOA.PartOfSpeechRAHvo       = hvoPOS;
            compRuleNew.RightMsaOA.PartOfSpeechRAHvo      = hvoPOS;
            compRuleNew.OverridingMsaOA.PartOfSpeechRAHvo = hvoPOS;
            // Change compound rule description
            const string ksCompRuleDescription = "new description";
            compRuleNew.Description.AnalysisDefaultWritingSystem.Text = ksCompRuleDescription;
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, morphData.Hvo, (int)MoMorphData.MoMorphDataTags.kflidCompoundRules, morphData.CompoundRulesOS.Count - 1, 1, 0);

            // delete a compound rule
            IMoExoCompound compRuleDeleted            = morphData.CompoundRulesOS.FirstItem as IMoExoCompound;
            int            hvoCompRuleDeletedLeftMsa  = compRuleDeleted.LeftMsaOAHvo;
            int            hvoCompRuleDeletedRightMsa = compRuleDeleted.RightMsaOAHvo;
            int            hvoCompRuleDeletedToMsa    = compRuleDeleted.ToMsaOAHvo;
            morphData.CompoundRulesOS.RemoveAt(0);

            // add an ad hoc co-prohibition
            IMoAlloAdhocProhib alloAdHoc = new MoAlloAdhocProhib();
            morphData.AdhocCoProhibitionsOC.Add(alloAdHoc);
            alloAdHoc.Adjacency           = 2;
            alloAdHoc.FirstAllomorphRAHvo = hvoNewAffixAllomorph;
            alloAdHoc.RestOfAllosRS.Append(hvoNewAffixAllomorph);

            // change a "rest of allos" in extant ad hoc co-prohibition
            int[] hvosAdHocProhibs          = morphData.AdhocCoProhibitionsOC.HvoArray;
            IMoAlloAdhocProhib alloAdHocOld =
                CmObject.CreateFromDBObject(Cache, hvosAdHocProhibs[9]) as IMoAlloAdhocProhib;
            IMoAffixAllomorph alloAdHicOldFirstRestOfAllos = alloAdHocOld.RestOfAllosRS.FirstItem as IMoAffixAllomorph;
            IMoAffixAllomorph affixAllomorph2 = lexEntry.AlternateFormsOS[0] as IMoAffixAllomorph;
            alloAdHocOld.RestOfAllosRS.Append(affixAllomorph2);
            alloAdHocOld.RestOfAllosRS.RemoveAt(0);
            alloAdHocOld.Adjacency = 2;

            //Add a new productivity restriction
            ICmPossibilityList prodRestricts   = morphData.ProdRestrictOA;
            ICmPossibility     prodRestriction = new CmPossibility();
            prodRestricts.PossibilitiesOS.Append(prodRestriction);
            string sNewProdRestrictName = "new exception feature";
            prodRestriction.Name.AnalysisDefaultWritingSystem = sNewProdRestrictName;
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, prodRestricts.Hvo, (int)CmPossibilityList.CmPossibilityListTags.kflidPossibilities, prodRestricts.PossibilitiesOS.Count - 1, 1, 0);

            // Change a phonological enviroment string representation
            IPhPhonData    phonData       = lp.PhonologicalDataOA;
            IPhEnvironment env            = phonData.EnvironmentsOS.FirstItem;
            const string   ksEnvStringRep = "/ _ [C] [V] a e i o u";
            env.StringRepresentation.Text = ksEnvStringRep;

            // Add a new phonological enviroment string representation
            IPhEnvironment envNew = new PhEnvironment();
            phonData.EnvironmentsOS.Append(envNew);
            envNew.StringRepresentation.Text = "/ _ m";
            int hvoPhonData = phonData.Hvo;
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, hvoPhonData, (int)PhPhonData.PhPhonDataTags.kflidEnvironments, phonData.EnvironmentsOS.Count - 1, 1, 0);

            // Change parser parameters (to test Unicode string field type)
            string sParserParameters = morphData.ParserParameters.Trim();
            int    i = sParserParameters.IndexOf("</ParserParameters>");
            string sNewParserParameters = sParserParameters.Substring(0, i) + "<HermitCrab><stuff>1</stuff></HermitCrab>" + "</ParserParameters>";
            morphData.ParserParameters = sNewParserParameters;

            // Delete a lex entry
            int[]     hvosEntries                    = lexdb.EntriesOC.HvoArray;
            int       hvoEntryDeleted                = hvosEntries[hvosEntries.Length - 4];
            ILexEntry entryDeleted                   = CmObject.CreateFromDBObject(Cache, hvoEntryDeleted) as ILexEntry;
            int       hvoEntryDeletedLexemeForm      = entryDeleted.LexemeFormOAHvo;
            int[]     hvosEntryDeletedAlternateForms = entryDeleted.AlternateFormsOS.HvoArray;
            int[]     hvosEntryDeletedMSAs           = entryDeleted.MorphoSyntaxAnalysesOC.HvoArray;
            int[]     hvosEntryDeletedSenses         = entryDeleted.SensesOS.HvoArray;
            //entryDeleted.LexemeFormOA.DeleteUnderlyingObject();
            lexdb.EntriesOC.Remove(hvosEntries[hvosEntries.Length - 4]);
            //Cache.PropChanged(null, PropChangeType.kpctNotifyAll, morphData.Hvo, (int)MoMorphData.MoMorphDataTags.kflidParserParameters, 0, 0, 0);

            // Create a new lex entry
            ILexEntry entryNew = new LexEntry();
            lexdb.EntriesOC.Add(entryNew);

            IMoAffixAllomorph alloNew = new MoAffixAllomorph();
            entryNew.LexemeFormOA = alloNew;
            string sNewAlloForm = "dem";
            alloNew.Form.VernacularDefaultWritingSystem = sNewAlloForm;
            alloNew.MorphTypeRA = (IMoMorphType)lexdb.MorphTypesOA.LookupPossibilityByGuid(new Guid(MoMorphType.kguidMorphPrefix));

            IMoAffixAllomorph alloNew2 = new MoAffixAllomorph();
            entryNew.AlternateFormsOS.Append(alloNew2);
            string sNewAlloForm2 = "den";
            alloNew2.Form.VernacularDefaultWritingSystem = sNewAlloForm2;
            alloNew2.MorphTypeRA = (IMoMorphType)lexdb.MorphTypesOA.LookupPossibilityByGuid(new Guid(MoMorphType.kguidMorphPrefix));
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, entryNew.Hvo, (int)LexEntry.LexEntryTags.kflidAlternateForms, entryNew.AlternateFormsOS.Count - 1, 1, 0);

            ILexSense sense = new LexSense();
            entryNew.SensesOS.Append(sense);
            string sGloss = "MeToo";
            sense.Gloss.AnalysisDefaultWritingSystem = sGloss;

            IMoInflAffMsa inflAffixMsa = new MoInflAffMsa();
            entryNew.MorphoSyntaxAnalysesOC.Add(inflAffixMsa);
            sense.MorphoSyntaxAnalysisRA = inflAffixMsa;
            int[] hvosPOSes = lp.PartsOfSpeechOA.PossibilitiesOS.HvoArray;
            int   hvoVerb   = hvosPOSes[12];
            inflAffixMsa.PartOfSpeechRAHvo = hvoVerb;
            IPartOfSpeech pos     = CmObject.CreateFromDBObject(Cache, hvoVerb) as IPartOfSpeech;
            int           hvoSlot = pos.AffixSlotsOC.HvoArray[2];
            inflAffixMsa.SlotsRC.Add(hvoSlot);
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, entryNew.Hvo, (int)LexEntry.LexEntryTags.kflidSenses, entryNew.SensesOS.Count - 1, 1, 0);

            // Add an inflectional template
            int[]                hvoVerbSubCats = pos.SubPossibilitiesOS.HvoArray;
            int                  hvoIntransVerb = hvoVerbSubCats[2];
            IPartOfSpeech        posVI          = CmObject.CreateFromDBObject(Cache, hvoIntransVerb) as IPartOfSpeech;
            IMoInflAffixTemplate affixTemplate  = new MoInflAffixTemplate();
            posVI.AffixTemplatesOS.Append(affixTemplate);
            affixTemplate.Name.AnalysisDefaultWritingSystem = "derived verb";
            affixTemplate.Final = false;
            affixTemplate.SuffixSlotsRS.Append(hvoSlot);
            Cache.PropChanged(null, PropChangeType.kpctNotifyAll, posVI.Hvo, (int)PartOfSpeech.PartOfSpeechTags.kflidAffixTemplates, posVI.AffixTemplatesOS.Count - 1, 1, 0);

            // add a phonological feature
            IFsClosedFeature consFeat = new FsClosedFeature();
            Cache.LangProject.PhFeatureSystemOA.FeaturesOC.Add(consFeat);
            consFeat.Name.AnalysisDefaultWritingSystem         = "consonantal";
            consFeat.Abbreviation.AnalysisDefaultWritingSystem = "cons";
            IFsSymFeatVal consPlus = new FsSymFeatVal();
            consFeat.ValuesOC.Add(consPlus);
            consPlus.SimpleInit("+", "positive");
            IFsSymFeatVal consMinus = new FsSymFeatVal();
            consFeat.ValuesOC.Add(consMinus);
            consMinus.SimpleInit("-", "negative");
            IFsFeatStrucType fsType = null;
            if (Cache.LangProject.PhFeatureSystemOA.TypesOC.Count == 0)
            {
                fsType = new FsFeatStrucType();
                Cache.LangProject.PhFeatureSystemOA.TypesOC.Add(fsType);
                fsType.Abbreviation.AnalysisDefaultWritingSystem = "Phon";
            }
            else
            {
                foreach (IFsFeatStrucType type in Cache.LangProject.PhFeatureSystemOA.TypesOC)
                {
                    fsType = type;
                    break;
                }
            }
            fsType.FeaturesRS.Append(consFeat);

            // add a feature-based NC
            IPhNCFeatures featNC = new PhNCFeatures();
            Cache.LangProject.PhonologicalDataOA.NaturalClassesOS.Append(featNC);
            featNC.Name.AnalysisDefaultWritingSystem         = "Consonants (Features)";
            featNC.Abbreviation.AnalysisDefaultWritingSystem = "CF";
            IFsFeatStruc fs = new FsFeatStruc();
            featNC.FeaturesOA = fs;
            IFsClosedValue val = fs.FindOrCreateClosedValue(consFeat.Hvo);
            val.FeatureRA = consFeat;
            val.ValueRA   = consPlus;
            featNC.NotifyNew();

            // add phonological rule
            IPhRegularRule regRule = new PhRegularRule();
            Cache.LangProject.PhonologicalDataOA.PhonRulesOS.Append(regRule);
            regRule.NotifyNew();
            regRule.Name.AnalysisDefaultWritingSystem = "regular rule";
            IPhSimpleContextSeg segCtxt = new PhSimpleContextSeg();
            regRule.RightHandSidesOS[0].StrucChangeOS.Append(segCtxt);
            IPhPhoneme phoneme = null;
            foreach (IPhPhoneme phon in Cache.LangProject.PhonologicalDataOA.PhonemeSetsOS[0].PhonemesOC)
            {
                phoneme = phon;
                break;
            }
            segCtxt.FeatureStructureRA = phoneme;
            segCtxt.NotifyNew();

            IPhSimpleContextNC ncCtxt = new PhSimpleContextNC();
            regRule.RightHandSidesOS[0].LeftContextOA = ncCtxt;
            ncCtxt.FeatureStructureRA = featNC;
            ncCtxt.NotifyNew();

            // add a morphological rule
            IMoAffixProcess affRule = new MoAffixProcess();
            entryNew.AlternateFormsOS.Append(affRule);
            affRule.NotifyNew();
            ncCtxt = new PhSimpleContextNC();
            affRule.InputOS.Append(ncCtxt);
            ncCtxt.FeatureStructureRA = featNC;
            ncCtxt.NotifyNew();
            IMoCopyFromInput copy = new MoCopyFromInput();
            affRule.OutputOS.Append(copy);
            copy.ContentRA = ncCtxt;
            copy.NotifyNew();

            // -----------
            // Update the FXT result
            // -----------
            XmlDocument updatedFxtResult = UpdateFXT();

            // -----------
            // Test the updated results
            // -----------

            // Test changed stem allomorph: checks on MultiUnicode and boolean
            node = updatedFxtResult.SelectSingleNode("//MoStemAllomorph[@Id='" + hvoStemAllomorph + "']");
            Assert.IsNotNull(node);
            Assert.AreEqual(stemAllomorph.Form.VernacularDefaultWritingSystem, node.InnerText, "stem allomorph form change failed");
            XmlNode contentNode = node.SelectSingleNode("@IsAbstract");
            Assert.AreEqual("1", contentNode.InnerText, "stem allomorph is abstract should be true (=1)");

            // Test deleted affix allomorph: checks on owning sequence
            node = updatedFxtResult.SelectSingleNode("//MoAffixAllomorph[@Id='" + hvoAffixAllomorph + "']");
            Assert.IsNull(node, "Deleted affix allomorph should be null");
            node =
                updatedFxtResult.SelectSingleNode("//LexEntry[@id='" + hvoLexEntry + "']/AlternateForms[@dst='" +
                                                  hvoAffixAllomorph + "']");
            Assert.IsNull(node, "LexEntry should no longer have deleted alternate form");

            // Test added new affix allomorph: checks on owning sequence owned by an item with an @Id; also checks on addition of MoAffixAllomorph via AllAllomorphs
            string sXPath = "//LexEntry[@Id='" + hvoLexEntry + "']/AlternateForms[@dst='" +
                            hvoNewAffixAllomorph + "']";
            node = updatedFxtResult.SelectSingleNode(sXPath);
            Assert.IsNotNull(node, "LexEntry should have added alternate form");
            node = updatedFxtResult.SelectSingleNode("//MoAffixAllomorph[@Id='" + hvoNewAffixAllomorph + "']");
            Assert.IsNotNull(node, "Added affix allomorph should be present");
            sXPath = "//LexEntry[@Id='" + hvoLexEntry + "']";
            node   = updatedFxtResult.SelectSingleNode(sXPath);
            XmlNodeList nodes = node.SelectNodes("AlternateForms");
            Assert.AreEqual(3, nodes.Count, "Expected three Alternate forms in lex entry.");

            //Test newly added compound rule: checks on owning sequence owned by an Id-less element; also on multistring
            node = updatedFxtResult.SelectSingleNode("//MoEndoCompound[@Id='" + compRuleNew.Hvo + "']");
            Assert.IsNotNull(node, "did not find newly added compound rule");
            contentNode = node.SelectSingleNode("@HeadLast");
            Assert.IsNotNull(contentNode, "missing headlast attribute for coompound rule");
            Assert.AreEqual("1", contentNode.InnerText, "compound rule headlast value differs");
            contentNode = node.SelectSingleNode("Name");
            Assert.IsNotNull(contentNode, "missing Name for compound rule");
            Assert.AreEqual(sCompRuleName, contentNode.InnerText, "compound rule name differs");
            // check on MultiString
            contentNode = node.SelectSingleNode("Description");
            Assert.AreEqual(ksCompRuleDescription, contentNode.InnerText, "compound rule description differs");
            // check on count
            node  = updatedFxtResult.SelectSingleNode("//CompoundRules");
            nodes = node.SelectNodes("MoExoCompound | MoEndoCompound");
            Assert.AreEqual(6, nodes.Count, "Expected seven compound rules.");
            // check on owningAtom
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + compRuleNew.LeftMsaOAHvo + "']");
            Assert.IsNotNull(node, "missing real MoStemMsa for LeftMsa of newly added compound rule");
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + compRuleNew.RightMsaOAHvo + "']");
            Assert.IsNotNull(node, "missing real MoStemMsa for RightMsa of newly added compound rule");
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + compRuleNew.OverridingMsaOAHvo + "']");
            Assert.IsNotNull(node, "missing real MoStemMsa for OverridingMsa of newly added compound rule");

            // Test deleted compound rule
            node = updatedFxtResult.SelectSingleNode("//MoExoCompound[@Id='" + compRuleDeleted.Hvo + "']");
            Assert.IsNull(node, "compound rule should be deleted");
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + hvoCompRuleDeletedLeftMsa + "']");
            Assert.IsNull(node, "compound rule left MSA should be deleted");
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + hvoCompRuleDeletedRightMsa + "']");
            Assert.IsNull(node, "compound rule right MSA should be deleted");
            node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + hvoCompRuleDeletedToMsa + "']");
            Assert.IsNull(node, "compound rule to MSA should be deleted");

            //Test newly added allomorph ad hoc rule: checks on owning collection
            node = updatedFxtResult.SelectSingleNode("//MoAlloAdhocProhib[@Id='" + alloAdHoc.Hvo + "']");
            Assert.IsNotNull(node, "did not find newly added allo ad hoc rule");
            contentNode = node.SelectSingleNode("@Adjacency");
            Assert.IsNotNull(contentNode, "missing adjacency attribute for allo ad hoc rule");
            Assert.AreEqual("2", contentNode.InnerText, "allo ad hoc rule adjacency value differs");
            contentNode = node.SelectSingleNode("FirstAllomorph");
            Assert.IsNotNull(contentNode, "missing FirstAllomorph for allo ad hoc rule");
            contentNode = contentNode.SelectSingleNode("@dst");
            Assert.IsNotNull(contentNode, "missing dst attribute of FirstAllomorph for allo ad hoc rule");
            Assert.AreEqual(hvoNewAffixAllomorph.ToString(), contentNode.InnerText, "FirstAllomorph of allo ad hoc rule differs");
            contentNode = node.SelectSingleNode("RestOfAllos");
            Assert.IsNotNull(contentNode, "missing RestOfAllos for allo ad hoc rule");
            contentNode = contentNode.SelectSingleNode("@dst");
            Assert.IsNotNull(contentNode, "missing dst attribute of RestOfAllos for allo ad hoc rule");
            Assert.AreEqual(hvoNewAffixAllomorph.ToString(), contentNode.InnerText, "RestOfAllos of allo ad hoc rule differs");

            // test change of a "rest of allos" in extant ad hoc co-prohibition: check on reference sequence
            node = updatedFxtResult.SelectSingleNode("//MoAlloAdhocProhib[@Id='" + alloAdHocOld.Hvo + "']");
            Assert.IsNotNull(node, "did not find old allo ad hoc rule");
            contentNode = node.SelectSingleNode("RestOfAllos");
            Assert.IsNotNull(contentNode, "missing RestOfAllos for old allo ad hoc rule");
            contentNode = contentNode.SelectSingleNode("@dst");
            Assert.IsNotNull(contentNode, "missing dst attribute of RestOfAllos for old allo ad hoc rule");
            Assert.AreEqual(affixAllomorph2.Hvo.ToString(), contentNode.InnerText, "RestOfAllos of old allo ad hoc rule differs");
            nodes = node.SelectNodes("RestOfAllos");
            Assert.AreEqual(1, nodes.Count, "count of RestOfAllos of old allo ad hoc rule differs");
            // check on integer change
            contentNode = node.SelectSingleNode("@Adjacency");
            Assert.AreEqual("2", contentNode.InnerText, "Adjacency differs");
            node =
                updatedFxtResult.SelectSingleNode("//MoAffixAllomorph[@Id='" + alloAdHicOldFirstRestOfAllos.Hvo + "']");
            Assert.IsNotNull(node, "Original RestOfAllos allomorph should still be present");
            nodes = updatedFxtResult.SelectNodes("//MoAffixAllomorph[@Id='" + affixAllomorph2.Hvo + "']");
            Assert.AreEqual(1, nodes.Count, "Should only be one instance of new allomorph in RestOfAllos");


            // Test added productivity restriction: check on CmPossibilityList
            node = updatedFxtResult.SelectSingleNode("//ProdRestrict/CmPossibility");
            Assert.IsNotNull(node, "Did not find newly added productivity restriction");
            node = node.SelectSingleNode("Name");
            Assert.IsNotNull(node, "Expected Name node in productivity restrictioni");
            Assert.AreEqual(sNewProdRestrictName, node.InnerText, "name of productivity restriction differs");

            // Test phonological environment string representation: check on string
            node = updatedFxtResult.SelectSingleNode("//PhEnvironment[@Id='" + env.Hvo + "']/@StringRepresentation");
            Assert.AreEqual(ksEnvStringRep, node.InnerText, "phonological environment string differs");

            // Test adding a phonological environment string representation:
            // check on case where parent of owner has Id and is class name;
            // also check on case where there is a comment/text node within the result nodes
            node = updatedFxtResult.SelectSingleNode("//PhEnvironment[@Id='" + envNew.Hvo + "']");
            Assert.IsNotNull(node, "missing newly added phonological environment");
            nodes = updatedFxtResult.SelectNodes("//PhEnvironment");
            Assert.AreEqual(11, nodes.Count, "number of PhEnvironments differs");

            // Test Parser Parameters: check on unicode string
            node = updatedFxtResult.SelectSingleNode("//ParserParameters");
            string sResultParseParameters = node.OuterXml.Trim();
            Assert.AreEqual(sNewParserParameters, sResultParseParameters, "Parser Parameters content differs");

            // Test deletion of a lex entry: check on finding LexDb when there is no class LexDb in FXT file
            nodes = updatedFxtResult.SelectNodes("//LexEntry");
            Assert.AreEqual(61, nodes.Count, "number of LexEntries differs");
            node = updatedFxtResult.SelectSingleNode("//LexEntry[@Id='" + hvoEntryDeleted + "']");
            Assert.IsNull(node, "Deleted lex entry should be missing");
            foreach (int hvo in hvosEntryDeletedAlternateForms)
            {
                node = updatedFxtResult.SelectSingleNode("//MoStemAllomorph[@Id='" + hvo + "'] | //MoAffixAllomorph[@Id='" + hvo + "']");
                Assert.IsNull(node, "deleted entry's alternate form should also be gone");
            }
            foreach (int hvo in hvosEntryDeletedMSAs)
            {
                node = updatedFxtResult.SelectSingleNode("//MoStemMsa[@Id='" + hvo + "']");
                Assert.IsNull(node, "deleted entry's msa should also be gone");
            }
            foreach (int hvo in hvosEntryDeletedSenses)
            {
                node = updatedFxtResult.SelectSingleNode("//LexSense[@Id='" + hvo + "']");
                Assert.IsNull(node, "deleted entry's lexsense should also be gone");
            }
            node = updatedFxtResult.SelectSingleNode("//MoStemAllomorph[@Id='" + hvoEntryDeletedLexemeForm + "']");
            Assert.IsNull(node, "deleted entry's lexeme form should also be gone");

            // Test adding new entry
            node = updatedFxtResult.SelectSingleNode("//LexEntry[@Id='" + entryNew.Hvo + "']");
            Assert.IsNotNull(node, "new lex entry is missing");
            contentNode = node.SelectSingleNode("LexemeForm[@dst='" + alloNew.Hvo + "']");
            Assert.IsNotNull(contentNode, "missing lexeme form for new entry");
            contentNode = node.SelectSingleNode("AlternateForms[@dst='" + alloNew2.Hvo + "']");
            Assert.IsNotNull(contentNode, "missing alternate form in new lex entry");
            contentNode = node.SelectSingleNode("Sense[@dst='" + sense.Hvo + "']");
            Assert.IsNotNull(contentNode, "missing sense in new lex entry");
            contentNode = node.SelectSingleNode("MorphoSyntaxAnalysis[@dst='" + inflAffixMsa.Hvo + "']");
            Assert.IsNotNull(contentNode, "missing msa in new lex entry");
            contentNode = node.SelectSingleNode("AlternateForms[@dst='" + affRule.Hvo + "']");
            Assert.IsNotNull(contentNode, "missing affix process rule in new lex entry");

            node = updatedFxtResult.SelectSingleNode("//MoAffixAllomorph[@Id='" + alloNew.Hvo + "']");
            Assert.IsNotNull(node, "new lexeme form affix allomorph for new lex entry is missing");
            contentNode = node.SelectSingleNode("@MorphType");
            Assert.IsNotNull(contentNode, "@MorphType missing for new MoAffixAllomorph in lexeme form of new lex entry");
            IMoMorphType typeNew  = MoMorphType.CreateFromDBObject(Cache, Convert.ToInt32(contentNode.InnerText));
            string       sGuidNew = typeNew.Guid.ToString();
            Assert.AreEqual(MoMorphType.kguidMorphPrefix, sGuidNew, "morph type wrong for new MoAffixAllomorph in lexeme form of new lex entry");
            contentNode = node.SelectSingleNode("Form");
            Assert.IsNotNull(contentNode, "Form missing for new MoAffixAllomorph in lexeme form new lex entry");
            Assert.AreEqual(sNewAlloForm, contentNode.InnerText, "form wrong for new MoAffixAllomorph in lexeme form of new lex entry");

            node = updatedFxtResult.SelectSingleNode("//MoAffixAllomorph[@Id='" + alloNew2.Hvo + "']");
            Assert.IsNotNull(node, "new alternate form affix allomorph for new lex entry is missing");
            contentNode = node.SelectSingleNode("@MorphType");
            Assert.IsNotNull(contentNode, "@MorphType missing for new MoAffixAllomorph in alternate form of new lex entry");
            typeNew  = MoMorphType.CreateFromDBObject(Cache, Convert.ToInt32(contentNode.InnerText));
            sGuidNew = typeNew.Guid.ToString();
            Assert.AreEqual(MoMorphType.kguidMorphPrefix, sGuidNew, "morph type wrong for new MoAffixAllomorph in lexeme form of new lex entry");
            contentNode = node.SelectSingleNode("Form");
            Assert.IsNotNull(contentNode, "Form missing for new MoAffixAllomorph in alternate form new lex entry");
            Assert.AreEqual(sNewAlloForm2, contentNode.InnerText, "form wrong for new MoAffixAllomorph in alternate form of new lex entry");

            node = updatedFxtResult.SelectSingleNode("//LexSense[@Id='" + sense.Hvo + "']");
            Assert.IsNotNull(node, "new sense for new lex entry is missing");
            contentNode = node.SelectSingleNode("Gloss");
            Assert.IsNotNull(contentNode, "Gloss missing for new LexSense in new lex entry");
            Assert.AreEqual(sGloss, contentNode.InnerText, "Gloss wrong for new LexSense in new lex entry");

            node = updatedFxtResult.SelectSingleNode("//MoInflAffMsa[@Id='" + inflAffixMsa.Hvo + "']");
            Assert.IsNotNull(node, "new infl affix msa for new lex entry is missing");
            contentNode = node.SelectSingleNode("@PartOfSpeech");
            Assert.IsNotNull(contentNode, "@PartOfSpeech missing for new MoInflAffMsa in new lex entry");
            Assert.AreEqual(hvoVerb.ToString(), contentNode.InnerText, "part of speech wrong for new MoInflAffMsa in new lex entry");
            contentNode = node.SelectSingleNode("Slots/@dst");
            Assert.IsNotNull(contentNode, "Slots missing for new MoInflAffMsa in new lex entry");
            Assert.AreEqual(hvoSlot.ToString(), contentNode.InnerText, "slot wrong for new MoInflAffMsa in new lex entry");

            // Test adding new template
            node = updatedFxtResult.SelectSingleNode("//MoInflAffixTemplate[@Id='" + affixTemplate.Hvo + "']");
            Assert.IsNotNull(node, "new affix template missing");
            node =
                updatedFxtResult.SelectSingleNode("//PartOfSpeech[@Id='" + hvoIntransVerb +
                                                  "']/AffixTemplates/MoInflAffixTemplate[@Id='" + affixTemplate.Hvo +
                                                  "']");
            Assert.IsNotNull(node, "new affix template is in intransitive verb");

            // Test adding new phonological feature
            node = updatedFxtResult.SelectSingleNode("//PhFeatureSystem/Features/FsClosedFeature[@Id='" + consFeat.Hvo + "']");
            Assert.IsNotNull(node, "new phonological feature is missing");
            contentNode = node.SelectSingleNode("Abbreviation");
            Assert.IsNotNull(contentNode, "Abbreviation missing from new phonological feature");
            Assert.AreEqual(contentNode.InnerText, consFeat.Abbreviation.AnalysisDefaultWritingSystem, "Abbreviation wrong for new phonological feature");
            nodes = node.SelectNodes("Values/FsSymFeatVal");
            Assert.IsNotNull(nodes, "values missing from new phonological feature");
            Assert.AreEqual(nodes.Count, 2, "incorrect number of values in new phonological feature");
            node = updatedFxtResult.SelectSingleNode("//PhFeatureSystem/Types/FsFeatStrucType/Features/Feature[@dst='" + consFeat.Hvo + "']");
            Assert.IsNotNull(node, "reference to new phonological feature is missing from phonological feature system");

            // Test adding new feature-based NC
            node = updatedFxtResult.SelectSingleNode("//PhNCFeatures[@Id='" + featNC.Hvo + "']");
            Assert.IsNotNull(node, "new feature-based NC is missing");
            contentNode = node.SelectSingleNode("Abbreviation");
            Assert.IsNotNull(contentNode, "Abbreviation missing from new feature-based NC");
            Assert.AreEqual(contentNode.InnerText, featNC.Abbreviation.AnalysisDefaultWritingSystem, "Abbreviation wrong for new feature-based NC");
            contentNode = node.SelectSingleNode("FsFeatStruc/FsClosedValue[@Id='" + val.Hvo + "']");
            Assert.IsNotNull(contentNode, "value missing from new feature-based NC");
            Assert.AreEqual((contentNode as XmlElement).GetAttribute("Feature"), consFeat.Hvo.ToString(), "closed value feature is wrong in new feature-based NC");
            Assert.AreEqual((contentNode as XmlElement).GetAttribute("Value"), consPlus.Hvo.ToString(), "closed value is wrong in new feature-based NC");

            // Test adding new phonological rule
            node = updatedFxtResult.SelectSingleNode("//PhRegularRule[@Id='" + regRule.Hvo + "']");
            Assert.IsNotNull(node, "new phonological rule is missing");
            nodes = node.SelectNodes("StrucDesc/*");
            Assert.AreEqual(nodes.Count, 0);
            contentNode = node.SelectSingleNode("RightHandSides/PhSegRuleRHS/StrucChange/PhSimpleContextSeg[@dst='" + phoneme.Hvo + "']");
            Assert.IsNotNull(contentNode, "phoneme simple context missing in new phonological rule");
            contentNode = node.SelectSingleNode("RightHandSides/PhSegRuleRHS/LeftContext/PhSimpleContextNC[@dst='" + featNC.Hvo + "']");
            Assert.IsNotNull(contentNode, "NC simple context missing in new phonological rule");

            // Test adding new morphological rule
            node = updatedFxtResult.SelectSingleNode("//Lexicon/Allomorphs/MoAffixProcess[@Id='" + affRule.Hvo + "']");
            Assert.IsNotNull(node, "new morphological rule is missing");
            contentNode = node.SelectSingleNode("Input/PhSimpleContextNC[@dst='" + featNC.Hvo + "']");
            Assert.IsNotNull(contentNode, "NC simple context missing in new morphological rule");
            contentNode = node.SelectSingleNode("Output/MoCopyFromInput/Content[@dst='" + ncCtxt.Hvo + "']");
            Assert.IsNotNull(contentNode, "copy from input missing in new morphological rule");

            // Modify a phonological rule
            segCtxt = new PhSimpleContextSeg();
            regRule.StrucDescOS.Append(segCtxt);
            segCtxt.FeatureStructureRA = phoneme;
            segCtxt.NotifyNew();
            regRule.RightHandSidesOS[0].StrucChangeOS[0].DeleteUnderlyingObject();
            IPhPhonContext oldCtxt = regRule.RightHandSidesOS[0].LeftContextOA;
            Cache.LangProject.PhonologicalDataOA.ContextsOS.Append(oldCtxt);
            IPhSequenceContext seqCtxt = new PhSequenceContext();
            regRule.RightHandSidesOS[0].LeftContextOA = seqCtxt;
            seqCtxt.MembersRS.Append(oldCtxt);
            seqCtxt.NotifyNew();
            IPhSimpleContextBdry bdryCtxt = new PhSimpleContextBdry();
            Cache.LangProject.PhonologicalDataOA.ContextsOS.Append(bdryCtxt);
            bdryCtxt.FeatureStructureRAHvo = Cache.GetIdFromGuid(LangProject.kguidPhRuleWordBdry);
            bdryCtxt.NotifyNew();
            seqCtxt.MembersRS.Append(bdryCtxt);

            // Modify a morphological rule
            entryNew.LexemeFormOA = affRule;
            IMoInsertPhones insertPhones = new MoInsertPhones();
            affRule.OutputOS.InsertAt(insertPhones, 0);
            insertPhones.ContentRS.Append(phoneme);
            insertPhones.NotifyNew();
            affRule.InputOS[1].DeleteUnderlyingObject();

            // change order of a sequence vector
            lexEntry.AlternateFormsOS.InsertAt(newAffixAllomorph, 0);

            updatedFxtResult = UpdateFXT();

            // Test modifying a phonological rule
            node        = updatedFxtResult.SelectSingleNode("//PhRegularRule[@Id='" + regRule.Hvo + "']");
            contentNode = node.SelectSingleNode("StrucDesc/PhSimpleContextSeg[@dst='" + phoneme.Hvo + "']");
            Assert.IsNotNull(contentNode, "phoneme simple context missing from StrucDesc in modified phonological rule");
            contentNode = node.SelectSingleNode("RightHandSides/PhSegRuleRHS/StrucChange/PhSimpleContextSeg[@dst='" + phoneme.Hvo + "']");
            Assert.IsNull(contentNode, "phoneme simple context is not missing from StrucChange in modified phonological rule");
            contentNode = node.SelectSingleNode("RightHandSides/PhSegRuleRHS/LeftContext/PhSequenceContext[@Id='" + seqCtxt.Hvo + "']");
            Assert.IsNotNull(contentNode, "sequence context missing from modified phonological rule");
            contentNode = contentNode.SelectSingleNode("Members[@dst='" + bdryCtxt.Hvo + "']");
            Assert.IsNotNull(contentNode, "boundary context missing from sequence context in modified phonological rule");
            node = updatedFxtResult.SelectSingleNode("//PhPhonData/Contexts/PhSimpleContextBdry[@Id='" + bdryCtxt.Hvo + "']");
            Assert.IsNotNull(node, "boundary context missing from contexts in phonological data");

            // Test modifying a morphological rule
            node        = updatedFxtResult.SelectSingleNode("//LexEntry[@Id='" + entryNew.Hvo + "']");
            contentNode = node.SelectSingleNode("LexemeForm[@dst='" + affRule.Hvo + "']");
            Assert.IsNotNull(contentNode, "affix process rule is not the lexeme form for the lex entry");
            node        = updatedFxtResult.SelectSingleNode("//Lexicon/Allomorphs/MoAffixProcess[@Id='" + affRule.Hvo + "']");
            contentNode = node.SelectSingleNode("Input/PhSimpleContextNC[@dst='" + featNC.Hvo + "']");
            Assert.IsNull(contentNode, "NC simple context was not removed from morphological rule");
            nodes = node.SelectNodes("Output/*");
            Assert.AreEqual(nodes.Count, 2, "incorrect number of mappings in morphological rule");
            contentNode = node.SelectSingleNode("Output/*[position() = 1 and @Id='" + insertPhones.Hvo + "']");
            Assert.IsNotNull(contentNode, "insert phones missing from morphological rule");

            // Test changing order of a sequence vector
            node        = updatedFxtResult.SelectSingleNode("//LexEntry[@Id='" + lexEntry.Hvo + "']");
            contentNode = node.SelectSingleNode("AlternateForms[@dst='" + lexEntry.AlternateFormsOS[0].Hvo + "']/@ord");
            Assert.AreEqual("0", contentNode.InnerText);
            contentNode = node.SelectSingleNode("AlternateForms[@dst='" + lexEntry.AlternateFormsOS[1].Hvo + "']/@ord");
            Assert.AreEqual("1", contentNode.InnerText);
            contentNode = node.SelectSingleNode("AlternateForms[@dst='" + lexEntry.AlternateFormsOS[2].Hvo + "']/@ord");
            Assert.AreEqual("2", contentNode.InnerText);
        }