Login
XSLT transformační modul pro IIS

Tento modul pro IIS umí na HTTP odpověď aplikovat XSLT transformaci pokud je v jejím obsahu nalezena standardní XML instrukce xml-stylesheet. Zde je popis včetně zdrojového kódu.

XML dokument může na začátku obsahovat instrukci pro aplikaci transformační šablony napsané v jazyce XSLT:

<?xml-stylesheet href="Contacts.xslt" type="text/xsl"?>

Jazyk XSLT je standardizován organizací W3C a většina současných internetových prohlížečů umí takovou instrukci zpracovat a před zobrazením uživateli XML dokument transformovat například na HTML. Většina, ale neznamená všechny, obzvlášť nejaké starší verze nebo nějaké jednodušší prohlížeče pro mobilní zařízení toto podporovat nemusí. Taky se musíte vyvarovat použití Microsoft XPath Extension Functions, protože ty fungují pouze v IE. Volání nějakých externích komponent z XSLT napsaných třeba v .NET je samozřejmě naprosto zapovězeno. Nehledě na to, že transformace probíhá na klientské straně a nemáte tak možnost do tohoto procesu zapojit informace, které web server nepublikuje do internetu.

Z výše uvedených důvodů a ještě mnoha dalších jsem se rozhodl napsat modul pro IIS, který detekuje v HTTP odpovědi, že jde o XML dokument s výše uvedenou transformační instrukcí, a celou transformaci provede ještě na straně serveru pomocí Microsoft XSLT procesoru. Možnosti XSLT jazyku nejsou tak ničím omezeny. Klient již obdrží výsledek po transformaci.

Detekce xml dokumentu s výše uvedenou transformační intsrukcí je "šetrná" . Pokud HTTP odpověď obsahuje něco jiného než XML dokument, nebo XML dokument neobsahuje transformační instrukci, je včas přerušeno zachytávání odpovědi a vše se ihned posílá na klienta, takže modul nijak zásadně neovlivňuje výkonnost nebo paměťové nároky v případě jiných HTTP požadavků.

Nebylo by však dobře kdyby se po zprovoznění tohoto modulu všechny takové dokumenty transformovaly na serveru a nešlo toto ovlivnit u každého dokumentu samostatně. Proto je zde využit standardní atribut media, který W3C umožňuje použít i pro výše uvedenou transformační instrukci. Pokud má být tedy dokument transformován již na serveru musí tranfromační instrukce vypadat takto:

<?xml-stylesheet href="Contacts.xslt" type="text/xsl" media="server"?>

Transformační modul podporuje rekurzi, takže pokud výsledek transformace obsahuje opět transformační instrukci, je provedena transformace výsledku předchozí transformace.

Transformační modul předává do transformační šablony parametry z query stringu, takže do transformační logiky lze zapojit i parametry předané v url. Stačí v úvodu transformační šablony definovat parametr s odpovídajícím názvem:

<xsl:param name="Sort"/>

Modul jednoduše přidáte do souboru web.config takto:

<system.webServer>
	<modules>
		<add name="TransformationModule" type="Viga.Samples.TransformationModule, Viga.Samples"/>
	</modules>
</system.webServer>
		

Zdrojový kód modulu:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Xsl;

namespace Viga.Samples
{
	public class TransformationModule : IHttpModule
	{
		public void Init(HttpApplication context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			context.BeginRequest += BeginRequest;
		}

		public void Dispose()
		{
		}

		private void BeginRequest(object source, EventArgs a)
		{
			HttpApplication application = (HttpApplication) source;

			XsltArgumentList args = new XsltArgumentList();
			foreach (string item in application.Context.Request.QueryString)
				args.AddParam(item, string.Empty, application.Context.Request.QueryString[item]);
			args.XsltMessageEncountered += MessageEncountered;

			application.Context.Response.Filter = new TransformationStream(application.Context.Response.Filter, new LazyEncoding(() => application.Context.Response.ContentEncoding), new XsltSettings(true, true), new XmlUrlResolver(), args);
		}

		private static void MessageEncountered(object sender, XsltMessageEncounteredEventArgs a)
		{
			MethodBase.GetCurrentMethod().TraceInformation(a.Message);
		}

		private class TransformationStream : Stream
		{
			private Stream output;
			private readonly bool leaveOpen;
			private Encoding encoding;
			private XsltSettings settings;
			private XmlResolver resolver;
			private XsltArgumentList args;

			private Stream input;

			private string styleSheet = null;

			private long length = 0;

			private int lineNumber = 0;
			private int linePosition = 0;

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XsltSettings settings, XmlResolver resolver, XsltArgumentList args)
				: base()
			{
				if (output == null)
					throw new ArgumentNullException("output");

				this.output = output;
				this.leaveOpen = leaveOpen;
				this.encoding = encoding;
				this.settings = settings;
				this.resolver = resolver;
				this.args = args;

				input = new MemoryStream();
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XsltSettings settings, XmlResolver resolver)
				: this(output, leaveOpen, encoding, settings, resolver, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XsltSettings settings, XsltArgumentList args)
				: this(output, leaveOpen, encoding, settings, null, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XsltSettings settings)
				: this(output, leaveOpen, encoding, settings, null, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XmlResolver resolver, XsltArgumentList args)
				: this(output, leaveOpen, encoding, null, resolver, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XmlResolver resolver)
				: this(output, leaveOpen, encoding, null, resolver, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding, XsltArgumentList args)
				: this(output, leaveOpen, encoding, null, null, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, Encoding encoding)
				: this(output, leaveOpen, encoding, null, null, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XsltSettings settings, XmlResolver resolver, XsltArgumentList args)
				: this(output, leaveOpen, null, settings, resolver, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XsltSettings settings, XmlResolver resolver)
				: this(output, leaveOpen, null, settings, resolver, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XsltSettings settings, XsltArgumentList args)
				: this(output, leaveOpen, null, settings, null, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XsltSettings settings)
				: this(output, leaveOpen, null, settings, null, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XmlResolver resolver, XsltArgumentList args)
				: this(output, leaveOpen, null, null, resolver, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XmlResolver resolver)
				: this(output, leaveOpen, null, null, resolver, null)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen, XsltArgumentList args)
				: this(output, leaveOpen, null, null, null, args)
			{
			}

			public TransformationStream(Stream output, bool leaveOpen)
				: this(output, leaveOpen, null, null, null, null)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XsltSettings settings, XmlResolver resolver, XsltArgumentList args)
				: this(output, false, encoding, settings, resolver, args)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XsltSettings settings, XmlResolver resolver)
				: this(output, false, encoding, settings, resolver, null)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XsltSettings settings, XsltArgumentList args)
				: this(output, false, encoding, settings, null, args)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XsltSettings settings)
				: this(output, false, encoding, settings, null, null)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XmlResolver resolver, XsltArgumentList args)
				: this(output, false, encoding, null, resolver, args)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XmlResolver resolver)
				: this(output, false, encoding, null, resolver, null)
			{
			}

			public TransformationStream(Stream output, Encoding encoding, XsltArgumentList args)
				: this(output, false, encoding, null, null, args)
			{
			}

			public TransformationStream(Stream output, Encoding encoding)
				: this(output, false, encoding, null, null, null)
			{
			}

			public TransformationStream(Stream output, XsltSettings settings, XmlResolver resolver, XsltArgumentList args)
				: this(output, false, null, settings, resolver, args)
			{
			}

			public TransformationStream(Stream output, XsltSettings settings, XmlResolver resolver)
				: this(output, false, null, settings, resolver, null)
			{
			}

			public TransformationStream(Stream output, XsltSettings settings, XsltArgumentList args)
				: this(output, false, null, settings, null, args)
			{
			}

			public TransformationStream(Stream output, XsltSettings settings)
				: this(output, false, null, settings, null, null)
			{
			}

			public TransformationStream(Stream output, XmlResolver resolver, XsltArgumentList args)
				: this(output, false, null, null, resolver, args)
			{
			}

			public TransformationStream(Stream output, XmlResolver resolver)
				: this(output, false, null, null, resolver, null)
			{
			}

			public TransformationStream(Stream output, XsltArgumentList args)
				: this(output, false, null, null, null, args)
			{
			}

			public TransformationStream(Stream output)
				: this(output, false, null, null, null, null)
			{
			}

			protected override void Dispose(bool disposing)
			{
				try
				{
					if (disposing && input != null)
					{
						input.Position = 0;

						if (!string.IsNullOrEmpty(styleSheet))
						{
							XslCompiledTransform transformation = new XslCompiledTransform();
							transformation.Load(styleSheet, settings, resolver);

							XmlWriterSettings outputSettings = transformation.OutputSettings.Clone();
							outputSettings.CloseOutput = true;
							if (encoding != null)
								outputSettings.Encoding = encoding;

							using (XmlReader reader = XmlReader.Create(input, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
							using (XmlWriter writer = ObjectUtility.Create(i => outputSettings.OutputMethod == XmlOutputMethod.Html ? new HtmlWriter(i) : i, () => ObjectUtility.Create(i => XmlWriter.Create(i, outputSettings), () => new TransformationStream(output, true, encoding, settings, resolver, args))))
								transformation.Transform(reader, args, writer, resolver);
						}
						else
							input.CopyTo(output);
					}
				}
				finally
				{
					if (disposing)
					{
						if (input != null)
						{
							input.Dispose();
							input = null;
						}

						if (output != null && !leaveOpen)
						{
							output.Dispose();
							output = null;
						}
					}

					base.Dispose(disposing);
				}
			}

			private Stream This
			{
				get
				{
					return input ?? output;
				}
			}

			public Stream Output
			{
				get
				{
					return output;
				}
			}

			public override bool CanRead
			{
				get
				{
					return false;
				}
			}

			public override bool CanWrite
			{
				get
				{
					return true;
				}
			}

			public override bool CanSeek
			{
				get
				{
					return false;
				}
			}

			public override long Position
			{
				get
				{
					return This.Position;
				}

				set
				{
					throw new NotSupportedException();
				}
			}

			public override long Length
			{
				get
				{
					return This.Length;
				}
			}

			public override long Seek(long offset, SeekOrigin origin)
			{
				throw new NotSupportedException();
			}

			public override void SetLength(long value)
			{
				throw new NotSupportedException();
			}

			public override int Read(byte[] buffer, int offset, int count)
			{
				throw new NotSupportedException();
			}

			public override void Write(byte[] buffer, int offset, int count)
			{
				This.Write(buffer, offset, count);

				if (styleSheet == null)
				{
					input.Position = 0;

					using (XmlReader reader = XmlReader.Create(input, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
					{
						while (styleSheet == null && reader.TryRead())
						{
							switch (reader.NodeType)
							{
								case XmlNodeType.ProcessingInstruction:
									IDictionary<string, string> attributes = reader.Name == "xml-stylesheet" ? XmlUtility.TryParseAttributes(reader.Value) : null;
									if (attributes != null)
									{
										string value;
										if (attributes.TryGetValue("media", out value) && value == "server" && attributes.TryGetValue("href", out value))
											styleSheet = value;
									}

									break;
								case XmlNodeType.Element:
									styleSheet = string.Empty;

									break;
							}
						}

						IXmlLineInfo lineInfo = (IXmlLineInfo) reader;
						if (lineInfo.LineNumber > lineNumber || (lineInfo.LineNumber == lineNumber && lineInfo.LinePosition > linePosition))
						{
							length = input.Length;

							lineNumber = lineInfo.LineNumber;
							linePosition = lineInfo.LinePosition;
						}

						if (styleSheet == null && input.Length - length > 1024)
							styleSheet = string.Empty;
					}

					if (styleSheet.Try(i => i.Length == 0))
					{
						input.Position = 0;

						input.CopyTo(output);

						input.Dispose();
						input = null;
					}
					else
						input.Position = input.Length;
				}
			}

			public override void Flush()
			{
				This.Flush();
			}

			private class HtmlWriter : XmlWrappingWriter
			{
				public HtmlWriter(XmlWriter @this)
					: base(@this)
				{
				}

				public override void WriteEndElement()
				{
					WriteFullEndElement();
				}
			}

			private class XmlWrappingWriter : XmlWriter
			{
				private XmlWriter @this;

				public XmlWrappingWriter(XmlWriter @this)
					: base()
				{
					if (@this == null)
						throw new ArgumentNullException("this");

					this.@this = @this;
				}

				public override XmlWriterSettings Settings
				{
					get
					{
						return @this.Settings;
					}
				}

				public override WriteState WriteState
				{
					get
					{
						return @this != null ? @this.WriteState : WriteState.Closed;
					}
				}

				public override string XmlLang
				{
					get
					{
						return @this.XmlLang;
					}
				}

				public override XmlSpace XmlSpace
				{
					get
					{
						return @this.XmlSpace;
					}
				}

				protected override void Dispose(bool disposing)
				{
					if (disposing && @this != null)
					{
						@this.Dispose();
						@this = null;
					}

					base.Dispose(disposing);
				}

				public override void Flush()
				{
					@this.Flush();
				}

				public override string LookupPrefix(string ns)
				{
					return @this.LookupPrefix(ns);
				}

				public override void WriteAttributes(XmlReader reader, bool defattr)
				{
					@this.WriteAttributes(reader, defattr);
				}

				public override void WriteBase64(byte[] buffer, int index, int count)
				{
					@this.WriteBase64(buffer, index, count);
				}

				public override void WriteBinHex(byte[] buffer, int index, int count)
				{
					@this.WriteBinHex(buffer, index, count);
				}

				public override void WriteCData(string text)
				{
					@this.WriteCData(text);
				}

				public override void WriteCharEntity(char ch)
				{
					@this.WriteCharEntity(ch);
				}

				public override void WriteChars(char[] buffer, int index, int count)
				{
					@this.WriteChars(buffer, index, count);
				}

				public override void WriteComment(string text)
				{
					@this.WriteComment(text);
				}

				public override void WriteDocType(string name, string pubid, string sysid, string subset)
				{
					@this.WriteDocType(name, pubid, sysid, subset);
				}

				public override void WriteEndAttribute()
				{
					@this.WriteEndAttribute();
				}

				public override void WriteEndDocument()
				{
					@this.WriteEndDocument();
				}

				public override void WriteEndElement()
				{
					@this.WriteEndElement();
				}

				public override void WriteEntityRef(string name)
				{
					@this.WriteEntityRef(name);
				}

				public override void WriteFullEndElement()
				{
					@this.WriteFullEndElement();
				}

				public override void WriteName(string name)
				{
					@this.WriteName(name);
				}

				public override void WriteNmToken(string name)
				{
					@this.WriteNmToken(name);
				}

				public override void WriteNode(XmlReader reader, bool defattr)
				{
					@this.WriteNode(reader, defattr);
				}

				public override void WriteProcessingInstruction(string name, string text)
				{
					@this.WriteProcessingInstruction(name, text);
				}

				public override void WriteQualifiedName(string localName, string ns)
				{
					@this.WriteQualifiedName(localName, ns);
				}

				public override void WriteRaw(string data)
				{
					@this.WriteRaw(data);
				}

				public override void WriteRaw(char[] buffer, int index, int count)
				{
					@this.WriteRaw(buffer, index, count);
				}

				public override void WriteStartAttribute(string prefix, string localName, string ns)
				{
					@this.WriteStartAttribute(prefix, localName, ns);
				}

				public override void WriteStartDocument()
				{
					@this.WriteStartDocument();
				}

				public override void WriteStartDocument(bool standalone)
				{
					@this.WriteStartDocument(standalone);
				}

				public override void WriteStartElement(string prefix, string localName, string ns)
				{
					@this.WriteStartElement(prefix, localName, ns);
				}

				public override void WriteString(string text)
				{
					@this.WriteString(text);
				}

				public override void WriteSurrogateCharEntity(char lowChar, char highChar)
				{
					@this.WriteSurrogateCharEntity(lowChar, highChar);
				}

				public override void WriteValue(bool value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(decimal value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(double value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(float value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(int value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(long value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(object value)
				{
					@this.WriteValue(value);
				}

				public override void WriteValue(string value)
				{
					@this.WriteValue(value);
				}

				public override void WriteWhitespace(string ws)
				{
					@this.WriteWhitespace(ws);
				}
			}
		}

		private class XmlUrlResolver : System.Xml.XmlUrlResolver
		{
			public override Uri ResolveUri(Uri baseUri, string relativeUri)
			{
				return base.ResolveUri(baseUri ?? HttpContext.Current.Try(i => i.Request.Url), relativeUri);
			}

			public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
			{
				if (absoluteUri == null)
					throw new ArgumentNullException("absoluteUri");

				object result;

				switch (absoluteUri.Scheme)
				{
					case "resource":
						if (absoluteUri.Segments.Length != 2)
							throw new UriFormatException("Invalid format of resource uri.");

						result = Assembly.Load(HttpUtility.UrlDecode(absoluteUri.Segments[0].TrimEnd(Path.AltDirectorySeparatorChar))).GetManifestResourceStream(HttpUtility.UrlDecode(absoluteUri.Segments[1]));

						break;
					default:
						result = base.GetEntity(absoluteUri, role, ofObjectToReturn);

						break;
				}

				return result;
			}
		}

		private class LazyEncoding : Encoding
		{
			private Func<Encoding> create;

			private Encoding @this = null;

			public LazyEncoding(Func<Encoding> create)
				: base()
			{
				if (create == null)
					throw new ArgumentNullException("create");

				this.create = create;
			}

			private Encoding This
			{
				get
				{
					if (@this == null)
						@this = create();

					return @this;
				}
			}

			public override int GetHashCode()
			{
				return This.GetHashCode();
			}

			public override bool Equals(object value)
			{
				return This.Equals(value);
			}

			public override string WebName
			{
				get
				{
					return This.WebName;
				}
			}

			public override byte[] GetPreamble()
			{
				return This.GetPreamble();
			}

			public override int GetMaxByteCount(int charCount)
			{
				return This.GetMaxByteCount(charCount);
			}

			public override int GetMaxCharCount(int byteCount)
			{
				return This.GetMaxCharCount(byteCount);
			}

			public override int GetByteCount(char[] chars, int index, int count)
			{
				return This.GetByteCount(chars, index, count);
			}

			public override int GetByteCount(char[] chars)
			{
				return This.GetByteCount(chars);
			}

			public override int GetByteCount(string s)
			{
				return This.GetByteCount(s);
			}

			public override int GetCharCount(byte[] bytes, int index, int count)
			{
				return This.GetCharCount(bytes, index, count);
			}

			public override int GetCharCount(byte[] bytes)
			{
				return This.GetCharCount(bytes);
			}

			public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
			{
				return This.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
			}

			public override byte[] GetBytes(char[] chars, int index, int count)
			{
				return This.GetBytes(chars, index, count);
			}

			public override byte[] GetBytes(char[] chars)
			{
				return This.GetBytes(chars);
			}

			public override int GetBytes(string s, int charIndex, int charCount, byte[] bytes, int byteIndex)
			{
				return This.GetBytes(s, charIndex, charCount, bytes, byteIndex);
			}

			public override byte[] GetBytes(string s)
			{
				return This.GetBytes(s);
			}

			public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
			{
				return This.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
			}

			public override char[] GetChars(byte[] bytes, int index, int count)
			{
				return This.GetChars(bytes, index, count);
			}

			public override char[] GetChars(byte[] bytes)
			{
				return This.GetChars(bytes);
			}

			public override string GetString(byte[] bytes, int index, int count)
			{
				return This.GetString(bytes, index, count);
			}

			public override Encoder GetEncoder()
			{
				return This.GetEncoder();
			}

			public override Decoder GetDecoder()
			{
				return This.GetDecoder();
			}
		}
	}

	public static class ObjectUtility
	{
		public static TResult Try<T, TResult>(this T @this, Func<T, TResult> result, TResult @default)
		{
			if (result == null)
				throw new ArgumentNullException("result");

			return @this != null ? result(@this) : @default;
		}

		public static TResult Try<T, TResult>(this T @this, Func<T, TResult> result)
		{
			return @this.Try(result, default(TResult));
		}

		public static T Create<T>(Func<T> constructor, Action<T> initializer)
			where T : IDisposable
		{
			if (constructor == null)
				throw new ArgumentNullException("constructor");
			if (initializer == null)
				throw new ArgumentNullException("initializer");

			T result = constructor();

			try
			{
				initializer(result);
			}
			catch
			{
				if (result != null)
					result.Dispose();

				throw;
			}

			return result;
		}

		public static T Create<T, TParameter>(Func<TParameter, T> constructor, Func<TParameter> parameterConstructor)
			where T : IDisposable
			where TParameter : IDisposable
		{
			if (constructor == null)
				throw new ArgumentNullException("constructor");
			if (parameterConstructor == null)
				throw new ArgumentNullException("parameterConstructor");

			T result;

			TParameter parameter = parameterConstructor();
			try
			{
				result = constructor(parameter);
			}
			catch
			{
				if (parameter != null)
					parameter.Dispose();

				throw;
			}

			return result;
		}
	}

	public static class TraceUtility
	{
		public static void TraceInformation(this object @this, object value, params object[] args)
		{
			Trace.TraceInformation(Format(@this, value, args));
		}

		public static void TraceInformation(this object @this)
		{
			@this.TraceInformation(null);
		}

		public static void TraceInformation(this MemberInfo @this, object value, params object[] args)
		{
			if (@this != null && @this.DeclaringType != null)
				((object) @this.DeclaringType).TraceInformation(Format(@this, value, args));
			else
				((object) @this).TraceInformation(value, args);
		}

		public static void TraceInformation(this MemberInfo @this)
		{
			@this.TraceInformation(null);
		}

		public static void TraceWarning(this object @this, object value, params object[] args)
		{
			Trace.TraceWarning(Format(@this, value, args));
		}

		public static void TraceWarning(this object @this)
		{
			@this.TraceWarning(null);
		}

		public static void TraceWarning(this MemberInfo @this, object value, params object[] args)
		{
			if (@this != null && @this.DeclaringType != null)
				((object) @this.DeclaringType).TraceWarning(Format(@this, value, args));
			else
				((object) @this).TraceWarning(value, args);
		}

		public static void TraceWarning(this MemberInfo @this)
		{
			@this.TraceWarning(null);
		}

		public static void TraceError(this object @this, object value, params object[] args)
		{
			Trace.TraceError(Format(@this, value, args));
		}

		public static void TraceError(this object @this)
		{
			@this.TraceError(null);
		}

		public static void TraceError(this MemberInfo @this, object value, params object[] args)
		{
			if (@this != null && @this.DeclaringType != null)
				((object) @this.DeclaringType).TraceError(Format(@this, value, args));
			else
				((object) @this).TraceError(value, args);
		}

		public static void TraceError(this MemberInfo @this)
		{
			@this.TraceError(null);
		}

		private static string Format(object @this, object value, params object[] args)
		{
			value = value.Try(i => string.Format(CultureInfo.InvariantCulture, string.Format(CultureInfo.InvariantCulture, "{0}", value), args));

			return string.Format(CultureInfo.InvariantCulture, @this != null && value != null ? "{0}: {1}" : "{0}", @this ?? value, value);
		}
	}

	public static class XmlUtility
	{
		public static IDictionary<string, string> ParseAttributes(string attributes)
		{
			IDictionary<string, string> result = null;

			if (!string.IsNullOrEmpty(attributes))
			{
				using (XmlReader reader = ObjectUtility.Create(i => XmlReader.Create(i, new XmlReaderSettings { CloseInput = true }), () => new StringReader(string.Format(CultureInfo.InvariantCulture, "<attributes {0}/>", attributes))))
				{
					reader.Read();

					result = new Dictionary<string, string>(reader.AttributeCount);
					if (reader.MoveToFirstAttribute())
					{
						do
							result.Add(reader.Name, reader.Value);
						while (reader.MoveToNextAttribute());
					}
				}
			}

			return result;
		}

		public static IDictionary<string, string> TryParseAttributes(string attributes)
		{
			IDictionary<string, string> result;

			try
			{
				result = ParseAttributes(attributes);
			}
			catch (XmlException)
			{
				result = null;
			}

			return result;
		}
	}

	public static class XmlReaderUtility
	{
		public static bool TryRead(this XmlReader @this)
		{
			if (@this == null)
				throw new ArgumentNullException("this");

			bool result;

			try
			{
				result = @this.Read();
			}
			catch (XmlException)
			{
				result = false;
			}

			return result;
		}
	}
}
		
Number of comments: 0