Esempio n. 1
0
        /// <summary>
        /// Resets the sorter so that it will use the writing system of the given reversal index.
        /// </summary>
        /// <param name="ri"></param>
        private void ResetListSorter(IReversalIndex ri)
        {
            var sorter        = Sorter as GenRecordSorter;
            var writingSystem = (IWritingSystem)Cache.WritingSystemFactory.get_Engine(ri.WritingSystem);

            if (sorter != null)
            {
                var stringFinderComparer = sorter.Comparer as StringFinderCompare;
                if (stringFinderComparer != null)
                {
                    var comparer = new StringFinderCompare(stringFinderComparer.Finder, new WritingSystemComparer(writingSystem));
                    sorter.Comparer = comparer;
                }
            }
            else if (Sorter == null)
            {
                var fakevc = new XmlBrowseViewBaseVc {
                    SuppressPictures = true, Cache = Cache
                };                                                                                              // SuppressPictures to make sure that we don't leak anything as this will not be disposed.
                m_list.Sorter = new GenRecordSorter(new StringFinderCompare(LayoutFinder.CreateFinder(Cache,
                                                                                                      BrowseViewFormCol,
                                                                                                      fakevc,
                                                                                                      (IApp)m_mediator.PropertyTable.GetValue("App")),
                                                                            new WritingSystemComparer(writingSystem)));
            }
        }
Esempio n. 2
0
        private Set <int> GetDescendents(int hvoCommonAncestor, int relativesFlid)
        {
            string  listPropertyName = Cache.MetaDataCacheAccessor.GetFieldName((uint)relativesFlid);
            string  parentObjName    = Cache.MetaDataCacheAccessor.GetClassName((uint)m_cache.GetClassOfObject(hvoCommonAncestor));
            string  xpathToPart      = "./part[@id='" + parentObjName + "-Jt-" + listPropertyName + "']";
            XmlNode pathSpec         = m_parentToChildrenSpecs.SelectSingleNode(xpathToPart);

            if (pathSpec == null)
            {
                throw new ArgumentException("Expected to find part ({0}) in ParentClassPathsToChildren", xpathToPart);
            }
            // get the part spec that gives us the path from obsolete current (parent) list item object
            // to the new one.
            using (XmlBrowseViewBaseVc vc = new XmlBrowseViewBaseVc(m_cache, null))
            {
                ManyOnePathSortItem parentItem = new ManyOnePathSortItem(hvoCommonAncestor, null, null);
                XmlBrowseViewBaseVc.ItemsCollectorEnv collector =
                    new XmlBrowseViewBaseVc.ItemsCollectorEnv(null, m_cache, hvoCommonAncestor);
                vc.DisplayCell(parentItem, pathSpec, hvoCommonAncestor, collector);
                if (collector.HvosCollectedInCell != null && collector.HvosCollectedInCell.Count > 0)
                {
                    return(collector.HvosCollectedInCell);
                }
            }
            return(new Set <int>());
        }
Esempio n. 3
0
        private Set <int> GetDescendents(int hvoCommonAncestor, int relativesFlid)
        {
            string  listPropertyName = Cache.MetaDataCacheAccessor.GetFieldName(relativesFlid);
            string  parentObjName    = Cache.ServiceLocator.GetInstance <ICmObjectRepository>().GetObject(hvoCommonAncestor).ClassName;
            string  xpathToPart      = "./part[@id='" + parentObjName + "-Jt-" + listPropertyName + "']";
            XmlNode pathSpec         = m_parentToChildrenSpecs.SelectSingleNode(xpathToPart);

            Debug.Assert(pathSpec != null,
                         String.Format("You are experiencing a rare and difficult-to-reproduce error (LT- 11443 and linked issues). If you can add any information to the issue or fix it please do. If JohnT is available please call him over. Expected to find part ({0}) in ParentClassPathsToChildren", xpathToPart));
            if (pathSpec == null)
            {
                return(new Set <int>());               // This just means we don't find a related object. Better than crashing, but not what we intend.
            }
            // get the part spec that gives us the path from obsolete current (parent) list item object
            // to the new one.
            var vc         = new XmlBrowseViewBaseVc(m_cache, null);
            var parentItem = new ManyOnePathSortItem(hvoCommonAncestor, null, null);
            var collector  = new XmlBrowseViewBaseVc.ItemsCollectorEnv(null, m_cache, hvoCommonAncestor);

            vc.DisplayCell(parentItem, pathSpec, hvoCommonAncestor, collector);
            if (collector.HvosCollectedInCell != null && collector.HvosCollectedInCell.Count > 0)
            {
                return(collector.HvosCollectedInCell);
            }
            return(new Set <int>());
        }
Esempio n. 4
0
 /// ------------------------------------------------------------------------------------
 /// <summary>
 /// Inits the XML.
 /// </summary>
 /// <param name="node">The node.</param>
 /// ------------------------------------------------------------------------------------
 public override void InitXml(XmlNode node)
 {
     base.InitXml(node);
     SortMethod        = XmlUtils.GetManditoryAttributeValue(node, "sortmethod");
     WritingSystemName = XmlUtils.GetOptionalAttributeValue(node, "ws", null);
     // Enhance JohnT: if we start using string tables for browse views,
     // we will need a better way to provide one to the Vc we make here.
     // Note: we don't need a top-level spec because we're only going to process one
     // column's worth.
     m_vc = new XmlBrowseViewBaseVc();
     m_vc.SuppressPictures = true;             // we won't dispose of it, so it mustn't make pictures (which we don't need)
 }
        public void MigrateVersion18Columns_ExtendedNoteColumns()
        {
            var input          = @"<column layout='ExtNoteType' label='Ext. Note Type' multipara='true' ws='$ws=analysis' transduce='LexExtendedNote.ExtendedNoteType' ghostListField='LexDb.AllPossibleExtendedNotes' editable='false' visibility='dialog' />
				<column layout='ExtNoteDiscussion' label='Ext. Note Discussion' multipara='true' ws='$ws=analysis' transduce='LexExtendedNote.Discussion' ghostListField='LexDb.AllPossibleExtendedNotes' editable='false' visibility='dialog' />
				"                .Replace("'", "\"");
            var expectedOutput = @"<column layout='ExtNoteType' label='Ext. Note - Type' multipara='true' ghostListField='LexDb.AllExtendedNoteTargets' visibility='dialog' list='LexDb.ExtendedNoteTypes' field='LexExtendedNote.ExtendedNoteType' bulkEdit='atomicFlatListItem' displayWs='best vernoranal' displayNameProperty='ShortNameTSS'/>
				<column layout='ExtNoteDiscussion' label='Ext. Note - Discussion' multipara='true' ws='$ws=analysis' transduce='LexExtendedNote.Discussion' ghostListField='LexDb.AllExtendedNoteTargets' editable='true' visibility='dialog' />
				"                .Replace("'", "\"");
            var output         = XmlBrowseViewBaseVc.FixVersion18Columns(input);

            Assert.That(output, Is.EqualTo(expectedOutput), "transduce attributes should be added");
        }
Esempio n. 6
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Keys the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="fForSorting">if set to <c>true</c> [f for sorting].</param>
        /// <returns></returns>
        /// ------------------------------------------------------------------------------------
        public ITsString Key(IManyOnePathSortItem item, bool fForSorting)
        {
            if (m_cache == null)
            {
                throw new ApplicationException("There's no way the browse VC (m_vc) can get a string in its current state.");
            }
            int hvo = item.RootObjectHvo;
            TsStringCollectorEnv collector;

            if (fForSorting)
            {
                collector = new SortCollectorEnv(null, m_sda, hvo);
            }
            else
            {
                collector = new TsStringCollectorEnv(null, m_sda, hvo);
            }

            // This will check to see if the VC is either null or disposed.  The disposed check is neccesary because
            // there are several instances where we can have a reference to an instance that was disposed, which will
            // cause problems later on.
            // Enhance CurtisH/EricP: If this VC gets used in other places, rather than adding more checks like this one,
            // it may be better to refactor XWorksViewBase to cause it to reload the sorter and filter from persistence
            // every time the tool is changed
            if (m_vc == null)
            {
                m_vc = new XmlBrowseViewBaseVc(m_cache, m_stringTbl);
                m_vc.SuppressPictures = true;                 // we won't dispose of it, so it mustn't make pictures (which we don't need)
                m_vc.DataAccess       = m_sda;
            }
            else
            {
                if (m_vc.Cache == null)
                {
                    m_vc.Cache = m_cache;
                }
                if (m_vc.Cache == null)
                {
                    throw new ApplicationException("There's no way the browse VC (m_vc) can get a string in its current state.");
                }
                if (m_vc.StringTbl == null)
                {
                    m_vc.StringTbl = m_stringTbl;
                }
            }
            m_vc.DisplayCell(item, m_colSpec, hvo, collector);
            return(collector.Result);
        }
Esempio n. 7
0
        public void MigrateVersion16Columns_Etymology()
        {
            var input          = @"<column layout='EtymologyGloss' label='Etymology - Gloss'  multipara='true' ws='$ws=analysis' editable='false' visibility='dialog' />
	<column layout='EtymologySource' label='Etymology - Source'  multipara='true' editable='false' visibility='dialog'/>
	<column layout='EtymologyForm' label='Etymology - Form'  multipara='true' ws='$ws=vernacular' editable='false' visibility='dialog' />
	<column layout='EtymologyComment' label='Etymology - Comment'  multipara='true' ws='$ws=analysis' editable='false' visibility='dialog' />
".Replace("'", "\"");
            var expectedOutput = @"<column layout='EtymologyGloss' label='Etymology - Gloss'  multipara='true' ws='$ws=analysis' editable='false' visibility='dialog' transduce='LexEntry.Etymology.Gloss'/>
	<column layout='EtymologySource' label='Etymology - Source'  multipara='true' editable='false' visibility='dialog' transduce='LexEntry.Etymology.Source'/>
	<column layout='EtymologyForm' label='Etymology - Form'  multipara='true' ws='$ws=vernacular' editable='false' visibility='dialog' transduce='LexEntry.Etymology.Form'/>
	<column layout='EtymologyComment' label='Etymology - Comment'  multipara='true' ws='$ws=analysis' editable='false' visibility='dialog' transduce='LexEntry.Etymology.Comment'/>
".Replace("'", "\"");
            var output         = XmlBrowseViewBaseVc.FixVersion16Columns(input);

            Assert.That(output, Is.EqualTo(expectedOutput), "transduce attributes should be added");
        }
Esempio n. 8
0
        /// <summary>
        /// The stored sorter files keep messing us up here, so we need to do a bit of post-deserialization processing.
        /// </summary>
        /// <returns>true if we restored something different from what was already there.</returns>
        protected override bool TryRestoreSorter(XmlNode clerkConfiguration, LcmCache cache)
        {
            var fakevc = new XmlBrowseViewBaseVc {
                SuppressPictures = true, Cache = Cache
            };                                                                                           // SuppressPictures to make sure that we don't leak anything as this will not be disposed.

            if (base.TryRestoreSorter(clerkConfiguration, cache) && Sorter is GenRecordSorter)
            {
                var sorter = (GenRecordSorter)Sorter;
                var stringFinderComparer = sorter.Comparer as StringFinderCompare;
                if (stringFinderComparer != null)
                {
                    var colSpec = ReflectionHelper.GetField(stringFinderComparer.Finder, "m_colSpec") as XmlNode ?? BrowseViewFormCol;
                    sorter.Comparer = new StringFinderCompare(LayoutFinder.CreateFinder(Cache, colSpec, fakevc,
                                                                                        m_propertyTable.GetValue <IApp>("App")),
                                                              stringFinderComparer.SubComparer);
                }
                return(true);
            }
            if (Sorter is GenRecordSorter)            // If we already have a GenRecordSorter, it's probably an existing, valid one.
            {
                return(false);
            }
            // Try to create a sorter based on the current Reversal Index's WritingSystem
            var newGuid = ReversalIndexEntryUi.GetObjectGuidIfValid(m_propertyTable, "ReversalIndexGuid");

            if (newGuid.Equals(Guid.Empty))
            {
                return(false);
            }
            var ri = cache.ServiceLocator.GetObject(newGuid) as IReversalIndex;

            if (ri == null)
            {
                return(false);
            }
            var writingSystem = (CoreWritingSystemDefinition)Cache.WritingSystemFactory.get_Engine(ri.WritingSystem);

            m_list.Sorter = new GenRecordSorter(new StringFinderCompare(LayoutFinder.CreateFinder(Cache, BrowseViewFormCol, fakevc,
                                                                                                  m_propertyTable.GetValue <IApp>("App")),
                                                                        new WritingSystemComparer(writingSystem)));
            return(true);
        }
Esempio n. 9
0
        public void MigrateVersion16Columns_CustomPossAtom()
        {
            var input          = @"
	<generate class='LexEntry' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForEntry_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexEntry.$fieldName' list='$targetList'/>
	</generate>
	<generate class='LexSense' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForSense_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexSense.$fieldName' list='$targetList'/>
	</generate>
			<generate class='MoForm' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForAllomorph_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='MoForm.$fieldName' list='$targetList'/>
	</generate>
		<generate class='LexExampleSentence' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForExample_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexExampleSentence.$fieldName' list='$targetList'/>
	</generate>
".Replace("'", "\"");
            var expectedOutput = @"
	<generate class='LexEntry' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForEntry_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexEntry.$fieldName' list='$targetList' displayNameProperty='ShortNameTSS'/>
	</generate>
	<generate class='LexSense' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForSense_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexSense.$fieldName' list='$targetList' displayNameProperty='ShortNameTSS'/>
	</generate>
			<generate class='MoForm' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForAllomorph_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='MoForm.$fieldName' list='$targetList' displayNameProperty='ShortNameTSS'/>
	</generate>
		<generate class='LexExampleSentence' fieldType='atom' destClass='CmPossibility' restrictions='customOnly'>
		<column layout='CustomPossAtomForExample_$fieldName' label='$label' visibility='menu'
				bulkEdit='atomicFlatListItem' field='LexExampleSentence.$fieldName' list='$targetList' displayNameProperty='ShortNameTSS'/>
	</generate>
".Replace("'", "\"");
            var output         = XmlBrowseViewBaseVc.FixVersion16Columns(input);

            Assert.That(output, Is.EqualTo(expectedOutput), "displayNameProperty should be added");
        }
Esempio n. 10
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Make a finder appropriate to the given column specification
        /// </summary>
        /// <param name="cache">FdoCache</param>
        /// <param name="colSpec">column specification</param>
        /// <param name="vc">The vc.</param>
        /// <param name="app">The application.</param>
        /// <returns>finder for colSpec</returns>
        /// ------------------------------------------------------------------------------------
        static public IStringFinder CreateFinder(FdoCache cache, XmlNode colSpec,
                                                 XmlBrowseViewBaseVc vc, IApp app)
        {
            string       layoutName  = XmlUtils.GetOptionalAttributeValue(colSpec, "layout");
            string       sSortMethod = XmlUtils.GetOptionalAttributeValue(colSpec, "sortmethod");
            string       sortType    = XmlUtils.GetOptionalAttributeValue(colSpec, "sortType", null);
            LayoutFinder result;

            if (sSortMethod != null)
            {
                result = new SortMethodFinder(cache, sSortMethod, layoutName, colSpec, app);
            }
            else if (sortType != null)
            {
                switch (sortType)
                {
                case "integer":
                    result = new IntCompareFinder(cache, layoutName, colSpec, app);
                    break;

                case "date":
                case "YesNo":
                case "stringList":
                case "genDate":
                    // no special action needed here for sorting dates or date that shows as 'yes" or "no";
                    // Using a SortCollectorEnv triggers special
                    // action in case "datetime"/"gendate" of XmlVc.ProcessFrag().
                    result = new LayoutFinder(cache, layoutName, colSpec, vc.StringTbl, app);
                    break;

                default:
                    throw new ConfigurationException("unexpected sort type: " + sortType, colSpec);
                }
            }
            else
            {
                result = new LayoutFinder(cache, layoutName, colSpec, vc.StringTbl, app);
            }
            result.Vc = vc;
            return(result);
        }
Esempio n. 11
0
        public void MigrateVersion16Columns_ConfigureWs()
        {
            var input          = @"
	<column layout='CVPattern' label='CV Patterns' multipara='true' editable='false' ws='pronunciation'
			transduce='LexPronunciation.CVPattern' ghostListField='LexDb.AllPossiblePronunciations'  cansortbylength='true' visibility='dialog'/>
	<column layout='Tone' label='Tones' multipara='true' editable='false' ws='pronunciation'
			transduce='LexPronunciation.Tone' ghostListField='LexDb.AllPossiblePronunciations' cansortbylength='true' visibility='dialog'/>
	<column layout='ScientificNameForSense' label='Scientific Names' multipara='true' ws='analysis' transduce='LexSense.ScientificName' visibility='dialog' />
	<column layout='SourceForSense'  label='Sources' ws='analysis' multipara='true' transduce='LexSense.Source' visibility='dialog' />
".Replace("'", "\"");
            var expectedOutput = @"
	<column layout='CVPattern' label='CV Patterns' multipara='true' editable='false' ws='$ws=pronunciation'
			transduce='LexPronunciation.CVPattern' ghostListField='LexDb.AllPossiblePronunciations'  cansortbylength='true' visibility='dialog'/>
	<column layout='Tone' label='Tones' multipara='true' editable='false' ws='$ws=pronunciation'
			transduce='LexPronunciation.Tone' ghostListField='LexDb.AllPossiblePronunciations' cansortbylength='true' visibility='dialog'/>
	<column layout='ScientificNameForSense' label='Scientific Names' multipara='true' ws='$ws=analysis' transduce='LexSense.ScientificName' visibility='dialog' />
	<column layout='SourceForSense'  label='Sources' ws='$ws=analysis' multipara='true' transduce='LexSense.Source' visibility='dialog' />
".Replace("'", "\"");
            var output         = XmlBrowseViewBaseVc.FixVersion16Columns(input);

            Assert.That(output, Is.EqualTo(expectedOutput), "$ws= should be added to various fields");
        }
Esempio n. 12
0
        public void MigrateVersion16Columns_EntryTypes()
        {
            var input          = @"<column layout='ComplexEntryTypesBrowse' ws='$ws=analysis' originalWs='$ws=analysis' label='Complex Form Types' multipara='true' editable='false'  visibility='dialog'
			chooserFilter='complexListMultiple' canChooseEmpty='true'
			ghostListField='LexDb.AllComplexEntryRefPropertyTargets'
			bulkEdit='complexEntryTypes' field='LexEntryRef.ComplexEntryTypes' bulkDelete='false' list='LexDb.ComplexEntryTypes' displayNameProperty='ShortNameTSS' displayWs='analysis'/>
			<column layout='VariantEntryTypesBrowse' ws='$ws=analysis' originalWs='$ws=analysis' label='Variant Types' multipara='true' editable='false' visibility='dialog'
			chooserFilter='complexListMultiple' canChooseEmpty='true'
			ghostListField='LexDb.AllVariantEntryRefPropertyTargets'
			bulkEdit='variantEntryTypes' field='LexEntryRef.VariantEntryTypes' list='LexDb.VariantEntryTypes' displayNameProperty='ShortNameTSS' displayWs='analysis'/>
".Replace("'", "\"");
            var expectedOutput = @"<column layout='ComplexEntryTypesBrowse' ws='$ws=best analysis' originalWs='$ws=best analysis' label='Complex Form Types' multipara='true' editable='false'  visibility='dialog'
			chooserFilter='complexListMultiple' canChooseEmpty='true'
			ghostListField='LexDb.AllComplexEntryRefPropertyTargets'
			bulkEdit='complexEntryTypes' field='LexEntryRef.ComplexEntryTypes' bulkDelete='false' list='LexDb.ComplexEntryTypes' displayNameProperty='ShortNameTSS' displayWs='analysis'/>
			<column layout='VariantEntryTypesBrowse' ws='$ws=best analysis' originalWs='$ws=best analysis' label='Variant Types' multipara='true' editable='false' visibility='dialog'
			chooserFilter='complexListMultiple' canChooseEmpty='true'
			ghostListField='LexDb.AllVariantEntryRefPropertyTargets'
			bulkEdit='variantEntryTypes' field='LexEntryRef.VariantEntryTypes' list='LexDb.VariantEntryTypes' displayNameProperty='ShortNameTSS' displayWs='analysis'/>
".Replace("'", "\"");
            var output         = XmlBrowseViewBaseVc.FixVersion16Columns(input);

            Assert.That(output, Is.EqualTo(expectedOutput), "ws and original Ws attributes should be changed to best analysis");
        }
Esempio n. 13
0
        public void MigrateBrowseColumns()
        {
            var input =
                "<root version=\"12\">" +
                "<column layout=\"Unknown Test\"/>" +
                "<column label=\"Headword\" sortmethod=\"FullSortKey\" ws=\"$ws=vernacular\" editable=\"false\" width=\"96000\"><span><properties><editable value=\"false\" /></properties><string field=\"MLHeadWord\" ws=\"vernacular\" /></span></column>" +
                "<column layout=\"Weather\" rubbish=\"nonsense\"/>" +
                "<column layout=\"IsAHeadwordForEntry\" label=\"Is a Headword\" visibility=\"dialog\"/>" +
                "<column layout=\"IsAbstractFormForEntry\" label=\"Is Abstract Form\" visibility=\"dialog\"  bulkEdit=\"integerOnSubfield\" bulkDelete=\"false\" field=\"LexEntry.LexemeForm\" subfield=\"MoForm.IsAbstract\" items=\"0:no;1:yes\" blankPossible=\"false\" sortType=\"YesNo\"/>" +
                "<column layout=\"ExceptionFeatures\" label=\"'Exception' Features\" multipara=\"true\" width=\"25%\"/>" +
                "<column layout=\"PictureCaptionForSense\" label=\"Picture-Caption\"  multipara=\"true\" editable=\"false\" visibility=\"dialog\" />" +
                "<column layout=\"AcademicDomainsForSense\" label=\"Academic Domains\" displayNameProperty=\"ShortNameTSS\" displayWs=\"analysis\" visibility=\"dialog\" />" +
                "<column layout=\"StatusForSense\" label=\"Status\"  field=\"LexSense.Status\" list=\"LexDb.Status\"  visibility=\"dialog\"/>" +
                "<column layout=\"ComplexEntryTypesBrowse\" ws=\"$ws=analysis\" label=\"Complex Form Types\" multipara=\"true\" />" +
                "<column layout=\"VariantEntryTypesBrowse\" ws=\"$ws=analysis\" label=\"Variant Types\" multipara=\"true\" />" +
                "<column layout=\"CustomIntegerForEntry_MyField\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomGenDateForEntry_SomeField\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomPossVectorForEntry_MyField\" label=\"$label\" multipara=\"true\" visibility=\"menu\"/>" +
                "<column layout=\"CustomPossAtomForEntry_AField\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomIntegerForSense_SenseField\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomPossVectorForSense_SenseVec\" label=\"$label\" multipara=\"true\" visibility=\"menu\"/>" +
                "<column layout=\"CustomPossAtomForSense_SenseAtom\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomGenDateForAllomorph_MorphDate\" label=\"$label\" visibility=\"menu\"/>" +
                "<column layout=\"CustomPossAtomForExample_ExAtom\" label=\"$label\" visibility=\"menu\"/>" +
                "</root>";

            using (var mediator = new Mediator())
            {
                var output = XmlBrowseViewBaseVc.GetSavedColumns(input, mediator, "myKey");
                Assert.That(XmlUtils.GetOptionalAttributeValue(output.DocumentElement, "version"), Is.EqualTo(BrowseViewer.kBrowseViewVersion.ToString()));
                var headwordNode = output.SelectSingleNode("//column[@label='Headword']");
                Assert.That(headwordNode, Is.Not.Null);
                Assert.That(XmlUtils.GetOptionalAttributeValue(headwordNode, "layout"), Is.EqualTo("EntryHeadwordForFindEntry"));
                Assert.That(mediator.PropertyTable.GetStringProperty("myKey", ""), Contains.Substring("EntryHeadwordForFindEntry"));
                var weatherNode = output.SelectSingleNode("//column[@layout='Weather']");
                Assert.That(weatherNode, Is.Null);
                Assert.That(mediator.PropertyTable.GetStringProperty("myKey", ""), Contains.Substring("EntryHeadwordForFindEntry"));
                // Should not affect other nodes
                var unknownNode = output.SelectSingleNode("//column[@layout='Unknown Test']");
                Assert.That(unknownNode, Is.Not.Null);
                var abstractFormNode = output.SelectSingleNode("//column[@layout='IsAbstractFormForEntry']");
                Assert.That(abstractFormNode, Is.Not.Null);
                Assert.That(XmlUtils.GetOptionalAttributeValue(abstractFormNode, "bulkEdit"), Is.EqualTo("booleanOnSubfield"));
                Assert.That(XmlUtils.GetOptionalAttributeValue(abstractFormNode, "visibility"), Is.EqualTo("dialog"));
                Assert.That(XmlUtils.GetOptionalAttributeValue(abstractFormNode, "bulkDelete"), Is.EqualTo("false"));
                VerifyColumn(output, "ExceptionFeatures", "label", "Exception 'Features'");
                VerifyColumn(output, "PictureCaptionForSense", "ws", "$ws=vernacular analysis");
                VerifyColumn(output, "PictureCaptionForSense", "visibility", "dialog");
                VerifyColumn(output, "AcademicDomainsForSense", "displayWs", "best analysis");
                VerifyColumn(output, "StatusForSense", "list", "LangProject.Status");
                VerifyColumn(output, "ComplexEntryTypesBrowse", "ghostListField", "LexDb.AllComplexEntryRefPropertyTargets");
                VerifyColumn(output, "VariantEntryTypesBrowse", "ghostListField", "LexDb.AllVariantEntryRefPropertyTargets");
                VerifyColumn(output, "CustomIntegerForEntry_MyField", "sortType", "integer");
                VerifyColumn(output, "CustomGenDateForEntry_SomeField", "sortType", "genDate");

                VerifyColumn(output, "CustomPossVectorForEntry_MyField", "bulkEdit", "complexListMultiple");
                VerifyColumn(output, "CustomPossVectorForEntry_MyField", "field", "LexEntry.$fieldName");
                VerifyColumn(output, "CustomPossVectorForEntry_MyField", "list", "$targetList");
                VerifyColumn(output, "CustomPossVectorForEntry_MyField", "displayNameProperty", "ShortNameTSS");

                VerifyColumn(output, "CustomPossAtomForEntry_AField", "bulkEdit", "atomicFlatListItem");
                VerifyColumn(output, "CustomPossAtomForEntry_AField", "field", "LexEntry.$fieldName");
                VerifyColumn(output, "CustomPossAtomForEntry_AField", "list", "$targetList");

                VerifyColumn(output, "CustomIntegerForSense_SenseField", "sortType", "integer");
                VerifyColumn(output, "CustomPossVectorForSense_SenseVec", "field", "LexSense.$fieldName");
                VerifyColumn(output, "CustomPossAtomForSense_SenseAtom", "field", "LexSense.$fieldName");

                VerifyColumn(output, "CustomGenDateForAllomorph_MorphDate", "sortType", "genDate");
                VerifyColumn(output, "CustomPossAtomForExample_ExAtom", "field", "LexExampleSentence.$fieldName");

                // version 15
                var isAHeadwordNode = output.SelectSingleNode("//column[@layout='IsAHeadwordForEntry']");
                Assert.That(isAHeadwordNode, Is.Null);
                var publishAsHeadwordNode = output.SelectSingleNode("//column[@layout='PublishAsHeadword']");
                Assert.That(publishAsHeadwordNode, Is.Not.Null);

                // version 14
                // Todo!

                // Just version 15
                input =
                    "<root version=\"14\">" +
                    "<column layout=\"Unknown Test\"/>" +
                    "<column layout=\"IsAHeadwordForEntry\" label=\"Is a Headword\" visibility=\"dialog\"/>" +
                    "</root>";
                output = XmlBrowseViewBaseVc.GetSavedColumns(input, mediator, "myKey");
                Assert.That(XmlUtils.GetOptionalAttributeValue(output.DocumentElement, "version"), Is.EqualTo(BrowseViewer.kBrowseViewVersion.ToString()));
                isAHeadwordNode = output.SelectSingleNode("//column[@layout='IsAHeadwordForEntry']");
                Assert.That(isAHeadwordNode, Is.Null);
                publishAsHeadwordNode = output.SelectSingleNode("//column[@layout='PublishAsHeadword']");
                Assert.That(publishAsHeadwordNode, Is.Not.Null);
                Assert.That(mediator.PropertyTable.GetStringProperty("myKey", ""), Contains.Substring("PublishAsHeadword"));
            }
        }