Wednesday, February 24, 2010

dynamic span insertion logic

last few days i have been cracking my head around to find a logic to insert spans dynamically and mark intersections in a different color.

most of the text editors i have seen doesn't support this feature, i hope i am the first one !

Problem



I have a text like this

"You are advised to grant access to these settings only if you are sure you want to allow this program to run automatically when your computer starts. Otherwise it is better to deny access."

and i have 3 selections,

1. StartIndex = 8, Length = 16 // "advised to grant"
2. StartIndex = 16, Length = 33 //"to grant access to these settings"
3. StartIndex = 35, Length = 26 // "these settings only if you"

i need to insert span tags and highlight selections, and intersections of words should be highlighted in different color,

result should be something like this


"You are advised to grant advised access to
these settings
only if you sure you want to allow this program to run automatically when your computer starts. Otherwise it is better to deny access."


Solution,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication5
{
public enum SortDirection
{
Ascending, Descending
}

public enum SpanType
{
Intersection, InnerSpan, Undefined
}

class Program
{
static void Main(string[] args)
{
string MessageText = @"You are advised to grant access to these settings only if you are sure you want to allow this program to run automatically when your computer starts. Otherwise it is better to deny access.";

List spanList = new List();
List intersectionSpanList = new List();

spanList.Add(new Span() { SpanID = "span1", Text = "advised to grant", ElementType = SpanType.Undefined, StartIndex = 8, Length = 16 });
spanList.Add(new Span() { SpanID = "span2", Text = "to grant access to these settings", ElementType = SpanType.Undefined, StartIndex = 16, Length = 33 });
spanList.Add(new Span() { SpanID = "span3", Text = "these settings only if you", ElementType = SpanType.Undefined, StartIndex = 35, Length = 26 });

// simple interseciotn
//spanList.Add(new Span() { SpanID = "span1", Text = "advised to grant", ElementType = TagType.Undefined, StartIndex = 8, Length = 16 });
//spanList.Add(new Span() { SpanID = "span2", Text = "to grant access", ElementType = TagType.Undefined, StartIndex = 16, Length = 15 });

// two different spans
//spanList.Add(new Span() { SpanID = "span1", Text = "advised to grant", ElementType = TagType.Undefined, StartIndex = 8, Length = 16 });
//spanList.Add(new Span() { SpanID = "span2", Text = "only if you are ", ElementType = TagType.Undefined, StartIndex = 50, Length = 16 });

// inner span
//spanList.Add(new Span() { SpanID = "span1", Text = "to grant access to these settings", ElementType = TagType.Undefined , StartIndex = 16, Length = 33 });
//spanList.Add(new Span() { SpanID = "span2", Text = "access to these", ElementType = TagType.Undefined, StartIndex = 25, Length = 15 });

// one inner span, and complex
//spanList.Add(new Span() { SpanID = "span1", Text = "to grant access to these settings only ", ElementType = TagType.Undefined, StartIndex = 16, Length = 39 });
//spanList.Add(new Span() { SpanID = "span2", Text = "access to these", ElementType = TagType.Undefined, StartIndex = 25, Length = 15 });
//spanList.Add(new Span() { SpanID = "span3", Text = "only if you are sure", ElementType = TagType.Undefined, StartIndex = 50, Length = 20 });

// one large span, and two intersections
//spanList.Add(new Span() { SpanID = "span1", Text = "grant access to these settings only if you are sure you want to allow this program to run automatically when", ElementType = SpanType.Undefined, StartIndex = 19, Length = 108 });
//spanList.Add(new Span() { SpanID = "span2", Text = "these settings only", ElementType = SpanType.Undefined, StartIndex = 35, Length = 19 });
//spanList.Add(new Span() { SpanID = "span3", Text = "you want to allow this", ElementType = SpanType.Undefined, StartIndex = 71, Length = 22 });

spanList.Sort("StartIndex asc");

foreach (var item in spanList)
item.SplitSpans(ref spanList, ref intersectionSpanList, ref MessageText);


// join intersections with span collection
foreach (var item in intersectionSpanList)
spanList.Add(item);

//remove duplicates
spanList = RemoveDuplicateSpans(spanList);

// sort spans by index ..
spanList.Sort("StartIndex asc"); //desc


foreach (var item in spanList)
{
item.InsertStartSpan(ref spanList, ref MessageText);
}



foreach (var item in spanList)
{
item.InsertEndSpan(ref spanList, ref MessageText);
}

//int count = spanList.Count -1;

//while (count > 0)
//{
// Span currentSpan = spanList[count];
// currentSpan.InsertEndSpan(ref spanList, ref MessageText);
// count--;
//}



}

internal static List RemoveDuplicateSpans(List list)
{
List uniqueList = new List();

for (int i = 0; i < j =" 0;" startindex ="="" endpossition ="="" elementtype ="="">(this List list, string sortExpression)
{
string[] sortExpressions = sortExpression.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

List comparers = new List();

foreach (string sortExpress in sortExpressions)
{
string sortProperty = sortExpress.Trim().Split(' ')[0].Trim();
string sortDirection = sortExpress.Trim().Split(' ')[1].Trim();

Type type = typeof(T);
PropertyInfo PropertyInfo = type.GetProperty(sortProperty);
if (PropertyInfo == null)
{
PropertyInfo[] props = type.GetProperties();
foreach (PropertyInfo info in props)
{
if (info.Name.ToString().ToLower() == sortProperty.ToLower())
{
PropertyInfo = info;
break;
}
}
if (PropertyInfo == null)
{
throw new Exception(String.Format("{0} is not a valid property of type: \"{1}\"", sortProperty, type.Name));
}
}

SortDirection SortDirection = SortDirection.Ascending;
if (sortDirection.ToLower() == "asc" || sortDirection.ToLower() == "ascending")
{
SortDirection = SortDirection.Ascending;
}
else if (sortDirection.ToLower() == "desc" || sortDirection.ToLower() == "descending")
{
SortDirection = SortDirection.Descending;
}
else
{
throw new Exception("Valid SortDirections are: asc, ascending, desc and descending");
}

comparers.Add(new GenericComparer { SortDirection = SortDirection, PropertyInfo = PropertyInfo, comparers = comparers });
}
list.Sort(comparers[0].Compare);
}
}

public class GenericComparer
{
public List comparers { get; set; }
int level = 0;

public SortDirection SortDirection { get; set; }
public PropertyInfo PropertyInfo { get; set; }

public int Compare(T t1, T t2)
{
int ret = 0;

if (level >= comparers.Count)
return 0;

object t1Value = comparers[level].PropertyInfo.GetValue(t1, null);
object t2Value = comparers[level].PropertyInfo.GetValue(t2, null);

if (t1 == null || t1Value == null)
{
if (t2 == null || t2Value == null)
{
ret = 0;
}
else
{
ret = -1;
}
}
else
{
if (t2 == null || t2Value == null)
{
ret = 1;
}
else
{
ret = ((IComparable)t1Value).CompareTo(((IComparable)t2Value));
}
}
if (ret == 0)
{
level += 1;
ret = Compare(t1, t2);
level -= 1;
}
else
{
if (comparers[level].SortDirection == SortDirection.Descending)
{
ret *= -1;
}
}
return ret;
}
}

public class Span
{
string _Color = "#F9DA00";

public const int SPAN_START_LENGTH = 40;
public const int SPAN_END_LENGTH = 7;
public const int SPAN_TOTAL_LENGTH = 47;

public string Color
{
get
{
return _Color;
}
set
{
_Color = value;
}
}

public string SpanID { get; set; }

public int StartIndex { get; set; }

public int HTMLTagEndPossition { get; set; }

public Span ParentSpan { get; set; }

public int Length { get; set; }

public SpanType ElementType { get; set; }

public string Text { get; set; }

public int EndPossition
{
get
{
return StartIndex + Length;
}
}

public string GetStartSpanHtml()
{
return "" + this.Text;
}

public string GetEndSpanHtml()
{
return "
";
}

public bool IsProcessed { get; set; }

internal void PostProcess(Span span, ref List spanList, ref string MessageText)
{
MessageText = MessageText.Remove(span.StartIndex, span.Length);
MessageText = MessageText.Insert(span.StartIndex, span.GetStartSpanHtml());

int offset = Span.SPAN_TOTAL_LENGTH;

AdjustStartOffsetOfSpans(spanList, span, offset);

}

internal void SplitSpans(ref List spanList, ref List intersectionSpanList, ref string MessageText)
{
foreach (var item in spanList)
{
if (this.SpanID == item.SpanID)
continue;

if (this.StartIndex <> item.EndPossition)
{
// inner

int innerSpanLength = this.EndPossition - item.StartIndex;
int innerSpanStartPos = this.StartIndex;
string innerSpanText = MessageText.Substring(item.StartIndex, item.Length);

Span innerSpan = new Span();
innerSpan.SpanID = "innerSpan" + Guid.NewGuid().ToString().Replace("-", "");
innerSpan.ElementType = SpanType.InnerSpan;
innerSpan.Text = innerSpanText;
innerSpan.Length = item.Length;
innerSpan.StartIndex = item.StartIndex;
innerSpan.ParentSpan = this;
intersectionSpanList.Add(innerSpan);

}
if (this.StartIndex <> this.EndPossition && this.EndPossition > item.StartIndex)
{
// end is overlapping

int intersectionLength = this.EndPossition - item.StartIndex;
int intersectionStartPos = item.StartIndex;
string intersectionText = MessageText.Substring(item.StartIndex, intersectionLength);

// Build intersection span
Span intersectonSpan = new Span();
intersectonSpan.SpanID = "intersectonSpan" + Guid.NewGuid().ToString().Replace("-", "");
intersectonSpan.Text = intersectionText;
intersectonSpan.Length = intersectionLength;
intersectonSpan.StartIndex = intersectionStartPos;
intersectonSpan.ElementType = SpanType.Intersection;
intersectionSpanList.Add(intersectonSpan);

// adjust my end pos.
this.Length = this.Length - intersectionLength;
this.Text = this.Text.Substring(0, this.Length);

item.StartIndex += intersectionLength;
item.Length -= intersectionLength;
item.Text = item.Text.Substring(intersectionLength, item.Length);

}
else if (this.StartIndex <> this.EndPossition && this.EndPossition <> spanList, Span span, int SPAN_END_LENGTH)
{
bool adjustAfterThisSpan = false;

foreach (var item in spanList)
{
if (item.SpanID == span.SpanID)
{
adjustAfterThisSpan = true;
continue;
}

if (adjustAfterThisSpan)
{
if (item.ParentSpan == null)
{
item.HTMLTagEndPossition += SPAN_END_LENGTH;

}
else if (span.ParentSpan != null && this.SpanID == item.ParentSpan.SpanID)
{ }
}
}
}

internal static void AdjustStartOffsetOfSpans(List spanList, Span fromSpan, int offset)
{
bool adjustAfterThisSpan = false;

foreach (var item in spanList)
{
if (item.SpanID == fromSpan.SpanID)
{
adjustAfterThisSpan = true;
continue;
}

if (adjustAfterThisSpan)
item.StartIndex += offset;
}
}


internal void InsertStartSpan(ref List spanList, ref string MessageText)
{

MessageText = MessageText.Remove(this.StartIndex, this.Length);
MessageText = MessageText.Insert(this.StartIndex, this.GetStartSpanHtml());


AdjustStartOffsetOfSpans(spanList, this, SPAN_START_LENGTH);

// Adjust end element tag
switch (this.ElementType)
{
case SpanType.Intersection:
{
this.HTMLTagEndPossition = this.Length + SPAN_START_LENGTH + this.StartIndex;
break;
}
case SpanType.InnerSpan:
{
this.HTMLTagEndPossition = this.Length + SPAN_START_LENGTH + this.StartIndex;

// increase the parent's tag offset conent length
this.ParentSpan.HTMLTagEndPossition += SPAN_START_LENGTH;
break;
}
case SpanType.Undefined:
{
this.HTMLTagEndPossition = this.Length + SPAN_START_LENGTH + this.StartIndex;
break;
}
default:
break;
}




}

internal void InsertEndSpan(ref List spanList, ref string MessageText)
{
switch (this.ElementType)
{
case SpanType.Intersection:
{
MessageText = MessageText.Insert(this.HTMLTagEndPossition, this.GetEndSpanHtml());
break;
}
case SpanType.InnerSpan:
{
MessageText = MessageText.Insert(this.HTMLTagEndPossition, this.GetEndSpanHtml());
break;
}
case SpanType.Undefined:
{
MessageText = MessageText.Insert(this.HTMLTagEndPossition, this.GetEndSpanHtml());
break;
}
default:
break;
}

AdjustEndOffsetOfSpans(spanList, this, SPAN_END_LENGTH);


}


}

}

Monday, February 01, 2010

SelectedIndexChanged not firing ?

Today i was trying to figure out why this is happening,

1. AutoPost = false

2. adding the same value Eg (new ListItem(string, value)) repeatedly will also stop firing SelectedIndexChanged