/// <summary> /// get the work item type mapping entry /// </summary> /// <param name="sourceSide"></param> /// <param name="sourceType"></param> /// <returns></returns> public HashSet <string> GetMappedTypeNames(SourceSideTypeEnum sourceSide) { AddMissingDefaultMap(); HashSet <string> mappedTypeNames = new HashSet <string>(); foreach (WorkItemTypeMappingElement map in this.WorkItemType) { if (sourceSide == SourceSideTypeEnum.Left) { if (map.LeftWorkItemTypeName.Equals(WitMappingConfigVocab.Any, System.StringComparison.OrdinalIgnoreCase)) { mappedTypeNames.Clear(); mappedTypeNames.Add(WitMappingConfigVocab.Any); break; } mappedTypeNames.Add(map.LeftWorkItemTypeName); } else { if (map.RightWorkItemTypeName.Equals(WitMappingConfigVocab.Any, System.StringComparison.OrdinalIgnoreCase)) { mappedTypeNames.Clear(); mappedTypeNames.Add(WitMappingConfigVocab.Any); break; } mappedTypeNames.Add(map.RightWorkItemTypeName); } } return(mappedTypeNames); }
/// <summary> /// Get the mapped field entry given a specific migration source Unique Id and source field name /// </summary> /// <param name="sourceSide"></param> /// <param name="fieldName"></param> /// <returns></returns> public MappedField GetMappedFieldEntry(SourceSideTypeEnum sourceSide, string fieldName) { MappedField retVal = null; foreach (MappedField mappedField in this.MappedField) { if (mappedField.MapFromSide == sourceSide) { if (fieldName == (sourceSide == SourceSideTypeEnum.Left ? mappedField.LeftName : mappedField.RightName)) { retVal = mappedField; break; } else { if (sourceSide == SourceSideTypeEnum.Left && mappedField.LeftName == WitMappingConfigVocab.Any && retVal == null) { retVal = mappedField; } else if (sourceSide == SourceSideTypeEnum.Right && mappedField.RightName == WitMappingConfigVocab.Any && retVal == null) { retVal = mappedField; } continue; } } } return(retVal); }
/// <summary> /// Given the migration source side and field reference name, gets the UserIdentityField configuration element. /// </summary> /// <param name="fromSide"></param> /// <param name="srcFieldRefName"></param> /// <returns>The UserIdFieldElement for the subject field; NULL if no configuration is present.</returns> public UserIdFieldElement GetUserIdField( SourceSideTypeEnum fromSide, string srcFieldRefName) { NotifyingCollection <UserIdFieldElement> userIdFields; switch (fromSide) { case SourceSideTypeEnum.Left: userIdFields = UserIdentityFields.LeftUserIdentityFields.UserIdField; break; case SourceSideTypeEnum.Right: userIdFields = UserIdentityFields.RightUserIdentityFields.UserIdField; break; default: return(null); } foreach (var userIdField in userIdFields) { if (userIdField.FieldReferenceName.Equals(srcFieldRefName, StringComparison.OrdinalIgnoreCase)) { return(userIdField); } } return(null); }
private bool IsTargetFieldMapped(SourceSideTypeEnum mapFromSide, string targetField) { if (string.IsNullOrEmpty(targetField)) { // if target field is string.Empty, we will drop the source field after translation // thus, it is legal that there are multiple source field to string.Empty in the config return(false); } if (IsFieldInCachedTargetField(mapFromSide, targetField)) { return(true); } if (mapFromSide == SourceSideTypeEnum.Any) { if (IsFieldInCachedTargetField(SourceSideTypeEnum.Left, targetField) || IsFieldInCachedTargetField(SourceSideTypeEnum.Right, targetField)) { return(true); } } return(false); }
/// <summary> /// Try getting a mapped value given a migration source unique Id and the value to be mapped. /// </summary> /// <param name="fromSide">Left or Right of the migration source in the session configuration</param> /// <param name="sourceFieldValue">Value before the mapping rule is applied</param> /// <param name="valueMapName"></param> /// <param name="descriptionDocRootNode">The document that contains all the field information in a particular revision</param> /// <param name="mappedValue">Value after the mapping rule is applied</param> /// <returns>True if a mapping rule is applied; FALSE otherwise</returns> private bool GetMappedFieldValue( SourceSideTypeEnum fromSide, string sourceFieldValue, string valueMapName, XmlElement descriptionDocRootNode, out string mappedValue) { TraceManager.TraceVerbose("Getting mapped field value of '{0}' in Value Map '{1}'", sourceFieldValue ?? string.Empty, valueMapName ?? string.Empty); ValueMap valueMapEntry = null; foreach (ValueMap valueMap in m_session.WITCustomSetting.ValueMaps.ValueMap) { if (valueMap.name == valueMapName) { valueMapEntry = valueMap; break; } } if (null != valueMapEntry) { TraceManager.TraceVerbose("Applying mapped field value of '{0}' in Value Map '{1}'", sourceFieldValue ?? string.Empty, valueMapName ?? string.Empty); return(valueMapEntry.TryGetMappedValue(fromSide, sourceFieldValue, descriptionDocRootNode, out mappedValue)); } else { TraceManager.TraceVerbose("Cannot find Value Map '{0}'", valueMapName ?? string.Empty); mappedValue = sourceFieldValue; return(false); } }
private void UpdateMappedFieldCache(SourceSideTypeEnum sourceSideTypeEnum, string leftName, string rightName) { switch (sourceSideTypeEnum) { case SourceSideTypeEnum.Left: if (!m_perDirectionSourceMappedFields[sourceSideTypeEnum].Contains(leftName)) { m_perDirectionSourceMappedFields[sourceSideTypeEnum].Add(leftName); } break; case SourceSideTypeEnum.Right: if (!m_perDirectionSourceMappedFields[sourceSideTypeEnum].Contains(rightName)) { m_perDirectionSourceMappedFields[sourceSideTypeEnum].Add(rightName); } break; case SourceSideTypeEnum.Any: if (!m_perDirectionSourceMappedFields[SourceSideTypeEnum.Left].Contains(rightName)) { m_perDirectionSourceMappedFields[SourceSideTypeEnum.Left].Add(rightName); } if (!m_perDirectionSourceMappedFields[SourceSideTypeEnum.Right].Contains(leftName)) { m_perDirectionSourceMappedFields[SourceSideTypeEnum.Right].Add(leftName); } break; default: throw new InvalidOperationException(); } }
private string MapUserIdentity( FieldMap fieldMap, SourceSideTypeEnum fromSide, string srcFieldRefName, string tgtFieldRefName, string srcFieldValue) { RichIdentity srcUserId = new RichIdentity(); UserIdFieldElement userIdField = fieldMap.GetUserIdField(fromSide, srcFieldRefName); srcUserId[userIdField.UserIdPropertyName] = srcFieldValue; RichIdentity mappedUserId; if (UserIdLookupService.TryLookup(srcUserId, m_contexts[fromSide], out mappedUserId)) { SourceSideTypeEnum toSide = (fromSide == SourceSideTypeEnum.Left) ? SourceSideTypeEnum.Right : SourceSideTypeEnum.Left; UserIdFieldElement tgtUserField = fieldMap.GetUserIdField(toSide, tgtFieldRefName); if (null != tgtUserField) { return(mappedUserId[tgtUserField.UserIdPropertyName]); } else { return(srcFieldValue); } } else { return(srcFieldValue); } }
private void MapFieldValue( FieldMap fieldMap, SourceSideTypeEnum fromSide, XmlNode fieldColumn, string srcFieldRefName, string tgtFieldRefName, string srcFieldValue, string valueMapName, XmlElement descriptionDocRootNode) { string mappedValue; if (!string.IsNullOrEmpty(valueMapName) && GetMappedFieldValue(fromSide, srcFieldValue, valueMapName, descriptionDocRootNode, out mappedValue)) { // do nothing, we have the mappedValue set } else if (UserIdLookupService.IsConfigured && fieldMap.IsUserIdField(fromSide, srcFieldRefName)) { // try user id lookup mappedValue = MapUserIdentity(fieldMap, fromSide, srcFieldRefName, tgtFieldRefName, srcFieldValue); } else { // in other cases, just use the srcFieldValue mappedValue = srcFieldValue; } fieldColumn.FirstChild.InnerText = mappedValue; }
private bool IsFieldInCachedSourceFields(SourceSideTypeEnum mapFromSide, string targetField) { if (m_perDirectionSourceMappedFields.ContainsKey(mapFromSide) && m_perDirectionSourceMappedFields[mapFromSide].Contains(targetField)) { return(true); } return(false); }
/// <summary> /// Get the mapped field name from a particular side of a MappedField entry /// </summary> /// <param name="sourceSide"></param> /// <param name="mappedFieldEntry"></param> /// <returns>The name of the mapped field</returns> public string GetMappedFieldName( SourceSideTypeEnum sourceSide, string sourceFieldName, MappedField mappedFieldEntry) { if (null == mappedFieldEntry) { throw new ArgumentNullException(); } return((sourceSide == SourceSideTypeEnum.Left) ? (mappedFieldEntry.RightName == WitMappingConfigVocab.Any ? sourceFieldName : mappedFieldEntry.RightName) : (mappedFieldEntry.LeftName == WitMappingConfigVocab.Any ? sourceFieldName : mappedFieldEntry.LeftName)); }
/// <summary> /// Get all the "missing" field entries given a specific migration source Unique Id /// </summary> /// <param name="sourceSide"></param> /// <returns></returns> public ReadOnlyCollection <MappedField> GetMissingFieldEntries(SourceSideTypeEnum sourceSide) { List <MappedField> fields = new List <MappedField>(); foreach (MappedField mappedField in this.MappedField) { string mapFromFieldName = (sourceSide == SourceSideTypeEnum.Left ? mappedField.LeftName : mappedField.RightName); if (mappedField.MapFromSide == sourceSide && WitMappingConfigVocab.MissingField.Equals(mapFromFieldName, System.StringComparison.OrdinalIgnoreCase)) { fields.Add(mappedField); } } return(fields.AsReadOnly()); }
/// <summary> /// Perform the adapter-specific initialization /// </summary> public virtual void InitializeClient(MigrationSource migrationSource) { TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(migrationSource.ServerUrl)); tfs.Authenticate(); m_workItemStore = (WorkItemStore)tfs.GetService(typeof(WorkItemStore)); m_projectName = migrationSource.SourceIdentifier; m_migrationSourceGuid = new Guid(migrationSource.InternalUniqueId); if (m_migrationSourceGuid.Equals(m_session.LeftMigrationSourceUniqueId)) { m_sourceSide = SourceSideTypeEnum.Left; } else { m_sourceSide = SourceSideTypeEnum.Right; } }
/// <summary> /// get the mapped work item type name /// </summary> /// <param name="sourceSide"></param> /// <param name="sourceWorkItemType"></param> /// <param name="mappingEntry"></param> /// <returns>the mapped work item type; the sourceType if wildcard character /// "*" is used; NULL if none is applicable</returns> /// <remarks> /// WorkItemTypes element must have at least one entry (WorkItemType). /// The following line shows where the wildcard characters can be used: /// <WorkItemType LeftWorkItemTypeName="*" RightWorkItemTypeName="*" fieldMap="@@ALL@@" /> /// - "*" means ANY in above context. /// - "@@ALL@@" means "map all fields" in above context. /// </remarks> public string GetMappedType( SourceSideTypeEnum sourceSide, string sourceWorkItemType, WorkItemTypeMappingElement mappingEntry) { if (null == mappingEntry) { throw new ArgumentNullException(); } string retVal = (sourceSide == SourceSideTypeEnum.Left) ? mappingEntry.RightWorkItemTypeName : mappingEntry.LeftWorkItemTypeName; if (retVal.Equals(WitMappingConfigVocab.Any, System.StringComparison.OrdinalIgnoreCase)) { retVal = sourceWorkItemType; } return(retVal); }
public List <string> GetReferencedFieldReferenceNames(SourceSideTypeEnum sourceSide) { if (!m_perSideReferencedFieldNames.ContainsKey(sourceSide)) { List <string> referencedFields = new List <string>(); foreach (var fieldMap in this.FieldMaps.FieldMap) { foreach (var aggregationGroup in fieldMap.AggregatedFields.FieldsAggregationGroup) { if (aggregationGroup.MapFromSide == sourceSide) { foreach (var f in aggregationGroup.SourceField) { if (!referencedFields.Contains(f.SourceFieldName, StringComparer.OrdinalIgnoreCase)) { referencedFields.Add(f.SourceFieldName); } } } } } foreach (var valueMap in this.ValueMaps.ValueMap) { foreach (var mappedValue in valueMap.Value) { if (mappedValue.When != null && mappedValue.When.ConditionalSrcFieldName != null && !mappedValue.When.ConditionalSrcFieldName.Equals(WitMappingConfigVocab.Any) && !referencedFields.Contains(mappedValue.When.ConditionalSrcFieldName, StringComparer.OrdinalIgnoreCase)) { referencedFields.Add(mappedValue.When.ConditionalSrcFieldName); } } } m_perSideReferencedFieldNames.Add(sourceSide, referencedFields); } return(m_perSideReferencedFieldNames[sourceSide]); }
private void MapOwnerUserId( XmlElement rootNode, SourceSideTypeEnum fromSide) { if (!UserIdLookupService.IsConfigured) { return; } var authorAttr = rootNode.Attributes["Author"]; if (authorAttr == null || string.IsNullOrEmpty(authorAttr.Value)) { return; } RichIdentity srcUserId = new RichIdentity(); var authorUserIdPropertyAttr = rootNode.Attributes[Constants.WITAuthorUserIdPropertyName]; string authorUserIdProperty; if (authorUserIdPropertyAttr == null || string.IsNullOrEmpty(authorUserIdPropertyAttr.Value)) { authorUserIdProperty = "DisplayName"; } else { authorUserIdProperty = authorUserIdPropertyAttr.Value; } srcUserId[authorUserIdProperty] = authorAttr.Value; RichIdentity mappedUserId; if (UserIdLookupService.TryLookup(srcUserId, m_contexts[fromSide], out mappedUserId)) { authorAttr.Value = mappedUserId.DisplayName; } }
/// <summary> /// get the work item type mapping entry /// </summary> /// <param name="sourceSide"></param> /// <param name="sourceType"></param> /// <returns></returns> public WorkItemTypeMappingElement GetMappingEntry(SourceSideTypeEnum sourceSide, string sourceType) { AddMissingDefaultMap(); WorkItemTypeMappingElement retVal = null; foreach (WorkItemTypeMappingElement map in this.WorkItemType) { switch (sourceSide) { case SourceSideTypeEnum.Left: if (map.LeftWorkItemTypeName == sourceType) { return(map); } else if (map.LeftWorkItemTypeName.Equals(WitMappingConfigVocab.Any, System.StringComparison.OrdinalIgnoreCase)) { retVal = map; } break; case SourceSideTypeEnum.Right: if (map.RightWorkItemTypeName == sourceType) { return(map); } else if (map.RightWorkItemTypeName.Equals(WitMappingConfigVocab.Any, System.StringComparison.OrdinalIgnoreCase)) { retVal = map; } break; } } return(retVal); }
private bool IsSourceFieldMapped(SourceSideTypeEnum mapFromSide, string sourceField) { if (string.IsNullOrEmpty(sourceField)) { // if source field is string.Empty, it is allowed but won't be applied to any field return(false); } if (IsFieldInCachedSourceFields(mapFromSide, sourceField)) { return(true); } if (mapFromSide == SourceSideTypeEnum.Any) { if (IsFieldInCachedSourceFields(SourceSideTypeEnum.Left, sourceField) || IsFieldInCachedSourceFields(SourceSideTypeEnum.Right, sourceField)) { return(true); } } return(false); }
/// <summary> /// Given the migration source side and field reference name, tells if it is a UserIdentity field. /// </summary> /// <param name="fromSide"></param> /// <param name="srcFieldRefName"></param> /// <returns></returns> public bool IsUserIdField( SourceSideTypeEnum fromSide, string srcFieldRefName) { return(null != GetUserIdField(fromSide, srcFieldRefName)); }
/// <summary> /// /// </summary> /// <param name="sourceSide"></param> /// <param name="conditionalMaps"></param> /// <param name="descriptionDocRootNode"></param> /// <param name="mappedValue"></param> /// <returns>True if a mapping rule is applied; FALSE otherwise</returns> /// <remarks> /// The following evaluation precedence is honored: /// map v1 => v2, when F.value = f /// map * => v2, when F.value = f /// map v1 => v2, when F.value = * /// map * => v2, when F.value = * /// </remarks> private bool TryConditionalMap( SourceSideTypeEnum sourceSide, List <Value> conditionalMaps, XmlElement descriptionDocRootNode, ref string mappedValue) { Value explicitCondExplicitValue = null; Value explicteCondWildcardValue = null; Value wildcardCondExplicitValue = null; Value wildcardCondWildcardValue = null; foreach (Value vMap in conditionalMaps) { string srcFldRefNameInCond = vMap.When.ConditionalSrcFieldName; string srcFldValueInCond = vMap.When.ConditionalSrcFieldValue; XmlNode srcFldColNode = descriptionDocRootNode.SelectSingleNode( string.Format("/WorkItemChanges/Columns/Column[@ReferenceName='{0}']", srcFldRefNameInCond)); if (null != srcFldColNode) { if (srcFldValueInCond.Equals(WitMappingConfigVocab.Any)) { if (WitMappingConfigVocab.Any == (sourceSide == SourceSideTypeEnum.Left ? vMap.LeftValue : vMap.RightValue)) { // map * => v2, when F.value = * wildcardCondWildcardValue = vMap; } else { // map v1 => v2, when F.value = * wildcardCondExplicitValue = vMap; } } else if (srcFldValueInCond.Equals(srcFldColNode.FirstChild.InnerText)) { if (WitMappingConfigVocab.Any == (sourceSide == SourceSideTypeEnum.Left ? vMap.LeftValue : vMap.RightValue)) { // map * => v2, when F.value = f explicteCondWildcardValue = vMap; } else { // map v1 => v2, when F.value = f explicitCondExplicitValue = vMap; break; } } } } bool retVal = false; if (explicitCondExplicitValue != null) { mappedValue = ApplyValueMap(explicitCondExplicitValue, sourceSide); retVal = true; } else if (explicteCondWildcardValue != null) { mappedValue = ApplyValueMap(explicteCondWildcardValue, sourceSide); retVal = true; } else if (wildcardCondExplicitValue != null) { mappedValue = ApplyValueMap(wildcardCondExplicitValue, sourceSide); retVal = true; } else if (wildcardCondWildcardValue != null) { mappedValue = ApplyValueMap(wildcardCondWildcardValue, sourceSide); retVal = true; } return(retVal); }
private string ApplyValueMap(Value valueMap, SourceSideTypeEnum sourceSide) { return((sourceSide == SourceSideTypeEnum.Left ? valueMap.RightValue : valueMap.LeftValue) ?? string.Empty); }
///// <summary> ///// Get a mapped value given a migration source unique Id and the value to be mapped. ///// </summary> ///// <param name="sourceSide"></param> ///// <param name="sourceValue"></param> ///// <returns></returns> ///// <remarks> ///// The following evaluation precedence is honored: ///// map v1 => v2, when F.value = f ///// map * => v2, when F.value = f ///// map v1 => v2, when F.value = * ///// map * => v2, when F.value = * ///// map v1 => v2 ///// map * => v2 ///// </remarks> /// <summary> /// Try getting a mapped value given a migration source unique Id and the value to be mapped. /// </summary> /// <param name="sourceSide">Left or Right of the migration source in the session configuration</param> /// <param name="sourceValue">Value before the mapping rule is applied</param> /// <param name="descriptionDocRootNode">The document that contains all the field information in a particular revision</param> /// <param name="mappedValue">Value after the mapping rule is applied</param> /// <returns>True if a mapping rule is applied; FALSE otherwise</returns> /// <remarks> /// The following evaluation precedence is honored: /// map v1 => v2, when F.value = f /// map * => v2, when F.value = f /// map v1 => v2, when F.value = * /// map * => v2, when F.value = * /// map v1 => v2 /// map * => v2 /// </remarks> public bool TryGetMappedValue( SourceSideTypeEnum sourceSide, string sourceValue, XmlElement descriptionDocRootNode, out string mappedValue) { mappedValue = sourceValue; #region find applicable rules List <Value> conditionalMaps = new List <Value>(); Value explicitMap = null; Value wildCardMap = null; foreach (Value valuePair in this.Value) { if (sourceValue == (sourceSide == SourceSideTypeEnum.Left ? valuePair.LeftValue : valuePair.RightValue)) { if (null == valuePair.When || (string.IsNullOrEmpty(valuePair.When.ConditionalSrcFieldName) && string.IsNullOrEmpty(valuePair.When.ConditionalSrcFieldValue))) { if (null == explicitMap) { explicitMap = valuePair; } else { Trace.TraceWarning("There are multiple value maps mapping from the same source value '{0}'.", sourceValue ?? string.Empty); } } else { conditionalMaps.Add(valuePair); } } else if (WitMappingConfigVocab.Any == (sourceSide == SourceSideTypeEnum.Left ? valuePair.LeftValue : valuePair.RightValue)) { if (null == valuePair.When || (string.IsNullOrEmpty(valuePair.When.ConditionalSrcFieldName) && string.IsNullOrEmpty(valuePair.When.ConditionalSrcFieldValue))) { if (null == wildCardMap) { wildCardMap = valuePair; } else { Trace.TraceWarning("There are multiple wildcard-character entries in the value map '{0}'.", this.name ?? string.Empty); } } else { conditionalMaps.Add(valuePair); } } } #endregion #region try applying rules bool mappingRuleApplied = TryConditionalMap(sourceSide, conditionalMaps, descriptionDocRootNode, ref mappedValue); if (!mappingRuleApplied) { if (null != explicitMap) { mappedValue = (sourceSide == SourceSideTypeEnum.Left ? explicitMap.RightValue : explicitMap.LeftValue); mappingRuleApplied = true; } else if (null != wildCardMap) { mappedValue = (sourceSide == SourceSideTypeEnum.Left ? wildCardMap.RightValue : wildCardMap.LeftValue); mappingRuleApplied = true; } } #endregion if (mappingRuleApplied && mappedValue.Equals(WitMappingConfigVocab.Any)) { // mapping * to * semantically means copy source value to target mappedValue = sourceValue; } return(mappingRuleApplied); }
public void MapWorkItemTypeFieldValues(string itemId, XmlDocument workItemDescriptionDocument, Guid sourceId) { XmlDocument copy = new XmlDocument(); copy.LoadXml(workItemDescriptionDocument.OuterXml); XmlElement rootNode = workItemDescriptionDocument.DocumentElement; if (null == rootNode) { throw new MigrationException(MigrationToolkitResources.InvalideChangeActionDescription, itemId); } bool isLeft = DecideSidenessInConfig(sourceId, m_session); SourceSideTypeEnum sourceSide = isLeft ? SourceSideTypeEnum.Left : SourceSideTypeEnum.Right; MapOwnerUserId(rootNode, sourceSide); #region Map work item type string sourceWorkItemType = rootNode.Attributes["WorkItemType"].Value; WorkItemTypeMappingElement typeMapEntry = m_session.WITCustomSetting.WorkItemTypes.GetMappingEntry(sourceSide, sourceWorkItemType); if (null == typeMapEntry) { throw new UnmappedWorkItemTypeException(sourceWorkItemType); } string targetWorkItemType = m_session.WITCustomSetting.WorkItemTypes.GetMappedType( sourceSide, sourceWorkItemType, typeMapEntry); rootNode.SetAttribute("WorkItemType", targetWorkItemType); #endregion if (null == typeMapEntry || /* backward compatibility */ string.IsNullOrEmpty(typeMapEntry.fieldMap) || /* backward compatibility */ typeMapEntry.fieldMap.Equals(WitMappingConfigVocab.All, StringComparison.OrdinalIgnoreCase)) { XmlNode columnsNode = rootNode.SelectSingleNode("/WorkItemChanges/Columns"); if (null == columnsNode) { throw new MigrationException(MigrationToolkitResources.InvalideChangeActionDescription, itemId); } XmlNodeList columns = rootNode.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == columns) { throw new MigrationException(MigrationToolkitResources.InvalideChangeActionDescription, itemId); } List <XmlNode> fieldsToExclude = new List <XmlNode>(); foreach (XmlNode fieldColumn in columnsNode) { if (IsSkippingField(fieldColumn)) { fieldsToExclude.Add(fieldColumn); } } foreach (XmlNode excludedField in fieldsToExclude) { excludedField.ParentNode.RemoveChild(excludedField); } fieldsToExclude.Clear(); } else { #region look up the field map entry to use FieldMap fieldMapEntry = null; foreach (FieldMap fieldMap in m_session.WITCustomSetting.FieldMaps.FieldMap) { if (fieldMap.name == typeMapEntry.fieldMap) { fieldMapEntry = fieldMap; break; } } #endregion #region Map field and value if (null != fieldMapEntry) { XmlNode columnsNode = rootNode.SelectSingleNode("/WorkItemChanges/Columns"); if (null == columnsNode) { throw new MigrationException(MigrationToolkitResources.InvalideChangeActionDescription, itemId); } #region map aggregated fields List <string> aggregatedFields = new List <string>(); // store the aggregated fields so that we won't delete them later if (fieldMapEntry.AggregatedFields.FieldsAggregationGroup.Count() > 0) { foreach (var fieldsGroup in fieldMapEntry.AggregatedFields.FieldsAggregationGroup) { if (fieldsGroup.MapFromSide != sourceSide || fieldsGroup.SourceField.Count == 0) { continue; } bool canUseMappingConfig = true; // apply value map to the aggregated fields and identify the field columns // Note: // 1. Aggregated fields may appear in normal field maps // 2. If an aggregated field does not appear in normal field map, // the field will be dropped after the translation // 3. If a field appear in both aggregated and normal field mapping, // it may use different value maps foreach (SourceField srcField in fieldsGroup.SourceField) { XmlNode srcFieldCol = rootNode.SelectSingleNode( string.Format("/WorkItemChanges/Columns/Column[@ReferenceName='{0}']", srcField.SourceFieldName)); if (null != srcFieldCol) { // map field value and cache the result srcField.FieldColumnNode = srcFieldCol; string mappedValue; if (GetMappedFieldValue(sourceSide, srcFieldCol.FirstChild.InnerText, srcField.valueMap, copy.DocumentElement, out mappedValue)) { srcField.MappedValue = mappedValue; } else { srcField.MappedValue = srcFieldCol.FirstChild.InnerText; } } else { // one of the aggregated field is not present in the WIT description document TraceManager.TraceInformation( "Aggregating field '{0}' is not in the WIT update document of Change Action {1}'", srcField.SourceFieldName, itemId); canUseMappingConfig = false; break; } } if (!canUseMappingConfig) { continue; } string[] srcFieldValues = new string[fieldsGroup.SourceField.Count]; foreach (SourceField srcField in fieldsGroup.SourceField) { if (srcField.Index >= 0 && srcField.Index < srcFieldValues.Length) { srcFieldValues[srcField.Index] = srcField.MappedValue ?? string.Empty; } else { TraceManager.TraceError( "Source aggregation field '{0}' has a wrong index of '{1}' in the configuration.", srcField.SourceFieldName ?? string.Empty, srcField.Index.ToString()); } } for (int i = 0; i < srcFieldValues.Length; ++i) { if (srcFieldValues[i] == null) { TraceManager.TraceError( "Aggregated field format '{0}' has NULL value at index '{1}'.", fieldsGroup.Format, i.ToString()); canUseMappingConfig = false; } } if (!canUseMappingConfig) { continue; } try { string aggregatedFieldName = fieldsGroup.TargetFieldName; string aggregatedFieldValue = string.Format( System.Globalization.CultureInfo.InvariantCulture, fieldsGroup.Format, srcFieldValues); // first, try looking for the field in the document (the field may exist on source side // and included in the delta document) XmlNode aggregatedFieldInDoc = rootNode.SelectSingleNode( string.Format("/WorkItemChanges/Columns/Column[@ReferenceName='{0}']", aggregatedFieldName)); if (null == aggregatedFieldInDoc) { XmlElement aggregatedFieldCol = columnsNode.OwnerDocument.CreateElement("Column"); aggregatedFieldCol.SetAttribute("ReferenceName", aggregatedFieldName); aggregatedFieldCol.SetAttribute("Type", string.Empty); aggregatedFieldCol.SetAttribute("DisplayName", string.Empty); columnsNode.AppendChild(aggregatedFieldCol); XmlElement v = columnsNode.OwnerDocument.CreateElement("Value"); v.InnerText = aggregatedFieldValue; aggregatedFieldCol.AppendChild(v); } else { aggregatedFieldInDoc.FirstChild.InnerText = aggregatedFieldValue; } aggregatedFields.Add(aggregatedFieldName); } catch (System.FormatException formatException) { TraceManager.TraceException(formatException); } } } #endregion // map field and values XmlNodeList columns = rootNode.SelectNodes("/WorkItemChanges/Columns/Column"); if (null == columns) { throw new MigrationException(MigrationToolkitResources.InvalideChangeActionDescription, itemId); } List <XmlNode> fieldsToExclude = new List <XmlNode>(); foreach (XmlNode fieldColumn in columns) { string srcFieldRefName = fieldColumn.Attributes["ReferenceName"].Value; string srcFieldValue = fieldColumn.FirstChild.InnerText; MappedField mappedFieldEntry = fieldMapEntry.MappedFields.GetMappedFieldEntry( sourceSide, srcFieldRefName); if (null == mappedFieldEntry) { if (!aggregatedFields.Contains(srcFieldRefName, StringComparer.OrdinalIgnoreCase)) { // record the unmapped fields and delete them after the field/value mapping is applied // aggregated fields are added by us and are target-specific - they shouldn't be deleted fieldsToExclude.Add(fieldColumn); } continue; } else { string mapFromField = (isLeft ? mappedFieldEntry.LeftName : mappedFieldEntry.RightName); if (mapFromField.Equals(WitMappingConfigVocab.Any, StringComparison.OrdinalIgnoreCase)) { if (IsSkippingField(fieldColumn)) { fieldsToExclude.Add(fieldColumn); } } } string tgtFieldRefName = fieldMapEntry.MappedFields.GetMappedFieldName(sourceSide, srcFieldRefName, mappedFieldEntry); if (string.IsNullOrEmpty(tgtFieldRefName)) { // record the fields that are mapped to "", semantically the field is excluded on target side fieldsToExclude.Add(fieldColumn); continue; } MapFieldValue(fieldMapEntry, sourceSide, fieldColumn, srcFieldRefName, tgtFieldRefName, srcFieldValue, mappedFieldEntry.valueMap, copy.DocumentElement); fieldColumn.Attributes["ReferenceName"].Value = tgtFieldRefName; } // delete the unmapped fields foreach (XmlNode unmappedField in fieldsToExclude) { unmappedField.ParentNode.RemoveChild(unmappedField); } fieldsToExclude.Clear(); aggregatedFields.Clear(); // apply missing field & default values var missingFieldMappings = fieldMapEntry.MappedFields.GetMissingFieldEntries(sourceSide); foreach (MappedField mappedFieldEntry in missingFieldMappings) { if (null == mappedFieldEntry) { continue; } string missingFieldName = (sourceSide == SourceSideTypeEnum.Left ? mappedFieldEntry.RightName : mappedFieldEntry.LeftName); XmlElement missingField = columnsNode.OwnerDocument.CreateElement("Column"); missingField.SetAttribute("ReferenceName", missingFieldName); missingField.SetAttribute("Type", string.Empty); missingField.SetAttribute("DisplayName", string.Empty); columnsNode.AppendChild(missingField); // note missing fields do not have "from" value, hence User Id lookup is not needed. string missingFieldValue = string.Empty; if (!string.IsNullOrEmpty(mappedFieldEntry.valueMap)) { string mappedValue; if (GetMappedFieldValue(sourceSide, missingFieldValue, mappedFieldEntry.valueMap, copy.DocumentElement, out mappedValue)) { missingFieldValue = mappedValue; } else { // use the source value when there is no value map, i.e. // missingFieldValue = missingFieldValue; } } XmlElement v = columnsNode.OwnerDocument.CreateElement("Value"); v.InnerText = missingFieldValue; missingField.AppendChild(v); } } #endregion } }