Click here to Skip to main content
15,897,718 members
Articles / Security / Encryption

Enhanced String Handling II

Rate me:
Please Sign up or sign in to vote.
3.76/5 (11 votes)
16 Dec 2010CPOL2 min read 36.4K   213   15  
Overcoming limitations of: Enhanced String Handling
using System;
using System.Linq;
using System.Collections.Generic;


namespace EnhancedStringEvaluate
{
	/// <summary>
	/// Purpose:
	///		Provide a vehicle that will:
	///			1	Transform a single substrings of the format of {identifier::value} 
	///				according to the rules set forth in the appropriate identifier's Process 
	///				class.  In the textual explanation, the article, this Process class
	///				is referred to as ProcessXxx class.  
	///			2	Transform a Dictionary<string, string> of (identifier, value) collection, 
	///				according to the rules set forth by the ProcessXxx classes.
	/// </summary>
	public class EnhancedStringEval
	{
		/// <summary>Prevent infinite looping in the evaluator</summary>
		private const int PassThroughUpperLimit = 1000;

		private const string TEMPKEY = "*** Temporary string element Key that is not likely to clash with another StringElement key !!!";
		private readonly IDelimitersAndSeparator _delim;

		/// <summary>
		/// The evaluation context pointing to the various ProcessXxx.Evaluate() routines.
		/// </summary>
		public event EventHandler<EnhancedStringEventArgs> OnEvaluateContext;

		/// <summary>
		/// .ctor
		/// </summary>
		/// <returns></returns>
		/// <param name="context"></param>
		/// <param name="delim"></param>
		public EnhancedStringEval() : this(null, DelimitersAndSeparator.DefaultDelimitedString) { }
		public EnhancedStringEval(IEnumerable<IProcessEvaluate> context) : this(context, DelimitersAndSeparator.DefaultDelimitedString) { }
		public EnhancedStringEval(IEnumerable<IProcessEvaluate> context, IDelimitersAndSeparator delim)
		{
			_delim = delim;

			if (context != null)
				foreach (IProcessEvaluate processXxx in context)
					if (processXxx != null)
						OnEvaluateContext += processXxx.Evaluate;
		}

		/// <summary>
		/// Purpose:
		///		Evaluate a simple expression.
		/// </summary>
		/// <param name="elem"></param>
		/// <returns></returns>
		protected virtual bool EvalSimpleExpression(EnhancedStrPairElement elem)
		{
			bool rc = _delim.IsSimpleExpression(elem.Value);
			if (!rc) return false;

			var ea = new EnhancedStringEventArgs(new EnhancedStrPairElement(elem.Indetifier, _delim.PreMatch(elem.Value)));
			foreach (Delegate processXxx in OnEvaluateContext.GetInvocationList())
			{
				try { processXxx.DynamicInvoke(new object[] { this, ea }); }
				catch { /* Logging the exception is appropriate here */ }
				if (ea.IsHandled)
				{
					elem.Value = _delim.PostMatch(ea.EhancedPairElem.Value);
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Purpose:
		///		Delimiter balance check.  Each open delimiter should have a close delimiter
		///	
		/// Comment:
		///		Called right before the evaluation ...
		/// </summary>
		private string BalancePreEvaluate(string text)
		{
			bool rc = _delim.IsBalancedOpenClose(text);
			if (!rc)
				throw new EnhancedStringException(null, text, "Delimiters are not balanced.");

			return text;
		}

		/// <summary>
		/// Used as equivalent of the single-string BalancePreEvaluate(..)
		/// 
		/// Comment:
		///		This routine operates insitue (in place--no new memory is needed)
		/// </summary>
		/// <param name="enhStrPairs"></param>
		/// <returns></returns>
		private void BalancePreEvaluate(IDictionary<string, EnhancedStrPairElement> enhStrPairs)
		{
			foreach (var elem in enhStrPairs)
			{
				bool rc = _delim.IsBalancedOpenClose(elem.Value.Value);
				if (!rc)
					throw new EnhancedStringException(elem.Value.Indetifier, elem.Value.Value, "Braces are not balanced.");
			}
		}

		/// <summary>
		/// In order to use it, one needs to override it.
		/// Used to scrub or transform text before it is passed through the Evaluator.
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		protected virtual string PreEvaluate(string text)
		{
			return text;
		}

		/// <summary>
		/// In order to use it, one needs to override it.
		/// Used as an "opposite" operation to PreEvaluate(..)
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		protected virtual string PostEvaluate(string text)
		{
			return text;
		}

		/// <summary>
		/// Equivalent to the single-string PreEvaluate(..) for a list of elements
		/// </summary>
		/// <param name="enhStrPairs"></param>
		/// <returns></returns>
		protected virtual void PreEvaluate(IDictionary<string, EnhancedStrPairElement> enhStrPairs)
		{
			return;
		}

		/// <summary>
		/// The opposite operation to multi-pair PreEvaluate(..)
		/// </summary>
		/// <param name="enhStrPairs"></param>
		/// <returns></returns>
		protected virtual void PostEvaluate(IDictionary<string, EnhancedStrPairElement> enhStrPairs)
		{
			return;
		}

		/// <summary>
		/// Transform a string according to the rules laid out by the
		///		>	Pre / post Evaluate overrides
		///		>	Context
		/// </summary>
		/// <returns></returns>
		public virtual string EvaluateString(string text)
		{
			string preText = PreEvaluate(text);
			string balanceText = BalancePreEvaluate(preText);

			string evalText = EvaluateStringPure(balanceText);
	
			string postText = PostEvaluate(evalText);
			return postText;
		}

		/// <summary>
		/// The magic happens here
		/// 
		/// Comment:
		///		The inner loop resembles the EvalSimpleExpression(..) method, with
		///		one notable exception the check for _delim.IsSimpleExpression(..)
		///		This is done deliberately so that the ProcessLiteral, processing 
		///		expressions that are not a simple expressions, could be processed
		///		as well.
		/// </summary>
		/// <param name="text"></param>
		/// <returns></returns>
		private string EvaluateStringPure(string text)
		{
			if (OnEvaluateContext == null) return text;

			var textElem = new EnhancedStrPairElement(TEMPKEY, _delim.PreMatch(text));
			var ea = new EnhancedStringEventArgs(textElem);

			bool bHandled = false;
			Delegate[] context = OnEvaluateContext.GetInvocationList();
			for (int i = 0; i < PassThroughUpperLimit; ++i)
			{
				bHandled = false;
				foreach (Delegate processXxx in context)
				{
					try { processXxx.DynamicInvoke(new object[] { this, ea }); }
					catch { /* Logging the exception is appropriate here. */ }
					if (ea.IsHandled)
					{
						bHandled = true;
						string val = _delim.PreMatch(ea.EhancedPairElem.Value);
						string preText = PreEvaluate(val);
						string balanceText = BalancePreEvaluate(preText);
						textElem = new EnhancedStrPairElement(TEMPKEY, balanceText);
						ea = new EnhancedStringEventArgs(textElem);
					}
				}

				if (!bHandled) break;
			}

			return _delim.PostMatch(ea.EhancedPairElem.Value);
		}

		/// <summary>
		/// Purpose:
		///		Optimization.
		///	
		/// Comment:
		///		The equivalent to the EvaluateString--evaluating a single string;
		///		EvaluateStrings, evaluates a collection of pairs as a unit
		///			>	As a unit pre-evaluate (user overridden)
		///			>	Check balanced delimiters
		///			>	Run through pure evaluate
		///			>	Post check balanced delimiters
		///			>	Post-evaluate (user overridden)
		/// </summary>
		/// <param name="enhStrPairs"></param>
		/// <returns></returns>
		public virtual void EvaluateStrings(IDictionary<string, EnhancedStrPairElement> enhStrPairs)
		{
			PreEvaluate(enhStrPairs);
			BalancePreEvaluate(enhStrPairs);

			EvaluateStringsPure(enhStrPairs);

			PostEvaluate(enhStrPairs);
		}

		/// <summary>
		/// The magic happens here--for a collection
		/// </summary>
		/// <param name="enhStrPairs"></param>
		private void EvaluateStringsPure(IDictionary<string, EnhancedStrPairElement> enhStrPairs)
		{
			if (OnEvaluateContext == null) return;

			//
			// Retrieve all nodes needing attention, into a linked list.
			// needing attention meanS a node that has a simple expression in it
			//
			var links = new LinkedList<EnhancedStrPairElement>();
			var pairNodes = from elem in enhStrPairs where _delim.IsSimpleExpression(elem.Value.Value) select elem.Value;
			foreach (EnhancedStrPairElement pairNode in pairNodes)
				links.AddLast(pairNode);

			if (links.Count == 0) return;

			// The PassThroughUpperLimit avoids an infinite loop.
			for (int i = 0; i < PassThroughUpperLimit; ++i)
			{
				// Cycle through the linked list
				LinkedListNode<EnhancedStrPairElement> linkNode = links.First;
				while (linkNode != null)
				{
					// The magic of handling the static EnhancedStrPairElement expressions
					// happen in EvalSimpleExpression(..).
					bool bEval = EvalSimpleExpression(linkNode.Value);
					if (!bEval)
					{
						// There is nothing to evaluate any further--Remove the node.
						// Before removing add to return pairs
						LinkedListNode<EnhancedStrPairElement> p = linkNode.Next;
						links.Remove(linkNode);
						linkNode = p;
					}
					else
					{
						PreEvaluate(enhStrPairs);
						BalancePreEvaluate(enhStrPairs);
						linkNode = linkNode.Next;
					}
				}

				if (links.Count == 0) break;
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
avifarah@gmail.com

Comments and Discussions