Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Enhanced String Handling II

, 16 Dec 2010
Overcoming limitations of: Enhanced String Handling
EnhancedStringEvaluate.zip
EnhancedStringEvaluate
bin
Debug
ConfigEvaluate.vshost.exe
ConfigEvaluate.vshost.exe.manifest
EnhancedStringEvaluate.dll
EnhancedStringEvaluate.pdb
ConcordPairEvaluate.csproj.vs10x
EnhancedStringEvaluate.csproj.user
EnhancedStringEvaluate.csproj.vs10x
obj
Debug
Decl
EnhancedStringEvaluate.dll
EnhancedStringEvaluate.pdb
DesignTimeResolveAssemblyReferencesInput.cache
EnhancedStringEvaluate.dll
EnhancedStringEvaluate.pdb
Refactor
ConfigEvaluate.dll
ResolveAssemblyReference.cache
TempPE
Properties
EvaluateTest
bin
Debug
EnhancedStringEvaluate.dll
EnhancedStringEvaluate.pdb
EvaluateSampleTest.dll
EvaluateSampleTest.pdb
EvaluateTest.dll
EvaluateTest.pdb
TestEvaluation.exe
TestEvaluation.pdb
EvaluateSampleTest.csproj.vs10x
EvaluateTest.csproj.vs10x
obj
Debug
Decl
EvaluateSampleTest.dll
EvaluateSampleTest.pdb
DesignTimeResolveAssemblyReferencesInput.cache
EvaluateSampleTest.dll
EvaluateSampleTest.pdb
EvaluateTest.dll
EvaluateTest.pdb
ResolveAssemblyReference.cache
TempPE
Properties
TestEvaluate
bin
Debug
abc.text
EnhancedStringEvaluate.dll
EnhancedStringEvaluate.pdb
TestEvaluate.exe
TestEvaluate.pdb
TestEvaluate.vshost.exe.manifest
TestEvaluation.exe
TestEvaluation.pdb
TestEvaluation.vshost.exe
TestEvaluation.vshost.exe.manifest
TestOnTheFlyConfig.vshost.exe
TestOnTheFlyConfig.vshost.exe.manifest
obj
Debug
Decl
TestEvaluation.exe
TestEvaluation.pdb
DesignTimeResolveAssemblyReferencesInput.cache
Refactor
ResolveAssemblyReference.cache
TempPE
TestEvaluate.exe
TestEvaluate.pdb
TestEvaluation.exe
TestEvaluation.pdb
ProcessEvaluate
Properties
TestEvaluate.csproj.vs10x
TestEvaluation.csproj.user
TestEvaluation.csproj.vs10x
TestOnTheFlyConfig.csproj.vs10x
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)

About the Author

Avi Farah

United States United States
avifarah@gmail.com

| Advertise | Privacy | Mobile
Web01 | 2.8.140709.1 | Last Updated 16 Dec 2010
Article Copyright 2010 by Avi Farah
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid