public CmPossibility MakeTemplate(out List<int> allCols)
		{
			// The exact organization of columns is not of great
			// importance for the current tests (still less the names), but we do want there
			// to be a hierarchy, since that is a common problem, and naming them conventionally
			// may make debugging easier. Currently this is the same set of columns as
			// m_logic.CreateDefaultColumns, but we make it explicit here so most of the test
			// code is unaffected by changes to the default.
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(
				"<template name=\"default\">"
				+ "<column name=\"prenuclear\">"
				+ "<column name=\"prenuc1\"/>"
				+ "<column name=\"prenuc2\"/>"
				+ "</column>"
				+ "<column name=\"nucleus\">"
				+ "<column name=\"Subject\"/>"
				+ "<column name=\"verb\"/>"
				+ "<column name=\"object\"/>"
				+ "</column>"
				+ "<column name=\"postnuc\"/>"
				+ "</template>");
			m_template = (CmPossibility)m_cache.LangProject.CreateChartTemplate(doc.DocumentElement);
			//m_template = m_logic.CreateTemplate(doc.DocumentElement);
			m_allColumns = m_logic.AllColumns(m_template);
			allCols = m_allColumns;
			return m_template;
		}
Beispiel #2
0
		public override void Exit()
		{
			m_chart = null;
			m_template = null;
			m_allColumns = null;
			m_mockRibbon = null;
			m_logic = null;
			m_firstParaWfics = null;

			base.Exit();
		}
Beispiel #3
0
		protected override void CreateTestData()
		{
			base.CreateTestData();
			m_firstParaWfics = m_helper.MakeAnnotations(m_firstPara);
			m_logic = new TestCCLogic(Cache, m_chart, m_stText.Hvo);
			m_helper.Logic = m_logic;
			m_logic.Ribbon = m_mockRibbon = new MockRibbon(Cache, m_stText.Hvo);
			m_template = m_helper.MakeTemplate(out m_allColumns);
			// Note: do this AFTER creating the template, which may also create the DiscourseData object.
			m_chart = new DsConstChart();
			Cache.LangProject.DiscourseDataOA.ChartsOC.Add(m_chart);
			m_chart.TemplateRA = m_template;
			m_logic.Chart = m_chart;
			m_helper.MakeDefaultChartMarkers();
			m_helper.Chart = m_chart;
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Creates the categories.
		/// </summary>
		/// <param name="cache">The cache.</param>
		/// <param name="list">The possibility (category) list.</param>
		/// <param name="ws">The writing system for setting the category names.</param>
		/// <returns></returns>
		/// ------------------------------------------------------------------------------------
		internal static ICmPossibilityList CreateCategories(FdoCache cache,
			ICmPossibilityList list, int ws)
		{
			list = cache.LangProject.TranslatedScriptureOA.NoteCategoriesOA;

			// Initialize text.
			ITsPropsBldr ttpBldr = TsPropsBldrClass.Create();
			ttpBldr.SetIntPropValues((int)FwTextPropType.ktptWs,
									 (int)FwTextPropVar.ktpvDefault, ws);
			ITsStrBldr tsStrBldr = TsStrBldrClass.Create();

			// Set possibilities on top level--"Level 1a"
			CmPossibility possibility1a = new CmPossibility();
			list.PossibilitiesOS.Append(possibility1a);
			tsStrBldr.ReplaceRgch(0, 0, "Level 1a", 8, ttpBldr.GetTextProps());
			possibility1a.Name.SetAlternative(tsStrBldr.GetString(), ws);

			// Add another on top level--"Level 1b"
			CmPossibility possibility1b = new CmPossibility();
			list.PossibilitiesOS.Append(possibility1b);
			tsStrBldr.ReplaceRgch(0, tsStrBldr.Length, "Level 1b", 8, ttpBldr.GetTextProps());
			possibility1b.Name.SetAlternative(tsStrBldr.GetString(), ws);

			// Add possibilities on second level under "Level 1b"--"Level 2a"
			CmPossibility subPossibility2a = new CmPossibility();
			possibility1b.SubPossibilitiesOS.Append(subPossibility2a);
			tsStrBldr.ReplaceRgch(0, tsStrBldr.Length, "Level 2a, parent is 1b", 22, ttpBldr.GetTextProps());
			subPossibility2a.Name.SetAlternative(tsStrBldr.GetString(), ws);

			// Add "Level 2b" under "Level 1b"
			CmPossibility subPossibility2b = new CmPossibility();
			possibility1b.SubPossibilitiesOS.Append(subPossibility2b);
			tsStrBldr.ReplaceRgch(0, tsStrBldr.Length, "Level 2b, parent is 1b", 22, ttpBldr.GetTextProps());
			subPossibility2b.Name.SetAlternative(tsStrBldr.GetString(), ws);

			// Add "Level 3" under "Level 2b"
			CmPossibility subSubPossibility3 = new CmPossibility();
			subPossibility2b.SubPossibilitiesOS.Append(subSubPossibility3);
			tsStrBldr.ReplaceRgch(0, tsStrBldr.Length, "Level 3, parent is 2b", 21, ttpBldr.GetTextProps());
			subSubPossibility3.Name.SetAlternative(tsStrBldr.GetString(), ws);

			return list;
		}
Beispiel #5
0
		protected void CreateTestData()
		{
			using (new UndoRedoTaskHelper(Cache, "Undo LogicTest - CreateTestData", "Redo LogicTest - CreateTestData"))
			{
				m_helper = new DiscourseTestHelper(Cache);
				m_firstPara = m_helper.FirstPara;
				m_stText = m_firstPara.Owner as StText;
				m_firstParaWfics = m_helper.MakeAnnotations(m_firstPara);
				m_logic = new TestCCLogic(Cache, m_chart, m_stText.Hvo);
				m_helper.Logic = m_logic;
				m_logic.Ribbon = m_mockRibbon = new MockRibbon(Cache, m_stText.Hvo);
				m_template = m_helper.MakeTemplate(out m_allColumns);
				// Note: do this AFTER creating the template, which may also create the DiscourseData object.
				m_chart = new DsConstChart();
				Cache.LangProject.DiscourseDataOA.ChartsOC.Add(m_chart);
				m_chart.TemplateRA = m_template;
				m_logic.Chart = m_chart;
				m_helper.Chart = m_chart;
			}
		}
		protected override void CreateTestData()
		{
			base.CreateTestData();
			m_cclogic = new TestCCLogic(Cache, m_chart, m_stText.Hvo);
			m_helper.Logic = m_cclogic;
			m_template = m_helper.MakeTemplate(out m_allColumns);
			// Note: do this AFTER creating the template, which may also create the DiscourseData object.
			m_chart = new DsConstChart();
			Cache.LangProject.DiscourseDataOA.ChartsOC.Add(m_chart);
			m_chart.TemplateRA = m_template;
			m_cclogic.Chart = m_chart;
			m_cclogic.Ribbon = m_mockRibbon = new MockRibbon(Cache, m_stText.Hvo);
			m_helper.Chart = m_chart;
			m_origCell = null; // Test must fill in the ClickedCell in the CChartSentenceElements object
			m_eligCols = m_allColumns.ToArray(); // CChartSentenceElements always starts with all columns
			m_eligRows = null; // Test must fill in EligibleRows in the CChartSentenceElements object
			m_sentElem = new CChartSentenceElements(m_origCell, m_eligRows, m_eligCols);
			m_dlgLogicPrepose = new AdvancedMTDialogLogic(Cache, true, m_sentElem);
			m_dlgLogicPostpose = new AdvancedMTDialogLogic(Cache, false, m_sentElem); // create one each direction; test both
		}
		protected override void CreateTestData()
		{
			base.CreateTestData();
			m_firstParaWfics = m_helper.MakeAnnotations(m_firstPara);
			m_logic = new TestCCLogic(Cache, m_chart, m_stText.Hvo); // m_chart is still null!
			m_helper.Logic = m_logic;
			m_logic.Ribbon = m_mockRibbon = new MockRibbon(Cache, m_stText.Hvo);
			m_template = m_helper.MakeTemplate(out m_allColumns);
			// Note: do this AFTER creating the template, which may also create the DiscourseData object.
			m_chart = new DsConstChart();
			Cache.LangProject.DiscourseDataOA.ChartsOC.Add(m_chart);
			m_chart.TemplateRA = m_template;
			m_logic.Chart = m_chart;
			m_helper.MakeDefaultChartMarkers();
			m_helper.Chart = m_chart;

			m_constChart = new ConstituentChart(Cache, m_logic);
			m_constChart.Init(null, null);
			m_chartBody = m_constChart.Body;
			m_chartBody.Cache = Cache; // don't know why constructor doesn't do this, but it doesn't.
			m_chartBody.SetRoot(m_chart.Hvo, m_allColumns.ToArray());
		}
Beispiel #8
0
		private void SetNameAndChildColumns(CmPossibility parent, XmlNode spec)
		{
			parent.Name.AnalysisDefaultWritingSystem = XmlUtils.GetManditoryAttributeValue(spec, "name");
			foreach (XmlNode child in spec.ChildNodes)
			{
				if (child.Name == "column")
					CreateColumn(parent, child);
			}
		}
Beispiel #9
0
		public void DBObjectInstantiation()
		{
			CheckDisposed();

			// Putting persons in the UsageTypes doesn't really make sense, but for testing purposes
			// it doesn't matter what we store there as long as it is derived from CmPossibility
			CmPerson person = new CmPerson();
			CmPossibility pos = new CmPossibility();
			Cache.LangProject.LexDbOA.UsageTypesOA.PossibilitiesOS.Append(person);
			Cache.LangProject.LexDbOA.UsageTypesOA.PossibilitiesOS.Append(pos);

			int khvoAPossibiltyObject = pos.Hvo;
			int khvoAPersonObject = person.Hvo;
			Assert.AreEqual("CmPerson", CmPossibility.CreateFromDBObject(Cache, khvoAPersonObject).GetType().Name);
			Assert.AreEqual("CmPossibility", CmPossibility.CreateFromDBObject(Cache, khvoAPossibiltyObject).GetType().Name);

			// Now try it not assuming anything about the class type (use the method on CmObject)
			// CmObject uses a different, less efficient method.
			Assert.AreEqual("CmPerson", CmObject.CreateFromDBObject(Cache, khvoAPersonObject).GetType().Name);

			// trying to turn a possibility into a person should throw an exception
			CmPerson.CreateFromDBObject(Cache, khvoAPossibiltyObject);
		}
Beispiel #10
0
		internal CmPossibility CreateColumn(CmPossibility parent, XmlNode spec)
		{
			CmPossibility result = (CmPossibility)parent.SubPossibilitiesOS.Append(new CmPossibility());
			SetNameAndChildColumns(result, spec);
			return result;
		}
Beispiel #11
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Adds a translation type to the list of types that a CmTranslation can be.
		/// </summary>
		/// <param name="type">The GUID that uniquely identifies the translation type</param>
		/// ------------------------------------------------------------------------------------
		private void AddTransType(Guid type)
		{
			CmPossibility transType = new CmPossibility();
			m_lp.TranslationTagsOA.PossibilitiesOS.Append(transType);
			//int hvoTransType = m_cacheBase.NewHvo(CmPossibility.kClassId);
			//CmPossibility transType = new CmPossibility(m_fdoCache, hvoTransType);
			//m_cacheBase.SetBasicProps(hvoTransType, m_lp.TranslationTagsOAHvo, CmPossibility.kClassId,
			//    (int)CmPossibilityList.CmPossibilityListTags.kflidPossibilities, 0);
			transType.Guid = type;
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Initializes scripture annotation categories
		/// </summary>
		/// ------------------------------------------------------------------------------------
		public void InitializeScrAnnotationCategories()
		{
			CheckDisposed();
			// Initialize the annotation category possibility list.
			IScripture scr = m_lp.TranslatedScriptureOA;
			scr.NoteCategoriesOAHvo = m_cacheBase.NewHvo(CmPossibilityList.kClassId);

			// Add an annotation category (for Discourse)
			m_categoryDiscourse = new CmPossibility();
			scr.NoteCategoriesOA.PossibilitiesOS.Append(m_categoryDiscourse);
			m_categoryDiscourse.Name.SetAlternative("Discourse", s_wsHvos.En);
			m_cacheBase.CacheGuidProp(m_categoryDiscourse.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// Add an annotation category (for Grammar)
			m_categoryGrammar = new CmPossibility();
			scr.NoteCategoriesOA.PossibilitiesOS.Append(m_categoryGrammar);
			m_categoryGrammar.Name.SetAlternative("Grammar", s_wsHvos.En);
			m_cacheBase.CacheGuidProp(m_categoryGrammar.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// add a sub-annotation category (for "Pronominal reference")
			m_categoryGrammar_PronominalRef = new CmPossibility();
			m_categoryGrammar.SubPossibilitiesOS.Append(m_categoryGrammar_PronominalRef);
			m_categoryGrammar_PronominalRef.Name.SetAlternative("Pronominal reference", s_wsHvos.En);
			m_cacheBase.CacheGuidProp(m_categoryGrammar_PronominalRef.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// add a sub-sub-annotation category (for "Extended use")
			m_categoryGrammar_PronominalRef_ExtendedUse = new CmPossibility();
			m_categoryGrammar_PronominalRef.SubPossibilitiesOS.Append(m_categoryGrammar_PronominalRef_ExtendedUse);
			m_categoryGrammar_PronominalRef_ExtendedUse.Name.SetAlternative("Extended use", s_wsHvos.En);
			m_cacheBase.CacheGuidProp(m_categoryGrammar_PronominalRef_ExtendedUse.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// Add an annotation category (for Gnarly)
			m_categoryGnarly = new CmPossibility();
			scr.NoteCategoriesOA.PossibilitiesOS.Append(m_categoryGnarly);
			m_categoryGnarly.Name.SetAlternative("Gnarly", s_wsHvos.En);
			m_cacheBase.CacheGuidProp(m_categoryGnarly.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());
		}
		private void SetNameAndChildTagsFromXml(CmPossibility parent, XmlNode spec)
		{
			parent.Name.AnalysisDefaultWritingSystem = XmlUtils.GetManditoryAttributeValue(spec, "name");
			if (spec.Name == "subpossibility")
				parent.Abbreviation.AnalysisDefaultWritingSystem = XmlUtils.GetManditoryAttributeValue(spec, "abbreviation");
			foreach (XmlNode child in spec.ChildNodes)
				CreateTagFromXml(parent, child);
		}
Beispiel #14
0
			public override void HandleSelect(int index)
			{
				CheckDisposed();
				int hvoPos = Items[index];
				ICmPossibility possibility = new CmPossibility(m_caches.MainCache, hvoPos);

				// Called only if it's a combo box.
				SelectItem(Items[index], possibility.Name.BestVernacularAnalysisAlternative.Text);
			}
Beispiel #15
0
			public void	PrintCmPossibility(FdoCache fcTLP)
			{
				CmPossibility p = new CmPossibility(fcTLP, 190);
				Console.WriteLine(p.Description.AnalysisDefaultWritingSystem);
			}
		public void FindOrCreateAnnotationCategory_NameOnlyButAbsent()
		{
			int initialCategoryCount = m_possList.PossibilitiesOS.Count;
			int hvoPossibility = m_scr.NoteCategoriesOA.FindOrCreatePossibility(
				"New category", m_wsSpanish, false);

			// Confirm that a new category was added and that FindOrCreatePossibility returns it.
			Assert.AreEqual(initialCategoryCount + 1, m_possList.PossibilitiesOS.Count);
			CmPossibility newCategory = new CmPossibility(Cache, hvoPossibility);
			int actualWs;
			Assert.AreEqual("New category", newCategory.Name.GetAlternativeOrBestTss(m_wsSpanish, out actualWs).Text,
				"The newly-created category was not found");
			Assert.AreEqual(m_wsSpanish, actualWs,
				"The new category name was not found in the expected writing system");
		}
		/// <summary>
		/// Checks for and reports any disallowed tag list moves.
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="movingTagItem">The proposed tag item to move.</param>
		/// <param name="hvoSubListRoot">The hvo of the top-level Tag Type Possibility containing the moving item.</param>
		/// <param name="hvoMainTagList">The hvo of the main PossiblityList grouping all TextMarkup Tags.</param>
		/// <param name="hvoDest">The hvo of the destination tag item.</param>
		/// <returns>true if we found and reported a bad move.</returns>
		private bool CheckAndReportBadTagListMove(FdoCache cache, CmPossibility movingTagItem, int hvoSubListRoot,
			int hvoMainTagList, int hvoDest)
		{
			// Check if movingTagItem is a top-level Tag Type.
			if (movingTagItem.Hvo == hvoSubListRoot)
			{
				if (hvoDest == hvoMainTagList) // top-level Tag Type can move to main list (probably already there)
					return false;

				// The moving item is a top-level Tag Type, it cannot be demoted.
				MessageBox.Show(m_tree, xWorksStrings.ksCantDemoteTagList,
								xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return true;
			}
			// Unless something is badly wrong, the destination is either the tag type root,
			// a tag one level down from the root, or the base list.
			if (cache.GetOwnerOfObject(hvoDest) == hvoMainTagList)
			{
				// Destination is Tag Type root, not a problem
				return false;
			}
			if (hvoDest == hvoMainTagList)
			{
				// Can't promote tag to Tag Type root
				MessageBox.Show(m_tree, xWorksStrings.ksCantPromoteTag,
								xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return true;
			}
			// This would give us hierarchy too deep (at least for now)
			MessageBox.Show(m_tree, xWorksStrings.ksTagListTooDeep,
							xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning);
			return true;
		}
		/// <summary>
		/// Checks for and reports any disallowed discourse template moves.
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="movingColumn">The proposed possibility item (template column) to move.</param>
		/// <param name="hvoTemplate">The hvo of the affected Chart Template (only 'default' exists so far).</param>
		/// <param name="hvoTemplateList">The hvo of the Template List.</param>
		/// <param name="hvoDest">The hvo of the destination item.</param>
		/// <returns>true means we found and reported a bad move.</returns>
		private bool CheckAndReportBadDiscourseTemplateMove(FdoCache cache, CmPossibility movingColumn, int hvoTemplate,
			int hvoTemplateList, int hvoDest)
		{
			// First, check whether we're allowed to manipulate this column at all. This is the same check as
			// whether we're allowed to delete it.
			if (movingColumn.CheckAndReportProtectedChartColumn())
				return true;
			// Other things being equal, we now need to make sure we aren't messing up the chart levels
			// Unless something is badly wrong, the destination is either the root template,
			// a column group one level down from the root template, a column two levels down,
			// or the base list.
			if (hvoDest == hvoTemplateList)
			{
				MessageBox.Show(m_tree, xWorksStrings.ksCantPromoteGroupToTemplate,
								xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return true;
			}
			// if the destination IS the root, that's fine...anything can move there.
			if (hvoDest == hvoTemplate)
				return false;
			// It's OK to move a leaf to a group (one level down from the root, as long as
			// the destination 'group' isn't a column that's in use.
			bool moveColumnIsLeaf = movingColumn.SubPossibilitiesOS.Count == 0;
			if (cache.GetOwnerOfObject(hvoDest) == hvoTemplate && moveColumnIsLeaf)
			{
				CmPossibility dest = (CmPossibility.CreateFromDBObject(cache, hvoDest))
									 as CmPossibility;
				// If it isn't already a group, we can only turn it into one if it's empty
				if (dest.SubPossibilitiesOS.Count == 0)
					return dest.CheckAndReportProtectedChartColumn();
				// If it's already a group it should be fine as a destination.
				return false;
			}
			// Anything else represents an attempt to make the tree too deep, e.g., moving a
			// column into child column, or a group into another group.
			MessageBox.Show(m_tree, xWorksStrings.ksTemplateTooDeep,
							xWorksStrings.ksProhibitedMovement, MessageBoxButtons.OK, MessageBoxIcon.Warning);
			return true;
		}
Beispiel #19
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Create an object that happens to have a MultiUnicode property and return its
		/// accessor.
		/// </summary>
		/// <returns>The accessor</returns>
		/// ------------------------------------------------------------------------------------
		public MultiUnicodeAccessor CreateArbitraryMultiUnicodeAccessor()
		{
			CheckDisposed();
			CmPossibility poss = new CmPossibility(Cache, m_cacheBase.NewHvo(CmPossibility.kClassId));
			return poss.Abbreviation;
		}
Beispiel #20
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Initializes annotation categories - we used to use scripture.ScriptureNotesCategories,
		/// now we use LangProject.AffixCategories instead. It doesn't really matter as long
		/// as we set the variables that the tests expect.
		/// </summary>
		/// ------------------------------------------------------------------------------------
		public void InitializeAnnotationCategories()
		{
			CheckDisposed();
			// Initialize the annotation category possibility list.
			m_lp.AffixCategoriesOAHvo = m_cacheBase.NewHvo(CmPossibilityList.kClassId);

			// Add an annotation category (for Discourse)
			m_categoryDiscourse = new CmPossibility();
			m_lp.AffixCategoriesOA.PossibilitiesOS.Append(m_categoryDiscourse);
			m_cacheBase.CacheGuidProp(m_categoryDiscourse.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// Add an annotation category (for Grammar)
			m_categoryGrammar = new CmPossibility();
			m_lp.AffixCategoriesOA.PossibilitiesOS.Append(m_categoryGrammar);
			m_cacheBase.CacheGuidProp(m_categoryGrammar.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// add a sub-annotation category (for "Pronominal reference")
			m_categoryGrammar_PronominalRef = new CmPossibility();
			m_categoryGrammar.SubPossibilitiesOS.Append(m_categoryGrammar_PronominalRef);
			m_cacheBase.CacheGuidProp(m_categoryGrammar_PronominalRef.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// add a sub-sub-annotation category (for "Extended use")
			m_categoryGrammar_PronominalRef_ExtendedUse = new CmPossibility();
			m_categoryGrammar_PronominalRef.SubPossibilitiesOS.Append(m_categoryGrammar_PronominalRef_ExtendedUse);
			m_cacheBase.CacheGuidProp(m_categoryGrammar_PronominalRef_ExtendedUse.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());

			// Add an annotation category (for Gnarly)
			m_categoryGnarly = new CmPossibility();
			m_lp.AffixCategoriesOA.PossibilitiesOS.Append(m_categoryGnarly);
			m_cacheBase.CacheGuidProp(m_categoryGnarly.Hvo,
				(int)CmObjectFields.kflidCmObject_Guid, Guid.NewGuid());
		}
Beispiel #21
0
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Adds an empty template to the DiscourseData object.
		/// </summary>
		/// <param name="name">Template name. (default)</param>
		/// <param name="discData"></param>
		/// <returns></returns>
		/// ------------------------------------------------------------------------------------
		public ICmPossibility AddEmptyTemplateToDiscData(string name, IDsDiscourseData discData)
		{
			CheckDisposed();

			if (discData == null)
				discData = m_lp.DiscourseDataOA = new DsDiscourseData();

			// Add an empty PossibilityList
			discData.ConstChartTemplOA = new CmPossibilityList();
			// Add an empty template object
			ICmPossibility template = new CmPossibility(Cache, m_cacheBase.NewHvo(CmPossibility.kClassId));

			// Add the template to the DiscourseData object
			m_cacheBase.AppendToFdoVector(discData.ConstChartTemplOAHvo,
				(int)DsDiscourseData.DsDiscourseDataTags.kflidConstChartTempl, template.Hvo);

			// Setup the new template
			m_cacheBase.SetBasicProps(template.Hvo, discData.ConstChartTemplOAHvo, (int)CmPossibility.kClassId,
				(int)DsDiscourseData.DsDiscourseDataTags.kflidConstChartTempl, 1);
			Cache.SetMultiStringAlt(template.Hvo, (int)CmPossibility.CmPossibilityTags.kflidName,
				Cache.LanguageEncodings.GetWsFromIcuLocale("en"), Cache.MakeAnalysisTss(name));

			return template; // This template has no columns, so far!!
		}
Beispiel #22
0
		/// <summary>
		/// Create a CmPossibility based on an XML specification of a constituent chart template.
		/// See CreateDefaultTemplate for an example.
		/// </summary>
		/// <param name="spec"></param>
		/// <returns></returns>
		public CmPossibility CreateChartTemplate(XmlNode spec)
		{
			// Make sure we have the containing objects; if not create them.
			IDsDiscourseData dData = m_cache.LangProject.DiscourseDataOA;
			if (dData == null)
			{
				dData = new DsDiscourseData();
				m_cache.LangProject.DiscourseDataOA = dData;
			}
			// Also make sure it has a templates list
			ICmPossibilityList templates = dData.ConstChartTemplOA;
			if (templates == null)
			{
				templates = new CmPossibilityList();
				dData.ConstChartTemplOA = templates;
			}
			CmPossibility template = new CmPossibility();
			m_cache.LangProject.DiscourseDataOA.ConstChartTemplOA.PossibilitiesOS.Append(template);
			SetNameAndChildColumns(template, spec);
			return template;
		}
		public void FindOrCreateAnnotationCategory_AbsentCategory_FirstLevel()
		{
			Assert.AreEqual(2, m_possList.PossibilitiesOS.Count);

			int hvoPossibility = m_scr.NoteCategoriesOA.FindOrCreatePossibility(
				"New Category", m_wsSpanish);

			// Confirm that a "New Category" category has been created at the top level.
			Assert.AreEqual(3, m_possList.PossibilitiesOS.Count,
				"Another category CmPossibility should have been created on first level.");
			CmPossibility newCategory = new CmPossibility(m_inMemoryCache.Cache, hvoPossibility);
			Assert.AreEqual("New Category", newCategory.Name.GetAlternativeTss(m_wsSpanish).Text);
			Assert.IsFalse(newCategory.Owner is ICmPossibility,
				"Category should have been created at the top level");
		}
		internal CmPossibility CreateTagFromXml(CmPossibility parent, XmlNode spec)
		{
			CmPossibility result = (CmPossibility)parent.SubPossibilitiesOS.Append(new CmPossibility());
			SetNameAndChildTagsFromXml(result, spec);
			return result;
		}
		public void FindOrCreateAnnotationCategory_AbsentCategory_ThirdLevel()
		{
			Assert.AreEqual(1, m_possList.PossibilitiesOS[1].SubPossibilitiesOS[1].SubPossibilitiesOS.Count);

			int hvoPossibility = m_scr.NoteCategoriesOA.FindOrCreatePossibility(
				"Level 1a" + StringUtils.kchObject + "New Level 2 Category" + StringUtils.kchObject +
				"New Level 3 Category", m_wsSpanish);

			// Confirm that a new third-level node was created.
			CmPossibility newCategory = new CmPossibility(m_inMemoryCache.Cache, hvoPossibility);
			Assert.AreEqual("New Level 3 Category", newCategory.Name.GetAlternativeTss(m_wsSpanish).Text);
			// Confirm that a new parent node for the third-level node was created on the second level.
			Assert.AreEqual(1, m_possList.PossibilitiesOS[0].SubPossibilitiesOS.Count,
				"Another category CmPossibility should have been created on second level under Level 1a.");
			Assert.AreEqual(1, m_possList.PossibilitiesOS[0].SubPossibilitiesOS[0].SubPossibilitiesOS.Count);

			Assert.IsTrue(newCategory.Owner is ICmPossibility,
				"Category should have been created under another category CmPossibility");
			Assert.AreEqual("New Level 2 Category",
				((CmPossibility)newCategory.Owner).Name.GetAlternativeTss(m_wsSpanish).Text);
			Assert.IsTrue(((CmPossibility)newCategory.Owner).Owner is ICmPossibility);
			CmPossibility level1Possibility = (CmPossibility)((CmPossibility)newCategory.Owner).Owner;
			Assert.AreEqual("Level 1a", level1Possibility.Name.GetAlternativeTss(m_wsSpanish).Text);
		}
		public ICmPossibilityList CreateDefaultListFromXML()
		{
			// Set up default XML to load in case permanent list is empty.
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(
			  "<taglist name=\"Text Markup Tags\">"
			  + "<possibility name=\"" + kFTO_RRG_Semantics + "\">"
				+ "<subpossibility name=\"" + kFTO_Actor + "\" abbreviation=\"ACT\"/>"
				+ "<subpossibility name=\"" + kFTO_Non_Macrorole + "\" abbreviation=\"NON-MR\"/>"
				+ "<subpossibility name=\"" + kFTO_Undergoer + "\" abbreviation=\"UND\"/>"
			  + "</possibility>"
			  + "<possibility name=\"" + kFTO_Syntax + "\">"
				+ "<subpossibility name=\"" + kFTO_Noun_Phrase + "\" abbreviation=\"NP\"/>"
				+ "<subpossibility name=\"" + kFTO_Verb_Phrase + "\" abbreviation=\"VP\"/>"
				+ "<subpossibility name=\"" + kFTO_Adjective_Phrase + "\" abbreviation=\"AdjP\"/>"
			  + "</possibility>"
			+ "</taglist>");

			XmlNode spec = doc.DocumentElement;
			int hvoPoss = Cache.CreateObject(CmPossibilityList.kClassId);
			ICmPossibilityList possList = CmPossibilityList.CreateFromDBObject(Cache, hvoPoss);

			foreach (XmlNode tagListXml in spec.ChildNodes)
			{
				CmPossibility tagList = new CmPossibility();
				possList.PossibilitiesOS.Append(tagList);
				SetNameAndChildTagsFromXml(tagList, tagListXml); // One Possibility (Syntax, Semantics, etc.)
			}
			return possList;
		}
Beispiel #27
0
		/// -----------------------------------------------------------------------------------
		/// <summary>
		/// Using the possibility name and writing system, find a possibility, or create a new
		/// one if it doesn't exist.
		/// </summary>
		/// <param name="possibilityPath">name of the possibility path delimited by ORCs (if
		/// the fFullPath is <c>false</c></param>
		/// <param name="ws">writing system</param>
		/// <param name="fFullPath">whether the full path is provided (possibilities that are
		/// not on the top level will provide the full possibilityPath with ORCs between the
		/// possibility names of each level)</param>
		/// -----------------------------------------------------------------------------------
		public int FindOrCreatePossibility(string possibilityPath, int ws, bool fFullPath)
		{
			if (m_possibilityMap == null)
				m_possibilityMap = new Dictionary<int, Dictionary<string, int>>();

			if (!m_possibilityMap.ContainsKey(ws))
				CacheNoteCategories(PossibilitiesOS, ws);

			int hvo;
			if (!fFullPath)
			{
				// The category name is not found in the hash table for the first level and
				// only the category name is provided. So... we will search through subpossibilities
				// for the first name that matches. If we don't find a matching name, we will
				// create a new possibility.
				hvo = FindPossibilityByName(PossibilitiesOS, possibilityPath, ws);
				if (hvo > 0)
					return hvo;
			}

			Dictionary<string, int> wsHashTable;
			if (m_possibilityMap.TryGetValue(ws, out wsHashTable))
			{
				if (wsHashTable.TryGetValue(possibilityPath, out hvo))
					return hvo;

				// Parse the category path and create any missing categories.
				FdoOwningSequence<ICmPossibility> possibilityList = PossibilitiesOS;
				int level = 1;
				foreach (string strName in possibilityPath.Split(StringUtils.kchObject))
				{
					string possibilityKey = GetPossibilitySubPath(possibilityPath, level);
					ICmPossibility possibility;
					if (!wsHashTable.TryGetValue(possibilityKey, out hvo))
					{
						// Category is missing, so create a new one.
						possibility = new CmPossibility();
						possibilityList.Append(possibility);
						possibility.Abbreviation.SetAlternative(strName, ws);
						possibility.Name.SetAlternative(strName, ws);
						m_possibilityMap[ws][possibilityKey] = hvo = possibility.Hvo;
					}
					else
					{
						// Get the category for this level that already exists.
						possibility = new CmPossibility(m_cache, hvo);
					}

					// Set the current possibility list to the category subpossibility list
					// as we continue our search.
					possibilityList = possibility.SubPossibilitiesOS;
					level++;
				}

				if (hvo != -1)
					return hvo; // only return the hvo if we were able to create a valid one
			}

			throw new InvalidProgramException(
				"Unable to create a dictionary for the writing system in the annotation category hash table");
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Fill in a note category node in the database from an XML node
		/// </summary>
		/// <param name="hvoOwner">owner's id</param>
		/// <param name="ownFlid">field ID in which the category is owned</param>
		/// <param name="index">0-based position in the list of possibilities</param>
		/// <param name="node">XML node to get info from</param>
		/// ------------------------------------------------------------------------------------
		private void CreateNoteCategory(int hvoOwner, int ownFlid, int index, XmlNode node)
		{
			int hvoPossibility = m_scr.Cache.MainCacheAccessor.MakeNewObject(
				CmPossibility.kClassId, hvoOwner, ownFlid, index);
			Debug.Assert(hvoPossibility > 0);
			CmPossibility category = new CmPossibility(m_scr.Cache, hvoPossibility);

			SetMultiUnicodeAlternatives(category.Name, node.SelectNodes("name"));
			SetMultiUnicodeAlternatives(category.Abbreviation, node.SelectNodes("abbreviation"));

			// get the description for the note category
			XmlNodeList descNodes = node.SelectNodes("description");
			if (descNodes != null)
			{
				foreach (XmlNode descNode in descNodes)
				{
					int ws = GetWs(descNode.Attributes);
					string alternative = descNode.InnerText;
					if (ws > 0 && alternative != null && alternative != string.Empty)
						category.Description.GetAlternative(ws).UnderlyingTsString =
							m_strFactory.MakeString(alternative, ws);
					// REVIEW: What should we do when the writing system is not defined in the database?
				}
			}

			// create note categories for any subcategories
			int subIndex = 0;
			foreach (XmlNode categoryNode in node.SelectNodes("category"))
			{
				CreateNoteCategory(hvoPossibility,
					(int)CmPossibility.CmPossibilityTags.kflidSubPossibilities,
					subIndex++, categoryNode);
			}
		}
		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);
		}