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 CPOL
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
Enhanced String Handling II.pdf
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using EnhancedStringEvaluate;


namespace TestEvaluation.ProcessEvaluate
{
	/// <summary>
	/// See article explanation possibility1 and possibility2
	/// </summary>
	public enum Possibility { DirectColonKnowledge, Substitution }

	/// <summary>
	/// Purpose:
	///		Counter supports a construct like {Counter::name} (where name may 
	///		be a name like "PageNumber" etc...).  The first time it is encountered 
	///		it returns the value of 0.  Then each subsequent time that it is 
	/// encountered it returns the previous value incremented by 1.  So after 
	/// 0 {Counter::name} will yield 1, then 2 ...
	/// 
	/// There may be multiple counters each one with its own name.
	/// 
	/// Other variations on Counter are:
	///		{Counter::name}
	///		{Counter::name::Init}		// Initialize or re-initialize the counter named "name".  Default action for 1st {Counter::name} evaluation
	///									// equivalent to {counter::name::=0}
	///		{Counter::name::next}		// Default action for {Counter::name} evaluation except for first evaluation, equivalent to {Counter::name::+1}
	///		{Counter::name::previous}	// Takes counter's current value and decrement it by 1, equivalent to {Counter::name::-1}
	///		{Counter::name::=n}			// Initialize the Counter name "name" to  Where n is an integer value (may be negative).
	///		{Counter::name::+n}			// Increment the counter's current value by n.
	///		{Counter::name::-n}			// Decrement the counter's current value by n.
	/// </summary>
	public sealed class ProcessCounter : IProcessEvaluate
	{
		/// <summary>Counter regular expression identifying the Counter based on the format given in the pattern supplied</summary>
		private Regex _reCounter;

		/// <summary></summary>
		private readonly IDelimitersAndSeparator _delim;

		/// <summary>Keep track of all the counters</summary>
		private Dictionary<string, int> _counters;

		/// <summary>Inject Possibility</summary>
		private Possibility _possibilityOption;
		public Possibility PosibilityOption
		{
			get { return _possibilityOption; }
			set
			{
				_possibilityOption = value;
				string pattern;
				RegexOptions reo = RegexOptions.Singleline | RegexOptions.IgnoreCase;
				if (_possibilityOption == Possibility.DirectColonKnowledge)
				{
					pattern = string.Format(
						@"({0})\s*Counter\s*::(?<name>:?[^{0}{1}:]+(:[^{0}{1}:]+)*:?)" +
							@"(::\s*(?<extras>(init)|(next)|(previous)|" +
							@"((?<op>[=+-])\s*(?<direction>[+-])?\s*(?<val>[0-9]+)))?)?\s*({1})",
						_delim.OpenDelimEquivalent, _delim.CloseDelimEquivalent);
				}
				else
				{
					pattern = string.Format(
						@"({0})\s*Counter\s*{2}(?<name>[^{0}{1}{2}]+)" +
						@"({2}\s*(?<extras>(init)|(next)|(previous)|" +
						@"((?<op>[=+-])\s*(?<direction>[+-])?\s*(?<val>[0-9]+)))?)?\s*({1})",
						_delim.OpenDelimEquivalent, _delim.CloseDelimEquivalent, _delim.SeparatorAlternate);
				}
				_reCounter = new Regex(pattern, reo);
			}
		}

		/// <summary>
		/// Action type given.  Action is the third field in the format pattern:
		///		{Counter::Name::Action}
		///	Action may be:
		///		Default: not given, in which it will be set to INIT if first encounter or NEXT if subsequent encounters
		///		INIT: set variable to 0 if the word "init" is given or set to a value if "= n" is given
		///		NEXT: Add one to identifier if the word "Next" is given, or n will be added to the counter if "+ n" is given
		///		PREVIOUS: similar to Next, but in the opposite direction.
		/// </summary>
		private enum Action { Default, Init, Next, Previous }

		public ProcessCounter() : this(DelimitersAndSeparator.DefaultDelimitedString) {}
		public ProcessCounter(IDelimitersAndSeparator delim)
		{
			_delim = delim;
			_counters = new Dictionary<string, int>();
			PosibilityOption = Possibility.DirectColonKnowledge;
		}

		#region IProcessEvaluate Members

		public void Evaluate(object src, EnhancedStringEventArgs ea)
		{
			// Initialize return code as false (this is default value anyway)
			ea.IsHandled = false;

			// Evaluate Task 1:
			string text = ea.EhancedPairElem.Value;
			if (string.IsNullOrWhiteSpace(text)) return;

			if (PosibilityOption == Possibility.DirectColonKnowledge)
			{
				bool rc = _reCounter.IsMatch(text);
				if (!rc) return;

				string replacement = _reCounter.Replace(text, CounterReplace);
				if (replacement == text) return;

				// Evaluate Task 2: Announce that expression was successfully handled
				ea.IsHandled = true;

				// Evaluate Task 3: Keep the new value
				ea.EhancedPairElem.Value = replacement;
				return;
			}
			else
			{
				string preText = text.Replace(_delim.Separator, _delim.SeparatorAlternate);
				bool rc = _reCounter.IsMatch(preText);
				if (!rc) return;

				string replacement = _reCounter.Replace(preText, CounterReplace);
				if (replacement == preText) return;

				// Announce that expression was successfully handled
				ea.IsHandled = true;

				// Keep new value
				string postText = replacement.Replace(_delim.SeparatorAlternate, _delim.Separator);
				ea.EhancedPairElem.Value = postText;
				return;
			}
		}

		#endregion

		/// <summary>
		/// We get here only if a match on _reCounter is true
		/// </summary>
		/// <param name="m"></param>
		/// <returns></returns>
		private string CounterReplace(Match m)
		{
			string name = m.Groups["name"].Value;
			if (string.IsNullOrEmpty(name))
				return m.ToString();

			Action action = Action.Default;
			int nExtraBy = 0;					// Either initialized to or Move by...
			string extras = m.Groups["extras"].Value;
			if (string.IsNullOrWhiteSpace(extras))
				action = Action.Default;
			else
			{
				// @"({0})\s*Counter\s*::(?<Name>[^{0}{1}:]+:?)
				//	(::\s*(?<extras>(init)|(next)|(previous)|((?<op>[=+-])\s*(?<direction>[+-])?\s*(?<val>[0-9]+)))?)?\s*(})"
				if (string.Compare(extras, "Init", true) == 0)
				{
					action = Action.Init;
					nExtraBy = 0;
				}
				else if (string.Compare(extras, "Next", true) == 0)
				{
					action = Action.Next;
					nExtraBy = 1;
				}
				else if (string.Compare(extras, "Previous", true) == 0)
				{
					action = Action.Previous;
					nExtraBy = 1;
				}
				else
				{
					string op = m.Groups["op"].Value;
					nExtraBy = int.Parse(m.Groups["val"].Value);
					string direction = m.Groups["direction"].Value;
					if (!string.IsNullOrWhiteSpace(direction))
						if (direction == "-") nExtraBy = -nExtraBy;
					switch (op)
					{
						case "=": action = Action.Init; break;
						case "+": action = Action.Next; break;
						case "-": action = Action.Previous; break;
						default: throw new Exception("This is impossible");
					}
				}
			}

			if (!_counters.ContainsKey(name))
			{
				if (action == Action.Default || action == Action.Init)
				{
					_counters.Add(name, nExtraBy);
					return _counters[name].ToString();
				}

				throw new Exception(string.Format("Counter is ill formatted.  {0}", m.ToString()));
			}

			switch (action)
			{
				case Action.Default: ++_counters[name]; break;
				case Action.Init: _counters[name] = nExtraBy; break;
				case Action.Next: _counters[name] += nExtraBy; break;
				case Action.Previous: _counters[name] -= nExtraBy; break;
				default: throw new Exception(string.Format("Counter is ill formatted.  {0}", m.ToString()));
			}

			return _counters[name].ToString();
		}
	}
}

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)

Share

About the Author

Avi Farah

United States United States
avifarah@gmail.com

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