public void ValidatePhEnvironment_StringRepresentation()
        {
            // Add a character to the set of phoneme representations so that the environment below
            // will pass muster.
            IPhPhonemeSet phset = Cache.ServiceLocator.GetInstance <IPhPhonemeSetFactory>().Create();

            Cache.LanguageProject.PhonologicalDataOA.PhonemeSetsOS.Add(phset);
            AddPhone(phset, "a");
            AddPhone(phset, "b");
            AddPhone(phset, "c");
            AddPhone(phset, "d");
            AddPhone(phset, "e");

            ConstraintFailure failure = null;
            int            strRepFlid = PhEnvironmentTags.kflidStringRepresentation;
            IPhEnvironment env        = Cache.ServiceLocator.GetInstance <IPhEnvironmentFactory>().Create();

            Cache.LangProject.PhonologicalDataOA.EnvironmentsOS.Add(env);
            IEnumerable <ICmBaseAnnotation> os = GetAnnotationsForObject(env);

            Assert.AreEqual(0, os.Count(), "Wrong starting count of annotations.");

            env.StringRepresentation = Cache.TsStrFactory.MakeString(@"/ [BADCLASS] _", Cache.DefaultAnalWs);
            Assert.IsFalse(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.IsNotNull(failure, "Didn't get an object back from the CheckConstraints method.");
            os = GetAnnotationsForObject(env);
            Assert.AreEqual(1, os.Count(), "Wrong invalid count of annotations.");

            env.StringRepresentation = Cache.TsStrFactory.MakeString(@"/ d _", Cache.DefaultAnalWs);
            Assert.IsTrue(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.IsNull(failure, "Got an object back from the CheckConstraints method.");
            os = GetAnnotationsForObject(env);
            Assert.AreEqual(0, os.Count(), "Wrong valid count of annotations.");
        }
Exemple #2
0
 public void InvalidEnvironment(IMoForm form, IPhEnvironment env, string reason, IMoMorphSynAnalysis msa)
 {
     m_xmlWriter.WriteStartElement("LoadError");
     m_xmlWriter.WriteAttributeString("type", "invalid-environment");
     m_xmlWriter.WriteElementString("Form", form.Form.VernacularDefaultWritingSystem.Text);
     m_xmlWriter.WriteElementString("Env", env.StringRepresentation.Text);
     m_xmlWriter.WriteElementString("Hvo", msa.Hvo.ToString(CultureInfo.InvariantCulture));
     m_xmlWriter.WriteElementString("Reason", reason);
     m_xmlWriter.WriteEndElement();
 }
		/// <summary>
		/// Handle launching of the environment chooser.
		/// </summary>
		/// <remarks>
		/// Subclasses should override this method, if the SimpleListChooser is not suitable.
		/// </remarks>
		protected override void HandleChooser()
		{
			// get all valid environments
			var candidates = new HashSet<ICmObject>();
			foreach (var env in m_cache.LangProject.PhonologicalDataOA.EnvironmentsOS)
			{
				ConstraintFailure failure;
				if (env.CheckConstraints(PhEnvironmentTags.kflidStringRepresentation, false, out failure))
					candidates.Add(env);
			}

			string displayWs = "analysis vernacular";
			IPhEnvironment selectedEnv = null;
			if (m_configurationNode != null)
			{
				XmlNode node = m_configurationNode.SelectSingleNode("deParams");
				if (node != null)
					displayWs = XmlUtils.GetAttributeValue(node, "ws", "analysis vernacular").ToLower();
			}

			var labels = ObjectLabel.CreateObjectLabels(m_cache, candidates.OrderBy(e => e.ShortName), null, displayWs);

			using (var chooser = new SimpleListChooser(m_persistProvider, labels,
				m_fieldName, m_mediator.HelpTopicProvider))
			{
				chooser.Cache = m_cache;
				chooser.TextParamHvo = m_cache.LangProject.PhonologicalDataOA.Hvo;
				chooser.SetHelpTopic(Slice.GetChooserHelpTopicID(Slice.HelpTopicID));
				chooser.InitializeExtras(m_configurationNode, m_mediator);

				DialogResult res = chooser.ShowDialog();
				if (res != DialogResult.Cancel)
				{
					chooser.HandleAnyJump();

					if (chooser.ChosenOne != null)
						selectedEnv = chooser.ChosenOne.Object as IPhEnvironment;
				}
			}

			// return focus to the view
			m_view.Select();
			if (selectedEnv != null)
			{
				int cellId = -1;
				UndoableUnitOfWorkHelper.Do(MEStrings.ksRuleUndoUpdateEnv, MEStrings.ksRuleRedoUpdateEnv, selectedEnv, () =>
				{
					cellId = UpdateEnvironment(selectedEnv);
				});

				ReconstructView(cellId, -1, true);
			}
		}
Exemple #4
0
        protected void AddAffixAllomorphToEntry(List <ICmObject> addList, ILexEntry le, string alloName,
                                                IPhEnvironment env)
        {
            var allomorph = m_affixFact.Create();

            le.AlternateFormsOS.Add(allomorph);
            if (env != null)
            {
                allomorph.PhoneEnvRC.Add(env);
            }
            allomorph.Form.set_String(Cache.DefaultVernWs, alloName);
            addList.Add(allomorph);
        }
Exemple #5
0
		public void MergeCollectionPropertyBackReferences()
		{
			CheckDisposed();

			ILexEntry lme = m_entriesCol.Add(new LexEntry());
			FdoOwningSequence<IPhEnvironment> envsSeq = Cache.LangProject.PhonologicalDataOA.EnvironmentsOS;
			IPhEnvironment envKeeper = envsSeq.Append(new PhEnvironment());
			IPhEnvironment envSrc = envsSeq.Append(new PhEnvironment());

			// Shift back references from reference sequence property.
			IMoAffixAllomorph referrer = (IMoAffixAllomorph)lme.AlternateFormsOS.Append(new MoAffixAllomorph());
			referrer.PhoneEnvRC.Add(envSrc);
			envKeeper.MergeObject(envSrc);
			Assert.AreEqual(envKeeper.Hvo, referrer.PhoneEnvRC.HvoArray[0]);
		}
Exemple #6
0
        public void ValidatePhEnvironment_StringRepresentation()
        {
            // Add a character to the set of phoneme representations so that the environment below
            // will pass muster.
            IPhPhonemeSet phset = Cache.ServiceLocator.GetInstance <IPhPhonemeSetFactory>().Create();

            Cache.LanguageProject.PhonologicalDataOA.PhonemeSetsOS.Add(phset);
            AddPhone(phset, "a");
            AddPhone(phset, "b");
            AddPhone(phset, "c");
            AddPhone(phset, "d");
            AddPhone(phset, "e");

            ConstraintFailure failure = null;
            int            strRepFlid = PhEnvironmentTags.kflidStringRepresentation;
            IPhEnvironment env        = Cache.ServiceLocator.GetInstance <IPhEnvironmentFactory>().Create();

            Cache.LangProject.PhonologicalDataOA.EnvironmentsOS.Add(env);
            IEnumerable <ICmBaseAnnotation> os = GetAnnotationsForObject(env);

            Assert.AreEqual(0, os.Count(), "Wrong starting count of annotations.");

            env.StringRepresentation = TsStringUtils.MakeString(@"/ [BADCLASS] _", Cache.DefaultAnalWs);

            m_actionHandler.EndUndoTask();             // so we can verify it makes its own as needed, and doesn't do unnecessary ones.

            Assert.IsFalse(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.IsNotNull(failure, "Didn't get an object back from the CheckConstraints method.");
            os = GetAnnotationsForObject(env);
            Assert.AreEqual(1, os.Count(), "Wrong invalid count of annotations.");

            int cUndoTasks = m_actionHandler.UndoableActionCount;

            Assert.IsFalse(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.That(m_actionHandler.UndoableActionCount, Is.EqualTo(cUndoTasks), "should not make database changes when situation is unchanged");

            NonUndoableUnitOfWorkHelper.Do(m_actionHandler, () =>
                                           env.StringRepresentation = TsStringUtils.MakeString(@"/ d _", Cache.DefaultAnalWs));
            Assert.IsTrue(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.IsNull(failure, "Got an object back from the CheckConstraints method.");
            os = GetAnnotationsForObject(env);
            Assert.AreEqual(0, os.Count(), "Wrong valid count of annotations.");

            cUndoTasks = m_actionHandler.UndoableActionCount;
            Assert.IsTrue(env.CheckConstraints(strRepFlid, true, out failure, true));
            Assert.That(m_actionHandler.UndoableActionCount, Is.EqualTo(cUndoTasks), "should not make database changes when situation is unchanged");
        }
            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose(bool disposing)
            {
                // Must not be run more than once.
                if (IsDisposed)
                {
                    return;
                }

                base.Dispose(disposing);

                if (disposing)
                {
                }

                m_env       = null;
                m_vc        = null;
                m_validator = null;                 // TODO: Make m_validator disposable?
            }
        protected override int UpdateEnvironment(IPhEnvironment env)
        {
            string envStr   = env.StringRepresentation.Text.Trim().Substring(1).Trim();
            int    index    = envStr.IndexOf('_');
            string leftEnv  = envStr.Substring(0, index).Trim();
            string rightEnv = envStr.Substring(index + 1).Trim();

            if (RHS.LeftContextOAHvo != 0)
            {
                RHS.LeftContextOA.DeleteUnderlyingObject();
            }
            InsertContextsFromEnv(leftEnv, (int)PhSegRuleRHS.PhSegRuleRHSTags.kflidLeftContext, null);

            if (RHS.RightContextOAHvo != 0)
            {
                RHS.RightContextOA.DeleteUnderlyingObject();
            }
            InsertContextsFromEnv(rightEnv, (int)PhSegRuleRHS.PhSegRuleRHSTags.kflidRightContext, null);

            return((int)PhSegRuleRHS.PhSegRuleRHSTags.kflidLeftContext);
        }
Exemple #9
0
            public override void MakeRoot()
            {
                CheckDisposed();

                if (m_cache == null || DesignMode)
                {
                    return;
                }

                // A crude way of making sure the property we want is loaded into the cache.
                m_env = m_cache.ServiceLocator.GetInstance <IPhEnvironmentRepository>().GetObject(m_hvoObj);
                m_vc  = new StringRepSliceVc();

                base.MakeRoot();

                // And maybe this too, at least by default?
                m_rootb.DataAccess = m_cache.MainCacheAccessor;

                // arg3 is a meaningless initial fragment, since this VC only displays one thing.
                // arg4 could be used to supply a stylesheet.
                m_rootb.SetRootObject(m_hvoObj, m_vc, StringRepSliceVc.Flid, null);
            }
        protected override int UpdateEnvironment(IPhEnvironment env)
        {
            string envStr   = env.StringRepresentation.Text.Trim().Substring(1).Trim();
            int    index    = envStr.IndexOf('_');
            string leftEnv  = envStr.Substring(0, index).Trim();
            string rightEnv = envStr.Substring(index + 1).Trim();

            if (Rhs.LeftContextOA != null)
            {
                Rhs.LeftContextOA.PreRemovalSideEffects();
                Rhs.LeftContextOA = null;
            }
            InsertContextsFromEnv(leftEnv, PhSegRuleRHSTags.kflidLeftContext, null);

            if (Rhs.RightContextOA != null)
            {
                Rhs.RightContextOA.PreRemovalSideEffects();
                Rhs.RightContextOA = null;
            }
            InsertContextsFromEnv(rightEnv, PhSegRuleRHSTags.kflidRightContext, null);

            return(PhSegRuleRHSTags.kflidLeftContext);
        }
Exemple #11
0
        /// <summary>
        /// Integrate changes in dummy cache to real cache and DB.
        /// </summary>
        public void ConnectToRealCache()
        {
            CheckDisposed();
            // We're saving any changes to the real cache, so can no longer Undo/Redo local edits.
            CommitLocalEdits();
            Form frm = FindForm();

            // frm will be null, if the record has been switched
            if (frm != null)
            {
                frm.Cursor = Cursors.WaitCursor;
            }
            // [NB: m_silCache is the same cache as m_vwCache,
            // but is is a different cache than m_fdoCache.
            // m_fdoCache has access to the database, and updates it,
            // but m_silCache does not.]
            if (DesignMode ||
                m_rootb == null
                // It may not be valid by now, since it may have been deleted.
                || !m_rootObj.IsValidObject())
            {
                if (frm != null)
                {
                    frm.Cursor = Cursors.Default;
                }
                return;
            }
            FdoOwningSequence <IPhEnvironment> phoneEnvs =
                m_fdoCache.LangProject.PhonologicalDataOA.EnvironmentsOS;
            int count = m_silCache.get_VecSize(m_rootObj.Hvo, kMainObjEnvironments);

            // We need one less than the size,
            // because the last 'env' is a dummy that lets the user type a new one.
            int[] hvos  = new int[count - 1];
            int   cvDel = 0;

            for (int i = hvos.Length - 1; i >= 0; --i)
            {
                IPhEnvironment env         = null;
                int            hvoDummyObj = m_silCache.get_VecItem(m_rootObj.Hvo, kMainObjEnvironments, i);
                ITsString      tss         = m_silCache.get_StringProp(hvoDummyObj, kEnvStringRep);
                ITsStrBldr     bldr        = tss.GetBldr();
                string         rep         = tss.Text;
                if (rep == null || rep.Length == 0)
                {
                    // The environment at 'i' is being deleted, so
                    // shrink the array of hvos that go into the real cache.
                    cvDel++;
                    m_realEnvs.Remove(hvoDummyObj);
                    // Remove it from the dummy cache.
                    int oldSelId = m_hvoOldSelection;
                    m_hvoOldSelection = hvoDummyObj;
                    RemoveFromDummyCache(i);
                    m_hvoOldSelection = oldSelId;
                }
                else
                {
                    foreach (IPhEnvironment envCurrent in phoneEnvs)
                    {
                        // Compare them without spaces, since they are not needed.
                        if (envCurrent.StringRepresentation.Text != null &&
                            envCurrent.StringRepresentation.Text.Replace(" ", null) ==
                            rep.Replace(" ", null))
                        {
                            env = envCurrent;
                            // Maybe the ws has changed, so change the real one, in case.
                            env.StringRepresentation.UnderlyingTsString = tss;
                            break;
                        }
                    }
                    if (env == null)
                    {
                        env = phoneEnvs.Append(new PhEnvironment());
                        env.StringRepresentation.UnderlyingTsString = tss;
                        m_fdoCache.PropChanged(null, PropChangeType.kpctNotifyAll, m_fdoCache.LangProject.PhonologicalDataOA.Hvo, (int)PhPhonData.PhPhonDataTags.kflidEnvironments, phoneEnvs.Count - 1, 1, 0);
                    }
                    ConstraintFailure failure;
                    if (env.CheckConstraints((int)PhEnvironment.PhEnvironmentTags.kflidStringRepresentation, out failure, /* adjust the squiggly line */ true))
                    {
                        ClearSquigglyLine(hvoDummyObj, ref tss, ref bldr);
                    }
                    else
                    {
                        MakeSquigglyLine(hvoDummyObj, failure.XmlDescription, ref tss, ref bldr);
                    }
                    hvos[i] = env.Hvo;
                    // Refresh
                    m_vwCache.CacheStringProp(hvoDummyObj, kEnvStringRep, bldr.GetString());
                    m_silCache.PropChanged(null, (int)PropChangeType.kpctNotifyAll,
                                           hvoDummyObj, kEnvStringRep, 0, tss.Length, tss.Length);
                }
            }
            int[] newHvos = new int[hvos.Length];
            hvos.CopyTo(newHvos, 0);
            if (cvDel > 0)
            {
                newHvos = new int[hvos.Length - cvDel];
                count   = 0;
                for (int i = 0; i < hvos.Length; ++i)
                {
                    int tempHvo = hvos[i];
                    if (tempHvo > 0)
                    {
                        newHvos[count++] = tempHvo;
                    }
                }
            }
            count = m_fdoCache.GetVectorSize(m_rootObj.Hvo, m_rootFlid);
            // Only reset the main property, if it has changed.
            // Otherwise, the parser gets too excited about needing to reload.
            if ((count != newHvos.Length) ||
                !equalArrays(m_fdoCache.GetVectorProperty(m_rootObj.Hvo, m_rootFlid, true), newHvos))
            {
                string fieldname =
                    (m_rootFlid == (int)MoAffixAllomorph.MoAffixAllomorphTags.kflidPhoneEnv)
                                        ? "PhoneEnv" : "Position";
                m_fdoCache.BeginUndoTask(
                    String.Format(DetailControlsStrings.ksUndoSet, fieldname),
                    String.Format(DetailControlsStrings.ksRedoSet, fieldname));
                m_fdoCache.ReplaceReferenceProperty(m_rootObj.Hvo, m_rootFlid, 0, count,
                                                    ref newHvos);
                m_fdoCache.EndUndoTask();
            }
            if (frm != null)
            {
                frm.Cursor = Cursors.Default;
            }
        }
Exemple #12
0
		private static void WritePhEnvironment(TextWriter w, IPhEnvironment env)
		{
			var repr = env.StringRepresentation.Text;
			if (repr != null)
				w.WriteLine("<trait name =\"environment\" value=\"{0}\"/>", XmlUtils.MakeSafeXmlAttribute(repr));
		}
Exemple #13
0
 public void InvalidEnvironment(IMoForm form, IPhEnvironment env, string reason, IMoMorphSynAnalysis msa)
 {
     Console.WriteLine("The environment \"{0}\" is invalid. Reason: {1}", env.StringRepresentation.Text, reason);
 }
        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);
        }
			/// <summary>
			/// Clean up any resources being used.
			/// </summary>
			protected override void Dispose(bool disposing)
			{
				// Must not be run more than once.
				if (IsDisposed)
					return;

				base.Dispose(disposing);

				if (disposing)
				{
				}

				m_env = null;
				m_vc = null;
				m_validator = null; // TODO: Make m_validator disposable?
			}
		/// <summary>
		/// Updates the environment.
		/// </summary>
		/// <param name="env">The environment.</param>
		/// <returns>The ID of the cell that the environment was updated in</returns>
		protected virtual int UpdateEnvironment(IPhEnvironment env)
		{
			throw new NotImplementedException();
		}
		protected override int UpdateEnvironment(IPhEnvironment env)
		{
			string envStr = env.StringRepresentation.Text.Trim().Substring(1).Trim();
			int index = envStr.IndexOf('_');
			string leftEnv = envStr.Substring(0, index).Trim();
			string rightEnv = envStr.Substring(index + 1).Trim();

			if (RHS.LeftContextOAHvo != 0)
				RHS.LeftContextOA.DeleteUnderlyingObject();
			InsertContextsFromEnv(leftEnv, (int)PhSegRuleRHS.PhSegRuleRHSTags.kflidLeftContext, null);

			if (RHS.RightContextOAHvo != 0)
				RHS.RightContextOA.DeleteUnderlyingObject();
			InsertContextsFromEnv(rightEnv, (int)PhSegRuleRHS.PhSegRuleRHSTags.kflidRightContext, null);

			return (int)PhSegRuleRHS.PhSegRuleRHSTags.kflidLeftContext;
		}
		/// <summary>
		/// Updates the environment.
		/// </summary>
		/// <param name="env">The environment.</param>
		/// <returns>The ID of the cell that the environment was updated in</returns>
		protected virtual int UpdateEnvironment(IPhEnvironment env)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Add new item to the collection (added in the chooser).
		/// </summary>
		/// <param name="realHvo">ID of the envirnoment from the chooser.</param>
		public void AddNewItem(IPhEnvironment env)
		{
			CheckDisposed();
			m_realEnvs[m_id] = env;
			int count = m_sda.get_VecSize(m_rootObj.Hvo, kMainObjEnvironments);
			InsertPhoneEnv(m_id++, env.StringRepresentation, count - 1);
			m_rootb.PropChanged(m_rootObj.Hvo, kMainObjEnvironments, count, 1, 0);
			m_heightView = m_rootb.Height;
		}
			public override void MakeRoot()
			{
				CheckDisposed();

				base.MakeRoot();

				if (m_fdoCache == null || DesignMode)
					return;

				// A crude way of making sure the property we want is loaded into the cache.
				m_env = m_fdoCache.ServiceLocator.GetInstance<IPhEnvironmentRepository>().GetObject(m_hvoObj);
				m_vc = new StringRepSliceVc();
				// Review JohnT: why doesn't the base class do this??
				m_rootb = VwRootBoxClass.Create();
				m_rootb.SetSite(this);
				// And maybe this too, at least by default?
				m_rootb.DataAccess = m_fdoCache.MainCacheAccessor;

				// arg3 is a meaningless initial fragment, since this VC only displays one thing.
				// arg4 could be used to supply a stylesheet.
				m_rootb.SetRootObject(m_hvoObj, m_vc, StringRepSliceVc.Flid, null);
			}
		protected override int UpdateEnvironment(IPhEnvironment env)
		{
			string envStr = env.StringRepresentation.Text.Trim().Substring(1).Trim();
			int index = envStr.IndexOf('_');
			string leftEnv = envStr.Substring(0, index).Trim();
			string rightEnv = envStr.Substring(index + 1).Trim();

			if (Rhs.LeftContextOA != null)
			{
				Rhs.LeftContextOA.PreRemovalSideEffects();
				Rhs.LeftContextOA = null;
			}
			InsertContextsFromEnv(leftEnv, PhSegRuleRHSTags.kflidLeftContext, null);

			if (Rhs.RightContextOA != null)
			{
				Rhs.RightContextOA.PreRemovalSideEffects();
				Rhs.RightContextOA = null;
			}
			InsertContextsFromEnv(rightEnv, PhSegRuleRHSTags.kflidRightContext, null);

			return PhSegRuleRHSTags.kflidLeftContext;
		}
		/// <summary>
		/// Remove an item from the collection (deleted in the chooser).
		/// </summary>
		/// <param name="realHvo">ID of the environment from the chooser.</param>
		public void RemoveItem(IPhEnvironment env)
		{
			CheckDisposed();
			int dummyHvo = 0;
			foreach (var kvp in m_realEnvs)
			{
				var realEnv = kvp.Value;
				if (realEnv == env)
				{
					dummyHvo = kvp.Key;
					break;
				}
			}
			if (dummyHvo == 0)
				return;
			m_realEnvs.Remove(dummyHvo);
			int count = m_sda.get_VecSize(m_rootObj.Hvo, kMainObjEnvironments);
			int loc = -1;
			int hvo;
			for (int i = 0; i < count; ++i)
			{
				hvo = m_sda.get_VecItem(m_rootObj.Hvo, kMainObjEnvironments, i);
				if (hvo == dummyHvo)
				{
					loc = i;
					break;
				}
			}
			if (loc >= 0)
			{
				m_sda.CacheReplace(m_rootObj.Hvo, kMainObjEnvironments, loc, loc + 1,
					new int[0], 0);
				m_sda.DeleteObj(dummyHvo);
				m_rootb.PropChanged(m_rootObj.Hvo, kMainObjEnvironments, loc, 0, 1);
			}
			m_heightView = m_rootb.Height;
		}