/// <summary> /// Deal with problems of the Morph child, if any. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <param name="guidString"></param> private void FixMorph(XElement rt, FwDataFixer.ErrorLogger logger, string guidString) { var destGuidString = ChildSurrogateGuid(rt, "Morph"); if (destGuidString == null) { return; } var objsur = rt.Element("Morph").Element("objsur"); // parts of path must exist, we got destGuidString if (m_guids.Contains(new Guid(destGuidString))) { return; // all is well, not dangling. } var senseGuid = ChildSurrogateGuid(rt, "Sense"); Guid entryGuid; XElement entryElt; if (senseGuid != null && m_owners.TryGetValue(new Guid(senseGuid), out entryGuid) && m_entrys.TryGetValue(entryGuid.ToString(), out entryElt)) { var soleForm = GetSoleForm(entryElt); if (soleForm != null) { // We can repair it to use the only form of the right entry, a very promising repair. objsur.SetAttributeValue("guid", soleForm); logger(guidString, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRepairingBundleFormFromEntry, guidString)); return; } } // If we can't fix it nicely, just clobber it (and its parent, since this is an atomic property). logger(guidString, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRemovingDanglingMorph, destGuidString, guidString)); objsur.Parent.Remove(); }
private void ReportBadLexReference(Guid guid, Guid guidOwner, FwDataFixer.ErrorLogger errorLogger) { // Example: if guid and rt belong to a "LexReference" that has fewer than two Targets, // then the LexReference will be removed from the file and this will report it. // The objsur reference to the row gets removed elsewhere. errorLogger(String.Format(Strings.ksRemovingBadLexReference, guid, guidOwner), true); }
/// <summary> /// This is the main routine that does actual repair on list names. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <returns>true (always, currently) to indicate that the list should survive.</returns> internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var guid = rt.Attribute("guid").Value; string name; if (!m_guidToName.TryGetValue(guid, out name)) { return(true); // we didn't see a list with this guid; do nothing } string firstGuid = m_nameToGuid[name]; if (firstGuid == guid) { return(true); // This one is allowed to keep this name } int append = 1; string fixedName = name + append; while (m_nameToGuid.ContainsKey(fixedName)) { fixedName = name + append++; } m_nameToGuid[fixedName] = guid; // this name is now taken too. rt.Element("Name").Element("AUni").SetValue(fixedName); logger(guid, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRepairingDuplicateListName, name, firstGuid, guid, fixedName)); return(true); }
/// <summary> /// This is called in the second pass that actually changes the objects. It does the following: /// - Deletes wordforms that have been merged (by returning false for them). /// - Adds analyses of deleted wordforms to the Analysis of the survivor that replaces them. /// - Fixes the ownerguid of those transferred analyses. /// - Fixes any segments which refer to deleted wordforms to refer instead to the replacement. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <returns></returns> internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var className = rt.Attribute("class").Value; if (className == "WfiWordform") { // Arrange for it to be deleted if that's what we decided to do. string replacement; var guid = rt.Attribute("guid").Value; if (m_guidsToDelete.TryGetValue(guid, out replacement)) { logger(guid, DateTime.Now.ToShortDateString(), String.Format(Strings.ksDuplicateWordform, guid, GetWordformKey(rt), replacement)); return(false); // discard this element } // Modify it if we need to add to its analyses. string modifiedAnalyses; if (m_modifiedWordforms.TryGetValue(guid, out modifiedAnalyses)) { var analyses = rt.Element("Analyses"); if (analyses != null) { analyses.Remove(); } var mergedAnalyses = XElement.Parse(modifiedAnalyses); rt.Add(mergedAnalyses); } } else if (className == "Segment") { // Fix refs to deleted wordforms. var analyses = rt.Element("Analyses"); if (analyses != null) { foreach (var objsur in analyses.Elements()) { var oldGuid = objsur.Attribute("guid").Value; string newGuid; if (m_guidsToDelete.TryGetValue(oldGuid, out newGuid)) { objsur.Attribute("guid").SetValue(newGuid); } } } } else if (className == "WfiAnalysis") { // Fix ownerguid for moved analyses. var ownerAttr = rt.Attribute("ownerguid"); if (ownerAttr != null) { string newGuid; if (m_guidsToDelete.TryGetValue(ownerAttr.Value, out newGuid)) { ownerAttr.SetValue(newGuid); } } } return(true); // keep the current element. }
//Go through the list of MSA references in the file and remove those which do not occur in any sense //Why? Because the Chorus merge will flag a conflict for the users but end up placing both references //in the entry. This incorrectly causes parts of speech to be referenced in the Entry which no sense uses. internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var guid = rt.Attribute("guid"); if (guid == null) { return(true); } if (rt.Attribute("class").Value == "LexEntry") { var MSAs = rt.Element("MorphoSyntaxAnalyses"); var rejects = new List <XNode>(); if (MSAs != null) { var entryGuidString = guid.Value; foreach (var objsur in MSAs.Descendants("objsur")) { if (IsRejectedMsa(objsur)) { rejects.Add(objsur); } } foreach (var reject in rejects) { logger(Strings.ksRemovedUnusedMsa, true); reject.Remove(); } } } return(true); }
//For each LexEntry element, set the homograph number as determined in the FinalFixerInitialization method. internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var guid = new Guid(rt.Attribute("guid").Value); var xaClass = rt.Attribute("class"); var className = xaClass == null ? kUnknown : xaClass.Value; switch (className) { case "LexEntry": string homographNum; if (!m_LexEntryHomographNumbers.TryGetValue(guid, out homographNum)) { homographNum = "0"; // If it's not a homograph, its HN must be zero. } var homographElement = rt.Element("HomographNumber"); if (homographElement == null) { if (homographNum != "0") // no need to make a change if already implicitly zero. { rt.Add(new XElement("HomographNumber", new XAttribute("val", homographNum))); } break; } homographElement.SetAttributeValue("val", homographNum); break; default: break; } return(true); }
private void ReportOwnerOfBadSegmentReferences(Guid guid, Guid guidOwner, string className, FwDataFixer.ErrorLogger errorLogger) { // Example: if guid, className, and rt belong to a "ConstChartWordGroup" that has no Begin // or EndSegment reference (being earlier deleted by OriginalFixer's dangling reference // repair), then this will remove the ConstChartWordGroup from the file and report it. // The objsur reference to the cell gets removed elsewhere. errorLogger(String.Format(Strings.ksRemovingBadAnalysisRefObj, guid, className, guidOwner), true); }
private void AdjustBadSegmentReferenceAndReport(XElement rt, Guid guid, Guid guidOwner, string className, FwDataFixer.ErrorLogger errorLogger) { // Example: if guid, className, and rt belong to a "ConstChartWordGroup" that has only one valid // Begin or EndSegment reference (the other being earlier deleted by OriginalFixer's // dangling reference repair), then this will replace the missing Segment reference // with the valid one and report the repair. var fieldModified = ReplaceMissingSegmentReferenceWithOtherOne(rt); // TODO: Fix message string errorLogger(String.Format(Strings.ksAdjustingAnalysisRefObj, guid, className, fieldModified), true); }
/// <summary> /// Make any required fixes to the MSA child element. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <param name="guidString"></param> private void FixMsa(XElement rt, FwDataFixer.ErrorLogger logger, string guidString) { var destGuidString = ChildSurrogateGuid(rt, "Msa"); if (destGuidString == null) { return; } var objsur = rt.Element("Msa").Element("objsur"); // parts of path must exist, we got destGuidString if (m_guids.Contains(new Guid(destGuidString)) && !m_senseFixer.IsRejectedMsa(objsur)) { return; // msa is not dangling. } var senseGuid = ChildSurrogateGuid(rt, "Sense"); string senseMsaGuid; if (senseGuid != null && m_senseToMsa.TryGetValue(senseGuid, out senseMsaGuid) && m_guids.Contains(new Guid(senseMsaGuid))) { // We can repair it to match the sense's msa, an ideal repair, since we checked the sense's msa IS valid. objsur.SetAttributeValue("guid", senseMsaGuid); logger(guidString, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRepairingMorphBundleFromSense, guidString)); return; } // Next see if we can link to a unique msa via our morph child. var morphGuidString = ChildSurrogateGuid(rt, "Morph"); Guid entryGuid; XElement entryElt; if (morphGuidString != null && m_owners.TryGetValue(new Guid(morphGuidString), out entryGuid) && m_entrys.TryGetValue(entryGuid.ToString(), out entryElt)) { var soleEntryMsa = GetSoleMsaGuid(entryElt); if (soleEntryMsa != null) { objsur.SetAttributeValue("guid", soleEntryMsa); logger(guidString, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRepairingMorphBundleFromEntry, guidString)); return; } } // If we can't fix it nicely, just clobber it (and its parent, since this is an atomic property). logger(guidString, DateTime.Now.ToShortDateString(), String.Format(Strings.ksRemovingDanglingMsa, destGuidString, guidString)); objsur.Parent.Remove(); }
internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var guid = new Guid(rt.Attribute("guid").Value); var xaClass = rt.Attribute("class"); var className = xaClass == null ? "<unknown>" : xaClass.Value; var customProperties = GetAllCustomProperties(rt); foreach (var kvp in customProperties) { if (!string.IsNullOrEmpty(kvp.Key) && m_customFieldNames.ContainsKey(kvp.Key)) { continue; } logger(String.Format(Strings.ksRemovingUndefinedCustomProperty, kvp.Key.Substring(0, kvp.Key.LastIndexOf('_')), className, guid), true); kvp.Value.Remove(); } return(true); }
internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var classAttr = rt.Attribute("class"); if (classAttr == null) { return(true); // bizarre, but leave it alone } List <string> requiredFields; if (!m_customFields.TryGetValue(classAttr.Value, out requiredFields)) { return(true); // has no custom fields we care about } var missingFields = new HashSet <string>(requiredFields); foreach (var child in rt.Elements()) { if (child.Name == "Custom") { var nameAttr = child.Attribute("name"); if (nameAttr != null) { missingFields.Remove(nameAttr.Value); // not missing, we don't need to add it. } } } foreach (var fieldName in missingFields) { rt.Add(new XElement("Custom", new XAttribute("name", fieldName), new XAttribute("val", "0"))); var guid = rt.Attribute("guid").Value; // This is such an insignificant fix from the user's point of view that we might prefer not // even to report it. But don't remove the logging without adding another mechanism for // the system to know that a problem has been fixed...this controls the important behavior // of re-splitting the file before we commit. logger(guid, DateTime.Now.ToShortDateString(), String.Format(Strings.ksAddingMissingDefaultForValueType, guid)); } return(true); }
/// <summary> /// This is the main routine that does actual repair on morph bundles. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <returns>true (always, currently) to indicate that the bundle should survive.</returns> internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger) { var guidString = rt.Attribute("guid").Value; var xaClass = rt.Attribute("class"); if (xaClass == null) { return(true); // we can't fix it, anyway. } var className = xaClass.Value; switch (className) { case "WfiMorphBundle": FixMsa(rt, logger, guidString); FixMorph(rt, logger, guidString); break; } return(true); }
internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger errorLogger) { var xaClass = rt.Attribute("class").Value; switch (xaClass) { case "StStyle": var guid = rt.Attribute("guid").Value; if (!m_deletedGuids.Contains(guid)) { return(true); // keep it as far as we're concerned. } var name = rt.Element("Name").Element("Uni").Value; // none can be null or we wouldn't have listed it for deletion errorLogger(guid.ToString(), DateTime.Now.ToShortDateString(), String.Format(Strings.ksRemovingDuplicateStyle, name)); return(false); // This element must go away! case "LangProject": case "Scripture": var styles = rt.Element("Styles"); if (styles == null) { return(true); } // Removing these here prevents additional error messages about missing objects, since the // targets of these objsurs are no longer present. foreach (var objsur in styles.Elements().ToArray()) // ToArray so as not to modify collection we're iterating over { var surGuid = objsur.Attribute("guid").Value; if (m_deletedGuids.Contains(surGuid)) { objsur.Remove(); } } break; } return(true); // we're not deleting it. }
private static void ReportOwnerOfEmptySequence(Guid guid, Guid guidOwner, string className, FwDataFixer.ErrorLogger errorLogger) { // Example: if guid, className, and rt belong to a "ConstChartRow" that has no cells and guidOwner belongs // to a DsConstChart, then this will remove the ConstChartRow from the file and report it. // The objsur reference to the row gets removed elsewhere. errorLogger(guid.ToString(), DateTime.Now.ToShortDateString(), String.Format(Strings.ksRemovingOwnerOfEmptySequence, guid, className, guidOwner)); }
/// <summary> /// Do any fixes to this particular root element here. /// Return true if we are done fixing this element and can write it out. /// Return false if we need to delete this root element. /// </summary> /// <param name="rt"></param> /// <param name="errorLogger"></param> /// <returns></returns> internal override bool FixElement(XElement rt, FwDataFixer.ErrorLogger errorLogger) { var guid = new Guid(rt.Attribute("guid").Value); var guidOwner = SafelyGetOwnerGuid(guid); var xaClass = rt.Attribute("class"); var className = xaClass == null ? kUnknown : xaClass.Value; if (guidOwner == Guid.Empty || className == kUnknown) { return(true); // these should have been fixed by another fixer! } List <Guid> danglingRefList; // used in 2 cases below. switch (className) { case "DsConstChart": // Check all chart rows for deletion and remove dangling references. if (!m_rowsToDelete.TryGetValue(guid, out danglingRefList)) { return(true); // this chart has no rows to delete. } // Report not needed: we will report deleting the row in case ConstChartRow rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); break; case "StText": // Check for cell refs to delete and remove reference. if (m_ownerThatWillLoseOwnee.TryGetValue(guid, out danglingRefList)) { // Report not needed: we will report deleting the row in case TextTag or similar rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); } break; case "ConstChartRow": // Step 1: if row is set for deletion, remove, report, and return. // guidOwner is chart, guid is row if (m_rowsToDelete.TryGetValue(guidOwner, out danglingRefList)) { if (danglingRefList.Contains(guid)) { ReportOwnerOfEmptySequence(guid, guidOwner, className, errorLogger); return(false); // delete this rt element } } // Step 2: check for cell refs to remove due to clause marker problem. if (m_cellRefsToDelete.TryGetValue(guid, out danglingRefList)) { rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); } // Step 3: check for cell refs to delete because of Segment problem. if (m_ownerThatWillLoseOwnee.TryGetValue(guid, out danglingRefList)) { rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); } break; case "ConstChartClauseMarker": // If marker guid is in m_emptyClauseMarkers remove and report. if (!m_emptyClauseMarkers.Contains(guid)) { return(true); } ReportOwnerOfEmptySequence(guid, guidOwner, className, errorLogger); return(false); // delete this rt element case "ConstChartWordGroup": case "TextTag": // If WordGroup or TextTag guid is in m_objsToDelete, remove it and report. if (m_objsToDelete.Contains(guid)) { ReportOwnerOfBadSegmentReferences(guid, guidOwner, className, errorLogger); return(false); // delete this rt element } // If WordGroup or TextTag guid is in m_objsToAdjust, adjust and report. if (m_objsToAdjust.Contains(guid)) { AdjustBadSegmentReferenceAndReport(rt, guid, guidOwner, className, errorLogger); } break; case "PhSegRuleRHS": // Check for sequence context refs to delete and remove reference. if (!m_phContextRefsToDelete.TryGetValue(guid, out danglingRefList)) { return(true); } rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); break; case "PhSequenceContext": // Remove a PhSequenceContext from the correct side of its owning rule, // if it has no Members. if (!m_emptySequenceContexts.Contains(guid)) { return(true); } ReportOwnerOfEmptySequence(guid, guidOwner, className, errorLogger); return(false); // delete this rt element case "LexRefType": // Check for LexRefTypes to remove reference. if (!m_typeRefsLosingReferences.TryGetValue(guid, out danglingRefList)) { return(true); } rt.Descendants("objsur").Where( objsur => danglingRefList.Contains(GetObjsurGuid(objsur))).Remove(); break; case "LexReference": // Remove a LexReference that only has one (or fewer) Targets. if (!m_lexReferencesToDelete.Contains(guid)) { return(true); } ReportBadLexReference(guid, guidOwner, errorLogger); return(false); // delete this rt element default: break; } return(true); }
/// <summary> /// Do any fixes to this particular root element here. /// Return true if we are done fixing this element and can write it out. /// Return false if we need to delete this root element. /// </summary> /// <param name="rt"></param> /// <param name="logger"></param> /// <returns></returns> internal abstract bool FixElement(XElement rt, FwDataFixer.ErrorLogger logger);