Пример #1
0
		private void CopyAnalysesToNewWordform(ICollection<ISegment> originalOccurencesInTexts, IWfiWordform wfOld, IWfiWordform wfNew)
		{
			var shiftedSegments = new List<ISegment>(originalOccurencesInTexts.Count);
			foreach (IWfiAnalysis oldAnalysis in wfOld.AnalysesOC)
			{
				// Only copy approved analyses.
				if (oldAnalysis.GetAgentOpinion(m_cache.LangProject.DefaultUserAgent) != Opinions.approves)
					continue;

				IWfiAnalysis newAnalysis = FactWfiAnal.Create();
				wfNew.AnalysesOC.Add(newAnalysis);
				foreach (var segment in originalOccurencesInTexts)
				{
					if (!m_changedParas.ContainsKey(segment.Owner.Hvo))
						continue; // Skip shifting it for items that were not checked

					var analysisIdx = segment.AnalysesRS.IndexOf(oldAnalysis);
					while (analysisIdx > -1)
					{
						shiftedSegments.Add(segment);
						segment.AnalysesRS.RemoveAt(analysisIdx);
						segment.AnalysesRS.Insert(analysisIdx, newAnalysis);
						analysisIdx = segment.AnalysesRS.IndexOf(oldAnalysis);
					}
				}
				foreach (var shiftedSegment in shiftedSegments)
				{
					originalOccurencesInTexts.Remove(shiftedSegment);
				}
				shiftedSegments.Clear();
				foreach (IWfiGloss oldGloss in oldAnalysis.MeaningsOC)
				{
					IWfiGloss newGloss = FactWfiGloss.Create();
					newAnalysis.MeaningsOC.Add(newGloss);
					newGloss.Form.CopyAlternatives(oldGloss.Form);
					foreach (var segment in originalOccurencesInTexts)
					{
						if (!m_changedParas.ContainsKey(segment.Owner.Hvo))
							continue; // Skip shifting it for items that were not checked

						var glossIdx = segment.AnalysesRS.IndexOf(oldGloss);
						while (glossIdx > -1)
						{
							shiftedSegments.Add(segment);
							segment.AnalysesRS.RemoveAt(glossIdx);
							segment.AnalysesRS.Insert(glossIdx, newGloss);
							glossIdx = segment.AnalysesRS.IndexOf(oldGloss);
						}
					}
				}
				foreach (var shiftedSegment in shiftedSegments)
				{
					originalOccurencesInTexts.Remove(shiftedSegment);
				}
				foreach (IWfiMorphBundle bundle in oldAnalysis.MorphBundlesOS)
				{
					IWfiMorphBundle newBundle = FactWfiMB.Create();
					newAnalysis.MorphBundlesOS.Add(newBundle);
					newBundle.Form.CopyAlternatives(bundle.Form);
					newBundle.SenseRA = bundle.SenseRA;
					newBundle.MorphRA = bundle.MorphRA;
					newBundle.MsaRA = bundle.MsaRA;
				}
			}
		}
Пример #2
0
		private void ProcessAnalysesAndLexEntries(ProgressDialogWorkingOn progress, IWfiWordform wfOld, IWfiWordform wfNew)
		{
			wfOld.SpellingStatus = (int)SpellingStatusStates.incorrect;

			//if (UpdateLexicalEntries)
			//{
			//    foreach (IWfiAnalysis wa in wfOld.AnalysesOC)
			//    {
			//        if (wa.MorphBundlesOS.Count == 1)
			//        {
			//        }
			//    }
			//}
			if (!KeepAnalyses)
			{
				// Remove multi-morpheme anals in src wf.
				List<IWfiAnalysis> goners = new List<IWfiAnalysis>();
				foreach (IWfiAnalysis goner in wfOld.AnalysesOC)
				{
					if (goner.MorphBundlesOS.Count > 1)
					{
						goners.Add(goner);
				}
				}
				foreach (IWfiAnalysis goner in goners)
				{
					IWfiWordform wf = goner.OwnerOfClass<IWfiWordform>();
					wf.AnalysesOC.Remove(goner);
				}
				goners.Clear();
			}
			if (UpdateLexicalEntries)
			{
				// Change LE allo on single morpheme anals.
				foreach (IWfiAnalysis update in wfOld.AnalysesOC)
				{
					if (update.MorphBundlesOS.Count != 1)
						continue; // Skip any with zero or more than one.

					IWfiMorphBundle mb = update.MorphBundlesOS[0];
					ITsString tss = mb.Form.get_String(m_vernWs);
					string srcForm = tss.Text;
					if (srcForm != null)
					{
						// Change morph bundle form.
						mb.Form.set_String(m_vernWs, NewSpelling);
					}
					IMoForm mf = mb.MorphRA;
					if (mf != null)
					{
						mf.Form.set_String(m_vernWs, NewSpelling);
					}
				}
			}

			// Move remaining anals from src wf to new wf.
			// This changes the owners of the remaining ones,
			// since it is an owning property.
			var analyses = new List<IWfiAnalysis>();
			analyses.AddRange(wfOld.AnalysesOC);
			foreach (var anal in analyses)
				wfNew.AnalysesOC.Add(anal);
		}
Пример #3
0
		/// <summary>
		/// Set up the referring lexical entries of an entry
		/// </summary>
		/// <param name="lexicalRelationHvos">an array of lexical relation HVOs</param>
		private void SetupLexRelsForEntry(int[] lexicalRelationHvos)
		{
			m_cdaTemp.CacheVecProp(m_hvoEntry, RelatedWordsVc.ktagLexRels, lexicalRelationHvos, lexicalRelationHvos.Length);

			var references = new List<int>();
			var lexRefRepository = m_cache.ServiceLocator.GetInstance<ILexReferenceRepository>();
			var lexEntry = m_cache.ServiceLocator.GetInstance<ILexEntryRepository>().GetObject(m_hvoEntry);
			var targets = new HashSet<ICmObject>(lexEntry.AllSenses.Cast<ICmObject>()) { lexEntry };

			foreach (var hvoLexRel in lexicalRelationHvos)
			{
				var lexReference = lexRefRepository.GetObject(hvoLexRel);
				foreach (ICmObject target in lexReference.TargetsRS)
				{
					// If at least one target is the lex entry or one of its senses.
					if ((from t in lexReference.TargetsRS where targets.Contains(t) select t).FirstOrDefault() != null &&
						(from t in lexReference.TargetsRS where !targets.Contains(t) select t).FirstOrDefault() != null)
					{
						ILexEntry targetEntry = target is ILexSense
							? target.OwnerOfClass(LexEntryTags.kClassId) as ILexEntry
							: target as ILexEntry;
						if (targetEntry != null && targetEntry.Hvo != m_hvoEntry && targetEntry.LexemeFormOA != null && targetEntry.LexemeFormOA.Form != null)
						{
							references.Add(targetEntry.Hvo);
							m_cdaTemp.CacheStringProp(targetEntry.Hvo, RelatedWordsVc.ktagName, targetEntry.HeadWord);
						}
					}
				}

				if (references.Count > 0)
				{
					m_cdaTemp.CacheVecProp(hvoLexRel, RelatedWordsVc.ktagWords, references.ToArray(), references.Count);
					references.Clear();
				}
			}
		}
Пример #4
0
		/// <summary>
		/// Set up the referring semantic domains for the domains found of an entry
		/// </summary>
		/// <param name="semanticDomainHvos">an array of semantic domain HVOs</param>
		void SetupDomainsForEntry(int[] semanticDomainHvos)
		{
			m_cdaTemp.CacheVecProp(m_hvoEntry, RelatedWordsVc.ktagDomains, semanticDomainHvos, semanticDomainHvos.Length);

			var entries = new List<int>();
			var semanticDomainRepository = m_cache.ServiceLocator.GetInstance<ICmSemanticDomainRepository>();

			foreach (var semanticDomainhvo in semanticDomainHvos)
			{
				var semanticDomain = semanticDomainRepository.GetObject(semanticDomainhvo);
				foreach (ICmObject obj in semanticDomain.ReferringObjects)
				{
					if (obj is ILexSense && (obj as ILexSense).SemanticDomainsRC.Contains(semanticDomain))
					{
						var entry = obj.OwnerOfClass(LexEntryTags.kClassId) as ILexEntry;
						if (entry != null && entry.LexemeFormOA != null && entry.LexemeFormOA.Form != null)
						{
							entries.Add(entry.Hvo);
							m_cdaTemp.CacheStringProp(entry.Hvo, RelatedWordsVc.ktagName,
								entry.LexemeFormOA.Form.VernacularDefaultWritingSystem);
						}
					}
				}
				if (entries.Count > 0)
				{
					m_cdaTemp.CacheVecProp(semanticDomainhvo, RelatedWordsVc.ktagWords, entries.ToArray(), entries.Count);
					entries.Clear();
				}
			}
		}
Пример #5
0
		/// <summary>
		/// Core of the DoIt method, may be called with or without progress dialog.
		/// </summary>
		/// <param name="dlg"></param>
		private void CoreDoIt(ProgressDialogWorkingOn dlg)
		{
			// While we do the changes which we can much more efficiently Undo/Redo in this action,
			// we don't want the various changes we make generating masses of SqlUndoActions.
			IActionHandler handler = m_cache.ActionHandlerAccessor;
			m_cache.MainCacheAccessor.SetActionHandler(null);
			m_hvoNewWf = WfiWordform.FindOrCreateWordform(m_cache, m_newSpelling, m_vernWs, true);
			try
			{
				foreach (KeyValuePair<int, ParaChangeInfo> pair in m_changedParas)
				{
					UpdateOffsets(pair.Key, pair.Value);
					// Review JohnT: should we do the PropChanged, or not?? If not, we should force
					// a Refresh when the dialog closes.
					if (!pair.Value.OldContents.Equals(pair.Value.NewContents))
						pair.Value.SetString(m_cache, pair.Value.NewContents, true);
					UpdateProgress(dlg);
				}
				UpdateInstanceOf(m_hvoNewWf, dlg);
			}
			finally
			{
				m_cache.MainCacheAccessor.SetActionHandler(handler);
			}
			if (dlg != null)
				dlg.WorkingOnText = MEStrings.ksDealingAnalyses;
			m_cache.BeginUndoTask(string.Format(MEStrings.ksUndoChangeSpelling, m_oldSpelling, m_newSpelling),
				string.Format(MEStrings.ksRedoChangeSpelling, m_oldSpelling, m_newSpelling));
			m_cache.ActionHandlerAccessor.AddAction(this);
			UpdateProgress(dlg);

			// The destination wordform really exists and should be marked correct.
			IWfiWordform wf = WfiWordform.CreateFromDBObject(m_cache, m_hvoNewWf);
			m_oldOccurrencesNewWf = wf.OccurrencesInTexts.ToArray();
			m_fWasNewSpellingCorrect = wf.SpellingStatus == (int)SpellingStatusStates.correct;
			wf.SpellingStatus = (int)SpellingStatusStates.correct;
			EnchantHelper.SetSpellingStatus(m_newSpelling, m_vernWs,
				m_cache.LanguageWritingSystemFactoryAccessor, true);
			UpdateProgress(dlg);

			m_hvoOldWf = WfiWordform.FindOrCreateWordform(m_cache, m_oldSpelling, m_vernWs, true);
			IWfiWordform wfOld = WfiWordform.CreateFromDBObject(m_cache, m_hvoOldWf);
			m_oldOccurrencesOldWf = wfOld.OccurrencesInTexts.ToArray();
			m_fWasOldSpellingCorrect = wfOld.SpellingStatus == (int)SpellingStatusStates.correct;
			UpdateProgress(dlg);

			// Compute new occurrence lists, save and cache
			Set<int> changes = new Set<int>();
			foreach (ParaChangeInfo info in m_changedParas.Values)
				changes.AddRange(info.Changes);
			if (AllChanged)
			{
				m_newOccurrencesOldWf = new int[0]; // no remaining occurrences
			}
			else
			{
				// Only some changed, need to figure m_newOccurrences
				List<int> newOccurrencesOldWf = new List<int>();
				foreach (int hvo in m_oldOccurrencesOldWf)
					if (!changes.Contains(hvo))
						newOccurrencesOldWf.Add(hvo);
				m_newOccurrencesOldWf = newOccurrencesOldWf.ToArray();
			}
			UpdateProgress(dlg);
			List<int> newOccurrences = new List<int>(m_oldOccurrencesNewWf.Length + changes.Count);
			newOccurrences.AddRange(m_oldOccurrencesNewWf);
			foreach (int hvo in changes)
			{
				if (m_cache.IsDummyObject(hvo))
					// if this is a dummy annotation, make sure we update the owner
					m_cache.VwCacheDaAccessor.CacheObjProp(hvo, (int)CmObjectFields.kflidCmObject_Owner, m_hvoNewWf);
				newOccurrences.Add(hvo);
			}
			m_newOccurrencesNewWf = newOccurrences.ToArray();
			int vhId = BaseVirtualHandler.GetInstalledHandlerTag(m_cache, "WfiWordform", "OccurrencesInTexts");
			m_cache.VwCacheDaAccessor.CacheVecProp(m_hvoOldWf, vhId, m_newOccurrencesOldWf, m_newOccurrencesOldWf.Length);
			m_cache.VwCacheDaAccessor.CacheVecProp(m_hvoNewWf, vhId, m_newOccurrencesNewWf, m_newOccurrencesNewWf.Length);
			SendCountVirtualPropChanged(m_hvoNewWf);
			SendCountVirtualPropChanged(m_hvoOldWf);
			UpdateProgress(dlg);

			// Deal with analyses.
			if (CopyAnalyses)
			{
				foreach (WfiAnalysis analysis in wfOld.AnalysesOC)
				{
					// Only copy approved analyses.
					if (analysis.GetAgentOpinion(m_cache.LangProject.DefaultUserAgent) != Opinions.approves)
						continue;
					IWfiAnalysis newAnalysis = wf.AnalysesOC.Add(new WfiAnalysis());
					foreach (WfiGloss gloss in analysis.MeaningsOC)
					{
						IWfiGloss newGloss = newAnalysis.MeaningsOC.Add(new WfiGloss());
						newGloss.Form.CopyAlternatives(gloss.Form);
					}
					foreach (WfiMorphBundle bundle in analysis.MorphBundlesOS)
					{
						IWfiMorphBundle newBundle = newAnalysis.MorphBundlesOS.Append(new WfiMorphBundle());
						newBundle.Form.CopyAlternatives(bundle.Form);
						newBundle.SenseRA = bundle.SenseRA;
						newBundle.MorphRA = bundle.MorphRA;
						newBundle.MsaRA = bundle.MsaRA;
					}
				}
			}
			UpdateProgress(dlg);
			if (AllChanged)
			{
				wfOld.SpellingStatus = (int)SpellingStatusStates.incorrect;
				EnchantHelper.SetSpellingStatus(m_oldSpelling, m_vernWs,
					m_cache.LanguageWritingSystemFactoryAccessor, false);
				if (UpdateLexicalEntries)
				{
					foreach (WfiAnalysis wa in wfOld.AnalysesOC)
					{
						if (wa.MorphBundlesOS.Count == 1)
						{
						}
					}
				}
				if (!KeepAnalyses)
				{
					// Remove multi-morpheme anals in src wf.
					List<IWfiAnalysis> goners = new List<IWfiAnalysis>();
					foreach (IWfiAnalysis goner in wfOld.AnalysesOC)
					{
						if (goner.MorphBundlesOS.Count > 1)
							goners.Add(goner);
					}
					foreach (IWfiAnalysis goner in goners)
					{
						goner.DeleteUnderlyingObject(); // This will shift twfic pointers up to wordform, if needed.
					}
					goners.Clear();
				}
				if (UpdateLexicalEntries)
				{
					// Change LE allo on single morpheme anals.
					foreach (IWfiAnalysis update in wfOld.AnalysesOC)
					{
						if (update.MorphBundlesOS.Count == 1)
						{
							IWfiMorphBundle mb = update.MorphBundlesOS[0];

							TsStringAccessor tsa = mb.Form.GetAlternative(m_vernWs);
							string srcForm = tsa.Text;
							if (srcForm != null)
							{
								// Change morph bundle form.
								mb.Form.SetAlternative(m_newSpelling, m_vernWs);
							}

							IMoForm mf = mb.MorphRA;
							if (mf != null)
							{
								mf.Form.SetAlternative(m_newSpelling, m_vernWs);
							}
						}
					}
				}

				// Move remaining anals from src wf to new wf.
				// This changes the owners of the remaining ones,
				// since it is an owning property.
				wf.AnalysesOC.Add(wfOld.AnalysesOC.HvoArray);

				SendAnalysisVirtualPropChanged(m_hvoNewWf);
				SendAnalysisVirtualPropChanged(m_hvoOldWf);
				UpdateProgress(dlg);
			}
			m_cache.EndUndoTask();
		}
Пример #6
0
		private List<int> CollectSegmentForms(int ichMinCurSeg, int ichLimCurSeg, ref int ichLimLast,
			ref ITsString tssFirstWordOfNextSegment)
		{
			List<int> formsInSegment = new List<int>();
			List<int> punctuationAnnotations = new List<int>();
			int ichMin, ichLim;
			m_wordMaker.CurrentCharOffset = ichMinCurSeg;
			ITsString tssWord;
			if (tssFirstWordOfNextSegment != null)
			{
				tssWord = tssFirstWordOfNextSegment;
				// back up by this word.
				ichLim = ichMinCurSeg;
				ichMin = ichLim - tssWord.Length;
			}
			else
			{
				tssWord = m_wordMaker.NextWord(out ichMin, out ichLim);
			}
			int ctwfics = 0;
			do
			{
				if (tssWord == null)
				{
					// we've run out of twfics. collect the last remaining punctuation annotations.
					//Debug.Assert(m_tssPara.Length == ichLimCurSeg);
					CreatePunctAnnotations(ichLimLast, ichLimCurSeg, punctuationAnnotations);
					break;
				}

				if (ichLimLast != ichMin)
				{
					// we need to add punctuations to the current segment.
					CreatePunctAnnotations(ichLimLast, Math.Min(ichMin, ichLimCurSeg), punctuationAnnotations);
					formsInSegment.AddRange(punctuationAnnotations);
					punctuationAnnotations.Clear();
					if (ichMin >= ichLimCurSeg)
					{
						// we need to add this twfic to the next segment.
						tssFirstWordOfNextSegment = tssWord;
						ichLimLast = ichLimCurSeg;
						break;
					}
				}
				// Create (or reuse if possible) twfic annotations.
				ITsString tssWordAnn;
				formsInSegment.Add(CreateOrReuseAnnotation(tssWord, ichMin, ichLim, ctwfics, out tssWordAnn));
				if (tssWordAnn != null && tssWord.Length < tssWordAnn.Length)
				{
					// this must be a phrase, so advance appropriately in the text.
					ichLimLast = ichMin + tssWordAnn.Length;
					m_wordMaker.CurrentCharOffset = ichLimLast;
				}
				else
				{
					// still stepping by the word boundary.
					ichLimLast = ichLim;
				}
				ctwfics++;
				tssWord = m_wordMaker.NextWord(out ichMin, out ichLim);
			} while (true);
			formsInSegment.AddRange(punctuationAnnotations);
			return formsInSegment;
		}
Пример #7
0
        private void ExportRecord(TextWriter writer, IRnGenericRec record, int level)
        {
            writer.WriteLine(
                "<Entry level=\"{0}\" dateCreated=\"{1}\" dateModified=\"{2}\" guid=\"{3}\">",
                level,
                record.DateCreated.ToString("yyyy-MM-ddThh:mm:ss"),
                record.DateModified.ToString("yyyy-MM-ddThh:mm:ss"),
                record.Guid);

            ExportString(writer, record.Title, "Title");

            ExportAtomicReference(writer, record.TypeRA, "Type", "CmPossibility");

            List<ICmPossibility> collection = new List<ICmPossibility>();
            collection.AddRange(record.RestrictionsRC);
            ExportReferenceList(writer, collection, "Restrictions", "CmPossibility",
                CellarPropertyType.ReferenceCollection);
            collection.Clear();

            if (!record.DateOfEvent.IsEmpty)
            {
                writer.WriteLine("<Field name=\"DateOfEvent\" type=\"GenDate\" card=\"atomic\">");
                writer.WriteLine("<Item ws=\"{0}\">{1}</Item>",
                    UserWsTag, XmlUtils.MakeSafeXml(record.DateOfEvent.ToXMLExportShortString()));
                writer.WriteLine("</Field>");
            }

            collection.AddRange(record.TimeOfEventRC);
            ExportReferenceList(writer, collection, "TimeOfEvent", "CmPossibility",
                CellarPropertyType.ReferenceCollection);
            collection.Clear();

            collection.AddRange(record.ResearchersRC.ToArray());
            ExportReferenceList(writer, collection, "Researchers", "CmPerson",
                CellarPropertyType.ReferenceCollection);
            collection.Clear();

            collection.AddRange(record.SourcesRC.ToArray());
            ExportReferenceList(writer, collection, "Sources", "CmPerson",
                CellarPropertyType.ReferenceCollection);
            collection.Clear();

            ExportAtomicReference(writer, record.ConfidenceRA, "Confidence", "CmPossibility");

            if (record.ParticipantsOC != null && record.ParticipantsOC.Count > 0)
            {
                foreach (var part in record.ParticipantsOC)
                {
                    collection.AddRange(part.ParticipantsRC.ToArray());
                    if (part.RoleRA != null)
                    {
                        int wsRole;
                        ITsString tssRole = part.RoleRA.Name.GetAlternativeOrBestTss(m_cache.DefaultAnalWs, out wsRole);
                        ExportReferenceList(writer, collection, tssRole.Text, "RnRoledPartic",
                            CellarPropertyType.ReferenceCollection);
                    }
                    else
                    {
                        ExportReferenceList(writer, collection, "Participants", "RnRoledPartic",
                            CellarPropertyType.ReferenceCollection);
                    }
                    collection.Clear();
                }
            }

            if (record.LocationsRC != null && record.LocationsRC.Count > 0)
            {
                collection.AddRange(record.LocationsRC.ToArray());
                ExportReferenceList(writer, collection, "Locations", "CmLocation",
                    CellarPropertyType.ReferenceCollection);
                collection.Clear();
            }

            ExportStText(writer, record.DescriptionOA, "Description");

            ExportStText(writer, record.HypothesisOA, "Hypothesis");

            ExportAtomicReference(writer, record.StatusRA, "Status", "CmPossibility");

            ExportStText(writer, record.DiscussionOA, "Discussion");

            if (record.AnthroCodesRC != null && record.AnthroCodesRC.Count > 0)
            {
                writer.WriteLine("<Field name=\"AnthroCodes\" type=\"CmAnthroItem\" card=\"collection\">");
                foreach (var item in record.AnthroCodesRC)
                    writer.WriteLine("<Item ws=\"{0}\">{1}</Item>", AnalWsTag, XmlUtils.MakeSafeXml(item.AbbrAndName));
                writer.WriteLine("</Field>");
            }

            ExportStText(writer, record.ConclusionsOA, "Conclusions");

            if (record.SupportingEvidenceRS != null && record.SupportingEvidenceRS.Count > 0)
            {
                writer.WriteLine("<Field name=\"SupportingEvidence\" type=\"RnGenericRec\" card=\"sequence\">");
                foreach (var item in record.SupportingEvidenceRS)
                {
                    writer.WriteLine("<Item guid=\"{0}\" ws=\"{1}\">{2}</Item>",
                        item.Guid, AnalWsTag, GetLinkLabelForRecord(item));
                }
                writer.WriteLine("</Field>");
            }
            if (record.CounterEvidenceRS != null && record.CounterEvidenceRS.Count > 0)
            {
                writer.WriteLine("<Field name=\"CounterEvidence\" type=\"RnGenericRec\" card=\"sequence\">");
                foreach (var item in record.CounterEvidenceRS)
                {
                    writer.WriteLine("<Item guid=\"{0}\" ws=\"{1}\">{2}</Item>",
                        item.Guid, AnalWsTag, GetLinkLabelForRecord(item));
                }
                writer.WriteLine("</Field>");
            }
            if (record.SupersededByRC != null && record.SupersededByRC.Count > 0)
            {
                writer.WriteLine("<Field name=\"SupersededBy\" type=\"RnGenericRec\" card=\"collection\">");
                foreach (var item in record.SupersededByRC)
                {
                    writer.WriteLine("<Item guid=\"{0}\" ws=\"{1}\">{2}</Item>",
                        item.Guid, AnalWsTag, GetLinkLabelForRecord(item));
                }
                writer.WriteLine("</Field>");
            }
            if (record.SeeAlsoRC != null && record.SeeAlsoRC.Count > 0)
            {
                writer.WriteLine("<Field name=\"SeeAlso\" type=\"RnGenericRec\" card=\"collection\">");
                foreach (var item in record.SeeAlsoRC)
                {
                    writer.WriteLine("<Item guid=\"{0}\" ws=\"{1}\">{2}</Item>",
                        item.Guid, AnalWsTag, GetLinkLabelForRecord(item));
                }
                writer.WriteLine("</Field>");
            }
            ExportStText(writer, record.ExternalMaterialsOA, "ExternalMaterials");
            ExportStText(writer, record.FurtherQuestionsOA, "FurtherQuestions");
            ExportStText(writer, record.ResearchPlanOA, "ResearchPlan");
            ExportStText(writer, record.PersonalNotesOA, "PersonalNotes");

            // The following are in the model, but not used in practice.
            //ExportStText(writer, record.VersionHistoryOA, "VersionHistory");
            //if (record.RemindersRC != null && record.RemindersRC.Count > 0)
            //    MessageBox.Show("Cannot export Reminders from RnGenericRec", "Not yet implemented");
            //if (record.CrossReferencesRC != null && record.CrossReferencesRC.Count > 0)
            //    MessageBox.Show("Cannot export CrossReferences from RnGenericRec", "Not yet implemented");

            ExportCustomFields(writer, record);

            if (record.SubRecordsOS != null && record.SubRecordsOS.Count > 0)
            {
                writer.WriteLine("<Field name=\"Subentries\" type=\"RnGenericRec\" card=\"sequence\">");
                foreach (var subrec in record.SubRecordsOS)
                    ExportRecord(writer, subrec, level + 1);
                writer.WriteLine("</Field>");
            }
            writer.WriteLine("</Entry>");
        }