Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to delegate to default format provider?

I'd like to create an EscapingFormatProvider that automatically HTML-escapes formatted strings before returning them, unless the format argument starts with a !:

string.Format(new EscapingFormatProvider(), "<div>{0}</div>", "<script src='foo'></script>");
// => <div>&lt;script src='foo'&gt;&lt;/script&gt;</div>

string.Format(new EscapingFormatProvider(), "<div>{0:!}</div>", "<script src='foo'></script>");
// => <div><script src='foo'></script></div>

I'd like it to pass the format argument, less the ! if there was one, to the default formatter. The only trouble is, I have no idea how to get at the default formatter. None of the examples I've found address the issue of delegation to the default formatter.

EDIT: This is what I came up with:

private class EscapingFormatProvider : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType) { return this; }

    private string DefaultFormat(string format, object arg)
    {
        return string.Format("{0:" + format + "}", arg);
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        return (format.First() == '!')
            ? HttpUtility.HtmlEncode(DefaultFormat(format.Substring(1), arg))
            : DefaultFormat(format, arg);
    }
}

It's a bit indirect, but I suppose it works.

like image 389
Thom Smith Avatar asked Oct 07 '22 06:10

Thom Smith


2 Answers

If I'm understanding you correctly, all you should need to do to use the default format provider is call the overload of string.Format that doesn't take an IFormatProvider! Or do you mean something else by "default formatter", or are you worried about localization?

like image 41
ekolis Avatar answered Oct 10 '22 09:10

ekolis


The correct solution is to re-implement what the default formatter does, which is the following:

var formattable = arg as IFormattable;
return formattable == null
    ? arg.ToString()
    : formattable.ToString(format, formatProvider);

On top of this, your format provider should only return itself if ICustomFormatter is the type asked for.

Therefore, in your case, your format provider should look like this:

class EscapingFormatProvider : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        return null;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        var escape = false;
        if (format.StartsWith("!"))
        {
            escape = true;
            format = format.Substring(1);
        }
        var formattable = arg as IFormattable;
        var formatted = formattable == null
            ? arg.ToString()
            : formattable.ToString(format, formatProvider);
        return escape ? HttpUtility.HtmlEncode(formatted) : formatted;
    }
}
like image 52
Timwi Avatar answered Oct 10 '22 07:10

Timwi