public static IEnumerable <AttributeToken> TokeniseString(IEnumerable <AttributeMetadata> attributeMetadatas, string formatString)
        {
            var tokens = new HashSet <AttributeToken>();

            var regex = new Regex(tokenRegex);
            var match = regex.Match(formatString);

            while (match.Success)
            {
                var substitutionKey       = SubstitutionKey(match);
                var attributeName         = AttributeName(match);
                var attributeFormatString = AttributeFormatString(match);

                var token = new AttributeToken
                {
                    Name   = attributeName,
                    Format = attributeFormatString
                };

                var attributeMetadata = attributeMetadatas.SingleOrDefault(m => m.LogicalName == token.Name);

                if (attributeMetadata != null)
                {
                    token.AttributeMetadata = attributeMetadata;
                    token.Status            = attributeMetadata.AttributeType.HasValue && SupportedAttributeTypes.Contains(attributeMetadata.AttributeType.Value)
                        ? AttributeTokenStatus.Valid
                        : AttributeTokenStatus.UnsupportedType;
                }
                else
                {
                    token.Status = AttributeTokenStatus.UnknownAttribute;
                }

                tokens.Add(token);

                match = match.NextMatch();
            }

            return(tokens);
        }
        private static string GetFormattedAttribute(IOrganizationService service, AttributeToken token, Entity changeEntity, Entity preChangeEntity)
        {
            switch (token.AttributeMetadata.AttributeType)
            {
            case AttributeTypeCode.Boolean:
            {
                var booleanMetadata = (BooleanAttributeMetadata)token.AttributeMetadata;
                var value           = GetAttributeValue <bool?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue ? GetBooleanString(booleanMetadata, value.Value) : "<<null>>");
            }

            case AttributeTypeCode.BigInt:
            {
                var value = GetAttributeValue <long?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.DateTime:
            {
                var value = GetAttributeValue <DateTime?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.Decimal:
            {
                var value = GetAttributeValue <decimal?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.Double:
            {
                var value = GetAttributeValue <double?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.EntityName:
            {
                var value = GetAttributeValue <string>(token.Name, changeEntity, preChangeEntity);
                return(value != null ? value : "<<null>>");
            }

            case AttributeTypeCode.Integer:
            {
                var value = GetAttributeValue <int?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.Lookup:
            {
                var value = GetAttributeValue <EntityReference>(token.Name, changeEntity, preChangeEntity);
                return(value != null
                            ? (value.Name != null ? value.Name : GetEntityPrimaryNameAttributeValue(service, value))
                            : "<<null>>");
            }

            case AttributeTypeCode.Memo:
            {
                var value = GetAttributeValue <string>(token.Name, changeEntity, preChangeEntity);
                return(value != null ? value : "<<null>>");
            }

            case AttributeTypeCode.Money:
            {
                var value = GetAttributeValue <Money>(token.Name, changeEntity, preChangeEntity);
                return(value != null
                            ? (string.IsNullOrEmpty(token.Format) ? value.Value.ToString() : value.Value.ToString(token.Format))
                            : "<<null>>");
            }

            case AttributeTypeCode.Picklist:
            {
                var pickListMetadata = (PicklistAttributeMetadata)token.AttributeMetadata;
                var value            = GetAttributeValue <OptionSetValue>(token.Name, changeEntity, preChangeEntity);
                return(value != null
                            ? GetPicklistString(pickListMetadata, value)
                            : "<<null>>");
            }

            case AttributeTypeCode.String:
            {
                var value = GetAttributeValue <string>(token.Name, changeEntity, preChangeEntity);
                return(value != null ? value : "<<null>>");
            }

            case AttributeTypeCode.Uniqueidentifier:
            {
                var value = GetAttributeValue <Guid?>(token.Name, changeEntity, preChangeEntity);
                return(value.HasValue ? value.ToString() : "<<null>>");
            }

            default:
                return("<<unsupported type>>");
            }
        }