/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Changes all StTxtParas that are owned by Scripture to be ScrTxtParas
		/// </summary>
		/// <param name="domainObjectDtoRepository">
		/// Repository of all CmObject DTOs available for one migration step.
		/// </param>
		/// <remarks>
		/// The method must add/remove/update the DTOs to the repository,
		/// as it adds/removes objects as part of it work.
		///
		/// Implementors of this interface should ensure the Repository's
		/// starting model version number is correct for the step.
		/// Implementors must also increment the Repository's model version number
		/// at the end of its migration work.
		///
		/// The method also should normally modify the xml string(s)
		/// of relevant DTOs, since that string will be used by the main
		/// data migration calling client (ie. BEP).
		/// </remarks>
		/// ------------------------------------------------------------------------------------
		public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository)
		{
			DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000001);

			var parasToChangeClasses = new List<DomainObjectDTO>();
			foreach (var stTxtPara in domainObjectDtoRepository.AllInstancesSansSubclasses("StTxtPara"))
			{
				var paraOwner = domainObjectDtoRepository.GetOwningDTO(stTxtPara);
				if (paraOwner.Classname != "StText" && paraOwner.Classname != "StFootnote")
				{
					continue; // Paragraph is not owned by an StText or StFootnote (shouldn't ever happen)
				}

				var textOwner = domainObjectDtoRepository.GetOwningDTO(paraOwner);
				if (textOwner.Classname != "ScrBook" && textOwner.Classname != "ScrSection")
				{
					continue; // StText is not part of Scripture, don't change.
				}
				// Its one of the paragraphs that we care about. We just need to change the
				// class in the xml to be a ScrTxtPara and change the objectsByClass map.
				DataMigrationServices.ChangeToSubClass(stTxtPara, "StTxtPara", "ScrTxtPara");
				parasToChangeClasses.Add(stTxtPara);
			}

			// Udate the repository
			var startingStructure = new ClassStructureInfo("StPara", "StTxtPara");
			var endingStructure = new ClassStructureInfo("StTxtPara", "ScrTxtPara");
			foreach (var changedPara in parasToChangeClasses)
				domainObjectDtoRepository.Update(changedPara,
					startingStructure,
					endingStructure);

			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
Example #2
0
        private Dictionary <string, List <DomainObjectDTO> > CreateFileGuidToPictureMap(IDomainObjectDTORepository repoDto)
        {
            var map = new Dictionary <string, List <DomainObjectDTO> >();

            foreach (var picture in repoDto.AllInstancesSansSubclasses("CmPicture"))
            {
                // all TE pictures are unowned, so no need to look at those with owners
                if (repoDto.GetOwningDTO(picture) == null)
                {
                    var pictureElement     = XElement.Parse(picture.Xml);
                    var pictureFileElement = pictureElement.Element("PictureFile");
                    // FWR-3385: not sure how this could happen, but it has occurred in
                    // real data
                    if (pictureFileElement == null)
                    {
                        continue;
                    }
                    var objSurrogateElement = pictureFileElement.Element("objsur");
                    var fileGuid            = objSurrogateElement.Attribute("guid").Value;

                    List <DomainObjectDTO> list;
                    if (!map.TryGetValue(fileGuid, out list))
                    {
                        list          = new List <DomainObjectDTO>();
                        map[fileGuid] = list;
                    }
                    list.Add(picture);
                }
            }
            return(map);
        }
Example #3
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Changes all StTxtParas that are owned by Scripture to be ScrTxtParas
        /// </summary>
        /// <param name="domainObjectDtoRepository">
        /// Repository of all CmObject DTOs available for one migration step.
        /// </param>
        /// <remarks>
        /// The method must add/remove/update the DTOs to the repository,
        /// as it adds/removes objects as part of it work.
        ///
        /// Implementors of this interface should ensure the Repository's
        /// starting model version number is correct for the step.
        /// Implementors must also increment the Repository's model version number
        /// at the end of its migration work.
        ///
        /// The method also should normally modify the xml string(s)
        /// of relevant DTOs, since that string will be used by the main
        /// data migration calling client (ie. BEP).
        /// </remarks>
        /// ------------------------------------------------------------------------------------
        public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository)
        {
            DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000001);

            var parasToChangeClasses = new List <DomainObjectDTO>();

            foreach (var stTxtPara in domainObjectDtoRepository.AllInstancesSansSubclasses("StTxtPara"))
            {
                var paraOwner = domainObjectDtoRepository.GetOwningDTO(stTxtPara);
                if (paraOwner.Classname != "StText" && paraOwner.Classname != "StFootnote")
                {
                    continue;                     // Paragraph is not owned by an StText or StFootnote (shouldn't ever happen)
                }

                var textOwner = domainObjectDtoRepository.GetOwningDTO(paraOwner);
                if (textOwner.Classname != "ScrBook" && textOwner.Classname != "ScrSection")
                {
                    continue;                     // StText is not part of Scripture, don't change.
                }
                // Its one of the paragraphs that we care about. We just need to change the
                // class in the xml to be a ScrTxtPara and change the objectsByClass map.
                DataMigrationServices.ChangeToSubClass(stTxtPara, "StTxtPara", "ScrTxtPara");
                parasToChangeClasses.Add(stTxtPara);
            }

            // Udate the repository
            var startingStructure = new ClassStructureInfo("StPara", "StTxtPara");
            var endingStructure   = new ClassStructureInfo("StTxtPara", "ScrTxtPara");

            foreach (var changedPara in parasToChangeClasses)
            {
                domainObjectDtoRepository.Update(changedPara,
                                                 startingStructure,
                                                 endingStructure);
            }

            DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
        }
Example #4
0
        /// <summary>
        /// Remove <paramref name="goner"/> and everything it owns.
        /// Be sure to include removing goner from its optional owning property.
        /// </summary>
        internal static void RemoveIncludingOwnedObjects(IDomainObjectDTORepository dtoRepos, DomainObjectDTO goner, bool removeFromOwner)
        {
            DomainObjectDTO gonerActual;

            if (!dtoRepos.TryGetValue(goner.Guid, out gonerActual))
            {
                return;                 // Not in repos.
            }
            if (removeFromOwner)
            {
                var ownerDto = dtoRepos.GetOwningDTO(goner);
                if (ownerDto != null)
                {
                    var ownerElement     = XElement.Parse(ownerDto.Xml);
                    var ownObjSurElement = (from objSurNode in ownerElement.Descendants("objsur")
                                            where objSurNode.Attribute("t").Value == "o" && objSurNode.Attribute("guid").Value.ToLower() == goner.Guid.ToLower()
                                            select objSurNode).FirstOrDefault();                                             // Ought not be null, but play it safe.
                    if (ownObjSurElement != null)
                    {
                        ownObjSurElement.Remove();
                    }

                    if (!RemoveEmptyPropertyElements(dtoRepos, ownerDto, ownerElement))
                    {
                        // No empty property elements removed, so we have to do the update.
                        UpdateDTO(dtoRepos, ownerDto, ownerElement.ToString());
                    }
                }
            }

            foreach (var ownedDto in dtoRepos.GetDirectlyOwnedDTOs(goner.Guid))
            {
                RemoveIncludingOwnedObjects(dtoRepos, ownedDto, false);
            }

            dtoRepos.Remove(goner);
        }
Example #5
0
        /// <summary>
        /// Changes the GUID of the specified DTO. It updates the owner and all specified referrers to point to the new GUID.
        /// </summary>
        /// <param name="dtoRepos">The dto repos.</param>
        /// <param name="dto">The dto.</param>
        /// <param name="newGuid">The new GUID.</param>
        /// <param name="possibleReferrers">The possible referrers.</param>
        internal static void ChangeGuid(IDomainObjectDTORepository dtoRepos, DomainObjectDTO dto, string newGuid,
                                        IEnumerable <DomainObjectDTO> possibleReferrers)
        {
            // if the DTO already has the new GUID, don't do anything
            if (dto.Guid.ToLowerInvariant() == newGuid.ToLowerInvariant())
            {
                return;
            }

            XElement rtElem = XElement.Parse(dto.Xml);

            rtElem.Attribute("guid").Value = newGuid;
            dtoRepos.Add(new DomainObjectDTO(newGuid, dto.Classname, rtElem.ToString()));
            foreach (DomainObjectDTO ownedDto in dtoRepos.GetDirectlyOwnedDTOs(dto.Guid))
            {
                XElement ownedElem = XElement.Parse(ownedDto.Xml);
                ownedElem.Attribute("ownerguid").Value = newGuid;
                UpdateDTO(dtoRepos, ownedDto, ownedElem.ToString());
            }

            var ownerDto = dtoRepos.GetOwningDTO(dto);

            if (ownerDto != null)
            {
                UpdateObjSurElement(dtoRepos, ownerDto, dto.Guid, newGuid);
            }

            if (possibleReferrers != null)
            {
                foreach (DomainObjectDTO referrer in possibleReferrers)
                {
                    UpdateObjSurElement(dtoRepos, referrer, dto.Guid, newGuid);
                }
            }
            dtoRepos.Remove(dto);
        }
        private void VerifyStylesRenamedOrDeleted(IDomainObjectDTORepository repoDTO)
        {
            int  cHyperlink    = 0;
            int  cWrtSysAbbr   = 0;
            int  cExternalLink = 0;
            int  cInternalLink = 0;
            int  cLanguageCode = 0;
            int  cStrong       = 0;
            int  cBuiltIn      = 0;
            int  cCustom       = 0;
            bool gotHeading3   = false;
            bool gotHeading5   = false;

            foreach (DomainObjectDTO dto in repoDTO.AllInstancesSansSubclasses("StStyle"))
            {
                Assert.That(dto.Guid.ToUpper(), Is.Not.EqualTo("71B2233D-8B14-42D5-A625-AAC8EDE7503B"),
                            "Heading 3 in LexDb duplicates one in LangProj and should have been deleted");
                if (dto.Guid.ToUpper() == "B82D12DE-EA5E-11DE-88CD-0013722F8DEC")                 // Keeper Heading 3
                {
                    gotHeading3 = true;
                    var h3Elt = XElement.Parse(dto.Xml);
                    Assert.That(h3Elt.Element("Rules").Element("Prop").Attribute("fontFamily").Value, Is.EqualTo("Times New Roman"),
                                "should transfer font family setting from lexDB version of heading 3 to lang proj");
                    Assert.That(h3Elt.Element("Rules").Element("Prop").Attribute("fontsize").Value, Is.EqualTo("11000"),
                                "should transfer font size setting from lexDB version of heading 3 to lang proj");
                    Assert.That(h3Elt.Element("BasedOn").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8238980-EA5E-11DE-86E1-0013722F8DEC"),
                                "should transfer based-on information to corresponding style in langproj");
                    Assert.That(h3Elt.Element("Next").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8179DD2-EA5E-11DE-8537-0013722F8DEC"),
                                "should transfer next information to corresponding style in langproj");
                }
                if (dto.Guid.ToUpper() == "44944544-DF17-4553-94B8-A5E13C3392C5")                 // keeper Heading 5
                {
                    gotHeading5 = true;
                    var h5Elt = XElement.Parse(dto.Xml);
                    Assert.That(h5Elt.Element("BasedOn").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8238980-EA5E-11DE-86E1-0013722F8DEC"),
                                "should transfer based-on information to corresponding style in langproj");
                    Assert.That(h5Elt.Element("Next").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8179DD2-EA5E-11DE-8537-0013722F8DEC"),
                                "should transfer next information to corresponding style in langproj");
                }
                string          sXml     = dto.Xml;
                string          sName    = GetStyleName(sXml);
                DomainObjectDTO dtoOwner = repoDTO.GetOwningDTO(dto);
                if (dtoOwner.Classname != "LangProject")
                {
                    Assert.AreEqual(dtoOwner.Classname, "Scripture", "Either LangProject or Scripture owns the style");
                    if (sName == "External Link")
                    {
                        ++cExternalLink;
                    }
                    else if (sName == "Internal Link")
                    {
                        ++cInternalLink;
                    }
                    else if (sName == "Language Code")
                    {
                        ++cLanguageCode;
                    }
                }
                else
                {
                    Assert.AreNotEqual(sName, "External Link", "The External Link style should no longer exist");
                    Assert.AreNotEqual(sName, "Internal Link", "The Internal Link style should no longer exist");
                    Assert.AreNotEqual(sName, "Language Code", "The Language Code style should no longer exist");
                    if (sName == "Hyperlink")
                    {
                        ++cHyperlink;
                    }
                    else if (sName == "Writing System Abbreviation")
                    {
                        ++cWrtSysAbbr;
                    }
                    else if (sName == "Strong")
                    {
                        ++cStrong;
                    }
                    if (sXml.Contains("<BasedOn>") || sXml.Contains("<Next>"))
                    {
                        XElement xeStyle = XElement.Parse(sXml);
                        ValidateStyleReference(repoDTO, xeStyle, "BasedOn");
                        ValidateStyleReference(repoDTO, xeStyle, "Next");
                    }
                    switch (sName)
                    {
                    case "Normal":
                    case "Numbered List":
                    case "Bulleted List":
                    case "Heading 1":
                    case "Heading 2":
                    case "Heading 3":
                    case "Block Quote":
                    case "Title Text":
                    case "Emphasized Text":
                    case "Writing System Abbreviation":
                    case "Added Text":
                    case "Deleted Text":
                    case "Hyperlink":
                    case "Strong":
                    case "Dictionary-Normal":
                    case "Classified-MainEntry":
                    case "Classified-Item":
                        Assert.IsTrue(IsBuiltInStyle(sXml), sName + " should be marked as built-in");
                        ++cBuiltIn;
                        break;

                    default:
                        // "Heading 4" and "Dictionary-Custom" should pass through here, plus 7 more
                        // created from direct formatting.
                        Assert.IsFalse(IsBuiltInStyle(sXml), sName + " should not be marked as built-in");
                        ++cCustom;
                        break;
                    }
                }
            }
            Assert.That(gotHeading3, "should have kept the LangProj Heading3");
            Assert.That(gotHeading5, "should have kept the LangProj Heading4");
            Assert.AreEqual(1, cHyperlink, "The Hyperlink style should exist (once)");
            Assert.AreEqual(1, cWrtSysAbbr, "The Writing System Abbreviation style should exist (once)");
            Assert.AreEqual(1, cStrong, "The Strong style should exist (once)");
            Assert.AreEqual(1, cExternalLink, "The External Link style should exist (once) in the Scripture stylesheet");
            Assert.AreEqual(1, cInternalLink, "The Internal Link style should exist (once) in the Scripture stylesheet");
            Assert.AreEqual(1, cLanguageCode, "The Language Code style should exist (once) in the Scripture stylesheet");
            Assert.AreEqual(17, cBuiltIn, "There should be 17 built-in LangProject styles.");
            Assert.AreEqual(9, cCustom, "There should be 9 custom LangProject styles.");
        }
		/// <summary>
		/// Changes the GUID of the specified DTO. It updates the owner and all specified referrers to point to the new GUID.
		/// </summary>
		/// <param name="dtoRepos">The dto repos.</param>
		/// <param name="dto">The dto.</param>
		/// <param name="newGuid">The new GUID.</param>
		/// <param name="possibleReferrers">The possible referrers.</param>
		internal static void ChangeGuid(IDomainObjectDTORepository dtoRepos, DomainObjectDTO dto, string newGuid,
			IEnumerable<DomainObjectDTO> possibleReferrers)
		{
			// if the DTO already has the new GUID, don't do anything
			if (dto.Guid.ToLowerInvariant() == newGuid.ToLowerInvariant())
				return;

			XElement rtElem = XElement.Parse(dto.Xml);
			rtElem.Attribute("guid").Value = newGuid;
			dtoRepos.Add(new DomainObjectDTO(newGuid, dto.Classname, rtElem.ToString()));
			foreach (DomainObjectDTO ownedDto in dtoRepos.GetDirectlyOwnedDTOs(dto.Guid))
			{
				XElement ownedElem = XElement.Parse(ownedDto.Xml);
				ownedElem.Attribute("ownerguid").Value = newGuid;
				UpdateDTO(dtoRepos, ownedDto, ownedElem.ToString());
			}

			var ownerDto = dtoRepos.GetOwningDTO(dto);
			if (ownerDto != null)
				UpdateObjSurElement(dtoRepos, ownerDto, dto.Guid, newGuid);

			if (possibleReferrers != null)
			{
				foreach (DomainObjectDTO referrer in possibleReferrers)
					UpdateObjSurElement(dtoRepos, referrer, dto.Guid, newGuid);
			}
			dtoRepos.Remove(dto);
		}
		/// <summary>
		/// Remove <paramref name="goner"/> and everything it owns.
		/// Be sure to include removing goner from its optional owning property.
		/// </summary>
		internal static void RemoveIncludingOwnedObjects(IDomainObjectDTORepository dtoRepos, DomainObjectDTO goner, bool removeFromOwner)
		{
			DomainObjectDTO gonerActual;
			if (!dtoRepos.TryGetValue(goner.Guid, out gonerActual))
				return; // Not in repos.

			if (removeFromOwner)
			{
				var ownerDto = dtoRepos.GetOwningDTO(goner);
				if (ownerDto != null)
				{
					var ownerElement = XElement.Parse(ownerDto.Xml);
					var ownObjSurElement = (from objSurNode in ownerElement.Descendants("objsur")
											where objSurNode.Attribute("t").Value == "o" && objSurNode.Attribute("guid").Value.ToLower() == goner.Guid.ToLower()
											select objSurNode).FirstOrDefault(); // Ought not be null, but play it safe.
					if (ownObjSurElement != null)
						ownObjSurElement.Remove();

					if (!RemoveEmptyPropertyElements(dtoRepos, ownerDto, ownerElement))
					{
						// No empty property elements removed, so we have to do the update.
						UpdateDTO(dtoRepos, ownerDto, ownerElement.ToString());
					}
				}
			}

			foreach (var ownedDto in dtoRepos.GetDirectlyOwnedDTOs(goner.Guid))
				RemoveIncludingOwnedObjects(dtoRepos, ownedDto, false);

			dtoRepos.Remove(goner);
		}
		private void VerifyStylesRenamedOrDeleted(IDomainObjectDTORepository repoDTO)
		{
			int cHyperlink = 0;
			int cWrtSysAbbr = 0;
			int cExternalLink = 0;
			int cInternalLink = 0;
			int cLanguageCode = 0;
			int cStrong = 0;
			int cBuiltIn = 0;
			int cCustom = 0;
			bool gotHeading3 = false;
			bool gotHeading5 = false;
			foreach (DomainObjectDTO dto in repoDTO.AllInstancesSansSubclasses("StStyle"))
			{
				Assert.That(dto.Guid.ToUpper(), Is.Not.EqualTo("71B2233D-8B14-42D5-A625-AAC8EDE7503B"),
					"Heading 3 in LexDb duplicates one in LangProj and should have been deleted");
				if (dto.Guid.ToUpper() == "B82D12DE-EA5E-11DE-88CD-0013722F8DEC") // Keeper Heading 3
				{
					gotHeading3 = true;
					var h3Elt = XElement.Parse(dto.Xml);
					Assert.That(h3Elt.Element("Rules").Element("Prop").Attribute("fontFamily").Value, Is.EqualTo("Times New Roman"),
						"should transfer font family setting from lexDB version of heading 3 to lang proj");
					Assert.That(h3Elt.Element("Rules").Element("Prop").Attribute("fontsize").Value, Is.EqualTo("11000"),
						"should transfer font size setting from lexDB version of heading 3 to lang proj");
					Assert.That(h3Elt.Element("BasedOn").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8238980-EA5E-11DE-86E1-0013722F8DEC"),
						"should transfer based-on information to corresponding style in langproj");
					Assert.That(h3Elt.Element("Next").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8179DD2-EA5E-11DE-8537-0013722F8DEC"),
						"should transfer next information to corresponding style in langproj");
				}
				if (dto.Guid.ToUpper() == "44944544-DF17-4553-94B8-A5E13C3392C5") // keeper Heading 5
				{
					gotHeading5 = true;
					var h5Elt = XElement.Parse(dto.Xml);
					Assert.That(h5Elt.Element("BasedOn").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8238980-EA5E-11DE-86E1-0013722F8DEC"),
						"should transfer based-on information to corresponding style in langproj");
					Assert.That(h5Elt.Element("Next").Element("objsur").Attribute("guid").Value.ToUpperInvariant, Is.EqualTo("B8179DD2-EA5E-11DE-8537-0013722F8DEC"),
						"should transfer next information to corresponding style in langproj");
				}
				string sXml = dto.Xml;
				string sName = GetStyleName(sXml);
				DomainObjectDTO dtoOwner = repoDTO.GetOwningDTO(dto);
				if (dtoOwner.Classname != "LangProject")
				{
					Assert.AreEqual(dtoOwner.Classname, "Scripture", "Either LangProject or Scripture owns the style");
					if (sName == "External Link")
						++cExternalLink;
					else if (sName == "Internal Link")
						++cInternalLink;
					else if (sName == "Language Code")
						++cLanguageCode;
				}
				else
				{
					Assert.AreNotEqual(sName, "External Link", "The External Link style should no longer exist");
					Assert.AreNotEqual(sName, "Internal Link", "The Internal Link style should no longer exist");
					Assert.AreNotEqual(sName, "Language Code", "The Language Code style should no longer exist");
					if (sName == "Hyperlink")
						++cHyperlink;
					else if (sName == "Writing System Abbreviation")
						++cWrtSysAbbr;
					else if (sName == "Strong")
						++cStrong;
					if (sXml.Contains("<BasedOn>") || sXml.Contains("<Next>"))
					{
						XElement xeStyle = XElement.Parse(sXml);
						ValidateStyleReference(repoDTO, xeStyle, "BasedOn");
						ValidateStyleReference(repoDTO, xeStyle, "Next");
					}
					switch (sName)
					{
						case "Normal":
						case "Numbered List":
						case "Bulleted List":
						case "Heading 1":
						case "Heading 2":
						case "Heading 3":
						case "Block Quote":
						case "Title Text":
						case "Emphasized Text":
						case "Writing System Abbreviation":
						case "Added Text":
						case "Deleted Text":
						case "Hyperlink":
						case "Strong":
						case "Dictionary-Normal":
						case "Classified-MainEntry":
						case "Classified-Item":
							Assert.IsTrue(IsBuiltInStyle(sXml), sName + " should be marked as built-in");
							++cBuiltIn;
							break;
						default:
							// "Heading 4" and "Dictionary-Custom" should pass through here, plus 7 more
							// created from direct formatting.
							Assert.IsFalse(IsBuiltInStyle(sXml), sName + " should not be marked as built-in");
							++cCustom;
							break;
					}
				}
			}
			Assert.That(gotHeading3, "should have kept the LangProj Heading3");
			Assert.That(gotHeading5, "should have kept the LangProj Heading4");
			Assert.AreEqual(1, cHyperlink, "The Hyperlink style should exist (once)");
			Assert.AreEqual(1, cWrtSysAbbr, "The Writing System Abbreviation style should exist (once)");
			Assert.AreEqual(1, cStrong, "The Strong style should exist (once)");
			Assert.AreEqual(1, cExternalLink, "The External Link style should exist (once) in the Scripture stylesheet");
			Assert.AreEqual(1, cInternalLink, "The Internal Link style should exist (once) in the Scripture stylesheet");
			Assert.AreEqual(1, cLanguageCode, "The Language Code style should exist (once) in the Scripture stylesheet");
			Assert.AreEqual(17, cBuiltIn, "There should be 17 built-in LangProject styles.");
			Assert.AreEqual(9, cCustom, "There should be 9 custom LangProject styles.");

		}
		private void FixOrAddMissingTypes(IDomainObjectDTORepository repoDto, IEnumerable<LexTypeInfo> extraTypes)
		{
			foreach (var info in extraTypes)
			{
				var fChanged = false;
				foreach (var xeAUni in info.XmlElement.XPathSelectElements("Name/AUni"))
				{
					var xaWs = xeAUni.Attribute("ws");
					if (xaWs == null || xaWs.Value.ToLowerInvariant() != "en")
						continue;
					var name = xeAUni.Value;
					string guidStd;
					if (!m_mapNameGuid.TryGetValue(name, out guidStd))
						continue;
					// We need to change the guid of this dto from 'guid to 'guidStd
					ChangeInvalidGuid(repoDto, info, name, guidStd);
				}
				var xeProt = info.XmlElement.XPathSelectElement("IsProtected");
				if (xeProt == null)
				{
					info.XmlElement.Add(new XElement("IsProtected", new XAttribute("val", "true")));
					fChanged = true;
				}
				else
				{
					var xaVal = xeProt.Attribute("val");
					if (xaVal == null)
					{
						xeProt.Add(new XAttribute("val", "true"));
						fChanged = true;
					}
					else if (xaVal.Value.ToLowerInvariant() != "true")
					{
						xaVal.SetValue("true");
						fChanged = true;
					}
				}
				if (fChanged)
				{
					info.DTO.Xml = info.XmlElement.ToString();
					repoDto.Update(info.DTO);
				}
			}

			if (m_mapNameGuid.Count > 0)
			{
				BuildNewTypeMaps();
				var newTypes = new HashSet<DomainObjectDTO>();
				foreach (var guid in m_mapGuidName.Keys)
				{
					// We need to create this LexEntryType!
					var rgNewDtos = m_mapGuidNewDtos[guid];
					foreach (var info in rgNewDtos)
					{
						var dto = new DomainObjectDTO(info.Guid, info.ClassName, info.Xml);
						repoDto.Add(dto);
						if (info.ClassName == "LexEntryType")
							newTypes.Add(dto);

					}
				}
				foreach (var dto in newTypes)
				{
					var dtoOwner = repoDto.GetOwningDTO(dto);
					var xeOwner = XElement.Parse(dtoOwner.Xml);
					XElement xePoss = null;
					if (dtoOwner.Classname == "CmPossibilityList")
					{
						xePoss = xeOwner.Element("Possibilities");
						if (xePoss == null)
						{
							xePoss = new XElement("Possibilities");
							xeOwner.Add(xePoss);
						}
					}
					else if (dtoOwner.Classname == "LexEntryType")
					{
						xePoss = xeOwner.Element("SubPossibilities");
						if (xePoss == null)
						{
							xePoss = new XElement("SubPossibilities");
							xeOwner.Add(xePoss);
						}
					}
					if (xePoss != null)
					{
						var fNeeded = true;
						foreach (var objsur in xePoss.Elements("objsur"))
						{
							var xaGuid = objsur.Attribute("guid");
							if (xaGuid == null)
								throw new Exception("missing guid in an objsur element");
							if (xaGuid.Value.Equals(dto.Guid, StringComparison.OrdinalIgnoreCase))
							{
								fNeeded = false;
								break;
							}
						}
						if (fNeeded)
						{
							xePoss.Add(DataMigrationServices.CreateOwningObjSurElement(dto.Guid));
							dtoOwner.Xml = xeOwner.ToString();
							repoDto.Update(dtoOwner);
						}
					}
				}
			}
		}
		private static XElement CheckNewSegment(IDomainObjectDTORepository dtoRepos, int beginOffset, DomainObjectDTO owningPara, XElement newSegmentObjSurElement, out DomainObjectDTO newSegmentDto)
		{
			newSegmentDto = dtoRepos.GetDTO(newSegmentObjSurElement.Attribute("guid").Value);
			Assert.IsNotNull(newSegmentDto, "Missing new Segment DTO.");
			var newSegmentElement = XElement.Parse(newSegmentDto.Xml);
			// Make sure it is owned by the expected para.
			Assert.AreSame(owningPara, dtoRepos.GetOwningDTO(newSegmentDto), "Wrong paragraph owner.");
			var newSegmentInnerElement = newSegmentElement.Element("Segment");
			Assert.IsNotNull(newSegmentInnerElement, "Missing new inner Segment Element in the new Segment object.");
			Assert.AreEqual(beginOffset, int.Parse(newSegmentInnerElement.Element("BeginOffset").Attribute("val").Value));

			return newSegmentInnerElement;
		}
		private static void CheckNotes(IDomainObjectDTORepository dtoRepos, DomainObjectDTO newSegmentDto, ICollection noteGuids, XContainer newSegmentInnerElement, bool expectedToHaveNotes, int expectedNumberOfNotes)
		{
			// Check Nts.
			var notesElements = newSegmentInnerElement.Elements("Notes");
			if (!expectedToHaveNotes)
			{
				Assert.AreEqual(0, notesElements.Count(), "Had unexpected number of notes.");
				return;
			}

			var objsurElements = notesElements.Elements("objsur");
			Assert.AreEqual(expectedNumberOfNotes, objsurElements.Count(), "Wrong number of Notes.");
			foreach (var objsurElement in objsurElements)
			{
				// Make sure the objsur guid is in 'noteGuids'.
				var objsurGuid = objsurElement.Attribute("guid").Value;
				Assert.Contains(objsurGuid, noteGuids);

				// Make sure the objsur element is t="o" (owning).
				var type = objsurElement.Attribute("t").Value;
				Assert.AreEqual("o", type, "Not an owning property.");

				// Make sure the owner of the objsur guid is the new Segment.
				var noteDto = dtoRepos.GetDTO(objsurGuid);
				Assert.AreSame(newSegmentDto, dtoRepos.GetOwningDTO(noteDto));

				// Each Nt should have two alts.
				var noteElement = XElement.Parse(noteDto.Xml);
				var contentElement = noteElement.Element("Note").Element("Content");
				Assert.IsNotNull(contentElement, "No Nt Content element.");
				CheckAlternatives(contentElement, 2);
			}
		}
		private Dictionary<string, List<DomainObjectDTO>> CreateFileGuidToPictureMap(IDomainObjectDTORepository repoDto)
		{
			var map = new Dictionary<string, List<DomainObjectDTO>>();
			foreach (var picture in repoDto.AllInstancesSansSubclasses("CmPicture"))
			{
				// all TE pictures are unowned, so no need to look at those with owners
				if (repoDto.GetOwningDTO(picture) == null)
				{
					var pictureElement = XElement.Parse(picture.Xml);
					var pictureFileElement = pictureElement.Element("PictureFile");
					// FWR-3385: not sure how this could happen, but it has occurred in
					// real data
					if (pictureFileElement == null)
						continue;
					var objSurrogateElement = pictureFileElement.Element("objsur");
					var fileGuid = objSurrogateElement.Attribute("guid").Value;

					List<DomainObjectDTO> list;
					if (!map.TryGetValue(fileGuid, out list))
					{
						list = new List<DomainObjectDTO>();
						map[fileGuid] = list;
					}
					list.Add(picture);
				}
			}
			return map;
		}
		private static string GetStTextGuidFromXfic(IDomainObjectDTORepository dtoRepos, string xficGuid)
		{
			var dtoXfic = dtoRepos.GetDTO(xficGuid.ToLowerInvariant());
			var paraGuid = GetBeginObjectGuid(dtoXfic.XmlBytes);
			return dtoRepos.GetOwningDTO(dtoRepos.GetDTO(paraGuid.ToLowerInvariant())).Guid;
		}
		private static void DeleteUnneededGoners(IDomainObjectDTORepository dtoRepos,
			IEnumerable<DomainObjectDTO> goners, ICollection<string> neededGoners)
		{
			// We have run out of memory during this data migration on large projects.  (See
			// FWR-3849.) One possible reason is that we can make 15000+ copies of LangProject
			// as we slowly squeeze out the owning links to Annotations.  So let's collect them
			// all up at once, and remove them with a single change to the LangProject dto.
			var dtoLangProject = dtoRepos.AllInstancesSansSubclasses("LangProject").FirstOrDefault();
			if (dtoLangProject != null)
			{
				var xeLangProject = XElement.Parse(dtoLangProject.Xml);
				var cAnn = xeLangProject.Descendants("objsur").
					Where(t => (t.Attribute("t") != null && t.Attribute("t").Value == "o")).Count();
				if (cAnn > 0)
				{
					var gonersInLangProject = new List<DomainObjectDTO>(cAnn);
					foreach (var goner in goners)
					{
						DomainObjectDTO gonerActual;
						if (!dtoRepos.TryGetValue(goner.Guid, out gonerActual))
							continue; // Not in repos.
						if (neededGoners.Contains(goner.Guid))
							continue; // still need this in the repository.
						var ownerDto = dtoRepos.GetOwningDTO(goner);
						if (ownerDto != null && ownerDto.Guid == dtoLangProject.Guid)
							gonersInLangProject.Add(gonerActual);
					}
					if (gonersInLangProject.Count > 0)
					{
						DataMigrationServices.RemoveMultipleIncludingOwnedObjects(dtoRepos,
							gonersInLangProject, dtoLangProject);
						gonersInLangProject.Clear();
						// We don't try removing those from goners because that operation may
						// involve a linear lookup in the list to find the object to remove.
						// And RemoveIncludingOwnedObjects() checks whether the goner still
						// exists before doing anything else.
					}
				}
			}
			// Remove old stuff.
			foreach (var goner in goners)
			{
				if (neededGoners.Contains(goner.Guid))
					continue;
				DataMigrationServices.RemoveIncludingOwnedObjects(dtoRepos, goner, true);
			}
		}
		private static XElement AddSegmentAnalyses(
			IDomainObjectDTORepository dtoRepos,
			IEnumerable<KeyValuePair<byte[], XElement>> halfBakedCcwgItemsForCurrentPara,
			byte[] oldSegElement,
			IDictionary<int, byte[]> xficsForCurrentPara,
			ICollection<byte[]> oldTextTags,
			string newSegmentGuid,
			bool isLastOldSegment,
			DomainObjectDTO paraDto)
		{
			XElement retval = null;

			var oldSegBeginOffset = GetBeginOffset(oldSegElement);
			var oldSegEndOffset = GetEndOffset(oldSegElement);

			var xficsForCurrentOldSegment = new SortedList<int, byte[]>();
			var halfBakedCcwgsForCurrentOldSegment = new List<XElement>();
			if (xficsForCurrentPara != null)
			{
				foreach (var kvp in xficsForCurrentPara)
				{
					var xficGuid = GetGuid(kvp.Value);
					var beginOffset = kvp.Key;

					// Try to find a CCWG that has a matching instanceOfGuid.
					XElement halfBakedCcwg = null;
					foreach (var halfBakedKvp in halfBakedCcwgItemsForCurrentPara)
					{
						// NB: halfBakedKvp.Value.Element("ConstChartWordGroup").Element("BeginAnalysisIndex").Attribute("val").Value.ToLower()
						// This is the 'InstanceOf' guid of the xfic, not the xfic's guid.
						if (
							halfBakedKvp.Value.Element("ConstChartWordGroup").Element("BeginAnalysisIndex").Attribute(
								"val").Value.ToLower() != xficGuid)
							continue;

						// If there happen to be more than one CCWG pointing to the same xfic, only one gets 'finished'!
						halfBakedCcwg = halfBakedKvp.Value;
						break;
					}

					if (beginOffset >= oldSegBeginOffset && beginOffset < oldSegEndOffset)
					{
						xficsForCurrentOldSegment.Add(beginOffset, kvp.Value);
						if (halfBakedCcwg != null)
							halfBakedCcwgsForCurrentOldSegment.Add(halfBakedCcwg);
					}
					else if (isLastOldSegment)
					{
						if (halfBakedCcwg != null)
							halfBakedCcwgsForCurrentOldSegment.Add(halfBakedCcwg);
						xficsForCurrentOldSegment.Add(beginOffset, kvp.Value);
					}
				}
			}

			if (xficsForCurrentOldSegment.Count > 0)
			{
				foreach (var key in xficsForCurrentOldSegment.Keys)
					xficsForCurrentPara.Remove(key);

				// The one returned element is "Analyses" (or null, if no twfics/pfics).
				// It will have one, or more, 'objsur' type 'r' elements in it.
				// The 'Analyses' property is a seq,
				// which is why is xficsForCurrentOldSegment is sorted the BeginOffset of the contained twfics/pfics.

				// All xfics will have an 'InstanceOf' by now, even pfics.
				// The pfics had a new InstanceOf prop set earlier
				// (or were removed, if InstanceOf could not be set),
				// and twfics with no InstanceOf prop were filtered out earlier.
				retval = new XElement("Analyses",
					from xfix in xficsForCurrentOldSegment.Values
					select DataMigrationServices.CreateReferenceObjSurElement(GetInstanceOfGuid(xfix)));

				// Finish converting half-baked CCWG stuff.
				if (halfBakedCcwgsForCurrentOldSegment.Count > 0)
				{
					var allOldXficGuids = (from xfix in xficsForCurrentOldSegment.Values
										   select GetGuid(xfix).ToLower()).ToList();
					foreach (var halfBakedCcwg in halfBakedCcwgsForCurrentOldSegment)
					{
						// Fix up halfbaked CCWG items here.
						var innerElement = halfBakedCcwg.Element("ConstChartWordGroup");
						// NB: The guids temporarily stored in the begin/end index props are for
						// the xfic guid, but we want to look up the index of its InstanceOf property,
						// which will be what is actually in the new "Analyses" prop of the new Segment.
						var guidAttr = innerElement.Element("BeginAnalysisIndex").Attribute("val");
						guidAttr.Value = allOldXficGuids.IndexOf(guidAttr.Value.ToLower()).ToString();
						guidAttr = innerElement.Element("EndAnalysisIndex").Attribute("val");
						guidAttr.Value = allOldXficGuids.IndexOf(guidAttr.Value.ToLower()).ToString();
						// NB: The recently created CCWG has already been added to dto repos,
						// so the only remaining task is to update the xml on the CCWG dto.
						var dto = dtoRepos.GetDTO(GetGuid(halfBakedCcwg));
						dto.Xml = halfBakedCcwg.ToString();
					}
				}

				// Find and convert any any old Text Tag indirect annotations for this segment.
				// NB: xficsForCurrentOldSegment.Values may have pfics,
				// and we don't want those here.
				// oldTextTags is all old TextTag annotations for everything.
				// This variable holds the twfics (in BeginOffset order),
				// where pfics are removed from xficsForCurrentOldSegment.
				var twficBeginOffsetListForCurrentSegment = new List<int>();
				var twficGuidListForCurrentSegment = new List<string>();
				var twficElementListForCurrentSegment = new List<byte[]>();
				var twficMap = new Dictionary<string, byte[]>();
				foreach (var twficKvp in from xficKvp in xficsForCurrentOldSegment
										 where GetAnnotationTypeGuid(xficKvp.Value) == DataMigrationServices.kTwficAnnDefnGuid
										 select xficKvp)
				{
					twficBeginOffsetListForCurrentSegment.Add(twficKvp.Key);
					var twficGuid = GetGuid(twficKvp.Value);
					twficGuidListForCurrentSegment.Add(twficGuid);
					twficElementListForCurrentSegment.Add(twficKvp.Value);
					twficMap.Add(twficGuid, twficKvp.Value);
				}

				var textTagElementsForCurrentSegment = new List<byte[]>();
				foreach (var oldTextTagAnnElement in oldTextTags)
				{
					// Find the ones that are used in the current segment,
					// and add them to textTagElementsForCurrentSegment.

					var appliesToGuids = new List<string>();
					foreach (var guid in GetAppliesToObjsurGuids(oldTextTagAnnElement))
					{
						if (twficGuidListForCurrentSegment.Contains(guid))
							appliesToGuids.Add(guid);
					}
					if (appliesToGuids.Count() <= 0)
						continue;

					// Store them for a while.
					textTagElementsForCurrentSegment.Add(oldTextTagAnnElement);

					// Get the index of the First twfic in appliesToGuids collection (which may hold pfics)
					// and the index of the Last twfic in appliesToGuids collection (which may hold pfics).
					var beginIdx = 0;
					for (var i = 0; i < appliesToGuids.Count(); ++i)
					{
						var currentXfixGuid = appliesToGuids[i].ToLower();
						byte[] currentTwficElement;
						if (!twficMap.TryGetValue(currentXfixGuid, out currentTwficElement))
							continue;

						beginIdx = xficsForCurrentOldSegment.IndexOfValue(currentTwficElement);
						break;
					}
					var endIdx = 0;
					for (var i = appliesToGuids.Count() - 1; i > -1; --i)
					{
						var currentXfixGuid = appliesToGuids[i].ToLower();
						byte[] currentTwficElement;
						if (!twficMap.TryGetValue(currentXfixGuid, out currentTwficElement))
							continue;

						endIdx = xficsForCurrentOldSegment.IndexOfValue(currentTwficElement);
						break;
					}

					var owningStText = dtoRepos.GetOwningDTO(paraDto);
					var newTextTagGuid = Guid.NewGuid().ToString().ToLower();
					var newTextTagElement = new XElement("rt",
						new XAttribute("class", "TextTag"),
						new XAttribute("guid", newTextTagGuid),
						new XAttribute("ownerguid", owningStText.Guid.ToLower()),
						new XElement("CmObject"),
						new XElement("TextTag",
							new XElement("BeginSegment", DataMigrationServices.CreateReferenceObjSurElement(newSegmentGuid)),
							new XElement("EndSegment", DataMigrationServices.CreateReferenceObjSurElement(newSegmentGuid)),
							new XElement("BeginAnalysisIndex", new XAttribute("val", beginIdx)),
							new XElement("EndAnalysisIndex", new XAttribute("val", endIdx)),
							new XElement("Tag", DataMigrationServices.CreateReferenceObjSurElement(GetInstanceOfGuid(oldTextTagAnnElement)))));

					// Add new DTO to repos.
					var newTextTagDto = new DomainObjectDTO(newTextTagGuid, "TextTag", newTextTagElement.ToString());
					dtoRepos.Add(newTextTagDto);
					// Add new TextTag to owning prop on owner as objsur element.
					var owningStTextElement = XElement.Parse(owningStText.Xml);
					var innerStTextElement = owningStTextElement.Element("StText");
					var tagsPropElement = innerStTextElement.Element("Tags");
					if (tagsPropElement == null)
					{
						tagsPropElement = new XElement("Tags");
						innerStTextElement.Add(tagsPropElement);
					}
					tagsPropElement.Add(DataMigrationServices.CreateOwningObjSurElement(newTextTagGuid));
					// Tell repos of the modification.
					owningStText.Xml = owningStTextElement.ToString();
					dtoRepos.Update(owningStText);
				}
				// Remove current text tags from input list
				foreach (var currentTextTagElement in textTagElementsForCurrentSegment)
					oldTextTags.Remove(currentTextTagElement);
			}
			//else
			//{
			//    // No xfics at all, so make sure para is set to be tokenized again.
			//    // Done globally for each Para in ProcessParagraphs
			//    //MarkParaAsNeedingTokenization(dtoRepos, paraDto, paraElement);
			//}

			return retval;
		}