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());
}
}
}