Tuesday, July 12, 2011

Turning URLs into Hyperlinks (with Truncating Long URLs) in ASP.NET

Turning URLs into hypyerlinks is fairly easy and straightforward, and actually has been discussed many times before (for example, see here and here). But some times, you also need to truncate the URL in the link text if it's too long. For example, if you have some text with a very long URL like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco http://www.example.com/thisisaveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylongurl.html laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

You'll probably want to not only turn the URL into a hyperlink, but also truncate the URL in the hyperlink's text to look something like this:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco http://www.example.com/thisisaveeeeeeeeee... laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Finding and Truncating the URLs

Finding the URLs is straightforward and is easily done using Regular expressions, but to truncate the long URLs, we'll need to to check whether the URL is too long, and if so, truncate it before using it in the hyperlink's text.

In this post, I'm going to discuss the details of how to find the URLs. For truncating long URLs, I'm going to use code from my previous post about truncating long text in ASP.NET. Please refer to that post for the details of how the text is truncated.


The Code
public static class UIHelper
{
    private static string ConvertURLs(string text, int textMaxWidth, string fontName, int fontSizeInPixels, bool isFontBold)
    {
        if (string.IsNullOrEmpty(text))
            return text;

        return Regex.Replace(
            text, 
            @"(http(s)*://([\w.]+/?)\S*)",
            LinkTextTruncator.GetLinkTextTruncator(textMaxWidth, fontName, fontSizeInPixels, isFontBold).MatchEvaluator,
            RegexOptions.IgnoreCase | RegexOptions.Compiled);
    }
}

class LinkTextTruncator
{
    public LinkTextTruncator(
        int textMaxWidth, 
        string fontName, 
        int fontSizeInPixels, 
        bool isFontBold)
    {
        _textMaxWidth = textMaxWidth;
        _fontName = fontName;
        _fontSizeInPixels = fontSizeInPixels;
        _isFontBold = isFontBold;
        _matchEvaluator = new MatchEvaluator(TruncateText);
    }

    private int _textMaxWidth;
    private string _fontName;
    private int _fontSizeInPixels;
    private bool _isFontBold;
    private MatchEvaluator _matchEvaluator;

    public string TruncateText(Match m)
    {
        string url = m.ToString();
        return string.Format(
            "{1}",
            url,
            TextTruncator.TruncateText( // For the implementation of TextTruncator.TruncateText(), please refer to my post about truncating long text in ASP.NET
                url,
                _textMaxWidth,
                _fontName,
                _fontSizeInPixels,
                _isFontBold));
    }

    public MatchEvaluator MatchEvaluator
    {
        get
        {
            return _matchEvaluator;
        }
    }

    //
    // Static Members
    //

    private static Dictionary _linkTextTruncatorDic;
    private static Dictionary LinkTextTruncatorDic
    {
        get
        {
            if (_linkTextTruncatorDic == null)
            {
                _linkTextTruncatorDic = new Dictionary();
            }
            return _linkTextTruncatorDic;
        }
    }

    private static object _linkTextTruncatorLocker = new object();
    public static LinkTextTruncator GetLinkTextTruncator(int textMaxWidth, string fontName, int fontSizeInPixels, bool isFontBold)
    {
        string specs = textMaxWidth.ToString() + "px_" + fontName.ToLower() + "_" + fontSizeInPixels.ToString() + "px" + (isFontBold ? "_bold" : "");
        if (!LinkTextTruncatorDic.ContainsKey(specs))
        {
            lock (_linkTextTruncatorLocker)
            {
                LinkTextTruncatorDic.Add(specs, new LinkTextTruncator(textMaxWidth, fontName, fontSizeInPixels, isFontBold));
            }
        }

        return LinkTextTruncatorDic[specs];
    }
}

Usage Example
<%= UIHelper.ConvertURLs("Some text that contains URLs", 200, "Verdana", 12, false) %>

Notes About the Code

For the implementation of TextTruncator.TruncateText(), please check my previous post about truncating long text in ASP.NET

If you check the code in UIHelper.ConvertURLs(), you'll find that I use one of the overloaded versions of Regex.Replace() that takes a MatchEvaluator delegate. Every time a match is found (a URL in this case), LinkTextTruncator.TruncateText() is called, which converts the URL into an anchor tag and calls TextTruncator.TruncateText(), which truncates the URL if it's longer than the specified width, otherwise it returns the same URL untruncated.

I chose to cache the LinkTextTruncator objects (by font family, size and weight) in order to avoid creating a new object every time URLs are truncated and so slightly alleviate the overhead on the garbage collector. But, it should be easy to remove the caching if you find it unneeded.

Needless to say, you need to encode the text passed to ConvertURLs() (using HttpUtility.HtmlEncode(), or Html.Encode() if you're using ASP.NET MVC). You should notice that any ampersands in the URL will be converted to & when the text is encoded, but this is not only valid in HTML, it's actually required but often ignored. If for some reason you don't want the ampersands in the URLs to be converted, you can use Replace("&", "&") on the URL in LinkTextTruncator.TruncateText().

8 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. Creating a new object every time URLs are truncated and so slightly alleviate the overhead on the garbage collector.

    .net Programmer India

    ReplyDelete
  4. Hi,A complete understanding of how websites and the internet work, along with the experienced professionals is what separates Web Design Cochin from the average Web Design company.Thanks....

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Hi…I think it is destiny that I accidently stumbled and fell into your website. You are my savior. It is uncanny that yesterday was the day I was fired or rather pleasantly put, `let go’ from my job, and today you are here with your writing tips on how to make money from home. Now…I give a damn to my day job. My profile already reads `Freelancer’, proudly and happily. I already know that there are some excellent money-making marketplaces like www.zopcodes.com,signature logo design. It is time for change. Can you give me more website names? I’m waiting…

    ReplyDelete
  7. I think the admin of this web site is actually working hard in favor of his web site, since here every data is quality based information. Truly it’s called Search engine marketing that when i search for this paragraph I found this site at the top of all blogs in search engine.

    The Best Medical Website Firm in USA,

    ReplyDelete