public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository)
		{
			DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000050);

			var newGuidValue = Guid.NewGuid().ToString().ToLowerInvariant();
			const string className = "LangProject";
			var lpDto = domainObjectDtoRepository.AllInstancesSansSubclasses(className).First();
			var ownedDtos = domainObjectDtoRepository.GetDirectlyOwnedDTOs(lpDto.Guid).ToList();
			var data = lpDto.Xml;
			domainObjectDtoRepository.Remove(lpDto); // It is pretty hard to change an immutable Guid identifier in BEP-land, so nuke it, and make a new one.

			var lpElement = XElement.Parse(data);
			lpElement.Attribute("guid").Value = newGuidValue;
			var newLpDto = new DomainObjectDTO(newGuidValue, className, lpElement.ToString());
			domainObjectDtoRepository.Add(newLpDto);

			// Change ownerguid attr for each owned item to new guid.
			foreach (var ownedDto in ownedDtos)
			{
				var ownedElement = XElement.Parse(ownedDto.Xml);
				ownedElement.Attribute("ownerguid").Value = newGuidValue;
				ownedDto.Xml = ownedElement.ToString();
				domainObjectDtoRepository.Update(ownedDto);
			}

			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
		/// <summary>
		/// The Weather list is never used, so delete it (and remove any empty Weather elements
		/// from the RnGenericRec elements).
		/// </summary>
		private void DeleteWeatherListAndField(IDomainObjectDTORepository repoDTO)
		{
			// Remove the Weather list.
			DomainObjectDTO dtoLP = GetDtoLangProj(repoDTO);
			string sWeatherListGuid = RemoveWeatherConditionsElement(dtoLP).ToLowerInvariant();
			repoDTO.Update(dtoLP);
			DomainObjectDTO dtoDeadList = null;
			foreach (var dto in repoDTO.AllInstancesWithSubclasses("CmPossibilityList"))
			{
				if (dto.Guid.ToLowerInvariant() == sWeatherListGuid)
				{
					dtoDeadList = dto;
					break;
				}
			}
			List<DomainObjectDTO> rgdtoDead = new List<DomainObjectDTO>();
			GatherDeadObjects(repoDTO, dtoDeadList, rgdtoDead);
			foreach (var dto in rgdtoDead)
				repoDTO.Remove(dto);

			// Remove any empty Weather elements in the RnGenericRec objects.
			foreach (var dto in repoDTO.AllInstancesWithSubclasses("RnGenericRec"))
			{
				string sXml = dto.Xml;
				int idx = sXml.IndexOf("<Weather");
				if (idx > 0)
				{
					dto.Xml = RemoveEmptyWeather(sXml, idx);
					repoDTO.Update(dto);
				}
			}
		}
		private void RemoveUnwantedOverlays(IDomainObjectDTORepository repoDTO, List<DomainObjectDTO> collectOverlaysToRemove)
		{
			DomainObjectDTO dtoLP = GetDtoLangProj(repoDTO);
			foreach (var dto in collectOverlaysToRemove)
			{
				RemoveOverlayElement(dtoLP, dto.Guid);
				repoDTO.Remove(dto);
			}
		}
        private void RemoveUnwantedOverlays(IDomainObjectDTORepository repoDTO, List <DomainObjectDTO> collectOverlaysToRemove)
        {
            DomainObjectDTO dtoLP = GetDtoLangProj(repoDTO);

            foreach (var dto in collectOverlaysToRemove)
            {
                RemoveOverlayElement(dtoLP, dto.Guid);
                repoDTO.Remove(dto);
            }
        }
Example #5
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Cleans up legacy ChkRendering objects with null SurfaceForms.
        /// </summary>
        /// <param name="repoDto">
        /// 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 repoDto)
        {
            DataMigrationServices.CheckVersionNumber(repoDto, 7000046);

            Dictionary <Guid, DomainObjectDTO> mapOfRenderingsToChk = new Dictionary <Guid, DomainObjectDTO>();
            HashSet <DomainObjectDTO>          renderingsToDelete   = new HashSet <DomainObjectDTO>();

            foreach (DomainObjectDTO dto in repoDto.AllInstances())
            {
                XElement   data      = XElement.Parse(dto.Xml);
                XAttribute classAttr = data.Attribute("class");
                if (classAttr.Value == "ChkTerm")
                {
                    XElement renderings = data.Element("Renderings");
                    if (renderings != null)
                    {
                        foreach (XElement r in renderings.Elements())
                        {
                            mapOfRenderingsToChk[new Guid(r.Attribute("guid").Value)] = dto;
                        }
                    }
                }
                else if (classAttr.Value == "ChkRendering")
                {
                    XElement surfaceForm = data.Element("SurfaceForm");
                    if (surfaceForm == null || !surfaceForm.HasElements)
                    {
                        renderingsToDelete.Add(dto);
                    }
                }
            }

            foreach (DomainObjectDTO rendering in renderingsToDelete)
            {
                DomainObjectDTO chkTerm        = mapOfRenderingsToChk[new Guid(rendering.Guid)];
                XElement        termData       = XElement.Parse(chkTerm.Xml);
                XElement        renderings     = termData.Element("Renderings");
                XElement        bogusRendering = renderings.Elements().First(e => e.Attribute("guid").Value.Equals(rendering.Guid, StringComparison.OrdinalIgnoreCase));
                bogusRendering.Remove();
                DataMigrationServices.UpdateDTO(repoDto, chkTerm, termData.ToString());
                repoDto.Remove(rendering);
            }

            DataMigrationServices.IncrementVersionNumber(repoDto);
        }
Example #6
0
        private void MoveFileReferences(IDomainObjectDTORepository repoDto, DomainObjectDTO langProj,
                                        DomainObjectDTO srcFolder, DomainObjectDTO destFolder)
        {
            var srcFolderElement  = XElement.Parse(srcFolder.Xml);
            var destFolderElement = XElement.Parse(destFolder.Xml);
            var destFileRefs      = destFolderElement.Element("Files");

            foreach (var fileRef in srcFolderElement.Element("Files").Elements())
            {
                destFileRefs.Add(fileRef);
                var guid        = fileRef.Attribute("guid").Value;
                var file        = repoDto.GetDTO(guid);
                var fileElement = XElement.Parse(file.Xml);
                fileElement.Attribute("ownerguid").SetValue(destFolder.Guid);
                DataMigrationServices.UpdateDTO(repoDto, file, fileElement.ToString());
            }

            RemoveReferenceFromPictures(repoDto, langProj, srcFolder.Guid);
            repoDto.Remove(srcFolder);
            DataMigrationServices.UpdateDTO(repoDto, destFolder, destFolderElement.ToString());
        }
Example #7
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Perform one increment migration step.
        /// </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, 7000000);

            DataMigrationServices.Delint(domainObjectDtoRepository);

            var lpDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First();
            // 1. Remove property from LangProg.
            var lpElement = XElement.Parse(lpDto.Xml);

            lpElement.Descendants("WordformInventory").Remove();
            DataMigrationServices.UpdateDTO(domainObjectDtoRepository, lpDto, lpElement.ToString());

            // 2. Remove three owner-related attributes from each WfiWordform.
            // (There may be zero, or more, instances to upgrade.)
            foreach (var wordformDto in domainObjectDtoRepository.AllInstancesSansSubclasses("WfiWordform"))
            {
                var wfElement = XElement.Parse(wordformDto.Xml);
                wfElement.Attribute("ownerguid").Remove();
                var flidAttr = wfElement.Attribute("owningflid");
                if (flidAttr != null)
                {
                    flidAttr.Remove();
                }
                var ordAttr = wfElement.Attribute("owningord");
                if (ordAttr != null)
                {
                    ordAttr.Remove();
                }
                DataMigrationServices.UpdateDTO(domainObjectDtoRepository, wordformDto, wfElement.ToString());
            }

            // 3. Remove WordformInventory instance.
            domainObjectDtoRepository.Remove(
                domainObjectDtoRepository.AllInstancesSansSubclasses("WordformInventory").First());

            // There are no references to the WFI, so don't fret about leaving dangling refs to it.

            DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
        }
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Cleans up legacy ChkRendering objects with null SurfaceForms.
		/// </summary>
		/// <param name="repoDto">
		/// 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 repoDto)
		{
			DataMigrationServices.CheckVersionNumber(repoDto, 7000046);

			Dictionary<Guid, DomainObjectDTO> mapOfRenderingsToChk = new Dictionary<Guid, DomainObjectDTO>();
			HashSet<DomainObjectDTO> renderingsToDelete = new HashSet<DomainObjectDTO>();

			foreach (DomainObjectDTO dto in repoDto.AllInstances())
			{
				XElement data = XElement.Parse(dto.Xml);
				XAttribute classAttr = data.Attribute("class");
				if (classAttr.Value == "ChkTerm")
				{
					XElement renderings = data.Element("Renderings");
					if (renderings != null)
						foreach (XElement r in renderings.Elements())
							mapOfRenderingsToChk[new Guid(r.Attribute("guid").Value)] = dto;
				}
				else if (classAttr.Value == "ChkRendering")
				{
					XElement surfaceForm = data.Element("SurfaceForm");
					if (surfaceForm == null || !surfaceForm.HasElements)
						renderingsToDelete.Add(dto);
				}
			}

			foreach (DomainObjectDTO rendering in renderingsToDelete)
			{
				DomainObjectDTO chkTerm = mapOfRenderingsToChk[new Guid(rendering.Guid)];
				XElement termData = XElement.Parse(chkTerm.Xml);
				XElement renderings = termData.Element("Renderings");
				XElement bogusRendering = renderings.Elements().First(e => e.Attribute("guid").Value.Equals(rendering.Guid, StringComparison.OrdinalIgnoreCase));
				bogusRendering.Remove();
				DataMigrationServices.UpdateDTO(repoDto, chkTerm, termData.ToString());
				repoDto.Remove(rendering);
			}

			DataMigrationServices.IncrementVersionNumber(repoDto);
		}
Example #9
0
        /// <summary>
        /// The Weather list is never used, so delete it (and remove any empty Weather elements
        /// from the RnGenericRec elements).
        /// </summary>
        private void DeleteWeatherListAndField(IDomainObjectDTORepository repoDTO)
        {
            // Remove the Weather list.
            DomainObjectDTO dtoLP            = GetDtoLangProj(repoDTO);
            string          sWeatherListGuid = RemoveWeatherConditionsElement(dtoLP).ToLowerInvariant();

            repoDTO.Update(dtoLP);
            DomainObjectDTO dtoDeadList = null;

            foreach (var dto in repoDTO.AllInstancesWithSubclasses("CmPossibilityList"))
            {
                if (dto.Guid.ToLowerInvariant() == sWeatherListGuid)
                {
                    dtoDeadList = dto;
                    break;
                }
            }
            List <DomainObjectDTO> rgdtoDead = new List <DomainObjectDTO>();

            GatherDeadObjects(repoDTO, dtoDeadList, rgdtoDead);
            foreach (var dto in rgdtoDead)
            {
                repoDTO.Remove(dto);
            }

            // Remove any empty Weather elements in the RnGenericRec objects.
            foreach (var dto in repoDTO.AllInstancesWithSubclasses("RnGenericRec"))
            {
                string sXml = dto.Xml;
                int    idx  = sXml.IndexOf("<Weather");
                if (idx > 0)
                {
                    dto.Xml = RemoveEmptyWeather(sXml, idx);
                    repoDTO.Update(dto);
                }
            }
        }
Example #10
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 #11
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 MoveFileReferences(IDomainObjectDTORepository repoDto, DomainObjectDTO langProj,
			DomainObjectDTO srcFolder, DomainObjectDTO destFolder)
		{
			var srcFolderElement = XElement.Parse(srcFolder.Xml);
			var destFolderElement = XElement.Parse(destFolder.Xml);
			var destFileRefs = destFolderElement.Element("Files");
			foreach (var fileRef in srcFolderElement.Element("Files").Elements())
			{
				destFileRefs.Add(fileRef);
				var guid = fileRef.Attribute("guid").Value;
				var file = repoDto.GetDTO(guid);
				var fileElement = XElement.Parse(file.Xml);
				fileElement.Attribute("ownerguid").SetValue(destFolder.Guid);
				DataMigrationServices.UpdateDTO(repoDto, file, fileElement.ToString());
			}

			RemoveReferenceFromPictures(repoDto, langProj, srcFolder.Guid);
			repoDto.Remove(srcFolder);
			DataMigrationServices.UpdateDTO(repoDto, destFolder, destFolderElement.ToString());
		}
Example #13
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Changes the use of CmAgentEvaluation, so that instead of one evaluation per agent/target pair
        /// owned by Agent.Evauations and referring to the target, there are only two CmAgentEvaluations
        /// owned by each CmAgent in their Approves and Disapproves properties, and they are referred to
        /// by the new WfiAnalysis.Evaluations Reference Collection.
        ///
        /// Specifically, for each CmAgent,
        /// - Create two CmAgentEvaluations, one owned in Approves and one in Disapproves.
        /// - For each item in Evaluations, if Accepted, add the appropriate Approves evaluation to
        ///     the target of the evaluation, otherwise, add the appropriate Disapproves (if there is a target).
        /// - Delete the Evaluations.
        ///
        /// As a side effect, since all existing CmAgentEvaluations are deleted, obsolete properties are removed.
        /// </summary>
        /// <param name="domainObjectDtoRepository">Repository of all CmObject DTOs available for
        /// one migration step.</param>
        /// ------------------------------------------------------------------------------------
        public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository)
        {
            DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000013);

            // from guid of a WfiAnalysis to list of evaluations.
            var map = new Dictionary <string, List <string> >();

            foreach (var agentDto in domainObjectDtoRepository.AllInstancesSansSubclasses("CmAgent"))
            {
                var rtElement    = XElement.Parse(agentDto.Xml);
                var agentElement = rtElement.Elements("CmAgent").First();
                // Create two new CmAgentEvaluations.
                var newApprovesEval    = MakeEvaluation(agentDto.Guid, domainObjectDtoRepository, agentElement, "Approves");
                var newDisapprovesEval = MakeEvaluation(agentDto.Guid, domainObjectDtoRepository, agentElement, "Disapproves");
                var evaluations        = agentElement.Elements("Evaluations").FirstOrDefault();
                if (evaluations != null)
                {
                    foreach (var objsur in evaluations.Elements("objsur"))
                    {
                        var evalGuidAttr = objsur.Attribute("guid");
                        if (evalGuidAttr == null)
                        {
                            continue;
                        }

                        var evalGuid        = evalGuidAttr.Value;
                        var obsoleteEvalDto = domainObjectDtoRepository.GetDTO(evalGuid);
                        if (obsoleteEvalDto == null)
                        {
                            continue;                             // defensive (RandyR says there is no need to be defensive, since the repos will throw an exception, if the guid ois not found.)
                        }
                        var agentEvalElt = XElement.Parse(obsoleteEvalDto.Xml).Element("CmAgentEvaluation");
                        if (agentEvalElt == null)
                        {
                            continue;                             // paranoid! (Also, no need for paranoia, since the element *must* be present, or the object cannot be reconstituted.)
                        }
                        // Delete/Remove the old evaluation.
                        domainObjectDtoRepository.Remove(obsoleteEvalDto);

                        var  acceptedElt = agentEvalElt.Element("Accepted");
                        bool accepted    = false;
                        if (acceptedElt != null)
                        {
                            var attr = acceptedElt.Attribute("val");
                            if (attr != null)
                            {
                                accepted = attr.Value.ToLowerInvariant() == "true";
                            }
                        }

                        var targetElt = agentEvalElt.Element("Target");
                        if (targetElt == null)
                        {
                            continue;
                        }
                        var targetObjsur = targetElt.Element("objsur");
                        if (targetObjsur == null)
                        {
                            continue;                             // paranoid
                        }
                        var targetGuidAttr = targetObjsur.Attribute("guid");
                        if (targetGuidAttr == null)
                        {
                            continue;                             // paranoid
                        }
                        var           targetGuid = targetGuidAttr.Value;
                        List <String> evals;
                        if (!map.TryGetValue(targetGuid, out evals))
                        {
                            evals           = new List <string>();
                            map[targetGuid] = evals;
                        }

                        evals.Add(accepted ? newApprovesEval.Guid : newDisapprovesEval.Guid);
                    }
                    evaluations.Remove();
                }
                agentDto.Xml = rtElement.ToString();
                domainObjectDtoRepository.Update(agentDto);
            }
            foreach (var kvp in map)
            {
                var analysisDto   = domainObjectDtoRepository.GetDTO(kvp.Key);
                var analysisRtElt = XElement.Parse(analysisDto.Xml);
                var analysisElt   = analysisRtElt.Element("WfiAnalysis");
                if (analysisElt == null)
                {
                    continue;                     // Paranoid
                }
                var evals = new XElement("Evaluations");
                analysisElt.Add(evals);
                foreach (var eval in kvp.Value)
                {
                    evals.Add(new XElement("objsur", new XAttribute("t", "r"), new XAttribute("guid", eval)));
                }
                analysisDto.Xml = analysisRtElt.ToString();
                domainObjectDtoRepository.Update(analysisDto);
            }

            DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
        }
		/// <summary>
		/// Perform one increment migration step.
		/// </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, 7000018);

			// collect all writing system info
			var guidToWsInfo = new Dictionary<string, Tuple<string, DomainObjectDTO, XElement>>();
			foreach (DomainObjectDTO wsDto in domainObjectDtoRepository.AllInstancesSansSubclasses("LgWritingSystem").ToArray())
			{
				XElement wsElem = XElement.Parse(wsDto.Xml);
				XElement icuLocaleElem = wsElem.Element("ICULocale");
				var icuLocale = icuLocaleElem.Element("Uni").Value;
				string langTag = Version19LangTagUtils.ToLangTag(icuLocale);
				guidToWsInfo[wsDto.Guid.ToLowerInvariant()] = Tuple.Create(langTag, wsDto, wsElem);
			}

			// remove all CmSortSpec objects
			foreach (DomainObjectDTO sortSpecDto in domainObjectDtoRepository.AllInstancesSansSubclasses("CmSortSpec").ToArray())
				domainObjectDtoRepository.Remove(sortSpecDto);

			// remove SortSpecs property from LangProject
			DomainObjectDTO lpDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First();
			XElement lpElem = XElement.Parse(lpDto.Xml);
			XElement sortSpecsElem = lpElem.Element("SortSpecs");
			bool lpModified = false;
			if (sortSpecsElem != null)
			{
				sortSpecsElem.Remove();
				lpModified = true;
			}

			var referencedWsIds = new HashSet<string>();

			// convert all LangProject writing system references to strings
			if (ConvertRefToString(lpElem.Element("AnalysisWss"), guidToWsInfo, referencedWsIds))
				lpModified = true;
			if (ConvertRefToString(lpElem.Element("VernWss"), guidToWsInfo, referencedWsIds))
				lpModified = true;
			if (ConvertRefToString(lpElem.Element("CurAnalysisWss"), guidToWsInfo, referencedWsIds))
				lpModified = true;
			if (ConvertRefToString(lpElem.Element("CurPronunWss"), guidToWsInfo, referencedWsIds))
				lpModified = true;
			if (ConvertRefToString(lpElem.Element("CurVernWss"), guidToWsInfo, referencedWsIds))
				lpModified = true;
			if (lpModified)
				DataMigrationServices.UpdateDTO(domainObjectDtoRepository, lpDto, lpElem.ToString());

			// convert all ReversalIndex writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "ReversalIndex", guidToWsInfo, referencedWsIds);

			// convert all WordformLookupList writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "WordformLookupList", guidToWsInfo, referencedWsIds);

			// convert all CmPossibilityList writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "CmPossibilityList", guidToWsInfo, referencedWsIds);

			// convert all UserViewField writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "UserViewField", guidToWsInfo, referencedWsIds);

			// convert all CmBaseAnnotation writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "CmBaseAnnotation", guidToWsInfo, referencedWsIds);

			// convert all FsOpenFeature writing system references to strings
			ConvertAllRefsToStrings(domainObjectDtoRepository, "FsOpenFeature", guidToWsInfo, referencedWsIds);

			// convert all ScrMarkerMapping ICU locales to lang tags
			ConvertAllIcuLocalesToLangTags(domainObjectDtoRepository, "ScrMarkerMapping", referencedWsIds);

			// convert all ScrImportSource ICU locales to lang tags
			ConvertAllIcuLocalesToLangTags(domainObjectDtoRepository, "ScrImportSource", referencedWsIds);

			// convert all ICU locales to Language Tags and remove legacy magic font names
			foreach (DomainObjectDTO dto in domainObjectDtoRepository.AllInstances())
				UpdateStringsAndProps(domainObjectDtoRepository, dto, referencedWsIds);

			var localStoreFolder = Path.Combine(domainObjectDtoRepository.ProjectFolder, FdoFileHelper.ksWritingSystemsDir);

			// If any writing systems that project needs don't already exist as LDML files,
			// create them, either by copying relevant data from a shipping LDML file, or by
			// extracting data from the obsolete writing system object's XML.
			if (!string.IsNullOrEmpty(domainObjectDtoRepository.ProjectFolder))
			{
				foreach (Tuple<string, DomainObjectDTO, XElement> wsInfo in guidToWsInfo.Values)
				{
					if (referencedWsIds.Contains(wsInfo.Item1))
					{
						var ws = new Version19WritingSystemDefn();
						var langTag = wsInfo.Item1;
						ws.LangTag = langTag;
						var ldmlFileName = Path.ChangeExtension(langTag, "ldml");
						string localPath = Path.Combine(localStoreFolder, ldmlFileName);
						if (File.Exists(localPath))
							continue; // already have one.
						string globalPath = Path.Combine(DirectoryFinder.GlobalWritingSystemStoreDirectory, ldmlFileName);
						if (File.Exists(globalPath))
							continue; // already have one.
						// Need to make one.

						// Code similar to this was in the old migrator (prior to 7000043). It does not work
						// because the XML files it is looking for are in the Languages subdirectory of the
						// FieldWorks 6 data directory, and this is looking in the FW 7 one. No one has complained
						// so we decided not to try to fix it for the new implementation of the migration.

						//string langDefPath = Path.Combine(FwDirectoryFinder.GetDataSubDirectory("Languages"),
						//    Path.ChangeExtension(langTag, "xml"));
						//if (File.Exists(langDefPath))
						//    FillWritingSystemFromLangDef(XElement.Load(langDefPath), ws);
						//else

						FillWritingSystemFromFDO(domainObjectDtoRepository, wsInfo.Item3, ws);
						ws.Save(localPath);
					}
				}
			}
			foreach (Tuple<string, DomainObjectDTO, XElement> wsInfo in guidToWsInfo.Values)
			{
				// this should also remove all LgCollations as well
				DataMigrationServices.RemoveIncludingOwnedObjects(domainObjectDtoRepository, wsInfo.Item2, false);
			}

			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
		/// <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 a number of objects with a common owner, and everything they own.
		/// </summary>
		internal static void RemoveMultipleIncludingOwnedObjects(IDomainObjectDTORepository dtoRepos,
			List<DomainObjectDTO> goners, DomainObjectDTO ownerDto)
		{
			if (ownerDto != null)
			{
				var ownerElement = XElement.Parse(ownerDto.Xml);
				foreach (var goner in goners)
				{
					var goner1 = goner;
					var ownObjSurElement = (from objSurNode in ownerElement.Descendants("objsur")
											where
												objSurNode.Attribute("t").Value == "o" &&
												objSurNode.Attribute("guid").Value.ToLower() == goner1.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 elememtns removed, so we have to do the update.
					UpdateDTO(dtoRepos, ownerDto, ownerElement.ToString());
				}
			}
			foreach (var goner in goners)
			{
				foreach (var ownedDto in dtoRepos.GetDirectlyOwnedDTOs(goner.Guid))
					RemoveIncludingOwnedObjects(dtoRepos, ownedDto, false);
				dtoRepos.Remove(goner);
			}
		}
		/// <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);
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Changes the use of CmAgentEvaluation, so that instead of one evaluation per agent/target pair
		/// owned by Agent.Evauations and referring to the target, there are only two CmAgentEvaluations
		/// owned by each CmAgent in their Approves and Disapproves properties, and they are referred to
		/// by the new WfiAnalysis.Evaluations Reference Collection.
		///
		/// Specifically, for each CmAgent,
		/// - Create two CmAgentEvaluations, one owned in Approves and one in Disapproves.
		/// - For each item in Evaluations, if Accepted, add the appropriate Approves evaluation to
		///     the target of the evaluation, otherwise, add the appropriate Disapproves (if there is a target).
		/// - Delete the Evaluations.
		///
		/// As a side effect, since all existing CmAgentEvaluations are deleted, obsolete properties are removed.
		/// </summary>
		/// <param name="domainObjectDtoRepository">Repository of all CmObject DTOs available for
		/// one migration step.</param>
		/// ------------------------------------------------------------------------------------
		public void PerformMigration(IDomainObjectDTORepository domainObjectDtoRepository)
		{
			DataMigrationServices.CheckVersionNumber(domainObjectDtoRepository, 7000013);

			// from guid of a WfiAnalysis to list of evaluations.
			var map = new Dictionary<string, List<string>>();
			foreach (var agentDto in domainObjectDtoRepository.AllInstancesSansSubclasses("CmAgent"))
			{
				var rtElement = XElement.Parse(agentDto.Xml);
				var agentElement = rtElement.Elements("CmAgent").First();
				// Create two new CmAgentEvaluations.
				var newApprovesEval = MakeEvaluation(agentDto.Guid, domainObjectDtoRepository, agentElement, "Approves");
				var newDisapprovesEval = MakeEvaluation(agentDto.Guid, domainObjectDtoRepository, agentElement, "Disapproves");
				var evaluations = agentElement.Elements("Evaluations").FirstOrDefault();
				if (evaluations != null)
				{
					foreach (var objsur in evaluations.Elements("objsur"))
					{
						var evalGuidAttr = objsur.Attribute("guid");
						if (evalGuidAttr == null) continue;

						var evalGuid = evalGuidAttr.Value;
						var obsoleteEvalDto = domainObjectDtoRepository.GetDTO(evalGuid);
						if (obsoleteEvalDto == null)
							continue; // defensive (RandyR says there is no need to be defensive, since the repos will throw an exception, if the guid ois not found.)
						var agentEvalElt = XElement.Parse(obsoleteEvalDto.Xml).Element("CmAgentEvaluation");
						if (agentEvalElt == null)
							continue; // paranoid! (Also, no need for paranoia, since the element *must* be present, or the object cannot be reconstituted.)

						// Delete/Remove the old evaluation.
						domainObjectDtoRepository.Remove(obsoleteEvalDto);

						var acceptedElt = agentEvalElt.Element("Accepted");
						bool accepted = false;
						if (acceptedElt != null)
						{
							var attr = acceptedElt.Attribute("val");
							if (attr != null)
								accepted = attr.Value.ToLowerInvariant() == "true";
						}

						var targetElt = agentEvalElt.Element("Target");
						if (targetElt == null)
							continue;
						var targetObjsur = targetElt.Element("objsur");
						if (targetObjsur == null)
							continue; // paranoid
						var targetGuidAttr = targetObjsur.Attribute("guid");
						if (targetGuidAttr == null)
							continue; // paranoid
						var targetGuid = targetGuidAttr.Value;
						List<String> evals;
						if (!map.TryGetValue(targetGuid, out evals))
						{
							evals = new List<string>();
							map[targetGuid] = evals;
						}

						evals.Add(accepted ? newApprovesEval.Guid : newDisapprovesEval.Guid);
					}
					evaluations.Remove();
				}
				agentDto.Xml = rtElement.ToString();
				domainObjectDtoRepository.Update(agentDto);
			}
			foreach (var kvp in map)
			{
				var analysisDto = domainObjectDtoRepository.GetDTO(kvp.Key);
				var analysisRtElt = XElement.Parse(analysisDto.Xml);
				var analysisElt = analysisRtElt.Element("WfiAnalysis");
				if (analysisElt == null)
					continue; // Paranoid
				var evals = new XElement("Evaluations");
				analysisElt.Add(evals);
				foreach (var eval in kvp.Value)
				{
					evals.Add(new XElement("objsur", new XAttribute("t", "r"), new XAttribute("guid", eval)));
				}
				analysisDto.Xml = analysisRtElt.ToString();
				domainObjectDtoRepository.Update(analysisDto);
			}

			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
		private void ChangeInvalidGuid(IDomainObjectDTORepository repoDto, LexTypeInfo info,
			string name, string guidStd)
		{
			var xaGuid = info.XmlElement.Attribute("guid");
			if (xaGuid == null)
				throw new Exception("The object does not have a guid -- this is impossible!");
			xaGuid.SetValue(guidStd);
			var guidBad = info.DTO.Guid;
			if (!m_mapBadGoodGuids.ContainsKey(guidBad))
				m_mapBadGoodGuids.Add(guidBad, guidStd);
			var className = info.DTO.Classname;
			repoDto.Remove(info.DTO);
			info.DTO = new DomainObjectDTO(guidStd, className, info.XmlElement.ToString());
			repoDto.Add(info.DTO);
			// Fix the owning reference (but only if it's one of the two lists, because otherwise
			// it might be contained in a LexTypeInfo that hasn't yet been processed).
			var bad = String.Format("guid=\"{0}\"", guidBad);
			var good = String.Format("guid=\"{0}\"", guidStd);
			var bad2 = String.Format("guid='{0}'", guidBad);	// probably pure paranoia...
			var good2 = String.Format("guid='{0}'", guidStd);
			DomainObjectDTO dtoOwner;
			if (repoDto.TryGetOwner(info.DTO.Guid, out dtoOwner) && dtoOwner.Classname == "CmPossibilityList")
			{
				dtoOwner.Xml = dtoOwner.Xml.Replace(bad, good).Replace(bad2, good2);
				repoDto.Update(dtoOwner);
			}
			// Fix any references from LexEntryRef objects.
			foreach (var dtoRef in repoDto.AllInstancesWithSubclasses("LexEntryRef"))
			{
				var xml = dtoRef.Xml;
				if (xml.Contains(guidBad))
				{
					dtoRef.Xml = xml.Replace(bad, good).Replace(bad2, good2);
					repoDto.Update(dtoRef);
				}
			}
			m_mapNameGuid.Remove(name);
			m_mapGuidName.Remove(guidStd);
		}
		/// <summary>
		/// 1. Create the "Strong" style if it doesn't exist (FWR-741).
		/// 2. Convert direct formatting to styles (FWR-648).
		/// 3. Move the StStyle objects in LexDb.Styles to LangProject.Styles, deleting those
		///	   with the same name as one already in LangProject.Styles (FWR-1163).
		///	4. Delete the Styles field from LexDb (FWR-1163).
		///	5. Remove "External Link" and "Internal Link" styles - migrate use of these to
		///	   "Hyperlink" style (FWR-1163).
		///	6. Rename "Language Code" style to "Writing System Abbreviation" (FWR-1163).
		///	7. Ensure that built-in styles from LangProject.Styles are marked built-in.
		/// </summary>
		public void PerformMigration(IDomainObjectDTORepository repoDTO)
		{
			m_repoDTO = repoDTO;
			// Get the list of StStyle DTOs from the LexDb.Styles field, and delete the
			// LexDb.Styles field.
			string sClass = "LexDb";
			DomainObjectDTO dtoLexDb = GetFirstInstance(sClass);
			string sXmlLexDb = dtoLexDb.Xml;
			int idxStyles = sXmlLexDb.IndexOf("<Styles>");
			int idxStylesLim = sXmlLexDb.IndexOf("</Styles>") + 9;
			string sLexDbStyles = sXmlLexDb.Substring(idxStyles, idxStylesLim - idxStyles);
			List<DomainObjectDTO> stylesLexDb = new List<DomainObjectDTO>();
			foreach (string sGuid in GetGuidList(sLexDbStyles))
			{
				var dto = m_repoDTO.GetDTO(sGuid);
				stylesLexDb.Add(dto);
			}
			dtoLexDb.Xml = sXmlLexDb.Remove(idxStyles, idxStylesLim - idxStyles);
			m_repoDTO.Update(dtoLexDb);

			// Get the list of StStyle DTOs (and style names) from the LangProject.Styles field.
			m_dtoLangProj = GetFirstInstance("LangProject");
			string sXmlLangProj = m_dtoLangProj.Xml;
			idxStyles = sXmlLangProj.IndexOf("<Styles>");
			idxStylesLim = sXmlLangProj.IndexOf("</Styles>") + 9;
			m_sLangProjStyles = sXmlLangProj.Substring(idxStyles, idxStylesLim - idxStyles);
			string sLangProjStylesOrig = m_sLangProjStyles;
			int idxEnd = m_sLangProjStyles.Length - 9;
			List<string> styleNames = new List<string>();
			m_langProjStyles.Clear();
			m_mapStyleNameToGuid.Clear();
			DomainObjectDTO dtoHyperlink = null;
			DomainObjectDTO dtoExternalLink = null;
			DomainObjectDTO dtoInternalLink = null;
			DomainObjectDTO dtoLanguageCode = null;
			DomainObjectDTO dtoWrtSysAbbr = null;
			DomainObjectDTO dtoStrong = null;
			foreach (string sGuid in GetGuidList(m_sLangProjStyles))
			{
				var dto = m_repoDTO.GetDTO(sGuid);
				string sName = GetStyleName(dto.Xml);
				styleNames.Add(sName);
				m_langProjStyles.Add(dto);
				m_mapStyleNameToGuid.Add(sName, dto.Guid.ToLowerInvariant());
				switch (sName)
				{
					case "Hyperlink":
						dtoHyperlink = dto;
						break;
					case "External Link":
						dtoExternalLink = dto;
						break;
					case "Internal Link":
						dtoInternalLink = dto;
						break;
					case "Language Code":
						dtoLanguageCode = dto;
						break;
					case "Writing System Abbreviation":
						dtoWrtSysAbbr = dto;
						break;
					case "Strong":
						dtoStrong = dto;
						break;
				}
			}
			var mapLexDbStyleGuidName = new Dictionary<string, string>();
			// For each style in the ones we might delete we need to know the name of the style it is based on and its next style
			foreach (var dto in stylesLexDb)
			{
				var elt = XElement.Parse(dto.Xml);
				var name = elt.Element("Name").Element("Uni").Value;
				mapLexDbStyleGuidName[dto.Guid] = name;
			}
			Dictionary<string, string> mapStyleGuids = new Dictionary<string, string>();
			foreach (var dto in stylesLexDb)
			{
				string sXml = dto.Xml;
				string sName = GetStyleName(sXml);
				if (styleNames.Contains(sName))
				{
					var keeperGuid = m_mapStyleNameToGuid[sName];
					mapStyleGuids.Add(dto.Guid.ToLowerInvariant(), keeperGuid);
					// Duplicate between Notebook (LangProj) styles already in the dictionary,
					// and Lexicon (LexDb) ones being added. Discard the Lexicon style OBJECT, but keep its rules.
					var dtoKeeper = repoDTO.GetDTO(keeperGuid);
					var keeperElement = XElement.Parse(dtoKeeper.Xml);
					var dropElement = XElement.Parse(dto.Xml);
					var rules = dropElement.Element("Rules");
					if (rules != null)
					{
						var oldRules = keeperElement.Element("Rules");
						oldRules.ReplaceWith(rules);
					}
					UpdateStyleCrossReference(mapLexDbStyleGuidName, dropElement, keeperElement, "BasedOn");
					UpdateStyleCrossReference(mapLexDbStyleGuidName, dropElement, keeperElement, "Next");
					dtoKeeper.Xml = keeperElement.ToString();
					repoDTO.Update(dtoKeeper);
					m_repoDTO.Remove(dto);
				}
				else
				{
					// change the ownership links
					m_sLangProjStyles = m_sLangProjStyles.Insert(idxEnd, string.Format("<objsur guid=\"{0}\" t=\"o\"/>{1}", dto.Guid, Environment.NewLine));
					idxEnd = m_sLangProjStyles.Length - 9;
					SetOwnerGuid(dto, m_dtoLangProj.Guid);
					switch (sName)
					{
						case "Hyperlink":					dtoHyperlink = dto;		break;
						case "External Link":				dtoExternalLink = dto;	break;
						case "Internal Link":				dtoInternalLink = dto;	break;
						case "Language Code":				dtoLanguageCode = dto;	break;
						case "Writing System Abbreviation":	dtoWrtSysAbbr = dto;	break;
						case "Strong":						dtoStrong = dto;		break;
					}
					styleNames.Add(sName);
					m_langProjStyles.Add(dto);
					m_mapStyleNameToGuid.Add(sName, dto.Guid.ToLowerInvariant());
				}
			}
			// if "Hyperlink" does not exist, create it.
			if (dtoHyperlink == null)
			{
				dtoHyperlink = CreateCharStyle("Hyperlink",
					"<Prop forecolor=\"blue\" undercolor=\"blue\" underline=\"single\" />",
					ContextValues.Internal);
				m_langProjStyles.Add(dtoHyperlink);
				m_mapStyleNameToGuid.Add("Hyperlink", dtoHyperlink.Guid.ToLowerInvariant());
			}
			else
			{
				// ensure that the Hyperlink style has an "internal" context.
				string sXml = dtoHyperlink.Xml;
				int idx = sXml.IndexOf("<Context");
				if (idx > 0)
				{
					XElement xeHyper = XElement.Parse(sXml);
					foreach (XElement xe in xeHyper.Descendants("Context"))
					{
						int nVal;
						XAttribute xa = xe.Attribute("val");
						if (xa != null && Int32.TryParse(xa.Value, out nVal) && nVal != (int)ContextValues.Internal)
						{
							nVal = (int)ContextValues.Internal;
							xa.Value = nVal.ToString();
							dtoHyperlink.Xml = xeHyper.ToString();
							m_repoDTO.Update(dtoHyperlink);
							break;
						}
					}
				}
			}
			// delete "External Link" and "Internal Link", and prepare to replace links to
			// them with a link to "Hyperlink".
			if (dtoExternalLink != null)
			{
				mapStyleGuids.Add(dtoExternalLink.Guid.ToLowerInvariant(), dtoHyperlink.Guid.ToLowerInvariant());
				m_sLangProjStyles = DeleteStyle(m_sLangProjStyles, dtoExternalLink);
				m_mapStyleNameToGuid["External Link"] = dtoHyperlink.Guid.ToLowerInvariant();
			}
			if (dtoInternalLink != null)
			{
				mapStyleGuids.Add(dtoInternalLink.Guid.ToLowerInvariant(), dtoHyperlink.Guid.ToLowerInvariant());
				m_sLangProjStyles = DeleteStyle(m_sLangProjStyles, dtoInternalLink);
				m_mapStyleNameToGuid["Internal Link"] = dtoHyperlink.Guid.ToLowerInvariant();
			}
			if (dtoLanguageCode == null)
			{
				// If neither "Language Code" nor "Writing System Abbreviation" exist, create the
				// "Writing System Abbreviation" style.
				if (dtoWrtSysAbbr == null)
				{
					dtoWrtSysAbbr = CreateCharStyle("Writing System Abbreviation",
						"<Prop fontsize=\"8000\" fontsizeUnit=\"mpt\" forecolor=\"2f60ff\" />",
						ContextValues.General);
					m_langProjStyles.Add(dtoWrtSysAbbr);
					m_mapStyleNameToGuid.Add("Writing System Abbreviation", dtoWrtSysAbbr.Guid.ToLowerInvariant());
				}
				else
				{
					// We don't need to do anything.
				}
			}
			else
			{
				if (dtoWrtSysAbbr == null)
				{
					// Rename "Language Code" to "Writing System Abbreviation".
					string sXml = dtoLanguageCode.Xml;
					dtoLanguageCode.Xml = sXml.Replace("<Uni>Language Code</Uni>",
						"<Uni>Writing System Abbreviation</Uni>");
					m_repoDTO.Update(dtoLanguageCode);
					m_mapStyleNameToGuid.Add("Writing System Abbreviation", dtoLanguageCode.Guid.ToLowerInvariant());
				}
				else
				{
					// delete "Language Code", and prepare to replace links to it with a link to
					// "Writing System Abbreviation".
					mapStyleGuids.Add(dtoLanguageCode.Guid.ToLowerInvariant(), dtoWrtSysAbbr.Guid.ToLowerInvariant());
					m_sLangProjStyles = DeleteStyle(m_sLangProjStyles, dtoLanguageCode);
					m_mapStyleNameToGuid["Language Code"] = dtoWrtSysAbbr.Guid.ToLowerInvariant();
				}
			}
			// if "Strong" does not exist, create it.
			if (dtoStrong == null)
			{
				dtoStrong = CreateCharStyle("Strong", "<Prop bold=\"invert\" />", ContextValues.General);
				m_langProjStyles.Add(dtoStrong);
				m_mapStyleNameToGuid.Add("Strong", dtoStrong.Guid.ToLowerInvariant());
			}
			ChangeStyleReferences();
			UpdateStyleLinks(mapStyleGuids);
			ReplaceDirectFormattingWithStyles();
			EnsureBuiltinStylesAreMarkedBuiltin();

			if (m_sLangProjStyles != sLangProjStylesOrig)
			{
				sXmlLangProj = sXmlLangProj.Remove(idxStyles, idxStylesLim - idxStyles);
				m_dtoLangProj.Xml = sXmlLangProj.Insert(idxStyles, m_sLangProjStyles);
				m_repoDTO.Update(m_dtoLangProj);
			}

			DataMigrationServices.IncrementVersionNumber(m_repoDTO);
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Combine Sense and Sense Status lists into the Sense list.
		/// </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 its 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, 7000023);

			// 1. Change the owning property from LangProject_AnalysisStatus to LangProject_Status

			string statusConfirmedGuid = "";
			IEnumerable<XElement> langPossList = null;
			var langProjDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First();
			var langProjElement = XElement.Parse(langProjDto.Xml);
			var statusElement = langProjElement.Element("AnalysisStatus");
			if (statusElement != null)
				statusElement.Name= "Status";
			UpdateDto(domainObjectDtoRepository,langProjDto,langProjElement);

			var langPossListGuid = GetGuidOfObjSurChild(statusElement);

			var langPossListDto = domainObjectDtoRepository.GetDTO(langPossListGuid);
			var langPossListElement = XElement.Parse(langPossListDto.Xml);

			var langPossListPossibilities = langPossListElement.Element("Possibilities");
			if (langPossListPossibilities == null)
			{
				langPossListElement.Add(XElement.Parse("<Possibilities></Possibilities>"));
				UpdateDto(domainObjectDtoRepository,langPossListDto,langPossListElement);
				// Put the 'Confirmed' status into the list.
				statusConfirmedGuid = MakeStatus(domainObjectDtoRepository, langPossListGuid, langPossListElement,
					"Confirmed", "Conf");
				langPossList = GetPossibilitiesInList(domainObjectDtoRepository, langPossListElement);
				UpdateDto(domainObjectDtoRepository,langPossListDto,langPossListElement);
			}
			else
			{
				// Get the actual elements that represent the possibility items in the AnalysisStatus possibility list.
				langPossList = GetPossibilitiesInList(domainObjectDtoRepository, langPossListElement);

				// 1a.  Verify the 'Confirmed' status exists in the list.
				statusConfirmedGuid = IsStatusInList(langPossList, "Confirmed");
				if (statusConfirmedGuid == null) //if not, add it
				{
					statusConfirmedGuid = MakeStatus(domainObjectDtoRepository, langPossListGuid, langPossListElement,
						"Confirmed", "Conf");
				}
				UpdateDto(domainObjectDtoRepository,langPossListDto,langPossListElement);
			}

			// 1b.  If any rnGeneric records point to the 'Approved' status, point them to 'Confirnmed'.
			var approvedGuid = IsStatusInList(langPossList, "Approved");
			if (approvedGuid != null)
				ReassignStatuses(domainObjectDtoRepository, approvedGuid, statusConfirmedGuid);

			// 2. Get the pointers to the Lexdb Status list (sense status)
			var lexDbElement = langProjElement.Element("LexDb");
			if (lexDbElement != null)
			{
				var lexDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LexDb").First();
				var lexElement = XElement.Parse(lexDto.Xml);
				var lexPossListGuid = GetGuidOfObjSurChild(lexElement.Element("Status"));

				var lexPossListDto = domainObjectDtoRepository.GetDTO(lexPossListGuid);
				var lexPossListElement = XElement.Parse(lexPossListDto.Xml);

				var lexPossList = GetPossibilitiesInList(domainObjectDtoRepository, lexPossListElement).ToList();

				var statusMap = new Dictionary<string, string>();

				// figure out the status substitutions we need to do.
				var langPossibilitiesElt = langPossListElement.Element("Possibilities");
				var lexPossibilitiesElt = lexPossListElement.Element("Possibilities");

				foreach (var lexStatus in lexPossList)
				{
					var nameElt = lexStatus.XPathSelectElement("Name/AUni[@ws='en']");
					var name = nameElt == null ? "" : nameElt.Value; // may have no English name
					if (name == "Approved") name = "Confirmed";
					var lexStatusGuid = lexStatus.Attribute("guid").Value;
					var langStatusGuid = IsStatusInList(langPossList, name);
					if (langStatusGuid == null)
					{
						// Move the old status (LexDb) to the surviving list(LangProj).
						var oldRef = FindObjSurWithGuid(lexPossibilitiesElt, lexStatusGuid);
						oldRef.Remove();
						langPossibilitiesElt.Add(oldRef);
						lexStatus.SetAttributeValue("ownerguid", langPossListGuid);
						UpdateDto(domainObjectDtoRepository, lexStatus);
						// Don't need to put anything in statusMap, we don't need to change refs
						// to a status that is being moved rather than deleted.
					}
					else
					{
						statusMap[lexStatusGuid.ToLowerInvariant()] = langStatusGuid;
						domainObjectDtoRepository.Remove(domainObjectDtoRepository.GetDTO(lexStatusGuid));
					}
				}
				UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);

				// We need to go through tha collection to point all statuses to the LangProj Status list
				foreach (var lexRecDto in domainObjectDtoRepository.AllInstancesSansSubclasses("LexSense").ToArray())
				{
					var lexSenseElement = XElement.Parse(lexRecDto.Xml);
					if (lexSenseElement.Element("Status") != null)
					{
						var statusObjSur = lexSenseElement.Element("Status").Element("objsur");
						var oldStatus = statusObjSur.Attribute("guid").Value;
						string newStatus;
						if (statusMap.TryGetValue(oldStatus.ToLowerInvariant(), out newStatus))
						{
							// We need to update this one.
							statusObjSur.SetAttributeValue("guid", newStatus);
							UpdateDto(domainObjectDtoRepository, lexRecDto, lexSenseElement);
						}
					}
				}
				if (approvedGuid != null)
				{
					// Delete the DTO for the 'Approved' item, and also the objsur that points at it
					// in the possibility list. Hopefully we already fixed all the refs to it.
					domainObjectDtoRepository.Remove(domainObjectDtoRepository.GetDTO(approvedGuid));
					var oldRef = FindObjSurWithGuid(langPossibilitiesElt, approvedGuid);
					oldRef.Remove();
					UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);
				}
				// 6. Delete the CmPossibilityList in LexDb_Status
				domainObjectDtoRepository.Remove(lexPossListDto);
				// 7. Delete the LexDb_Status owning property.
				lexElement.Element("Status").Remove();
				UpdateDto(domainObjectDtoRepository, lexDto, lexElement);
			}
			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
		/// ------------------------------------------------------------------------------------
		/// <summary>
		/// Perform one increment migration step.
		/// </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, 7000000);

			DataMigrationServices.Delint(domainObjectDtoRepository);

			var lpDto = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First();
			// 1. Remove property from LangProg.
			var lpElement = XElement.Parse(lpDto.Xml);
			lpElement.Descendants("WordformInventory").Remove();
			DataMigrationServices.UpdateDTO(domainObjectDtoRepository, lpDto, lpElement.ToString());

			// 2. Remove three owner-related attributes from each WfiWordform.
			// (There may be zero, or more, instances to upgrade.)
			foreach (var wordformDto in domainObjectDtoRepository.AllInstancesSansSubclasses("WfiWordform"))
			{
				var wfElement = XElement.Parse(wordformDto.Xml);
				wfElement.Attribute("ownerguid").Remove();
				var flidAttr = wfElement.Attribute("owningflid");
				if (flidAttr != null)
					flidAttr.Remove();
				var ordAttr = wfElement.Attribute("owningord");
				if (ordAttr != null)
					ordAttr.Remove();
				DataMigrationServices.UpdateDTO(domainObjectDtoRepository, wordformDto, wfElement.ToString());
			}

			// 3. Remove WordformInventory instance.
			domainObjectDtoRepository.Remove(
				domainObjectDtoRepository.AllInstancesSansSubclasses("WordformInventory").First());

			// There are no references to the WFI, so don't fret about leaving dangling refs to it.

			DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
		}
Example #23
0
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Combine Sense and Sense Status lists into the Sense list.
        /// </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 its 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, 7000023);

            // 1. Change the owning property from LangProject_AnalysisStatus to LangProject_Status

            string statusConfirmedGuid          = "";
            IEnumerable <XElement> langPossList = null;
            var langProjDto     = domainObjectDtoRepository.AllInstancesSansSubclasses("LangProject").First();
            var langProjElement = XElement.Parse(langProjDto.Xml);
            var statusElement   = langProjElement.Element("AnalysisStatus");

            if (statusElement != null)
            {
                statusElement.Name = "Status";
            }
            UpdateDto(domainObjectDtoRepository, langProjDto, langProjElement);

            var langPossListGuid = GetGuidOfObjSurChild(statusElement);

            var langPossListDto     = domainObjectDtoRepository.GetDTO(langPossListGuid);
            var langPossListElement = XElement.Parse(langPossListDto.Xml);

            var langPossListPossibilities = langPossListElement.Element("Possibilities");

            if (langPossListPossibilities == null)
            {
                langPossListElement.Add(XElement.Parse("<Possibilities></Possibilities>"));
                UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);
                // Put the 'Confirmed' status into the list.
                statusConfirmedGuid = MakeStatus(domainObjectDtoRepository, langPossListGuid, langPossListElement,
                                                 "Confirmed", "Conf");
                langPossList = GetPossibilitiesInList(domainObjectDtoRepository, langPossListElement);
                UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);
            }
            else
            {
                // Get the actual elements that represent the possibility items in the AnalysisStatus possibility list.
                langPossList = GetPossibilitiesInList(domainObjectDtoRepository, langPossListElement);

                // 1a.  Verify the 'Confirmed' status exists in the list.
                statusConfirmedGuid = IsStatusInList(langPossList, "Confirmed");
                if (statusConfirmedGuid == null)                 //if not, add it
                {
                    statusConfirmedGuid = MakeStatus(domainObjectDtoRepository, langPossListGuid, langPossListElement,
                                                     "Confirmed", "Conf");
                }
                UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);
            }

            // 1b.  If any rnGeneric records point to the 'Approved' status, point them to 'Confirnmed'.
            var approvedGuid = IsStatusInList(langPossList, "Approved");

            if (approvedGuid != null)
            {
                ReassignStatuses(domainObjectDtoRepository, approvedGuid, statusConfirmedGuid);
            }

            // 2. Get the pointers to the Lexdb Status list (sense status)
            var lexDbElement = langProjElement.Element("LexDb");

            if (lexDbElement != null)
            {
                var lexDto          = domainObjectDtoRepository.AllInstancesSansSubclasses("LexDb").First();
                var lexElement      = XElement.Parse(lexDto.Xml);
                var lexPossListGuid = GetGuidOfObjSurChild(lexElement.Element("Status"));

                var lexPossListDto     = domainObjectDtoRepository.GetDTO(lexPossListGuid);
                var lexPossListElement = XElement.Parse(lexPossListDto.Xml);

                var lexPossList = GetPossibilitiesInList(domainObjectDtoRepository, lexPossListElement).ToList();

                var statusMap = new Dictionary <string, string>();

                // figure out the status substitutions we need to do.
                var langPossibilitiesElt = langPossListElement.Element("Possibilities");
                var lexPossibilitiesElt  = lexPossListElement.Element("Possibilities");

                foreach (var lexStatus in lexPossList)
                {
                    var nameElt = lexStatus.XPathSelectElement("Name/AUni[@ws='en']");
                    var name    = nameElt == null ? "" : nameElt.Value;                  // may have no English name
                    if (name == "Approved")
                    {
                        name = "Confirmed";
                    }
                    var lexStatusGuid  = lexStatus.Attribute("guid").Value;
                    var langStatusGuid = IsStatusInList(langPossList, name);
                    if (langStatusGuid == null)
                    {
                        // Move the old status (LexDb) to the surviving list(LangProj).
                        var oldRef = FindObjSurWithGuid(lexPossibilitiesElt, lexStatusGuid);
                        oldRef.Remove();
                        langPossibilitiesElt.Add(oldRef);
                        lexStatus.SetAttributeValue("ownerguid", langPossListGuid);
                        UpdateDto(domainObjectDtoRepository, lexStatus);
                        // Don't need to put anything in statusMap, we don't need to change refs
                        // to a status that is being moved rather than deleted.
                    }
                    else
                    {
                        statusMap[lexStatusGuid.ToLowerInvariant()] = langStatusGuid;
                        domainObjectDtoRepository.Remove(domainObjectDtoRepository.GetDTO(lexStatusGuid));
                    }
                }
                UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);

                // We need to go through tha collection to point all statuses to the LangProj Status list
                foreach (var lexRecDto in domainObjectDtoRepository.AllInstancesSansSubclasses("LexSense").ToArray())
                {
                    var lexSenseElement = XElement.Parse(lexRecDto.Xml);
                    if (lexSenseElement.Element("Status") != null)
                    {
                        var    statusObjSur = lexSenseElement.Element("Status").Element("objsur");
                        var    oldStatus    = statusObjSur.Attribute("guid").Value;
                        string newStatus;
                        if (statusMap.TryGetValue(oldStatus.ToLowerInvariant(), out newStatus))
                        {
                            // We need to update this one.
                            statusObjSur.SetAttributeValue("guid", newStatus);
                            UpdateDto(domainObjectDtoRepository, lexRecDto, lexSenseElement);
                        }
                    }
                }
                if (approvedGuid != null)
                {
                    // Delete the DTO for the 'Approved' item, and also the objsur that points at it
                    // in the possibility list. Hopefully we already fixed all the refs to it.
                    domainObjectDtoRepository.Remove(domainObjectDtoRepository.GetDTO(approvedGuid));
                    var oldRef = FindObjSurWithGuid(langPossibilitiesElt, approvedGuid);
                    oldRef.Remove();
                    UpdateDto(domainObjectDtoRepository, langPossListDto, langPossListElement);
                }
                // 6. Delete the CmPossibilityList in LexDb_Status
                domainObjectDtoRepository.Remove(lexPossListDto);
                // 7. Delete the LexDb_Status owning property.
                lexElement.Element("Status").Remove();
                UpdateDto(domainObjectDtoRepository, lexDto, lexElement);
            }
            DataMigrationServices.IncrementVersionNumber(domainObjectDtoRepository);
        }