All pastes #1317655 Raw Edit

Dynamic CSS Processor

public text v1 · immutable
#1317655 ·published 2009-01-25 02:57 UTC
rendered paste body
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;

public class StyleSheetHandler : IHttpHandler
{
    protected Dictionary<StylesheetCacheKey, string> _cache; // Memory cache of processed stylesheets.

    public StyleSheetHandler()
    {
        _cache = new Dictionary<StylesheetCacheKey, string>();
    }
    
    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        var rawFileName = context.Request.PhysicalPath;
        var rawFileLastModified = File.GetLastWriteTimeUtc(rawFileName);
        var browserName = context.Request.Browser.Browser;
        var browserVersion = new Version(context.Request.Browser.Version);
        var newCacheKey = new StylesheetCacheKey(rawFileName, rawFileLastModified, browserName, browserVersion);

        // Check for entry in cache of processed contents of stylesheets that is at least as recent as last
        // modified time of raw file.
        var cacheEntry = _cache.SingleOrDefault(entry => entry.Key.Equals(newCacheKey) &&
            entry.Key.RawFileLastModified >= newCacheKey.RawFileLastModified);
        string processedContent;
        if (cacheEntry.Value != null)
        {
            // Found entry in cache.
            processedContent = cacheEntry.Value;
        }
        else
        {
            // Process raw content of stylesheet file.
            var rawContent = File.ReadAllText(rawFileName);
            processedContent = ProcessStylesheet(rawContent, browserName, browserVersion);

            // Add processed stylesheet to cache.
            _cache[newCacheKey] = processedContent;
        }

        // Write process content of stylesheet to response stream.
        context.Response.ContentType = "text/css";
        context.Response.Output.Write(processedContent);
        context.Response.Flush();
    }

    protected string ProcessStylesheet(string content, string browserName, Version browserVersion)
    {
        // Matches blocks (regions) of commented text (of form "/*{comment}*/").
        var regexComments = new Regex(@"/\*.+?\*/", RegexOptions.Singleline);
        // Matches browser-conditional tags of form "[[?{browser condition} {true result} | {false result}]]".
        var regexTags = new Regex(@"\[\?(?<browser>.+?) +?(?<trueResult>.*?) ?\| ?(?<falseResult>.*?) *?\]",
            RegexOptions.None);

        // Evaluate tags within each comment block.
        var processedContent = regexComments.Replace(content, new MatchEvaluator(blockMatch =>
            {
                var tagCount = 0;
                var evaluatedText = regexTags.Replace(blockMatch.Value, new MatchEvaluator(tagMatch =>
                {
                    tagCount++;

                    // Invert condition if preceded by '!'.
                    var browser = tagMatch.Groups["browser"].Value;
                    bool inverted = false;
                    if (browser[0] == '!')
                    {
                        inverted = true;
                        browser = browser.Substring(1);
                    }
                    return (string.Equals(browser, browserName, StringComparison.InvariantCultureIgnoreCase) ^
                        inverted) ? tagMatch.Groups["trueResult"].Value : tagMatch.Groups["falseResult"].Value;
                }));
                return (tagCount > 0) ? evaluatedText.Substring(2, evaluatedText.Length - 4) : blockMatch.Value;
            }));

        return processedContent;
    }

    protected struct StylesheetCacheKey
    {
        public string StylesheetPath;        // Path of raw stylesheet file.
        public DateTime RawFileLastModified; // Time at which raw file was last modified.
        public string BrowserName;           // Name of browser.
        public Version BrowserVersion;       // Version of browser.

        public StylesheetCacheKey(string stylesheetPath, DateTime rawFileLastModified, string browserName,
            Version browserVersion)
        {
            this.StylesheetPath = stylesheetPath;
            this.RawFileLastModified = rawFileLastModified;
            this.BrowserName = browserName;
            this.BrowserVersion = browserVersion;
        }

        public override bool Equals(object obj)
        {
            var objKey = (StylesheetCacheKey)obj;
            return this.StylesheetPath.Equals(objKey.StylesheetPath) &&
                this.BrowserName.Equals(objKey.BrowserName) && this.BrowserVersion.Equals(objKey.BrowserVersion);
        }

        public override int GetHashCode()
        {
            return this.StylesheetPath.GetHashCode() ^ this.BrowserName.GetHashCode() ^
                this.BrowserVersion.GetHashCode();
        }

        public override string ToString()
        {
            return string.Format("{0}, {1} {2}", this.StylesheetPath, this.BrowserName, 
                this.BrowserVersion.ToString());
        }
    }
}